1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi> 3*7c478bd9Sstevel@tonic-gate * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4*7c478bd9Sstevel@tonic-gate * All rights reserved 5*7c478bd9Sstevel@tonic-gate * Server main loop for handling the interactive session. 6*7c478bd9Sstevel@tonic-gate * 7*7c478bd9Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software 8*7c478bd9Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this 9*7c478bd9Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is 10*7c478bd9Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be 11*7c478bd9Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell". 12*7c478bd9Sstevel@tonic-gate * 13*7c478bd9Sstevel@tonic-gate * SSH2 support by Markus Friedl. 14*7c478bd9Sstevel@tonic-gate * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 15*7c478bd9Sstevel@tonic-gate * 16*7c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 17*7c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 18*7c478bd9Sstevel@tonic-gate * are met: 19*7c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 20*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 21*7c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 22*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 23*7c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 24*7c478bd9Sstevel@tonic-gate * 25*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26*7c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27*7c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28*7c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 29*7c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30*7c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31*7c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32*7c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33*7c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35*7c478bd9Sstevel@tonic-gate */ 36*7c478bd9Sstevel@tonic-gate /* 37*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 38*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 39*7c478bd9Sstevel@tonic-gate */ 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate #include "includes.h" 42*7c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: serverloop.c,v 1.104 2002/09/19 16:03:15 stevesk Exp $"); 43*7c478bd9Sstevel@tonic-gate 44*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 45*7c478bd9Sstevel@tonic-gate 46*7c478bd9Sstevel@tonic-gate #include "xmalloc.h" 47*7c478bd9Sstevel@tonic-gate #include "packet.h" 48*7c478bd9Sstevel@tonic-gate #include "buffer.h" 49*7c478bd9Sstevel@tonic-gate #include "log.h" 50*7c478bd9Sstevel@tonic-gate #include "servconf.h" 51*7c478bd9Sstevel@tonic-gate #include "canohost.h" 52*7c478bd9Sstevel@tonic-gate #include "sshpty.h" 53*7c478bd9Sstevel@tonic-gate #include "channels.h" 54*7c478bd9Sstevel@tonic-gate #include "compat.h" 55*7c478bd9Sstevel@tonic-gate #include "ssh1.h" 56*7c478bd9Sstevel@tonic-gate #include "ssh2.h" 57*7c478bd9Sstevel@tonic-gate #include "auth.h" 58*7c478bd9Sstevel@tonic-gate #include "session.h" 59*7c478bd9Sstevel@tonic-gate #include "dispatch.h" 60*7c478bd9Sstevel@tonic-gate #include "auth-options.h" 61*7c478bd9Sstevel@tonic-gate #include "serverloop.h" 62*7c478bd9Sstevel@tonic-gate #include "misc.h" 63*7c478bd9Sstevel@tonic-gate #include "kex.h" 64*7c478bd9Sstevel@tonic-gate 65*7c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 66*7c478bd9Sstevel@tonic-gate #include "altprivsep.h" 67*7c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP*/ 68*7c478bd9Sstevel@tonic-gate 69*7c478bd9Sstevel@tonic-gate extern ServerOptions options; 70*7c478bd9Sstevel@tonic-gate 71*7c478bd9Sstevel@tonic-gate /* XXX */ 72*7c478bd9Sstevel@tonic-gate extern Kex *xxx_kex; 73*7c478bd9Sstevel@tonic-gate static Authctxt *xxx_authctxt; 74*7c478bd9Sstevel@tonic-gate 75*7c478bd9Sstevel@tonic-gate static Buffer stdin_buffer; /* Buffer for stdin data. */ 76*7c478bd9Sstevel@tonic-gate static Buffer stdout_buffer; /* Buffer for stdout data. */ 77*7c478bd9Sstevel@tonic-gate static Buffer stderr_buffer; /* Buffer for stderr data. */ 78*7c478bd9Sstevel@tonic-gate static int fdin; /* Descriptor for stdin (for writing) */ 79*7c478bd9Sstevel@tonic-gate static int fdout; /* Descriptor for stdout (for reading); 80*7c478bd9Sstevel@tonic-gate May be same number as fdin. */ 81*7c478bd9Sstevel@tonic-gate static int fderr; /* Descriptor for stderr. May be -1. */ 82*7c478bd9Sstevel@tonic-gate static long stdin_bytes = 0; /* Number of bytes written to stdin. */ 83*7c478bd9Sstevel@tonic-gate static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */ 84*7c478bd9Sstevel@tonic-gate static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */ 85*7c478bd9Sstevel@tonic-gate static long fdout_bytes = 0; /* Number of stdout bytes read from program. */ 86*7c478bd9Sstevel@tonic-gate static int stdin_eof = 0; /* EOF message received from client. */ 87*7c478bd9Sstevel@tonic-gate static int fdout_eof = 0; /* EOF encountered reading from fdout. */ 88*7c478bd9Sstevel@tonic-gate static int fderr_eof = 0; /* EOF encountered readung from fderr. */ 89*7c478bd9Sstevel@tonic-gate static int fdin_is_tty = 0; /* fdin points to a tty. */ 90*7c478bd9Sstevel@tonic-gate static int connection_in; /* Connection to client (input). */ 91*7c478bd9Sstevel@tonic-gate static int connection_out; /* Connection to client (output). */ 92*7c478bd9Sstevel@tonic-gate static int connection_closed = 0; /* Connection to client closed. */ 93*7c478bd9Sstevel@tonic-gate static u_int buffer_high; /* "Soft" max buffer size. */ 94*7c478bd9Sstevel@tonic-gate static int client_alive_timeouts = 0; 95*7c478bd9Sstevel@tonic-gate 96*7c478bd9Sstevel@tonic-gate /* 97*7c478bd9Sstevel@tonic-gate * This SIGCHLD kludge is used to detect when the child exits. The server 98*7c478bd9Sstevel@tonic-gate * will exit after that, as soon as forwarded connections have terminated. 99*7c478bd9Sstevel@tonic-gate */ 100*7c478bd9Sstevel@tonic-gate 101*7c478bd9Sstevel@tonic-gate static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate /* prototypes */ 104*7c478bd9Sstevel@tonic-gate static void server_init_dispatch(void); 105*7c478bd9Sstevel@tonic-gate 106*7c478bd9Sstevel@tonic-gate /* 107*7c478bd9Sstevel@tonic-gate * we write to this pipe if a SIGCHLD is caught in order to avoid 108*7c478bd9Sstevel@tonic-gate * the race between select() and child_terminated 109*7c478bd9Sstevel@tonic-gate */ 110*7c478bd9Sstevel@tonic-gate static int notify_pipe[2]; 111*7c478bd9Sstevel@tonic-gate static void 112*7c478bd9Sstevel@tonic-gate notify_setup(void) 113*7c478bd9Sstevel@tonic-gate { 114*7c478bd9Sstevel@tonic-gate if (pipe(notify_pipe) < 0) { 115*7c478bd9Sstevel@tonic-gate error("pipe(notify_pipe) failed %s", strerror(errno)); 116*7c478bd9Sstevel@tonic-gate } else if ((fcntl(notify_pipe[0], F_SETFD, 1) == -1) || 117*7c478bd9Sstevel@tonic-gate (fcntl(notify_pipe[1], F_SETFD, 1) == -1)) { 118*7c478bd9Sstevel@tonic-gate error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); 119*7c478bd9Sstevel@tonic-gate (void) close(notify_pipe[0]); 120*7c478bd9Sstevel@tonic-gate (void) close(notify_pipe[1]); 121*7c478bd9Sstevel@tonic-gate } else { 122*7c478bd9Sstevel@tonic-gate set_nonblock(notify_pipe[0]); 123*7c478bd9Sstevel@tonic-gate set_nonblock(notify_pipe[1]); 124*7c478bd9Sstevel@tonic-gate return; 125*7c478bd9Sstevel@tonic-gate } 126*7c478bd9Sstevel@tonic-gate notify_pipe[0] = -1; /* read end */ 127*7c478bd9Sstevel@tonic-gate notify_pipe[1] = -1; /* write end */ 128*7c478bd9Sstevel@tonic-gate } 129*7c478bd9Sstevel@tonic-gate static void 130*7c478bd9Sstevel@tonic-gate notify_parent(void) 131*7c478bd9Sstevel@tonic-gate { 132*7c478bd9Sstevel@tonic-gate if (notify_pipe[1] != -1) 133*7c478bd9Sstevel@tonic-gate (void) write(notify_pipe[1], "", 1); 134*7c478bd9Sstevel@tonic-gate } 135*7c478bd9Sstevel@tonic-gate static void 136*7c478bd9Sstevel@tonic-gate notify_prepare(fd_set *readset) 137*7c478bd9Sstevel@tonic-gate { 138*7c478bd9Sstevel@tonic-gate if (notify_pipe[0] != -1) 139*7c478bd9Sstevel@tonic-gate FD_SET(notify_pipe[0], readset); 140*7c478bd9Sstevel@tonic-gate } 141*7c478bd9Sstevel@tonic-gate static void 142*7c478bd9Sstevel@tonic-gate notify_done(fd_set *readset) 143*7c478bd9Sstevel@tonic-gate { 144*7c478bd9Sstevel@tonic-gate char c; 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) 147*7c478bd9Sstevel@tonic-gate while (read(notify_pipe[0], &c, 1) != -1) 148*7c478bd9Sstevel@tonic-gate debug2("notify_done: reading"); 149*7c478bd9Sstevel@tonic-gate } 150*7c478bd9Sstevel@tonic-gate 151*7c478bd9Sstevel@tonic-gate static void 152*7c478bd9Sstevel@tonic-gate sigchld_handler(int sig) 153*7c478bd9Sstevel@tonic-gate { 154*7c478bd9Sstevel@tonic-gate int save_errno = errno; 155*7c478bd9Sstevel@tonic-gate debug("Received SIGCHLD."); 156*7c478bd9Sstevel@tonic-gate child_terminated = 1; 157*7c478bd9Sstevel@tonic-gate #ifndef _UNICOS 158*7c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler); 159*7c478bd9Sstevel@tonic-gate #endif 160*7c478bd9Sstevel@tonic-gate notify_parent(); 161*7c478bd9Sstevel@tonic-gate errno = save_errno; 162*7c478bd9Sstevel@tonic-gate } 163*7c478bd9Sstevel@tonic-gate 164*7c478bd9Sstevel@tonic-gate /* 165*7c478bd9Sstevel@tonic-gate * Make packets from buffered stderr data, and buffer it for sending 166*7c478bd9Sstevel@tonic-gate * to the client. 167*7c478bd9Sstevel@tonic-gate */ 168*7c478bd9Sstevel@tonic-gate static void 169*7c478bd9Sstevel@tonic-gate make_packets_from_stderr_data(void) 170*7c478bd9Sstevel@tonic-gate { 171*7c478bd9Sstevel@tonic-gate int len; 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate /* Send buffered stderr data to the client. */ 174*7c478bd9Sstevel@tonic-gate while (buffer_len(&stderr_buffer) > 0 && 175*7c478bd9Sstevel@tonic-gate packet_not_very_much_data_to_write()) { 176*7c478bd9Sstevel@tonic-gate len = buffer_len(&stderr_buffer); 177*7c478bd9Sstevel@tonic-gate if (packet_is_interactive()) { 178*7c478bd9Sstevel@tonic-gate if (len > 512) 179*7c478bd9Sstevel@tonic-gate len = 512; 180*7c478bd9Sstevel@tonic-gate } else { 181*7c478bd9Sstevel@tonic-gate /* Keep the packets at reasonable size. */ 182*7c478bd9Sstevel@tonic-gate if (len > packet_get_maxsize()) 183*7c478bd9Sstevel@tonic-gate len = packet_get_maxsize(); 184*7c478bd9Sstevel@tonic-gate } 185*7c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDERR_DATA); 186*7c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stderr_buffer), len); 187*7c478bd9Sstevel@tonic-gate packet_send(); 188*7c478bd9Sstevel@tonic-gate buffer_consume(&stderr_buffer, len); 189*7c478bd9Sstevel@tonic-gate stderr_bytes += len; 190*7c478bd9Sstevel@tonic-gate } 191*7c478bd9Sstevel@tonic-gate } 192*7c478bd9Sstevel@tonic-gate 193*7c478bd9Sstevel@tonic-gate /* 194*7c478bd9Sstevel@tonic-gate * Make packets from buffered stdout data, and buffer it for sending to the 195*7c478bd9Sstevel@tonic-gate * client. 196*7c478bd9Sstevel@tonic-gate */ 197*7c478bd9Sstevel@tonic-gate static void 198*7c478bd9Sstevel@tonic-gate make_packets_from_stdout_data(void) 199*7c478bd9Sstevel@tonic-gate { 200*7c478bd9Sstevel@tonic-gate int len; 201*7c478bd9Sstevel@tonic-gate 202*7c478bd9Sstevel@tonic-gate /* Send buffered stdout data to the client. */ 203*7c478bd9Sstevel@tonic-gate while (buffer_len(&stdout_buffer) > 0 && 204*7c478bd9Sstevel@tonic-gate packet_not_very_much_data_to_write()) { 205*7c478bd9Sstevel@tonic-gate len = buffer_len(&stdout_buffer); 206*7c478bd9Sstevel@tonic-gate if (packet_is_interactive()) { 207*7c478bd9Sstevel@tonic-gate if (len > 512) 208*7c478bd9Sstevel@tonic-gate len = 512; 209*7c478bd9Sstevel@tonic-gate } else { 210*7c478bd9Sstevel@tonic-gate /* Keep the packets at reasonable size. */ 211*7c478bd9Sstevel@tonic-gate if (len > packet_get_maxsize()) 212*7c478bd9Sstevel@tonic-gate len = packet_get_maxsize(); 213*7c478bd9Sstevel@tonic-gate } 214*7c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDOUT_DATA); 215*7c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stdout_buffer), len); 216*7c478bd9Sstevel@tonic-gate packet_send(); 217*7c478bd9Sstevel@tonic-gate buffer_consume(&stdout_buffer, len); 218*7c478bd9Sstevel@tonic-gate stdout_bytes += len; 219*7c478bd9Sstevel@tonic-gate } 220*7c478bd9Sstevel@tonic-gate } 221*7c478bd9Sstevel@tonic-gate 222*7c478bd9Sstevel@tonic-gate static void 223*7c478bd9Sstevel@tonic-gate client_alive_check(void) 224*7c478bd9Sstevel@tonic-gate { 225*7c478bd9Sstevel@tonic-gate static int had_channel = 0; 226*7c478bd9Sstevel@tonic-gate int id; 227*7c478bd9Sstevel@tonic-gate 228*7c478bd9Sstevel@tonic-gate id = channel_find_open(); 229*7c478bd9Sstevel@tonic-gate if (id == -1) { 230*7c478bd9Sstevel@tonic-gate if (!had_channel) 231*7c478bd9Sstevel@tonic-gate return; 232*7c478bd9Sstevel@tonic-gate packet_disconnect("No open channels after timeout!"); 233*7c478bd9Sstevel@tonic-gate } 234*7c478bd9Sstevel@tonic-gate had_channel = 1; 235*7c478bd9Sstevel@tonic-gate 236*7c478bd9Sstevel@tonic-gate /* timeout, check to see how many we have had */ 237*7c478bd9Sstevel@tonic-gate if (++client_alive_timeouts > options.client_alive_count_max) 238*7c478bd9Sstevel@tonic-gate packet_disconnect("Timeout, your session not responding."); 239*7c478bd9Sstevel@tonic-gate 240*7c478bd9Sstevel@tonic-gate /* 241*7c478bd9Sstevel@tonic-gate * send a bogus channel request with "wantreply", 242*7c478bd9Sstevel@tonic-gate * we should get back a failure 243*7c478bd9Sstevel@tonic-gate */ 244*7c478bd9Sstevel@tonic-gate channel_request_start(id, "keepalive@openssh.com", 1); 245*7c478bd9Sstevel@tonic-gate packet_send(); 246*7c478bd9Sstevel@tonic-gate } 247*7c478bd9Sstevel@tonic-gate 248*7c478bd9Sstevel@tonic-gate /* 249*7c478bd9Sstevel@tonic-gate * Sleep in select() until we can do something. This will initialize the 250*7c478bd9Sstevel@tonic-gate * select masks. Upon return, the masks will indicate which descriptors 251*7c478bd9Sstevel@tonic-gate * have data or can accept data. Optionally, a maximum time can be specified 252*7c478bd9Sstevel@tonic-gate * for the duration of the wait (0 = infinite). 253*7c478bd9Sstevel@tonic-gate */ 254*7c478bd9Sstevel@tonic-gate static void 255*7c478bd9Sstevel@tonic-gate wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 256*7c478bd9Sstevel@tonic-gate int *nallocp, u_int max_time_milliseconds) 257*7c478bd9Sstevel@tonic-gate { 258*7c478bd9Sstevel@tonic-gate struct timeval tv, *tvp; 259*7c478bd9Sstevel@tonic-gate int ret; 260*7c478bd9Sstevel@tonic-gate int client_alive_scheduled = 0; 261*7c478bd9Sstevel@tonic-gate 262*7c478bd9Sstevel@tonic-gate /* 263*7c478bd9Sstevel@tonic-gate * if using client_alive, set the max timeout accordingly, 264*7c478bd9Sstevel@tonic-gate * and indicate that this particular timeout was for client 265*7c478bd9Sstevel@tonic-gate * alive by setting the client_alive_scheduled flag. 266*7c478bd9Sstevel@tonic-gate * 267*7c478bd9Sstevel@tonic-gate * this could be randomized somewhat to make traffic 268*7c478bd9Sstevel@tonic-gate * analysis more difficult, but we're not doing it yet. 269*7c478bd9Sstevel@tonic-gate */ 270*7c478bd9Sstevel@tonic-gate if (compat20 && 271*7c478bd9Sstevel@tonic-gate max_time_milliseconds == 0 && options.client_alive_interval) { 272*7c478bd9Sstevel@tonic-gate client_alive_scheduled = 1; 273*7c478bd9Sstevel@tonic-gate max_time_milliseconds = options.client_alive_interval * 1000; 274*7c478bd9Sstevel@tonic-gate } 275*7c478bd9Sstevel@tonic-gate 276*7c478bd9Sstevel@tonic-gate /* Allocate and update select() masks for channel descriptors. */ 277*7c478bd9Sstevel@tonic-gate channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0); 278*7c478bd9Sstevel@tonic-gate 279*7c478bd9Sstevel@tonic-gate if (compat20) { 280*7c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 281*7c478bd9Sstevel@tonic-gate int pipe_fd; 282*7c478bd9Sstevel@tonic-gate 283*7c478bd9Sstevel@tonic-gate if ((pipe_fd = altprivsep_get_pipe_fd()) != -1) { 284*7c478bd9Sstevel@tonic-gate *maxfdp = MAX(*maxfdp, pipe_fd); 285*7c478bd9Sstevel@tonic-gate FD_SET(altprivsep_get_pipe_fd(), *readsetp); 286*7c478bd9Sstevel@tonic-gate } 287*7c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 288*7c478bd9Sstevel@tonic-gate #if 0 289*7c478bd9Sstevel@tonic-gate /* wrong: bad condition XXX */ 290*7c478bd9Sstevel@tonic-gate if (channel_not_very_much_buffered_data()) 291*7c478bd9Sstevel@tonic-gate #endif 292*7c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp); 293*7c478bd9Sstevel@tonic-gate } else { 294*7c478bd9Sstevel@tonic-gate /* 295*7c478bd9Sstevel@tonic-gate * Read packets from the client unless we have too much 296*7c478bd9Sstevel@tonic-gate * buffered stdin or channel data. 297*7c478bd9Sstevel@tonic-gate */ 298*7c478bd9Sstevel@tonic-gate if (buffer_len(&stdin_buffer) < buffer_high && 299*7c478bd9Sstevel@tonic-gate channel_not_very_much_buffered_data()) 300*7c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp); 301*7c478bd9Sstevel@tonic-gate /* 302*7c478bd9Sstevel@tonic-gate * If there is not too much data already buffered going to 303*7c478bd9Sstevel@tonic-gate * the client, try to get some more data from the program. 304*7c478bd9Sstevel@tonic-gate */ 305*7c478bd9Sstevel@tonic-gate if (packet_not_very_much_data_to_write()) { 306*7c478bd9Sstevel@tonic-gate if (!fdout_eof) 307*7c478bd9Sstevel@tonic-gate FD_SET(fdout, *readsetp); 308*7c478bd9Sstevel@tonic-gate if (!fderr_eof) 309*7c478bd9Sstevel@tonic-gate FD_SET(fderr, *readsetp); 310*7c478bd9Sstevel@tonic-gate } 311*7c478bd9Sstevel@tonic-gate /* 312*7c478bd9Sstevel@tonic-gate * If we have buffered data, try to write some of that data 313*7c478bd9Sstevel@tonic-gate * to the program. 314*7c478bd9Sstevel@tonic-gate */ 315*7c478bd9Sstevel@tonic-gate if (fdin != -1 && buffer_len(&stdin_buffer) > 0) 316*7c478bd9Sstevel@tonic-gate FD_SET(fdin, *writesetp); 317*7c478bd9Sstevel@tonic-gate } 318*7c478bd9Sstevel@tonic-gate notify_prepare(*readsetp); 319*7c478bd9Sstevel@tonic-gate 320*7c478bd9Sstevel@tonic-gate /* 321*7c478bd9Sstevel@tonic-gate * If we have buffered packet data going to the client, mark that 322*7c478bd9Sstevel@tonic-gate * descriptor. 323*7c478bd9Sstevel@tonic-gate */ 324*7c478bd9Sstevel@tonic-gate if (packet_have_data_to_write()) 325*7c478bd9Sstevel@tonic-gate FD_SET(connection_out, *writesetp); 326*7c478bd9Sstevel@tonic-gate 327*7c478bd9Sstevel@tonic-gate /* 328*7c478bd9Sstevel@tonic-gate * If child has terminated and there is enough buffer space to read 329*7c478bd9Sstevel@tonic-gate * from it, then read as much as is available and exit. 330*7c478bd9Sstevel@tonic-gate */ 331*7c478bd9Sstevel@tonic-gate if (child_terminated && packet_not_very_much_data_to_write()) 332*7c478bd9Sstevel@tonic-gate if (max_time_milliseconds == 0 || client_alive_scheduled) 333*7c478bd9Sstevel@tonic-gate max_time_milliseconds = 100; 334*7c478bd9Sstevel@tonic-gate 335*7c478bd9Sstevel@tonic-gate if (max_time_milliseconds == 0) 336*7c478bd9Sstevel@tonic-gate tvp = NULL; 337*7c478bd9Sstevel@tonic-gate else { 338*7c478bd9Sstevel@tonic-gate tv.tv_sec = max_time_milliseconds / 1000; 339*7c478bd9Sstevel@tonic-gate tv.tv_usec = 1000 * (max_time_milliseconds % 1000); 340*7c478bd9Sstevel@tonic-gate tvp = &tv; 341*7c478bd9Sstevel@tonic-gate } 342*7c478bd9Sstevel@tonic-gate 343*7c478bd9Sstevel@tonic-gate /* Wait for something to happen, or the timeout to expire. */ 344*7c478bd9Sstevel@tonic-gate ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 345*7c478bd9Sstevel@tonic-gate 346*7c478bd9Sstevel@tonic-gate if (ret == -1) { 347*7c478bd9Sstevel@tonic-gate memset(*readsetp, 0, *nallocp); 348*7c478bd9Sstevel@tonic-gate memset(*writesetp, 0, *nallocp); 349*7c478bd9Sstevel@tonic-gate if (errno != EINTR) 350*7c478bd9Sstevel@tonic-gate error("select: %.100s", strerror(errno)); 351*7c478bd9Sstevel@tonic-gate } else if (ret == 0 && client_alive_scheduled) 352*7c478bd9Sstevel@tonic-gate client_alive_check(); 353*7c478bd9Sstevel@tonic-gate 354*7c478bd9Sstevel@tonic-gate notify_done(*readsetp); 355*7c478bd9Sstevel@tonic-gate } 356*7c478bd9Sstevel@tonic-gate 357*7c478bd9Sstevel@tonic-gate /* 358*7c478bd9Sstevel@tonic-gate * Processes input from the client and the program. Input data is stored 359*7c478bd9Sstevel@tonic-gate * in buffers and processed later. 360*7c478bd9Sstevel@tonic-gate */ 361*7c478bd9Sstevel@tonic-gate static void 362*7c478bd9Sstevel@tonic-gate process_input(fd_set * readset) 363*7c478bd9Sstevel@tonic-gate { 364*7c478bd9Sstevel@tonic-gate int len; 365*7c478bd9Sstevel@tonic-gate char buf[16384]; 366*7c478bd9Sstevel@tonic-gate 367*7c478bd9Sstevel@tonic-gate /* Read and buffer any input data from the client. */ 368*7c478bd9Sstevel@tonic-gate if (FD_ISSET(connection_in, readset)) { 369*7c478bd9Sstevel@tonic-gate len = read(connection_in, buf, sizeof(buf)); 370*7c478bd9Sstevel@tonic-gate if (len == 0) { 371*7c478bd9Sstevel@tonic-gate verbose("Connection closed by %.100s", 372*7c478bd9Sstevel@tonic-gate get_remote_ipaddr()); 373*7c478bd9Sstevel@tonic-gate connection_closed = 1; 374*7c478bd9Sstevel@tonic-gate if (compat20) 375*7c478bd9Sstevel@tonic-gate return; 376*7c478bd9Sstevel@tonic-gate fatal_cleanup(); 377*7c478bd9Sstevel@tonic-gate } else if (len < 0) { 378*7c478bd9Sstevel@tonic-gate if (errno != EINTR && errno != EAGAIN) { 379*7c478bd9Sstevel@tonic-gate verbose("Read error from remote host " 380*7c478bd9Sstevel@tonic-gate "%.100s: %.100s", 381*7c478bd9Sstevel@tonic-gate get_remote_ipaddr(), strerror(errno)); 382*7c478bd9Sstevel@tonic-gate fatal_cleanup(); 383*7c478bd9Sstevel@tonic-gate } 384*7c478bd9Sstevel@tonic-gate } else { 385*7c478bd9Sstevel@tonic-gate /* Buffer any received data. */ 386*7c478bd9Sstevel@tonic-gate packet_process_incoming(buf, len); 387*7c478bd9Sstevel@tonic-gate } 388*7c478bd9Sstevel@tonic-gate } 389*7c478bd9Sstevel@tonic-gate if (compat20) 390*7c478bd9Sstevel@tonic-gate return; 391*7c478bd9Sstevel@tonic-gate 392*7c478bd9Sstevel@tonic-gate /* Read and buffer any available stdout data from the program. */ 393*7c478bd9Sstevel@tonic-gate if (!fdout_eof && FD_ISSET(fdout, readset)) { 394*7c478bd9Sstevel@tonic-gate len = read(fdout, buf, sizeof(buf)); 395*7c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) { 396*7c478bd9Sstevel@tonic-gate /* EMPTY */ 397*7c478bd9Sstevel@tonic-gate } else if (len <= 0) { 398*7c478bd9Sstevel@tonic-gate fdout_eof = 1; 399*7c478bd9Sstevel@tonic-gate } else { 400*7c478bd9Sstevel@tonic-gate buffer_append(&stdout_buffer, buf, len); 401*7c478bd9Sstevel@tonic-gate fdout_bytes += len; 402*7c478bd9Sstevel@tonic-gate } 403*7c478bd9Sstevel@tonic-gate } 404*7c478bd9Sstevel@tonic-gate /* Read and buffer any available stderr data from the program. */ 405*7c478bd9Sstevel@tonic-gate if (!fderr_eof && FD_ISSET(fderr, readset)) { 406*7c478bd9Sstevel@tonic-gate len = read(fderr, buf, sizeof(buf)); 407*7c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) { 408*7c478bd9Sstevel@tonic-gate /* EMPTY */ 409*7c478bd9Sstevel@tonic-gate } else if (len <= 0) { 410*7c478bd9Sstevel@tonic-gate fderr_eof = 1; 411*7c478bd9Sstevel@tonic-gate } else { 412*7c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, buf, len); 413*7c478bd9Sstevel@tonic-gate } 414*7c478bd9Sstevel@tonic-gate } 415*7c478bd9Sstevel@tonic-gate } 416*7c478bd9Sstevel@tonic-gate 417*7c478bd9Sstevel@tonic-gate /* 418*7c478bd9Sstevel@tonic-gate * Sends data from internal buffers to client program stdin. 419*7c478bd9Sstevel@tonic-gate */ 420*7c478bd9Sstevel@tonic-gate static void 421*7c478bd9Sstevel@tonic-gate process_output(fd_set * writeset) 422*7c478bd9Sstevel@tonic-gate { 423*7c478bd9Sstevel@tonic-gate struct termios tio; 424*7c478bd9Sstevel@tonic-gate u_char *data; 425*7c478bd9Sstevel@tonic-gate u_int dlen; 426*7c478bd9Sstevel@tonic-gate int len; 427*7c478bd9Sstevel@tonic-gate 428*7c478bd9Sstevel@tonic-gate /* Write buffered data to program stdin. */ 429*7c478bd9Sstevel@tonic-gate if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { 430*7c478bd9Sstevel@tonic-gate data = buffer_ptr(&stdin_buffer); 431*7c478bd9Sstevel@tonic-gate dlen = buffer_len(&stdin_buffer); 432*7c478bd9Sstevel@tonic-gate len = write(fdin, data, dlen); 433*7c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) { 434*7c478bd9Sstevel@tonic-gate /* EMPTY */ 435*7c478bd9Sstevel@tonic-gate } else if (len <= 0) { 436*7c478bd9Sstevel@tonic-gate if (fdin != fdout) 437*7c478bd9Sstevel@tonic-gate (void) close(fdin); 438*7c478bd9Sstevel@tonic-gate else 439*7c478bd9Sstevel@tonic-gate (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */ 440*7c478bd9Sstevel@tonic-gate fdin = -1; 441*7c478bd9Sstevel@tonic-gate } else { 442*7c478bd9Sstevel@tonic-gate /* Successful write. */ 443*7c478bd9Sstevel@tonic-gate if (fdin_is_tty && dlen >= 1 && data[0] != '\r' && 444*7c478bd9Sstevel@tonic-gate tcgetattr(fdin, &tio) == 0 && 445*7c478bd9Sstevel@tonic-gate !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { 446*7c478bd9Sstevel@tonic-gate /* 447*7c478bd9Sstevel@tonic-gate * Simulate echo to reduce the impact of 448*7c478bd9Sstevel@tonic-gate * traffic analysis 449*7c478bd9Sstevel@tonic-gate */ 450*7c478bd9Sstevel@tonic-gate packet_send_ignore(len); 451*7c478bd9Sstevel@tonic-gate packet_send(); 452*7c478bd9Sstevel@tonic-gate } 453*7c478bd9Sstevel@tonic-gate /* Consume the data from the buffer. */ 454*7c478bd9Sstevel@tonic-gate buffer_consume(&stdin_buffer, len); 455*7c478bd9Sstevel@tonic-gate /* Update the count of bytes written to the program. */ 456*7c478bd9Sstevel@tonic-gate stdin_bytes += len; 457*7c478bd9Sstevel@tonic-gate } 458*7c478bd9Sstevel@tonic-gate } 459*7c478bd9Sstevel@tonic-gate /* Send any buffered packet data to the client. */ 460*7c478bd9Sstevel@tonic-gate if (FD_ISSET(connection_out, writeset)) 461*7c478bd9Sstevel@tonic-gate packet_write_poll(); 462*7c478bd9Sstevel@tonic-gate } 463*7c478bd9Sstevel@tonic-gate 464*7c478bd9Sstevel@tonic-gate /* 465*7c478bd9Sstevel@tonic-gate * Wait until all buffered output has been sent to the client. 466*7c478bd9Sstevel@tonic-gate * This is used when the program terminates. 467*7c478bd9Sstevel@tonic-gate */ 468*7c478bd9Sstevel@tonic-gate static void 469*7c478bd9Sstevel@tonic-gate drain_output(void) 470*7c478bd9Sstevel@tonic-gate { 471*7c478bd9Sstevel@tonic-gate /* Send any buffered stdout data to the client. */ 472*7c478bd9Sstevel@tonic-gate if (buffer_len(&stdout_buffer) > 0) { 473*7c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDOUT_DATA); 474*7c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stdout_buffer), 475*7c478bd9Sstevel@tonic-gate buffer_len(&stdout_buffer)); 476*7c478bd9Sstevel@tonic-gate packet_send(); 477*7c478bd9Sstevel@tonic-gate /* Update the count of sent bytes. */ 478*7c478bd9Sstevel@tonic-gate stdout_bytes += buffer_len(&stdout_buffer); 479*7c478bd9Sstevel@tonic-gate } 480*7c478bd9Sstevel@tonic-gate /* Send any buffered stderr data to the client. */ 481*7c478bd9Sstevel@tonic-gate if (buffer_len(&stderr_buffer) > 0) { 482*7c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDERR_DATA); 483*7c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stderr_buffer), 484*7c478bd9Sstevel@tonic-gate buffer_len(&stderr_buffer)); 485*7c478bd9Sstevel@tonic-gate packet_send(); 486*7c478bd9Sstevel@tonic-gate /* Update the count of sent bytes. */ 487*7c478bd9Sstevel@tonic-gate stderr_bytes += buffer_len(&stderr_buffer); 488*7c478bd9Sstevel@tonic-gate } 489*7c478bd9Sstevel@tonic-gate /* Wait until all buffered data has been written to the client. */ 490*7c478bd9Sstevel@tonic-gate packet_write_wait(); 491*7c478bd9Sstevel@tonic-gate } 492*7c478bd9Sstevel@tonic-gate 493*7c478bd9Sstevel@tonic-gate static void 494*7c478bd9Sstevel@tonic-gate process_buffered_input_packets(void) 495*7c478bd9Sstevel@tonic-gate { 496*7c478bd9Sstevel@tonic-gate dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL); 497*7c478bd9Sstevel@tonic-gate } 498*7c478bd9Sstevel@tonic-gate 499*7c478bd9Sstevel@tonic-gate /* 500*7c478bd9Sstevel@tonic-gate * Performs the interactive session. This handles data transmission between 501*7c478bd9Sstevel@tonic-gate * the client and the program. Note that the notion of stdin, stdout, and 502*7c478bd9Sstevel@tonic-gate * stderr in this function is sort of reversed: this function writes to 503*7c478bd9Sstevel@tonic-gate * stdin (of the child program), and reads from stdout and stderr (of the 504*7c478bd9Sstevel@tonic-gate * child program). 505*7c478bd9Sstevel@tonic-gate */ 506*7c478bd9Sstevel@tonic-gate void 507*7c478bd9Sstevel@tonic-gate server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) 508*7c478bd9Sstevel@tonic-gate { 509*7c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL; 510*7c478bd9Sstevel@tonic-gate int max_fd = 0, nalloc = 0; 511*7c478bd9Sstevel@tonic-gate int wait_status; /* Status returned by wait(). */ 512*7c478bd9Sstevel@tonic-gate pid_t wait_pid; /* pid returned by wait(). */ 513*7c478bd9Sstevel@tonic-gate int waiting_termination = 0; /* Have displayed waiting close message. */ 514*7c478bd9Sstevel@tonic-gate u_int max_time_milliseconds; 515*7c478bd9Sstevel@tonic-gate u_int previous_stdout_buffer_bytes; 516*7c478bd9Sstevel@tonic-gate u_int stdout_buffer_bytes; 517*7c478bd9Sstevel@tonic-gate int type; 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate debug("Entering interactive session."); 520*7c478bd9Sstevel@tonic-gate 521*7c478bd9Sstevel@tonic-gate /* Initialize the SIGCHLD kludge. */ 522*7c478bd9Sstevel@tonic-gate child_terminated = 0; 523*7c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler); 524*7c478bd9Sstevel@tonic-gate 525*7c478bd9Sstevel@tonic-gate /* Initialize our global variables. */ 526*7c478bd9Sstevel@tonic-gate fdin = fdin_arg; 527*7c478bd9Sstevel@tonic-gate fdout = fdout_arg; 528*7c478bd9Sstevel@tonic-gate fderr = fderr_arg; 529*7c478bd9Sstevel@tonic-gate 530*7c478bd9Sstevel@tonic-gate /* nonblocking IO */ 531*7c478bd9Sstevel@tonic-gate set_nonblock(fdin); 532*7c478bd9Sstevel@tonic-gate set_nonblock(fdout); 533*7c478bd9Sstevel@tonic-gate /* we don't have stderr for interactive terminal sessions, see below */ 534*7c478bd9Sstevel@tonic-gate if (fderr != -1) 535*7c478bd9Sstevel@tonic-gate set_nonblock(fderr); 536*7c478bd9Sstevel@tonic-gate 537*7c478bd9Sstevel@tonic-gate if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin)) 538*7c478bd9Sstevel@tonic-gate fdin_is_tty = 1; 539*7c478bd9Sstevel@tonic-gate 540*7c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in(); 541*7c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out(); 542*7c478bd9Sstevel@tonic-gate 543*7c478bd9Sstevel@tonic-gate notify_setup(); 544*7c478bd9Sstevel@tonic-gate 545*7c478bd9Sstevel@tonic-gate previous_stdout_buffer_bytes = 0; 546*7c478bd9Sstevel@tonic-gate 547*7c478bd9Sstevel@tonic-gate /* Set approximate I/O buffer size. */ 548*7c478bd9Sstevel@tonic-gate if (packet_is_interactive()) 549*7c478bd9Sstevel@tonic-gate buffer_high = 4096; 550*7c478bd9Sstevel@tonic-gate else 551*7c478bd9Sstevel@tonic-gate buffer_high = 64 * 1024; 552*7c478bd9Sstevel@tonic-gate 553*7c478bd9Sstevel@tonic-gate #if 0 554*7c478bd9Sstevel@tonic-gate /* Initialize max_fd to the maximum of the known file descriptors. */ 555*7c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 556*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdin); 557*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdout); 558*7c478bd9Sstevel@tonic-gate if (fderr != -1) 559*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fderr); 560*7c478bd9Sstevel@tonic-gate #endif 561*7c478bd9Sstevel@tonic-gate 562*7c478bd9Sstevel@tonic-gate /* Initialize Initialize buffers. */ 563*7c478bd9Sstevel@tonic-gate buffer_init(&stdin_buffer); 564*7c478bd9Sstevel@tonic-gate buffer_init(&stdout_buffer); 565*7c478bd9Sstevel@tonic-gate buffer_init(&stderr_buffer); 566*7c478bd9Sstevel@tonic-gate 567*7c478bd9Sstevel@tonic-gate /* 568*7c478bd9Sstevel@tonic-gate * If we have no separate fderr (which is the case when we have a pty 569*7c478bd9Sstevel@tonic-gate * - there we cannot make difference between data sent to stdout and 570*7c478bd9Sstevel@tonic-gate * stderr), indicate that we have seen an EOF from stderr. This way 571*7c478bd9Sstevel@tonic-gate * we don\'t need to check the descriptor everywhere. 572*7c478bd9Sstevel@tonic-gate */ 573*7c478bd9Sstevel@tonic-gate if (fderr == -1) 574*7c478bd9Sstevel@tonic-gate fderr_eof = 1; 575*7c478bd9Sstevel@tonic-gate 576*7c478bd9Sstevel@tonic-gate server_init_dispatch(); 577*7c478bd9Sstevel@tonic-gate 578*7c478bd9Sstevel@tonic-gate /* Main loop of the server for the interactive session mode. */ 579*7c478bd9Sstevel@tonic-gate for (;;) { 580*7c478bd9Sstevel@tonic-gate 581*7c478bd9Sstevel@tonic-gate /* Process buffered packets from the client. */ 582*7c478bd9Sstevel@tonic-gate process_buffered_input_packets(); 583*7c478bd9Sstevel@tonic-gate 584*7c478bd9Sstevel@tonic-gate /* 585*7c478bd9Sstevel@tonic-gate * If we have received eof, and there is no more pending 586*7c478bd9Sstevel@tonic-gate * input data, cause a real eof by closing fdin. 587*7c478bd9Sstevel@tonic-gate */ 588*7c478bd9Sstevel@tonic-gate if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { 589*7c478bd9Sstevel@tonic-gate if (fdin != fdout) 590*7c478bd9Sstevel@tonic-gate (void) close(fdin); 591*7c478bd9Sstevel@tonic-gate else 592*7c478bd9Sstevel@tonic-gate (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */ 593*7c478bd9Sstevel@tonic-gate fdin = -1; 594*7c478bd9Sstevel@tonic-gate } 595*7c478bd9Sstevel@tonic-gate /* Make packets from buffered stderr data to send to the client. */ 596*7c478bd9Sstevel@tonic-gate make_packets_from_stderr_data(); 597*7c478bd9Sstevel@tonic-gate 598*7c478bd9Sstevel@tonic-gate /* 599*7c478bd9Sstevel@tonic-gate * Make packets from buffered stdout data to send to the 600*7c478bd9Sstevel@tonic-gate * client. If there is very little to send, this arranges to 601*7c478bd9Sstevel@tonic-gate * not send them now, but to wait a short while to see if we 602*7c478bd9Sstevel@tonic-gate * are getting more data. This is necessary, as some systems 603*7c478bd9Sstevel@tonic-gate * wake up readers from a pty after each separate character. 604*7c478bd9Sstevel@tonic-gate */ 605*7c478bd9Sstevel@tonic-gate max_time_milliseconds = 0; 606*7c478bd9Sstevel@tonic-gate stdout_buffer_bytes = buffer_len(&stdout_buffer); 607*7c478bd9Sstevel@tonic-gate if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && 608*7c478bd9Sstevel@tonic-gate stdout_buffer_bytes != previous_stdout_buffer_bytes) { 609*7c478bd9Sstevel@tonic-gate /* try again after a while */ 610*7c478bd9Sstevel@tonic-gate max_time_milliseconds = 10; 611*7c478bd9Sstevel@tonic-gate } else { 612*7c478bd9Sstevel@tonic-gate /* Send it now. */ 613*7c478bd9Sstevel@tonic-gate make_packets_from_stdout_data(); 614*7c478bd9Sstevel@tonic-gate } 615*7c478bd9Sstevel@tonic-gate previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); 616*7c478bd9Sstevel@tonic-gate 617*7c478bd9Sstevel@tonic-gate /* Send channel data to the client. */ 618*7c478bd9Sstevel@tonic-gate if (packet_not_very_much_data_to_write()) 619*7c478bd9Sstevel@tonic-gate channel_output_poll(); 620*7c478bd9Sstevel@tonic-gate 621*7c478bd9Sstevel@tonic-gate /* 622*7c478bd9Sstevel@tonic-gate * Bail out of the loop if the program has closed its output 623*7c478bd9Sstevel@tonic-gate * descriptors, and we have no more data to send to the 624*7c478bd9Sstevel@tonic-gate * client, and there is no pending buffered data. 625*7c478bd9Sstevel@tonic-gate */ 626*7c478bd9Sstevel@tonic-gate if (fdout_eof && fderr_eof && !packet_have_data_to_write() && 627*7c478bd9Sstevel@tonic-gate buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { 628*7c478bd9Sstevel@tonic-gate if (!channel_still_open()) 629*7c478bd9Sstevel@tonic-gate break; 630*7c478bd9Sstevel@tonic-gate if (!waiting_termination) { 631*7c478bd9Sstevel@tonic-gate const char *s = "Waiting for forwarded connections to terminate...\r\n"; 632*7c478bd9Sstevel@tonic-gate char *cp; 633*7c478bd9Sstevel@tonic-gate waiting_termination = 1; 634*7c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, s, strlen(s)); 635*7c478bd9Sstevel@tonic-gate 636*7c478bd9Sstevel@tonic-gate /* Display list of open channels. */ 637*7c478bd9Sstevel@tonic-gate cp = channel_open_message(); 638*7c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, cp, strlen(cp)); 639*7c478bd9Sstevel@tonic-gate xfree(cp); 640*7c478bd9Sstevel@tonic-gate } 641*7c478bd9Sstevel@tonic-gate } 642*7c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 643*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdin); 644*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdout); 645*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fderr); 646*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]); 647*7c478bd9Sstevel@tonic-gate 648*7c478bd9Sstevel@tonic-gate /* Sleep in select() until we can do something. */ 649*7c478bd9Sstevel@tonic-gate wait_until_can_do_something(&readset, &writeset, &max_fd, 650*7c478bd9Sstevel@tonic-gate &nalloc, max_time_milliseconds); 651*7c478bd9Sstevel@tonic-gate 652*7c478bd9Sstevel@tonic-gate /* Process any channel events. */ 653*7c478bd9Sstevel@tonic-gate channel_after_select(readset, writeset); 654*7c478bd9Sstevel@tonic-gate 655*7c478bd9Sstevel@tonic-gate /* Process input from the client and from program stdout/stderr. */ 656*7c478bd9Sstevel@tonic-gate process_input(readset); 657*7c478bd9Sstevel@tonic-gate 658*7c478bd9Sstevel@tonic-gate /* Process output to the client and to program stdin. */ 659*7c478bd9Sstevel@tonic-gate process_output(writeset); 660*7c478bd9Sstevel@tonic-gate } 661*7c478bd9Sstevel@tonic-gate if (readset) 662*7c478bd9Sstevel@tonic-gate xfree(readset); 663*7c478bd9Sstevel@tonic-gate if (writeset) 664*7c478bd9Sstevel@tonic-gate xfree(writeset); 665*7c478bd9Sstevel@tonic-gate 666*7c478bd9Sstevel@tonic-gate /* Cleanup and termination code. */ 667*7c478bd9Sstevel@tonic-gate 668*7c478bd9Sstevel@tonic-gate /* Wait until all output has been sent to the client. */ 669*7c478bd9Sstevel@tonic-gate drain_output(); 670*7c478bd9Sstevel@tonic-gate 671*7c478bd9Sstevel@tonic-gate debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", 672*7c478bd9Sstevel@tonic-gate stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); 673*7c478bd9Sstevel@tonic-gate 674*7c478bd9Sstevel@tonic-gate /* Free and clear the buffers. */ 675*7c478bd9Sstevel@tonic-gate buffer_free(&stdin_buffer); 676*7c478bd9Sstevel@tonic-gate buffer_free(&stdout_buffer); 677*7c478bd9Sstevel@tonic-gate buffer_free(&stderr_buffer); 678*7c478bd9Sstevel@tonic-gate 679*7c478bd9Sstevel@tonic-gate /* Close the file descriptors. */ 680*7c478bd9Sstevel@tonic-gate if (fdout != -1) 681*7c478bd9Sstevel@tonic-gate (void) close(fdout); 682*7c478bd9Sstevel@tonic-gate fdout = -1; 683*7c478bd9Sstevel@tonic-gate fdout_eof = 1; 684*7c478bd9Sstevel@tonic-gate if (fderr != -1) 685*7c478bd9Sstevel@tonic-gate (void) close(fderr); 686*7c478bd9Sstevel@tonic-gate fderr = -1; 687*7c478bd9Sstevel@tonic-gate fderr_eof = 1; 688*7c478bd9Sstevel@tonic-gate if (fdin != -1) 689*7c478bd9Sstevel@tonic-gate (void) close(fdin); 690*7c478bd9Sstevel@tonic-gate fdin = -1; 691*7c478bd9Sstevel@tonic-gate 692*7c478bd9Sstevel@tonic-gate channel_free_all(); 693*7c478bd9Sstevel@tonic-gate 694*7c478bd9Sstevel@tonic-gate /* We no longer want our SIGCHLD handler to be called. */ 695*7c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, SIG_DFL); 696*7c478bd9Sstevel@tonic-gate 697*7c478bd9Sstevel@tonic-gate while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0) 698*7c478bd9Sstevel@tonic-gate if (errno != EINTR) 699*7c478bd9Sstevel@tonic-gate packet_disconnect("wait: %.100s", strerror(errno)); 700*7c478bd9Sstevel@tonic-gate if (wait_pid != pid) 701*7c478bd9Sstevel@tonic-gate error("Strange, wait returned pid %ld, expected %ld", 702*7c478bd9Sstevel@tonic-gate (long)wait_pid, (long)pid); 703*7c478bd9Sstevel@tonic-gate 704*7c478bd9Sstevel@tonic-gate /* Check if it exited normally. */ 705*7c478bd9Sstevel@tonic-gate if (WIFEXITED(wait_status)) { 706*7c478bd9Sstevel@tonic-gate /* Yes, normal exit. Get exit status and send it to the client. */ 707*7c478bd9Sstevel@tonic-gate debug("Command exited with status %d.", WEXITSTATUS(wait_status)); 708*7c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_EXITSTATUS); 709*7c478bd9Sstevel@tonic-gate packet_put_int(WEXITSTATUS(wait_status)); 710*7c478bd9Sstevel@tonic-gate packet_send(); 711*7c478bd9Sstevel@tonic-gate packet_write_wait(); 712*7c478bd9Sstevel@tonic-gate 713*7c478bd9Sstevel@tonic-gate /* 714*7c478bd9Sstevel@tonic-gate * Wait for exit confirmation. Note that there might be 715*7c478bd9Sstevel@tonic-gate * other packets coming before it; however, the program has 716*7c478bd9Sstevel@tonic-gate * already died so we just ignore them. The client is 717*7c478bd9Sstevel@tonic-gate * supposed to respond with the confirmation when it receives 718*7c478bd9Sstevel@tonic-gate * the exit status. 719*7c478bd9Sstevel@tonic-gate */ 720*7c478bd9Sstevel@tonic-gate do { 721*7c478bd9Sstevel@tonic-gate type = packet_read(); 722*7c478bd9Sstevel@tonic-gate } 723*7c478bd9Sstevel@tonic-gate while (type != SSH_CMSG_EXIT_CONFIRMATION); 724*7c478bd9Sstevel@tonic-gate 725*7c478bd9Sstevel@tonic-gate debug("Received exit confirmation."); 726*7c478bd9Sstevel@tonic-gate return; 727*7c478bd9Sstevel@tonic-gate } 728*7c478bd9Sstevel@tonic-gate /* Check if the program terminated due to a signal. */ 729*7c478bd9Sstevel@tonic-gate if (WIFSIGNALED(wait_status)) 730*7c478bd9Sstevel@tonic-gate packet_disconnect("Command terminated on signal %d.", 731*7c478bd9Sstevel@tonic-gate WTERMSIG(wait_status)); 732*7c478bd9Sstevel@tonic-gate 733*7c478bd9Sstevel@tonic-gate /* Some weird exit cause. Just exit. */ 734*7c478bd9Sstevel@tonic-gate packet_disconnect("wait returned status %04x.", wait_status); 735*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 736*7c478bd9Sstevel@tonic-gate } 737*7c478bd9Sstevel@tonic-gate 738*7c478bd9Sstevel@tonic-gate static void 739*7c478bd9Sstevel@tonic-gate collect_children(void) 740*7c478bd9Sstevel@tonic-gate { 741*7c478bd9Sstevel@tonic-gate pid_t pid; 742*7c478bd9Sstevel@tonic-gate sigset_t oset, nset; 743*7c478bd9Sstevel@tonic-gate int status; 744*7c478bd9Sstevel@tonic-gate 745*7c478bd9Sstevel@tonic-gate /* block SIGCHLD while we check for dead children */ 746*7c478bd9Sstevel@tonic-gate (void) sigemptyset(&nset); 747*7c478bd9Sstevel@tonic-gate (void) sigaddset(&nset, SIGCHLD); 748*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &nset, &oset); 749*7c478bd9Sstevel@tonic-gate if (child_terminated) { 750*7c478bd9Sstevel@tonic-gate while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || 751*7c478bd9Sstevel@tonic-gate (pid < 0 && errno == EINTR)) 752*7c478bd9Sstevel@tonic-gate if (pid > 0) 753*7c478bd9Sstevel@tonic-gate session_close_by_pid(pid, status); 754*7c478bd9Sstevel@tonic-gate child_terminated = 0; 755*7c478bd9Sstevel@tonic-gate } 756*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL); 757*7c478bd9Sstevel@tonic-gate } 758*7c478bd9Sstevel@tonic-gate 759*7c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 760*7c478bd9Sstevel@tonic-gate /* 761*7c478bd9Sstevel@tonic-gate * For ALTPRIVSEP the wait_until_can_do_something function is very 762*7c478bd9Sstevel@tonic-gate * simple: select() on the read side of the pipe, and if there's packets 763*7c478bd9Sstevel@tonic-gate * to send, on the write side, and on the read side of the SIGCHLD 764*7c478bd9Sstevel@tonic-gate * handler pipe. That's it. 765*7c478bd9Sstevel@tonic-gate */ 766*7c478bd9Sstevel@tonic-gate static void 767*7c478bd9Sstevel@tonic-gate aps_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 768*7c478bd9Sstevel@tonic-gate int *maxfdp, int *nallocp, u_int max_time_milliseconds) 769*7c478bd9Sstevel@tonic-gate { 770*7c478bd9Sstevel@tonic-gate int ret; 771*7c478bd9Sstevel@tonic-gate 772*7c478bd9Sstevel@tonic-gate /* 773*7c478bd9Sstevel@tonic-gate * Use channel_prepare_select() to make the fd sets. 774*7c478bd9Sstevel@tonic-gate * 775*7c478bd9Sstevel@tonic-gate * This is cheating, really, since because the last argument in 776*7c478bd9Sstevel@tonic-gate * this call is '1' nothing related to channels will be done -- 777*7c478bd9Sstevel@tonic-gate * we're using this function only to callocate the fd sets. 778*7c478bd9Sstevel@tonic-gate */ 779*7c478bd9Sstevel@tonic-gate channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 1); 780*7c478bd9Sstevel@tonic-gate 781*7c478bd9Sstevel@tonic-gate if ((connection_in = packet_get_connection_in()) >= 0 && 782*7c478bd9Sstevel@tonic-gate !connection_closed) 783*7c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp); 784*7c478bd9Sstevel@tonic-gate 785*7c478bd9Sstevel@tonic-gate notify_prepare(*readsetp); 786*7c478bd9Sstevel@tonic-gate 787*7c478bd9Sstevel@tonic-gate if ((connection_out = packet_get_connection_out()) >= 0 && 788*7c478bd9Sstevel@tonic-gate packet_have_data_to_write() && !connection_closed) 789*7c478bd9Sstevel@tonic-gate FD_SET(connection_out, *writesetp); 790*7c478bd9Sstevel@tonic-gate 791*7c478bd9Sstevel@tonic-gate /* Wait for something to happen, or the timeout to expire. */ 792*7c478bd9Sstevel@tonic-gate ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL); 793*7c478bd9Sstevel@tonic-gate 794*7c478bd9Sstevel@tonic-gate if (ret == -1) { 795*7c478bd9Sstevel@tonic-gate memset(*readsetp, 0, *nallocp); 796*7c478bd9Sstevel@tonic-gate memset(*writesetp, 0, *nallocp); 797*7c478bd9Sstevel@tonic-gate if (errno != EINTR) 798*7c478bd9Sstevel@tonic-gate error("select: %.100s", strerror(errno)); 799*7c478bd9Sstevel@tonic-gate } 800*7c478bd9Sstevel@tonic-gate 801*7c478bd9Sstevel@tonic-gate notify_done(*readsetp); 802*7c478bd9Sstevel@tonic-gate } 803*7c478bd9Sstevel@tonic-gate 804*7c478bd9Sstevel@tonic-gate /* 805*7c478bd9Sstevel@tonic-gate * Slightly different than collect_children, aps_collect_child() has 806*7c478bd9Sstevel@tonic-gate * only the unprivileged sshd to wait for, no sessions, no channells, 807*7c478bd9Sstevel@tonic-gate * just one process. 808*7c478bd9Sstevel@tonic-gate */ 809*7c478bd9Sstevel@tonic-gate static int 810*7c478bd9Sstevel@tonic-gate aps_collect_child(pid_t child) 811*7c478bd9Sstevel@tonic-gate { 812*7c478bd9Sstevel@tonic-gate pid_t pid; 813*7c478bd9Sstevel@tonic-gate sigset_t oset, nset; 814*7c478bd9Sstevel@tonic-gate int status; 815*7c478bd9Sstevel@tonic-gate 816*7c478bd9Sstevel@tonic-gate /* block SIGCHLD while we check for dead children */ 817*7c478bd9Sstevel@tonic-gate (void) sigemptyset(&nset); 818*7c478bd9Sstevel@tonic-gate (void) sigaddset(&nset, SIGCHLD); 819*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &nset, &oset); 820*7c478bd9Sstevel@tonic-gate if (child_terminated) { 821*7c478bd9Sstevel@tonic-gate while ((pid = waitpid(child, &status, WNOHANG)) > 0 || 822*7c478bd9Sstevel@tonic-gate (pid < 0 && errno == EINTR)) 823*7c478bd9Sstevel@tonic-gate if (pid == child) { 824*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL); 825*7c478bd9Sstevel@tonic-gate return (1); 826*7c478bd9Sstevel@tonic-gate } 827*7c478bd9Sstevel@tonic-gate child_terminated = 0; 828*7c478bd9Sstevel@tonic-gate } 829*7c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL); 830*7c478bd9Sstevel@tonic-gate return (0); 831*7c478bd9Sstevel@tonic-gate } 832*7c478bd9Sstevel@tonic-gate 833*7c478bd9Sstevel@tonic-gate static int killed = 0; 834*7c478bd9Sstevel@tonic-gate 835*7c478bd9Sstevel@tonic-gate static void 836*7c478bd9Sstevel@tonic-gate aps_monitor_kill_handler(int sig) 837*7c478bd9Sstevel@tonic-gate { 838*7c478bd9Sstevel@tonic-gate int save_errno = errno; 839*7c478bd9Sstevel@tonic-gate killed = 1; 840*7c478bd9Sstevel@tonic-gate notify_parent(); 841*7c478bd9Sstevel@tonic-gate mysignal(sig, aps_monitor_kill_handler); 842*7c478bd9Sstevel@tonic-gate errno = save_errno; 843*7c478bd9Sstevel@tonic-gate } 844*7c478bd9Sstevel@tonic-gate 845*7c478bd9Sstevel@tonic-gate static void 846*7c478bd9Sstevel@tonic-gate aps_monitor_sigchld_handler(int sig) 847*7c478bd9Sstevel@tonic-gate { 848*7c478bd9Sstevel@tonic-gate int save_errno = errno; 849*7c478bd9Sstevel@tonic-gate debug("Monitor received SIGCHLD."); 850*7c478bd9Sstevel@tonic-gate child_terminated = 1; 851*7c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, aps_monitor_sigchld_handler); 852*7c478bd9Sstevel@tonic-gate notify_parent(); 853*7c478bd9Sstevel@tonic-gate errno = save_errno; 854*7c478bd9Sstevel@tonic-gate } 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate void 857*7c478bd9Sstevel@tonic-gate aps_monitor_loop(Authctxt *authctxt, int pipe, pid_t child_pid) 858*7c478bd9Sstevel@tonic-gate { 859*7c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL; 860*7c478bd9Sstevel@tonic-gate int max_fd, nalloc = 0; 861*7c478bd9Sstevel@tonic-gate 862*7c478bd9Sstevel@tonic-gate debug("Entering monitor loop."); 863*7c478bd9Sstevel@tonic-gate 864*7c478bd9Sstevel@tonic-gate /* 865*7c478bd9Sstevel@tonic-gate * Awful hack follows: fake compat20 == 1 to cause process_input() 866*7c478bd9Sstevel@tonic-gate * and process_output() to behave as they would for SSHv2 because that's 867*7c478bd9Sstevel@tonic-gate * the behaviour we need in SSHv2. 868*7c478bd9Sstevel@tonic-gate * 869*7c478bd9Sstevel@tonic-gate * This same hack is done in packet.c 870*7c478bd9Sstevel@tonic-gate */ 871*7c478bd9Sstevel@tonic-gate compat20 = 1; /* causes process_input/output() to ignore stdio */ 872*7c478bd9Sstevel@tonic-gate 873*7c478bd9Sstevel@tonic-gate mysignal(SIGHUP, aps_monitor_kill_handler); 874*7c478bd9Sstevel@tonic-gate mysignal(SIGINT, aps_monitor_kill_handler); 875*7c478bd9Sstevel@tonic-gate mysignal(SIGTERM, aps_monitor_kill_handler); 876*7c478bd9Sstevel@tonic-gate 877*7c478bd9Sstevel@tonic-gate child_terminated = 0; 878*7c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, aps_monitor_sigchld_handler); 879*7c478bd9Sstevel@tonic-gate 880*7c478bd9Sstevel@tonic-gate packet_set_monitor(pipe); 881*7c478bd9Sstevel@tonic-gate 882*7c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in(); 883*7c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out(); 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate notify_setup(); 886*7c478bd9Sstevel@tonic-gate 887*7c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 888*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]); 889*7c478bd9Sstevel@tonic-gate 890*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 891*7c478bd9Sstevel@tonic-gate dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_MAX, 892*7c478bd9Sstevel@tonic-gate &dispatch_protocol_error); 893*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_PRIV_MSG_ALTPRIVSEP, &aps_input_altpriv_msg); 894*7c478bd9Sstevel@tonic-gate 895*7c478bd9Sstevel@tonic-gate for (;;) { 896*7c478bd9Sstevel@tonic-gate process_buffered_input_packets(); 897*7c478bd9Sstevel@tonic-gate 898*7c478bd9Sstevel@tonic-gate aps_wait_until_can_do_something(&readset, &writeset, &max_fd, 899*7c478bd9Sstevel@tonic-gate &nalloc, 0); 900*7c478bd9Sstevel@tonic-gate 901*7c478bd9Sstevel@tonic-gate if (aps_collect_child(child_pid)) 902*7c478bd9Sstevel@tonic-gate break; 903*7c478bd9Sstevel@tonic-gate 904*7c478bd9Sstevel@tonic-gate if (killed) { 905*7c478bd9Sstevel@tonic-gate /* fatal cleanups will kill child, audit logout */ 906*7c478bd9Sstevel@tonic-gate log("Monitor killed; exiting"); 907*7c478bd9Sstevel@tonic-gate fatal_cleanup(); 908*7c478bd9Sstevel@tonic-gate } 909*7c478bd9Sstevel@tonic-gate 910*7c478bd9Sstevel@tonic-gate /* 911*7c478bd9Sstevel@tonic-gate * Unlike server_loop2() we don't care if connection_closed 912*7c478bd9Sstevel@tonic-gate * since we still want to wait for the monitor's child. 913*7c478bd9Sstevel@tonic-gate */ 914*7c478bd9Sstevel@tonic-gate process_input(readset); 915*7c478bd9Sstevel@tonic-gate process_output(writeset); 916*7c478bd9Sstevel@tonic-gate } 917*7c478bd9Sstevel@tonic-gate 918*7c478bd9Sstevel@tonic-gate packet_close(); 919*7c478bd9Sstevel@tonic-gate } 920*7c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 921*7c478bd9Sstevel@tonic-gate 922*7c478bd9Sstevel@tonic-gate void 923*7c478bd9Sstevel@tonic-gate server_loop2(Authctxt *authctxt) 924*7c478bd9Sstevel@tonic-gate { 925*7c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL; 926*7c478bd9Sstevel@tonic-gate int rekeying = 0, max_fd, nalloc = 0; 927*7c478bd9Sstevel@tonic-gate 928*7c478bd9Sstevel@tonic-gate debug("Entering interactive session for SSH2."); 929*7c478bd9Sstevel@tonic-gate 930*7c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler); 931*7c478bd9Sstevel@tonic-gate child_terminated = 0; 932*7c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in(); 933*7c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out(); 934*7c478bd9Sstevel@tonic-gate 935*7c478bd9Sstevel@tonic-gate notify_setup(); 936*7c478bd9Sstevel@tonic-gate 937*7c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out); 938*7c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]); 939*7c478bd9Sstevel@tonic-gate 940*7c478bd9Sstevel@tonic-gate xxx_authctxt = authctxt; 941*7c478bd9Sstevel@tonic-gate 942*7c478bd9Sstevel@tonic-gate server_init_dispatch(); 943*7c478bd9Sstevel@tonic-gate 944*7c478bd9Sstevel@tonic-gate for (;;) { 945*7c478bd9Sstevel@tonic-gate process_buffered_input_packets(); 946*7c478bd9Sstevel@tonic-gate 947*7c478bd9Sstevel@tonic-gate rekeying = (xxx_kex != NULL && !xxx_kex->done); 948*7c478bd9Sstevel@tonic-gate 949*7c478bd9Sstevel@tonic-gate if (!rekeying && packet_not_very_much_data_to_write()) 950*7c478bd9Sstevel@tonic-gate channel_output_poll(); 951*7c478bd9Sstevel@tonic-gate wait_until_can_do_something(&readset, &writeset, &max_fd, 952*7c478bd9Sstevel@tonic-gate &nalloc, 0); 953*7c478bd9Sstevel@tonic-gate 954*7c478bd9Sstevel@tonic-gate collect_children(); 955*7c478bd9Sstevel@tonic-gate 956*7c478bd9Sstevel@tonic-gate if (!rekeying) 957*7c478bd9Sstevel@tonic-gate channel_after_select(readset, writeset); 958*7c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 959*7c478bd9Sstevel@tonic-gate else 960*7c478bd9Sstevel@tonic-gate altprivsep_process_input(xxx_kex, readset); 961*7c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 962*7c478bd9Sstevel@tonic-gate 963*7c478bd9Sstevel@tonic-gate process_input(readset); 964*7c478bd9Sstevel@tonic-gate if (connection_closed) 965*7c478bd9Sstevel@tonic-gate break; 966*7c478bd9Sstevel@tonic-gate process_output(writeset); 967*7c478bd9Sstevel@tonic-gate } 968*7c478bd9Sstevel@tonic-gate collect_children(); 969*7c478bd9Sstevel@tonic-gate 970*7c478bd9Sstevel@tonic-gate if (readset) 971*7c478bd9Sstevel@tonic-gate xfree(readset); 972*7c478bd9Sstevel@tonic-gate if (writeset) 973*7c478bd9Sstevel@tonic-gate xfree(writeset); 974*7c478bd9Sstevel@tonic-gate 975*7c478bd9Sstevel@tonic-gate /* free all channels, no more reads and writes */ 976*7c478bd9Sstevel@tonic-gate channel_free_all(); 977*7c478bd9Sstevel@tonic-gate 978*7c478bd9Sstevel@tonic-gate /* free remaining sessions, e.g. remove wtmp entries */ 979*7c478bd9Sstevel@tonic-gate session_destroy_all(NULL); 980*7c478bd9Sstevel@tonic-gate } 981*7c478bd9Sstevel@tonic-gate 982*7c478bd9Sstevel@tonic-gate static void 983*7c478bd9Sstevel@tonic-gate server_input_channel_failure(int type, u_int32_t seq, void *ctxt) 984*7c478bd9Sstevel@tonic-gate { 985*7c478bd9Sstevel@tonic-gate debug("Got CHANNEL_FAILURE for keepalive"); 986*7c478bd9Sstevel@tonic-gate /* 987*7c478bd9Sstevel@tonic-gate * reset timeout, since we got a sane answer from the client. 988*7c478bd9Sstevel@tonic-gate * even if this was generated by something other than 989*7c478bd9Sstevel@tonic-gate * the bogus CHANNEL_REQUEST we send for keepalives. 990*7c478bd9Sstevel@tonic-gate */ 991*7c478bd9Sstevel@tonic-gate client_alive_timeouts = 0; 992*7c478bd9Sstevel@tonic-gate } 993*7c478bd9Sstevel@tonic-gate 994*7c478bd9Sstevel@tonic-gate 995*7c478bd9Sstevel@tonic-gate static void 996*7c478bd9Sstevel@tonic-gate server_input_stdin_data(int type, u_int32_t seq, void *ctxt) 997*7c478bd9Sstevel@tonic-gate { 998*7c478bd9Sstevel@tonic-gate char *data; 999*7c478bd9Sstevel@tonic-gate u_int data_len; 1000*7c478bd9Sstevel@tonic-gate 1001*7c478bd9Sstevel@tonic-gate /* Stdin data from the client. Append it to the buffer. */ 1002*7c478bd9Sstevel@tonic-gate /* Ignore any data if the client has closed stdin. */ 1003*7c478bd9Sstevel@tonic-gate if (fdin == -1) 1004*7c478bd9Sstevel@tonic-gate return; 1005*7c478bd9Sstevel@tonic-gate data = packet_get_string(&data_len); 1006*7c478bd9Sstevel@tonic-gate packet_check_eom(); 1007*7c478bd9Sstevel@tonic-gate buffer_append(&stdin_buffer, data, data_len); 1008*7c478bd9Sstevel@tonic-gate memset(data, 0, data_len); 1009*7c478bd9Sstevel@tonic-gate xfree(data); 1010*7c478bd9Sstevel@tonic-gate } 1011*7c478bd9Sstevel@tonic-gate 1012*7c478bd9Sstevel@tonic-gate static void 1013*7c478bd9Sstevel@tonic-gate server_input_eof(int type, u_int32_t seq, void *ctxt) 1014*7c478bd9Sstevel@tonic-gate { 1015*7c478bd9Sstevel@tonic-gate /* 1016*7c478bd9Sstevel@tonic-gate * Eof from the client. The stdin descriptor to the 1017*7c478bd9Sstevel@tonic-gate * program will be closed when all buffered data has 1018*7c478bd9Sstevel@tonic-gate * drained. 1019*7c478bd9Sstevel@tonic-gate */ 1020*7c478bd9Sstevel@tonic-gate debug("EOF received for stdin."); 1021*7c478bd9Sstevel@tonic-gate packet_check_eom(); 1022*7c478bd9Sstevel@tonic-gate stdin_eof = 1; 1023*7c478bd9Sstevel@tonic-gate } 1024*7c478bd9Sstevel@tonic-gate 1025*7c478bd9Sstevel@tonic-gate static void 1026*7c478bd9Sstevel@tonic-gate server_input_window_size(int type, u_int32_t seq, void *ctxt) 1027*7c478bd9Sstevel@tonic-gate { 1028*7c478bd9Sstevel@tonic-gate int row = packet_get_int(); 1029*7c478bd9Sstevel@tonic-gate int col = packet_get_int(); 1030*7c478bd9Sstevel@tonic-gate int xpixel = packet_get_int(); 1031*7c478bd9Sstevel@tonic-gate int ypixel = packet_get_int(); 1032*7c478bd9Sstevel@tonic-gate 1033*7c478bd9Sstevel@tonic-gate debug("Window change received."); 1034*7c478bd9Sstevel@tonic-gate packet_check_eom(); 1035*7c478bd9Sstevel@tonic-gate if (fdin != -1) 1036*7c478bd9Sstevel@tonic-gate pty_change_window_size(fdin, row, col, xpixel, ypixel); 1037*7c478bd9Sstevel@tonic-gate } 1038*7c478bd9Sstevel@tonic-gate 1039*7c478bd9Sstevel@tonic-gate static Channel * 1040*7c478bd9Sstevel@tonic-gate server_request_direct_tcpip(char *ctype) 1041*7c478bd9Sstevel@tonic-gate { 1042*7c478bd9Sstevel@tonic-gate Channel *c; 1043*7c478bd9Sstevel@tonic-gate int sock; 1044*7c478bd9Sstevel@tonic-gate char *target, *originator; 1045*7c478bd9Sstevel@tonic-gate int target_port, originator_port; 1046*7c478bd9Sstevel@tonic-gate 1047*7c478bd9Sstevel@tonic-gate target = packet_get_string(NULL); 1048*7c478bd9Sstevel@tonic-gate target_port = packet_get_int(); 1049*7c478bd9Sstevel@tonic-gate originator = packet_get_string(NULL); 1050*7c478bd9Sstevel@tonic-gate originator_port = packet_get_int(); 1051*7c478bd9Sstevel@tonic-gate packet_check_eom(); 1052*7c478bd9Sstevel@tonic-gate 1053*7c478bd9Sstevel@tonic-gate debug("server_request_direct_tcpip: originator %s port %d, target %s port %d", 1054*7c478bd9Sstevel@tonic-gate originator, originator_port, target, target_port); 1055*7c478bd9Sstevel@tonic-gate 1056*7c478bd9Sstevel@tonic-gate /* XXX check permission */ 1057*7c478bd9Sstevel@tonic-gate sock = channel_connect_to(target, target_port); 1058*7c478bd9Sstevel@tonic-gate 1059*7c478bd9Sstevel@tonic-gate xfree(target); 1060*7c478bd9Sstevel@tonic-gate xfree(originator); 1061*7c478bd9Sstevel@tonic-gate if (sock < 0) 1062*7c478bd9Sstevel@tonic-gate return NULL; 1063*7c478bd9Sstevel@tonic-gate c = channel_new(ctype, SSH_CHANNEL_CONNECTING, 1064*7c478bd9Sstevel@tonic-gate sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, 1065*7c478bd9Sstevel@tonic-gate CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1); 1066*7c478bd9Sstevel@tonic-gate return c; 1067*7c478bd9Sstevel@tonic-gate } 1068*7c478bd9Sstevel@tonic-gate 1069*7c478bd9Sstevel@tonic-gate static Channel * 1070*7c478bd9Sstevel@tonic-gate server_request_session(char *ctype) 1071*7c478bd9Sstevel@tonic-gate { 1072*7c478bd9Sstevel@tonic-gate Channel *c; 1073*7c478bd9Sstevel@tonic-gate 1074*7c478bd9Sstevel@tonic-gate debug("input_session_request"); 1075*7c478bd9Sstevel@tonic-gate packet_check_eom(); 1076*7c478bd9Sstevel@tonic-gate /* 1077*7c478bd9Sstevel@tonic-gate * A server session has no fd to read or write until a 1078*7c478bd9Sstevel@tonic-gate * CHANNEL_REQUEST for a shell is made, so we set the type to 1079*7c478bd9Sstevel@tonic-gate * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all 1080*7c478bd9Sstevel@tonic-gate * CHANNEL_REQUEST messages is registered. 1081*7c478bd9Sstevel@tonic-gate */ 1082*7c478bd9Sstevel@tonic-gate c = channel_new(ctype, SSH_CHANNEL_LARVAL, 1083*7c478bd9Sstevel@tonic-gate -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 1084*7c478bd9Sstevel@tonic-gate 0, xstrdup("server-session"), 1); 1085*7c478bd9Sstevel@tonic-gate if (session_open(xxx_authctxt, c->self) != 1) { 1086*7c478bd9Sstevel@tonic-gate debug("session open failed, free channel %d", c->self); 1087*7c478bd9Sstevel@tonic-gate channel_free(c); 1088*7c478bd9Sstevel@tonic-gate return NULL; 1089*7c478bd9Sstevel@tonic-gate } 1090*7c478bd9Sstevel@tonic-gate channel_register_cleanup(c->self, session_close_by_channel); 1091*7c478bd9Sstevel@tonic-gate return c; 1092*7c478bd9Sstevel@tonic-gate } 1093*7c478bd9Sstevel@tonic-gate 1094*7c478bd9Sstevel@tonic-gate static void 1095*7c478bd9Sstevel@tonic-gate server_input_channel_open(int type, u_int32_t seq, void *ctxt) 1096*7c478bd9Sstevel@tonic-gate { 1097*7c478bd9Sstevel@tonic-gate Channel *c = NULL; 1098*7c478bd9Sstevel@tonic-gate char *ctype; 1099*7c478bd9Sstevel@tonic-gate int rchan; 1100*7c478bd9Sstevel@tonic-gate u_int rmaxpack, rwindow, len; 1101*7c478bd9Sstevel@tonic-gate 1102*7c478bd9Sstevel@tonic-gate ctype = packet_get_string(&len); 1103*7c478bd9Sstevel@tonic-gate rchan = packet_get_int(); 1104*7c478bd9Sstevel@tonic-gate rwindow = packet_get_int(); 1105*7c478bd9Sstevel@tonic-gate rmaxpack = packet_get_int(); 1106*7c478bd9Sstevel@tonic-gate 1107*7c478bd9Sstevel@tonic-gate debug("server_input_channel_open: ctype %s rchan %d win %d max %d", 1108*7c478bd9Sstevel@tonic-gate ctype, rchan, rwindow, rmaxpack); 1109*7c478bd9Sstevel@tonic-gate 1110*7c478bd9Sstevel@tonic-gate if (strcmp(ctype, "session") == 0) { 1111*7c478bd9Sstevel@tonic-gate c = server_request_session(ctype); 1112*7c478bd9Sstevel@tonic-gate } else if (strcmp(ctype, "direct-tcpip") == 0) { 1113*7c478bd9Sstevel@tonic-gate c = server_request_direct_tcpip(ctype); 1114*7c478bd9Sstevel@tonic-gate } 1115*7c478bd9Sstevel@tonic-gate if (c != NULL) { 1116*7c478bd9Sstevel@tonic-gate debug("server_input_channel_open: confirm %s", ctype); 1117*7c478bd9Sstevel@tonic-gate c->remote_id = rchan; 1118*7c478bd9Sstevel@tonic-gate c->remote_window = rwindow; 1119*7c478bd9Sstevel@tonic-gate c->remote_maxpacket = rmaxpack; 1120*7c478bd9Sstevel@tonic-gate if (c->type != SSH_CHANNEL_CONNECTING) { 1121*7c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1122*7c478bd9Sstevel@tonic-gate packet_put_int(c->remote_id); 1123*7c478bd9Sstevel@tonic-gate packet_put_int(c->self); 1124*7c478bd9Sstevel@tonic-gate packet_put_int(c->local_window); 1125*7c478bd9Sstevel@tonic-gate packet_put_int(c->local_maxpacket); 1126*7c478bd9Sstevel@tonic-gate packet_send(); 1127*7c478bd9Sstevel@tonic-gate } 1128*7c478bd9Sstevel@tonic-gate } else { 1129*7c478bd9Sstevel@tonic-gate debug("server_input_channel_open: failure %s", ctype); 1130*7c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1131*7c478bd9Sstevel@tonic-gate packet_put_int(rchan); 1132*7c478bd9Sstevel@tonic-gate packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 1133*7c478bd9Sstevel@tonic-gate if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1134*7c478bd9Sstevel@tonic-gate packet_put_cstring("open failed"); 1135*7c478bd9Sstevel@tonic-gate packet_put_cstring(""); 1136*7c478bd9Sstevel@tonic-gate } 1137*7c478bd9Sstevel@tonic-gate packet_send(); 1138*7c478bd9Sstevel@tonic-gate } 1139*7c478bd9Sstevel@tonic-gate xfree(ctype); 1140*7c478bd9Sstevel@tonic-gate } 1141*7c478bd9Sstevel@tonic-gate 1142*7c478bd9Sstevel@tonic-gate static void 1143*7c478bd9Sstevel@tonic-gate server_input_global_request(int type, u_int32_t seq, void *ctxt) 1144*7c478bd9Sstevel@tonic-gate { 1145*7c478bd9Sstevel@tonic-gate char *rtype; 1146*7c478bd9Sstevel@tonic-gate int want_reply; 1147*7c478bd9Sstevel@tonic-gate int success = 0; 1148*7c478bd9Sstevel@tonic-gate 1149*7c478bd9Sstevel@tonic-gate rtype = packet_get_string(NULL); 1150*7c478bd9Sstevel@tonic-gate want_reply = packet_get_char(); 1151*7c478bd9Sstevel@tonic-gate debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); 1152*7c478bd9Sstevel@tonic-gate 1153*7c478bd9Sstevel@tonic-gate /* -R style forwarding */ 1154*7c478bd9Sstevel@tonic-gate if (strcmp(rtype, "tcpip-forward") == 0) { 1155*7c478bd9Sstevel@tonic-gate struct passwd *pw; 1156*7c478bd9Sstevel@tonic-gate char *listen_address; 1157*7c478bd9Sstevel@tonic-gate u_short listen_port; 1158*7c478bd9Sstevel@tonic-gate 1159*7c478bd9Sstevel@tonic-gate pw = auth_get_user(); 1160*7c478bd9Sstevel@tonic-gate if (pw == NULL) 1161*7c478bd9Sstevel@tonic-gate fatal("server_input_global_request: no user"); 1162*7c478bd9Sstevel@tonic-gate listen_address = packet_get_string(NULL); /* XXX currently ignored */ 1163*7c478bd9Sstevel@tonic-gate listen_port = (u_short)packet_get_int(); 1164*7c478bd9Sstevel@tonic-gate debug("server_input_global_request: tcpip-forward listen %s port %d", 1165*7c478bd9Sstevel@tonic-gate listen_address, listen_port); 1166*7c478bd9Sstevel@tonic-gate 1167*7c478bd9Sstevel@tonic-gate /* check permissions */ 1168*7c478bd9Sstevel@tonic-gate if (!options.allow_tcp_forwarding || 1169*7c478bd9Sstevel@tonic-gate no_port_forwarding_flag 1170*7c478bd9Sstevel@tonic-gate #ifndef NO_IPPORT_RESERVED_CONCEPT 1171*7c478bd9Sstevel@tonic-gate || (listen_port < IPPORT_RESERVED && pw->pw_uid != 0) 1172*7c478bd9Sstevel@tonic-gate #endif 1173*7c478bd9Sstevel@tonic-gate ) { 1174*7c478bd9Sstevel@tonic-gate success = 0; 1175*7c478bd9Sstevel@tonic-gate packet_send_debug("Server has disabled port forwarding."); 1176*7c478bd9Sstevel@tonic-gate } else { 1177*7c478bd9Sstevel@tonic-gate /* Start listening on the port */ 1178*7c478bd9Sstevel@tonic-gate success = channel_setup_remote_fwd_listener( 1179*7c478bd9Sstevel@tonic-gate listen_address, listen_port, options.gateway_ports); 1180*7c478bd9Sstevel@tonic-gate } 1181*7c478bd9Sstevel@tonic-gate xfree(listen_address); 1182*7c478bd9Sstevel@tonic-gate } 1183*7c478bd9Sstevel@tonic-gate 1184*7c478bd9Sstevel@tonic-gate if (want_reply) { 1185*7c478bd9Sstevel@tonic-gate packet_start(success ? 1186*7c478bd9Sstevel@tonic-gate SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 1187*7c478bd9Sstevel@tonic-gate packet_send(); 1188*7c478bd9Sstevel@tonic-gate packet_write_wait(); 1189*7c478bd9Sstevel@tonic-gate } 1190*7c478bd9Sstevel@tonic-gate xfree(rtype); 1191*7c478bd9Sstevel@tonic-gate } 1192*7c478bd9Sstevel@tonic-gate static void 1193*7c478bd9Sstevel@tonic-gate server_input_channel_req(int type, u_int32_t seq, void *ctxt) 1194*7c478bd9Sstevel@tonic-gate { 1195*7c478bd9Sstevel@tonic-gate Channel *c; 1196*7c478bd9Sstevel@tonic-gate int id, reply, success = 0; 1197*7c478bd9Sstevel@tonic-gate char *rtype; 1198*7c478bd9Sstevel@tonic-gate 1199*7c478bd9Sstevel@tonic-gate id = packet_get_int(); 1200*7c478bd9Sstevel@tonic-gate rtype = packet_get_string(NULL); 1201*7c478bd9Sstevel@tonic-gate reply = packet_get_char(); 1202*7c478bd9Sstevel@tonic-gate 1203*7c478bd9Sstevel@tonic-gate debug("server_input_channel_req: channel %d request %s reply %d", 1204*7c478bd9Sstevel@tonic-gate id, rtype, reply); 1205*7c478bd9Sstevel@tonic-gate 1206*7c478bd9Sstevel@tonic-gate if ((c = channel_lookup(id)) == NULL) 1207*7c478bd9Sstevel@tonic-gate packet_disconnect("server_input_channel_req: " 1208*7c478bd9Sstevel@tonic-gate "unknown channel %d", id); 1209*7c478bd9Sstevel@tonic-gate if (c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) 1210*7c478bd9Sstevel@tonic-gate success = session_input_channel_req(c, rtype); 1211*7c478bd9Sstevel@tonic-gate if (reply) { 1212*7c478bd9Sstevel@tonic-gate packet_start(success ? 1213*7c478bd9Sstevel@tonic-gate SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1214*7c478bd9Sstevel@tonic-gate packet_put_int(c->remote_id); 1215*7c478bd9Sstevel@tonic-gate packet_send(); 1216*7c478bd9Sstevel@tonic-gate } 1217*7c478bd9Sstevel@tonic-gate xfree(rtype); 1218*7c478bd9Sstevel@tonic-gate } 1219*7c478bd9Sstevel@tonic-gate 1220*7c478bd9Sstevel@tonic-gate static void 1221*7c478bd9Sstevel@tonic-gate server_init_dispatch_20(void) 1222*7c478bd9Sstevel@tonic-gate { 1223*7c478bd9Sstevel@tonic-gate debug("server_init_dispatch_20"); 1224*7c478bd9Sstevel@tonic-gate dispatch_init(&dispatch_protocol_error); 1225*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 1226*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 1227*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 1228*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 1229*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); 1230*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 1231*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 1232*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); 1233*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 1234*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); 1235*7c478bd9Sstevel@tonic-gate /* client_alive */ 1236*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure); 1237*7c478bd9Sstevel@tonic-gate /* rekeying */ 1238*7c478bd9Sstevel@tonic-gate 1239*7c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP 1240*7c478bd9Sstevel@tonic-gate /* unprivileged sshd has a kex packet handler that must not be reset */ 1241*7c478bd9Sstevel@tonic-gate debug3("server_init_dispatch_20 -- should we dispatch_set(KEXINIT) here? %d && !%d", 1242*7c478bd9Sstevel@tonic-gate packet_is_server(), packet_is_monitor()); 1243*7c478bd9Sstevel@tonic-gate if (packet_is_server() && !packet_is_monitor()) { 1244*7c478bd9Sstevel@tonic-gate debug3("server_init_dispatch_20 -- skipping dispatch_set(KEXINIT) in unpriv proc"); 1245*7c478bd9Sstevel@tonic-gate dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX, 1246*7c478bd9Sstevel@tonic-gate &altprivsep_rekey); 1247*7c478bd9Sstevel@tonic-gate return; 1248*7c478bd9Sstevel@tonic-gate } 1249*7c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */ 1250*7c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 1251*7c478bd9Sstevel@tonic-gate } 1252*7c478bd9Sstevel@tonic-gate static void 1253*7c478bd9Sstevel@tonic-gate server_init_dispatch_13(void) 1254*7c478bd9Sstevel@tonic-gate { 1255*7c478bd9Sstevel@tonic-gate debug("server_init_dispatch_13"); 1256*7c478bd9Sstevel@tonic-gate dispatch_init(NULL); 1257*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_EOF, &server_input_eof); 1258*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); 1259*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); 1260*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 1261*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 1262*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 1263*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 1264*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 1265*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 1266*7c478bd9Sstevel@tonic-gate } 1267*7c478bd9Sstevel@tonic-gate static void 1268*7c478bd9Sstevel@tonic-gate server_init_dispatch_15(void) 1269*7c478bd9Sstevel@tonic-gate { 1270*7c478bd9Sstevel@tonic-gate server_init_dispatch_13(); 1271*7c478bd9Sstevel@tonic-gate debug("server_init_dispatch_15"); 1272*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 1273*7c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); 1274*7c478bd9Sstevel@tonic-gate } 1275*7c478bd9Sstevel@tonic-gate static void 1276*7c478bd9Sstevel@tonic-gate server_init_dispatch(void) 1277*7c478bd9Sstevel@tonic-gate { 1278*7c478bd9Sstevel@tonic-gate if (compat20) 1279*7c478bd9Sstevel@tonic-gate server_init_dispatch_20(); 1280*7c478bd9Sstevel@tonic-gate else if (compat13) 1281*7c478bd9Sstevel@tonic-gate server_init_dispatch_13(); 1282*7c478bd9Sstevel@tonic-gate else 1283*7c478bd9Sstevel@tonic-gate server_init_dispatch_15(); 1284*7c478bd9Sstevel@tonic-gate } 1285