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