xref: /titanic_50/usr/src/cmd/ssh/sshd/serverloop.c (revision 9a8058b57457911fab0e3b4b6f0a97740e7a816d)
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