1323124Sdes/* $OpenBSD: serverloop.c,v 1.184 2016/03/07 19:02:43 djm Exp $ */
257429Smarkm/*
357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
557429Smarkm *                    All rights reserved
657429Smarkm * Server main loop for handling the interactive session.
765668Skris *
865668Skris * As far as I am concerned, the code I have written for this software
965668Skris * can be used freely for any purpose.  Any derived versions of this
1065668Skris * software must be clearly marked as such, and if the derived work is
1165668Skris * incompatible with the protocol description in the RFC file, it must be
1265668Skris * called by a name other than "ssh" or "Secure Shell".
1365668Skris *
1460573Skris * SSH2 support by Markus Friedl.
1592559Sdes * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
1665668Skris *
1765668Skris * Redistribution and use in source and binary forms, with or without
1865668Skris * modification, are permitted provided that the following conditions
1965668Skris * are met:
2065668Skris * 1. Redistributions of source code must retain the above copyright
2165668Skris *    notice, this list of conditions and the following disclaimer.
2265668Skris * 2. Redistributions in binary form must reproduce the above copyright
2365668Skris *    notice, this list of conditions and the following disclaimer in the
2465668Skris *    documentation and/or other materials provided with the distribution.
2565668Skris *
2665668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2765668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2865668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2965668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3065668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3165668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3265668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3365668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3465668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3565668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3660573Skris */
3757429Smarkm
3857429Smarkm#include "includes.h"
3969587Sgreen
40295367Sdes#include <sys/param.h>	/* MIN MAX */
41162856Sdes#include <sys/types.h>
42162856Sdes#include <sys/wait.h>
43162856Sdes#include <sys/socket.h>
44162856Sdes#ifdef HAVE_SYS_TIME_H
45162856Sdes# include <sys/time.h>
46162856Sdes#endif
47162856Sdes
48162856Sdes#include <netinet/in.h>
49162856Sdes
50162856Sdes#include <errno.h>
51162856Sdes#include <fcntl.h>
52162856Sdes#include <pwd.h>
53162856Sdes#include <signal.h>
54162856Sdes#include <string.h>
55162856Sdes#include <termios.h>
56162856Sdes#include <unistd.h>
57162856Sdes#include <stdarg.h>
58162856Sdes
59181111Sdes#include "openbsd-compat/sys-queue.h"
6057429Smarkm#include "xmalloc.h"
6157429Smarkm#include "packet.h"
6257429Smarkm#include "buffer.h"
6376262Sgreen#include "log.h"
64295367Sdes#include "misc.h"
6557429Smarkm#include "servconf.h"
66106130Sdes#include "canohost.h"
6776262Sgreen#include "sshpty.h"
6860573Skris#include "channels.h"
6960573Skris#include "compat.h"
7076262Sgreen#include "ssh1.h"
7160573Skris#include "ssh2.h"
72162856Sdes#include "key.h"
73162856Sdes#include "cipher.h"
74162856Sdes#include "kex.h"
75162856Sdes#include "hostfile.h"
7676262Sgreen#include "auth.h"
7760573Skris#include "session.h"
7860573Skris#include "dispatch.h"
7965668Skris#include "auth-options.h"
8076262Sgreen#include "serverloop.h"
81295367Sdes#include "ssherr.h"
8260573Skris
8369587Sgreenextern ServerOptions options;
8469587Sgreen
8576262Sgreen/* XXX */
86126277Sdesextern Authctxt *the_authctxt;
87157019Sdesextern int use_privsep;
8876262Sgreen
8957429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
9057429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
9157429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
9257429Smarkmstatic int fdin;		/* Descriptor for stdin (for writing) */
9357429Smarkmstatic int fdout;		/* Descriptor for stdout (for reading);
9457429Smarkm				   May be same number as fdin. */
9557429Smarkmstatic int fderr;		/* Descriptor for stderr.  May be -1. */
9657429Smarkmstatic long stdin_bytes = 0;	/* Number of bytes written to stdin. */
9757429Smarkmstatic long stdout_bytes = 0;	/* Number of stdout bytes sent to client. */
9857429Smarkmstatic long stderr_bytes = 0;	/* Number of stderr bytes sent to client. */
9957429Smarkmstatic long fdout_bytes = 0;	/* Number of stdout bytes read from program. */
10057429Smarkmstatic int stdin_eof = 0;	/* EOF message received from client. */
10157429Smarkmstatic int fdout_eof = 0;	/* EOF encountered reading from fdout. */
10257429Smarkmstatic int fderr_eof = 0;	/* EOF encountered readung from fderr. */
10374500Sgreenstatic int fdin_is_tty = 0;	/* fdin points to a tty. */
10457429Smarkmstatic int connection_in;	/* Connection to client (input). */
10557429Smarkmstatic int connection_out;	/* Connection to client (output). */
10676262Sgreenstatic int connection_closed = 0;	/* Connection to client closed. */
10776262Sgreenstatic u_int buffer_high;	/* "Soft" max buffer size. */
108181111Sdesstatic int no_more_sessions = 0; /* Disallow further sessions. */
10957429Smarkm
11057429Smarkm/*
11157429Smarkm * This SIGCHLD kludge is used to detect when the child exits.  The server
11257429Smarkm * will exit after that, as soon as forwarded connections have terminated.
11357429Smarkm */
11457429Smarkm
11592559Sdesstatic volatile sig_atomic_t child_terminated = 0;	/* The child has terminated. */
11657429Smarkm
117157019Sdes/* Cleanup on signals (!use_privsep case only) */
118157019Sdesstatic volatile sig_atomic_t received_sigterm = 0;
119157019Sdes
12092559Sdes/* prototypes */
12192559Sdesstatic void server_init_dispatch(void);
12260573Skris
12392559Sdes/*
12492559Sdes * we write to this pipe if a SIGCHLD is caught in order to avoid
12592559Sdes * the race between select() and child_terminated
12692559Sdes */
12792559Sdesstatic int notify_pipe[2];
12892559Sdesstatic void
12992559Sdesnotify_setup(void)
13057429Smarkm{
13192559Sdes	if (pipe(notify_pipe) < 0) {
13292559Sdes		error("pipe(notify_pipe) failed %s", strerror(errno));
133226046Sdes	} else if ((fcntl(notify_pipe[0], F_SETFD, FD_CLOEXEC) == -1) ||
134226046Sdes	    (fcntl(notify_pipe[1], F_SETFD, FD_CLOEXEC) == -1)) {
13592559Sdes		error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno));
13692559Sdes		close(notify_pipe[0]);
13792559Sdes		close(notify_pipe[1]);
13892559Sdes	} else {
13992559Sdes		set_nonblock(notify_pipe[0]);
14092559Sdes		set_nonblock(notify_pipe[1]);
14192559Sdes		return;
14257429Smarkm	}
14392559Sdes	notify_pipe[0] = -1;	/* read end */
14492559Sdes	notify_pipe[1] = -1;	/* write end */
14557429Smarkm}
14692559Sdesstatic void
14792559Sdesnotify_parent(void)
14857429Smarkm{
14992559Sdes	if (notify_pipe[1] != -1)
150255767Sdes		(void)write(notify_pipe[1], "", 1);
15192559Sdes}
15292559Sdesstatic void
15392559Sdesnotify_prepare(fd_set *readset)
15492559Sdes{
15592559Sdes	if (notify_pipe[0] != -1)
15692559Sdes		FD_SET(notify_pipe[0], readset);
15792559Sdes}
15892559Sdesstatic void
15992559Sdesnotify_done(fd_set *readset)
16092559Sdes{
16192559Sdes	char c;
16292559Sdes
16392559Sdes	if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset))
16492559Sdes		while (read(notify_pipe[0], &c, 1) != -1)
16592559Sdes			debug2("notify_done: reading");
16692559Sdes}
16792559Sdes
168162856Sdes/*ARGSUSED*/
16992559Sdesstatic void
17092559Sdessigchld_handler(int sig)
17192559Sdes{
17260573Skris	int save_errno = errno;
17360573Skris	child_terminated = 1;
174106130Sdes#ifndef _UNICOS
17598941Sdes	mysignal(SIGCHLD, sigchld_handler);
176106130Sdes#endif
17792559Sdes	notify_parent();
17860573Skris	errno = save_errno;
17957429Smarkm}
18057429Smarkm
181162856Sdes/*ARGSUSED*/
182157019Sdesstatic void
183157019Sdessigterm_handler(int sig)
184157019Sdes{
185157019Sdes	received_sigterm = sig;
186157019Sdes}
187157019Sdes
18857429Smarkm/*
18957429Smarkm * Make packets from buffered stderr data, and buffer it for sending
19057429Smarkm * to the client.
19157429Smarkm */
19292559Sdesstatic void
19376262Sgreenmake_packets_from_stderr_data(void)
19457429Smarkm{
195124211Sdes	u_int len;
19657429Smarkm
19757429Smarkm	/* Send buffered stderr data to the client. */
19857429Smarkm	while (buffer_len(&stderr_buffer) > 0 &&
19957429Smarkm	    packet_not_very_much_data_to_write()) {
20057429Smarkm		len = buffer_len(&stderr_buffer);
20157429Smarkm		if (packet_is_interactive()) {
20257429Smarkm			if (len > 512)
20357429Smarkm				len = 512;
20457429Smarkm		} else {
20557429Smarkm			/* Keep the packets at reasonable size. */
20657429Smarkm			if (len > packet_get_maxsize())
20757429Smarkm				len = packet_get_maxsize();
20857429Smarkm		}
20957429Smarkm		packet_start(SSH_SMSG_STDERR_DATA);
21057429Smarkm		packet_put_string(buffer_ptr(&stderr_buffer), len);
21157429Smarkm		packet_send();
21257429Smarkm		buffer_consume(&stderr_buffer, len);
21357429Smarkm		stderr_bytes += len;
21457429Smarkm	}
21557429Smarkm}
21657429Smarkm
21757429Smarkm/*
21857429Smarkm * Make packets from buffered stdout data, and buffer it for sending to the
21957429Smarkm * client.
22057429Smarkm */
22192559Sdesstatic void
22276262Sgreenmake_packets_from_stdout_data(void)
22357429Smarkm{
224124211Sdes	u_int len;
22557429Smarkm
22657429Smarkm	/* Send buffered stdout data to the client. */
22757429Smarkm	while (buffer_len(&stdout_buffer) > 0 &&
22857429Smarkm	    packet_not_very_much_data_to_write()) {
22957429Smarkm		len = buffer_len(&stdout_buffer);
23057429Smarkm		if (packet_is_interactive()) {
23157429Smarkm			if (len > 512)
23257429Smarkm				len = 512;
23357429Smarkm		} else {
23457429Smarkm			/* Keep the packets at reasonable size. */
23557429Smarkm			if (len > packet_get_maxsize())
23676262Sgreen				len = packet_get_maxsize();
23757429Smarkm		}
23857429Smarkm		packet_start(SSH_SMSG_STDOUT_DATA);
23957429Smarkm		packet_put_string(buffer_ptr(&stdout_buffer), len);
24057429Smarkm		packet_send();
24157429Smarkm		buffer_consume(&stdout_buffer, len);
24257429Smarkm		stdout_bytes += len;
24357429Smarkm	}
24457429Smarkm}
24557429Smarkm
24692559Sdesstatic void
24792559Sdesclient_alive_check(void)
24892559Sdes{
249126277Sdes	int channel_id;
25092559Sdes
25192559Sdes	/* timeout, check to see how many we have had */
252197679Sdes	if (packet_inc_alive_timeouts() > options.client_alive_count_max) {
253164149Sdes		logit("Timeout, client not responding.");
254164149Sdes		cleanup_exit(255);
255164149Sdes	}
25692559Sdes
25792559Sdes	/*
258126277Sdes	 * send a bogus global/channel request with "wantreply",
25992559Sdes	 * we should get back a failure
26092559Sdes	 */
261126277Sdes	if ((channel_id = channel_find_open()) == -1) {
262126277Sdes		packet_start(SSH2_MSG_GLOBAL_REQUEST);
263126277Sdes		packet_put_cstring("keepalive@openssh.com");
264126277Sdes		packet_put_char(1);	/* boolean: want reply */
265126277Sdes	} else {
266126277Sdes		channel_request_start(channel_id, "keepalive@openssh.com", 1);
267126277Sdes	}
26892559Sdes	packet_send();
26992559Sdes}
27092559Sdes
27157429Smarkm/*
27257429Smarkm * Sleep in select() until we can do something.  This will initialize the
27357429Smarkm * select masks.  Upon return, the masks will indicate which descriptors
27457429Smarkm * have data or can accept data.  Optionally, a maximum time can be specified
27557429Smarkm * for the duration of the wait (0 = infinite).
27657429Smarkm */
27792559Sdesstatic void
27876262Sgreenwait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
279323124Sdes    u_int *nallocp, u_int64_t max_time_ms)
28057429Smarkm{
28157429Smarkm	struct timeval tv, *tvp;
28257429Smarkm	int ret;
283240075Sdes	time_t minwait_secs = 0;
28476262Sgreen	int client_alive_scheduled = 0;
285181111Sdes	int program_alive_scheduled = 0;
28657429Smarkm
287240075Sdes	/* Allocate and update select() masks for channel descriptors. */
288240075Sdes	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
289240075Sdes	    &minwait_secs, 0);
290240075Sdes
291323124Sdes	/* XXX need proper deadline system for rekey/client alive */
292240075Sdes	if (minwait_secs != 0)
293323124Sdes		max_time_ms = MIN(max_time_ms, (u_int)minwait_secs * 1000);
294240075Sdes
29576262Sgreen	/*
29692559Sdes	 * if using client_alive, set the max timeout accordingly,
29776262Sgreen	 * and indicate that this particular timeout was for client
29876262Sgreen	 * alive by setting the client_alive_scheduled flag.
29976262Sgreen	 *
30076262Sgreen	 * this could be randomized somewhat to make traffic
30192559Sdes	 * analysis more difficult, but we're not doing it yet.
30276262Sgreen	 */
303323124Sdes	if (compat20 && options.client_alive_interval) {
304323124Sdes		uint64_t keepalive_ms =
305323124Sdes		    (uint64_t)options.client_alive_interval * 1000;
306323124Sdes
30792559Sdes		client_alive_scheduled = 1;
308323124Sdes		if (max_time_ms == 0 || max_time_ms > keepalive_ms)
309323124Sdes			max_time_ms = keepalive_ms;
31092559Sdes	}
31176262Sgreen
31260573Skris	if (compat20) {
31392559Sdes#if 0
31460573Skris		/* wrong: bad condition XXX */
31560573Skris		if (channel_not_very_much_buffered_data())
31692559Sdes#endif
31792559Sdes		FD_SET(connection_in, *readsetp);
31860573Skris	} else {
31961209Skris		/*
32061209Skris		 * Read packets from the client unless we have too much
32161209Skris		 * buffered stdin or channel data.
32261209Skris		 */
32361209Skris		if (buffer_len(&stdin_buffer) < buffer_high &&
32460573Skris		    channel_not_very_much_buffered_data())
32576262Sgreen			FD_SET(connection_in, *readsetp);
32661209Skris		/*
32761209Skris		 * If there is not too much data already buffered going to
32861209Skris		 * the client, try to get some more data from the program.
32961209Skris		 */
33061209Skris		if (packet_not_very_much_data_to_write()) {
331181111Sdes			program_alive_scheduled = child_terminated;
33261209Skris			if (!fdout_eof)
33376262Sgreen				FD_SET(fdout, *readsetp);
33461209Skris			if (!fderr_eof)
33576262Sgreen				FD_SET(fderr, *readsetp);
33661209Skris		}
33761209Skris		/*
33861209Skris		 * If we have buffered data, try to write some of that data
33961209Skris		 * to the program.
34061209Skris		 */
34161209Skris		if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
34276262Sgreen			FD_SET(fdin, *writesetp);
34360573Skris	}
34492559Sdes	notify_prepare(*readsetp);
34557429Smarkm
34657429Smarkm	/*
34757429Smarkm	 * If we have buffered packet data going to the client, mark that
34857429Smarkm	 * descriptor.
34957429Smarkm	 */
35057429Smarkm	if (packet_have_data_to_write())
35176262Sgreen		FD_SET(connection_out, *writesetp);
35257429Smarkm
35357429Smarkm	/*
35457429Smarkm	 * If child has terminated and there is enough buffer space to read
35557429Smarkm	 * from it, then read as much as is available and exit.
35657429Smarkm	 */
35757429Smarkm	if (child_terminated && packet_not_very_much_data_to_write())
358323124Sdes		if (max_time_ms == 0 || client_alive_scheduled)
359323124Sdes			max_time_ms = 100;
36057429Smarkm
361323124Sdes	if (max_time_ms == 0)
36257429Smarkm		tvp = NULL;
36357429Smarkm	else {
364323124Sdes		tv.tv_sec = max_time_ms / 1000;
365323124Sdes		tv.tv_usec = 1000 * (max_time_ms % 1000);
36657429Smarkm		tvp = &tv;
36757429Smarkm	}
36857429Smarkm
36957429Smarkm	/* Wait for something to happen, or the timeout to expire. */
37076262Sgreen	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
37157429Smarkm
37276262Sgreen	if (ret == -1) {
37392559Sdes		memset(*readsetp, 0, *nallocp);
37492559Sdes		memset(*writesetp, 0, *nallocp);
37557429Smarkm		if (errno != EINTR)
37657429Smarkm			error("select: %.100s", strerror(errno));
377181111Sdes	} else {
378181111Sdes		if (ret == 0 && client_alive_scheduled)
379181111Sdes			client_alive_check();
380181111Sdes		if (!compat20 && program_alive_scheduled && fdin_is_tty) {
381181111Sdes			if (!fdout_eof)
382181111Sdes				FD_SET(fdout, *readsetp);
383181111Sdes			if (!fderr_eof)
384181111Sdes				FD_SET(fderr, *readsetp);
385181111Sdes		}
386181111Sdes	}
38776262Sgreen
38892559Sdes	notify_done(*readsetp);
38957429Smarkm}
39057429Smarkm
39157429Smarkm/*
39257429Smarkm * Processes input from the client and the program.  Input data is stored
39357429Smarkm * in buffers and processed later.
39457429Smarkm */
39592559Sdesstatic void
396162856Sdesprocess_input(fd_set *readset)
39757429Smarkm{
398323124Sdes	struct ssh *ssh = active_state; /* XXX */
39957429Smarkm	int len;
40057429Smarkm	char buf[16384];
40157429Smarkm
40257429Smarkm	/* Read and buffer any input data from the client. */
40357429Smarkm	if (FD_ISSET(connection_in, readset)) {
404296781Sdes		len = read(connection_in, buf, sizeof(buf));
40557429Smarkm		if (len == 0) {
406323124Sdes			verbose("Connection closed by %.100s port %d",
407323124Sdes			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
40876262Sgreen			connection_closed = 1;
40976262Sgreen			if (compat20)
41076262Sgreen				return;
411126277Sdes			cleanup_exit(255);
41261209Skris		} else if (len < 0) {
413181111Sdes			if (errno != EINTR && errno != EAGAIN &&
414181111Sdes			    errno != EWOULDBLOCK) {
415106130Sdes				verbose("Read error from remote host "
416323124Sdes				    "%.100s port %d: %.100s",
417323124Sdes				    ssh_remote_ipaddr(ssh),
418323124Sdes				    ssh_remote_port(ssh), strerror(errno));
419126277Sdes				cleanup_exit(255);
42061209Skris			}
42161209Skris		} else {
42261209Skris			/* Buffer any received data. */
42361209Skris			packet_process_incoming(buf, len);
42457429Smarkm		}
42557429Smarkm	}
42660573Skris	if (compat20)
42760573Skris		return;
42860573Skris
42957429Smarkm	/* Read and buffer any available stdout data from the program. */
43057429Smarkm	if (!fdout_eof && FD_ISSET(fdout, readset)) {
431162856Sdes		errno = 0;
43257429Smarkm		len = read(fdout, buf, sizeof(buf));
433181111Sdes		if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
434181111Sdes		    errno == EWOULDBLOCK) && !child_terminated))) {
43561209Skris			/* do nothing */
436162856Sdes#ifndef PTY_ZEROREAD
43761209Skris		} else if (len <= 0) {
438162856Sdes#else
439162856Sdes		} else if ((!isatty(fdout) && len <= 0) ||
440162856Sdes		    (isatty(fdout) && (len < 0 || (len == 0 && errno != 0)))) {
441162856Sdes#endif
44257429Smarkm			fdout_eof = 1;
44361209Skris		} else {
44457429Smarkm			buffer_append(&stdout_buffer, buf, len);
44557429Smarkm			fdout_bytes += len;
44657429Smarkm		}
44757429Smarkm	}
44857429Smarkm	/* Read and buffer any available stderr data from the program. */
44957429Smarkm	if (!fderr_eof && FD_ISSET(fderr, readset)) {
450162856Sdes		errno = 0;
45157429Smarkm		len = read(fderr, buf, sizeof(buf));
452181111Sdes		if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
453181111Sdes		    errno == EWOULDBLOCK) && !child_terminated))) {
45461209Skris			/* do nothing */
455162856Sdes#ifndef PTY_ZEROREAD
45661209Skris		} else if (len <= 0) {
457162856Sdes#else
458162856Sdes		} else if ((!isatty(fderr) && len <= 0) ||
459162856Sdes		    (isatty(fderr) && (len < 0 || (len == 0 && errno != 0)))) {
460162856Sdes#endif
46157429Smarkm			fderr_eof = 1;
46261209Skris		} else {
46357429Smarkm			buffer_append(&stderr_buffer, buf, len);
46461209Skris		}
46557429Smarkm	}
46657429Smarkm}
46757429Smarkm
46857429Smarkm/*
46957429Smarkm * Sends data from internal buffers to client program stdin.
47057429Smarkm */
47192559Sdesstatic void
472162856Sdesprocess_output(fd_set *writeset)
47357429Smarkm{
47474500Sgreen	struct termios tio;
47592559Sdes	u_char *data;
47692559Sdes	u_int dlen;
47757429Smarkm	int len;
47857429Smarkm
47957429Smarkm	/* Write buffered data to program stdin. */
48060573Skris	if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) {
48192559Sdes		data = buffer_ptr(&stdin_buffer);
48292559Sdes		dlen = buffer_len(&stdin_buffer);
48392559Sdes		len = write(fdin, data, dlen);
484181111Sdes		if (len < 0 &&
485181111Sdes		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
48661209Skris			/* do nothing */
48761209Skris		} else if (len <= 0) {
48860573Skris			if (fdin != fdout)
48957429Smarkm				close(fdin);
49057429Smarkm			else
49157429Smarkm				shutdown(fdin, SHUT_WR); /* We will no longer send. */
49257429Smarkm			fdin = -1;
49357429Smarkm		} else {
49474500Sgreen			/* Successful write. */
49592559Sdes			if (fdin_is_tty && dlen >= 1 && data[0] != '\r' &&
49692559Sdes			    tcgetattr(fdin, &tio) == 0 &&
49774500Sgreen			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
49874500Sgreen				/*
49974500Sgreen				 * Simulate echo to reduce the impact of
50074500Sgreen				 * traffic analysis
50174500Sgreen				 */
50276262Sgreen				packet_send_ignore(len);
50374500Sgreen				packet_send();
50474500Sgreen			}
50574500Sgreen			/* Consume the data from the buffer. */
50657429Smarkm			buffer_consume(&stdin_buffer, len);
50757429Smarkm			/* Update the count of bytes written to the program. */
50857429Smarkm			stdin_bytes += len;
50957429Smarkm		}
51057429Smarkm	}
51157429Smarkm	/* Send any buffered packet data to the client. */
51257429Smarkm	if (FD_ISSET(connection_out, writeset))
51357429Smarkm		packet_write_poll();
51457429Smarkm}
51557429Smarkm
51657429Smarkm/*
51757429Smarkm * Wait until all buffered output has been sent to the client.
51857429Smarkm * This is used when the program terminates.
51957429Smarkm */
52092559Sdesstatic void
52176262Sgreendrain_output(void)
52257429Smarkm{
52357429Smarkm	/* Send any buffered stdout data to the client. */
52457429Smarkm	if (buffer_len(&stdout_buffer) > 0) {
52557429Smarkm		packet_start(SSH_SMSG_STDOUT_DATA);
52657429Smarkm		packet_put_string(buffer_ptr(&stdout_buffer),
52757429Smarkm				  buffer_len(&stdout_buffer));
52857429Smarkm		packet_send();
52957429Smarkm		/* Update the count of sent bytes. */
53057429Smarkm		stdout_bytes += buffer_len(&stdout_buffer);
53157429Smarkm	}
53257429Smarkm	/* Send any buffered stderr data to the client. */
53357429Smarkm	if (buffer_len(&stderr_buffer) > 0) {
53457429Smarkm		packet_start(SSH_SMSG_STDERR_DATA);
53557429Smarkm		packet_put_string(buffer_ptr(&stderr_buffer),
53657429Smarkm				  buffer_len(&stderr_buffer));
53757429Smarkm		packet_send();
53857429Smarkm		/* Update the count of sent bytes. */
53957429Smarkm		stderr_bytes += buffer_len(&stderr_buffer);
54057429Smarkm	}
54157429Smarkm	/* Wait until all buffered data has been written to the client. */
54257429Smarkm	packet_write_wait();
54357429Smarkm}
54457429Smarkm
54592559Sdesstatic void
54676262Sgreenprocess_buffered_input_packets(void)
54760573Skris{
548295367Sdes	dispatch_run(DISPATCH_NONBLOCK, NULL, active_state);
54960573Skris}
55060573Skris
55157429Smarkm/*
55257429Smarkm * Performs the interactive session.  This handles data transmission between
55357429Smarkm * the client and the program.  Note that the notion of stdin, stdout, and
55457429Smarkm * stderr in this function is sort of reversed: this function writes to
55557429Smarkm * stdin (of the child program), and reads from stdout and stderr (of the
55657429Smarkm * child program).
55757429Smarkm */
55860573Skrisvoid
55960573Skrisserver_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
56057429Smarkm{
56176262Sgreen	fd_set *readset = NULL, *writeset = NULL;
562137019Sdes	int max_fd = 0;
563137019Sdes	u_int nalloc = 0;
56460573Skris	int wait_status;	/* Status returned by wait(). */
56560573Skris	pid_t wait_pid;		/* pid returned by wait(). */
56657429Smarkm	int waiting_termination = 0;	/* Have displayed waiting close message. */
567255767Sdes	u_int64_t max_time_milliseconds;
56876262Sgreen	u_int previous_stdout_buffer_bytes;
56976262Sgreen	u_int stdout_buffer_bytes;
57057429Smarkm	int type;
57157429Smarkm
57257429Smarkm	debug("Entering interactive session.");
57357429Smarkm
57457429Smarkm	/* Initialize the SIGCHLD kludge. */
57557429Smarkm	child_terminated = 0;
57698941Sdes	mysignal(SIGCHLD, sigchld_handler);
57757429Smarkm
578157019Sdes	if (!use_privsep) {
579157019Sdes		signal(SIGTERM, sigterm_handler);
580157019Sdes		signal(SIGINT, sigterm_handler);
581157019Sdes		signal(SIGQUIT, sigterm_handler);
582157019Sdes	}
583157019Sdes
58457429Smarkm	/* Initialize our global variables. */
58557429Smarkm	fdin = fdin_arg;
58657429Smarkm	fdout = fdout_arg;
58757429Smarkm	fderr = fderr_arg;
58861209Skris
58961209Skris	/* nonblocking IO */
59061209Skris	set_nonblock(fdin);
59161209Skris	set_nonblock(fdout);
59261209Skris	/* we don't have stderr for interactive terminal sessions, see below */
59361209Skris	if (fderr != -1)
59461209Skris		set_nonblock(fderr);
59561209Skris
59674500Sgreen	if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin))
59774500Sgreen		fdin_is_tty = 1;
59874500Sgreen
59957429Smarkm	connection_in = packet_get_connection_in();
60057429Smarkm	connection_out = packet_get_connection_out();
60157429Smarkm
60292559Sdes	notify_setup();
60392559Sdes
60457429Smarkm	previous_stdout_buffer_bytes = 0;
60557429Smarkm
60657429Smarkm	/* Set approximate I/O buffer size. */
60757429Smarkm	if (packet_is_interactive())
60857429Smarkm		buffer_high = 4096;
60957429Smarkm	else
61057429Smarkm		buffer_high = 64 * 1024;
61157429Smarkm
61292559Sdes#if 0
61357429Smarkm	/* Initialize max_fd to the maximum of the known file descriptors. */
61492559Sdes	max_fd = MAX(connection_in, connection_out);
61592559Sdes	max_fd = MAX(max_fd, fdin);
61692559Sdes	max_fd = MAX(max_fd, fdout);
61776262Sgreen	if (fderr != -1)
61876262Sgreen		max_fd = MAX(max_fd, fderr);
61992559Sdes#endif
62057429Smarkm
62157429Smarkm	/* Initialize Initialize buffers. */
62257429Smarkm	buffer_init(&stdin_buffer);
62357429Smarkm	buffer_init(&stdout_buffer);
62457429Smarkm	buffer_init(&stderr_buffer);
62557429Smarkm
62657429Smarkm	/*
62757429Smarkm	 * If we have no separate fderr (which is the case when we have a pty
62857429Smarkm	 * - there we cannot make difference between data sent to stdout and
62957429Smarkm	 * stderr), indicate that we have seen an EOF from stderr.  This way
630157019Sdes	 * we don't need to check the descriptor everywhere.
63157429Smarkm	 */
63257429Smarkm	if (fderr == -1)
63357429Smarkm		fderr_eof = 1;
63457429Smarkm
63560573Skris	server_init_dispatch();
63660573Skris
63757429Smarkm	/* Main loop of the server for the interactive session mode. */
63857429Smarkm	for (;;) {
63957429Smarkm
64057429Smarkm		/* Process buffered packets from the client. */
64157429Smarkm		process_buffered_input_packets();
64257429Smarkm
64357429Smarkm		/*
64457429Smarkm		 * If we have received eof, and there is no more pending
64557429Smarkm		 * input data, cause a real eof by closing fdin.
64657429Smarkm		 */
64757429Smarkm		if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) {
64860573Skris			if (fdin != fdout)
64957429Smarkm				close(fdin);
65057429Smarkm			else
65157429Smarkm				shutdown(fdin, SHUT_WR); /* We will no longer send. */
65257429Smarkm			fdin = -1;
65357429Smarkm		}
65457429Smarkm		/* Make packets from buffered stderr data to send to the client. */
65557429Smarkm		make_packets_from_stderr_data();
65657429Smarkm
65757429Smarkm		/*
65857429Smarkm		 * Make packets from buffered stdout data to send to the
65957429Smarkm		 * client. If there is very little to send, this arranges to
66057429Smarkm		 * not send them now, but to wait a short while to see if we
66157429Smarkm		 * are getting more data. This is necessary, as some systems
66257429Smarkm		 * wake up readers from a pty after each separate character.
66357429Smarkm		 */
66457429Smarkm		max_time_milliseconds = 0;
66557429Smarkm		stdout_buffer_bytes = buffer_len(&stdout_buffer);
66657429Smarkm		if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 &&
66757429Smarkm		    stdout_buffer_bytes != previous_stdout_buffer_bytes) {
66857429Smarkm			/* try again after a while */
66957429Smarkm			max_time_milliseconds = 10;
67057429Smarkm		} else {
67157429Smarkm			/* Send it now. */
67257429Smarkm			make_packets_from_stdout_data();
67357429Smarkm		}
67457429Smarkm		previous_stdout_buffer_bytes = buffer_len(&stdout_buffer);
67557429Smarkm
67657429Smarkm		/* Send channel data to the client. */
67757429Smarkm		if (packet_not_very_much_data_to_write())
67857429Smarkm			channel_output_poll();
67957429Smarkm
68057429Smarkm		/*
68157429Smarkm		 * Bail out of the loop if the program has closed its output
68257429Smarkm		 * descriptors, and we have no more data to send to the
68357429Smarkm		 * client, and there is no pending buffered data.
68457429Smarkm		 */
68557429Smarkm		if (fdout_eof && fderr_eof && !packet_have_data_to_write() &&
68657429Smarkm		    buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) {
68757429Smarkm			if (!channel_still_open())
68860573Skris				break;
68957429Smarkm			if (!waiting_termination) {
69057429Smarkm				const char *s = "Waiting for forwarded connections to terminate...\r\n";
69157429Smarkm				char *cp;
69257429Smarkm				waiting_termination = 1;
69357429Smarkm				buffer_append(&stderr_buffer, s, strlen(s));
69457429Smarkm
69557429Smarkm				/* Display list of open channels. */
69657429Smarkm				cp = channel_open_message();
69757429Smarkm				buffer_append(&stderr_buffer, cp, strlen(cp));
698255767Sdes				free(cp);
69957429Smarkm			}
70057429Smarkm		}
70192559Sdes		max_fd = MAX(connection_in, connection_out);
70292559Sdes		max_fd = MAX(max_fd, fdin);
70392559Sdes		max_fd = MAX(max_fd, fdout);
70492559Sdes		max_fd = MAX(max_fd, fderr);
70592559Sdes		max_fd = MAX(max_fd, notify_pipe[0]);
70692559Sdes
70757429Smarkm		/* Sleep in select() until we can do something. */
70876262Sgreen		wait_until_can_do_something(&readset, &writeset, &max_fd,
70992559Sdes		    &nalloc, max_time_milliseconds);
71057429Smarkm
711157019Sdes		if (received_sigterm) {
712248619Sdes			logit("Exiting on signal %d", (int)received_sigterm);
713157019Sdes			/* Clean up sessions, utmp, etc. */
714157019Sdes			cleanup_exit(255);
715157019Sdes		}
716157019Sdes
71757429Smarkm		/* Process any channel events. */
71876262Sgreen		channel_after_select(readset, writeset);
71957429Smarkm
72057429Smarkm		/* Process input from the client and from program stdout/stderr. */
72176262Sgreen		process_input(readset);
72257429Smarkm
72357429Smarkm		/* Process output to the client and to program stdin. */
72476262Sgreen		process_output(writeset);
72557429Smarkm	}
726255767Sdes	free(readset);
727255767Sdes	free(writeset);
72857429Smarkm
72957429Smarkm	/* Cleanup and termination code. */
73057429Smarkm
73157429Smarkm	/* Wait until all output has been sent to the client. */
73257429Smarkm	drain_output();
73357429Smarkm
734240075Sdes	debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
73592559Sdes	    stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
73657429Smarkm
73757429Smarkm	/* Free and clear the buffers. */
73857429Smarkm	buffer_free(&stdin_buffer);
73957429Smarkm	buffer_free(&stdout_buffer);
74057429Smarkm	buffer_free(&stderr_buffer);
74157429Smarkm
74257429Smarkm	/* Close the file descriptors. */
74357429Smarkm	if (fdout != -1)
74457429Smarkm		close(fdout);
74557429Smarkm	fdout = -1;
74657429Smarkm	fdout_eof = 1;
74757429Smarkm	if (fderr != -1)
74857429Smarkm		close(fderr);
74957429Smarkm	fderr = -1;
75057429Smarkm	fderr_eof = 1;
75157429Smarkm	if (fdin != -1)
75257429Smarkm		close(fdin);
75357429Smarkm	fdin = -1;
75457429Smarkm
75592559Sdes	channel_free_all();
75657429Smarkm
75757429Smarkm	/* We no longer want our SIGCHLD handler to be called. */
75898941Sdes	mysignal(SIGCHLD, SIG_DFL);
75957429Smarkm
76098684Sdes	while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0)
76198684Sdes		if (errno != EINTR)
76298684Sdes			packet_disconnect("wait: %.100s", strerror(errno));
76398684Sdes	if (wait_pid != pid)
76498684Sdes		error("Strange, wait returned pid %ld, expected %ld",
76598684Sdes		    (long)wait_pid, (long)pid);
76692559Sdes
76757429Smarkm	/* Check if it exited normally. */
76857429Smarkm	if (WIFEXITED(wait_status)) {
76957429Smarkm		/* Yes, normal exit.  Get exit status and send it to the client. */
77057429Smarkm		debug("Command exited with status %d.", WEXITSTATUS(wait_status));
77157429Smarkm		packet_start(SSH_SMSG_EXITSTATUS);
77257429Smarkm		packet_put_int(WEXITSTATUS(wait_status));
77357429Smarkm		packet_send();
77457429Smarkm		packet_write_wait();
77557429Smarkm
77657429Smarkm		/*
77757429Smarkm		 * Wait for exit confirmation.  Note that there might be
77857429Smarkm		 * other packets coming before it; however, the program has
77957429Smarkm		 * already died so we just ignore them.  The client is
78057429Smarkm		 * supposed to respond with the confirmation when it receives
78157429Smarkm		 * the exit status.
78257429Smarkm		 */
78357429Smarkm		do {
78492559Sdes			type = packet_read();
78557429Smarkm		}
78657429Smarkm		while (type != SSH_CMSG_EXIT_CONFIRMATION);
78757429Smarkm
78857429Smarkm		debug("Received exit confirmation.");
78957429Smarkm		return;
79057429Smarkm	}
79157429Smarkm	/* Check if the program terminated due to a signal. */
79257429Smarkm	if (WIFSIGNALED(wait_status))
79357429Smarkm		packet_disconnect("Command terminated on signal %d.",
79457429Smarkm				  WTERMSIG(wait_status));
79557429Smarkm
79657429Smarkm	/* Some weird exit cause.  Just exit. */
79757429Smarkm	packet_disconnect("wait returned status %04x.", wait_status);
79857429Smarkm	/* NOTREACHED */
79957429Smarkm}
80060573Skris
80192559Sdesstatic void
80292559Sdescollect_children(void)
80392559Sdes{
80492559Sdes	pid_t pid;
80592559Sdes	sigset_t oset, nset;
80692559Sdes	int status;
80792559Sdes
80892559Sdes	/* block SIGCHLD while we check for dead children */
80992559Sdes	sigemptyset(&nset);
81092559Sdes	sigaddset(&nset, SIGCHLD);
81192559Sdes	sigprocmask(SIG_BLOCK, &nset, &oset);
81292559Sdes	if (child_terminated) {
813162856Sdes		debug("Received SIGCHLD.");
81498684Sdes		while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
81598684Sdes		    (pid < 0 && errno == EINTR))
81698684Sdes			if (pid > 0)
81798684Sdes				session_close_by_pid(pid, status);
81892559Sdes		child_terminated = 0;
81992559Sdes	}
82092559Sdes	sigprocmask(SIG_SETMASK, &oset, NULL);
82192559Sdes}
82292559Sdes
82360573Skrisvoid
82492559Sdesserver_loop2(Authctxt *authctxt)
82560573Skris{
82676262Sgreen	fd_set *readset = NULL, *writeset = NULL;
827296781Sdes	int max_fd;
828255767Sdes	u_int nalloc = 0;
829255767Sdes	u_int64_t rekey_timeout_ms = 0;
83060573Skris
83160573Skris	debug("Entering interactive session for SSH2.");
83260573Skris
83398941Sdes	mysignal(SIGCHLD, sigchld_handler);
83460573Skris	child_terminated = 0;
83560573Skris	connection_in = packet_get_connection_in();
83660573Skris	connection_out = packet_get_connection_out();
83776262Sgreen
838157019Sdes	if (!use_privsep) {
839157019Sdes		signal(SIGTERM, sigterm_handler);
840157019Sdes		signal(SIGINT, sigterm_handler);
841157019Sdes		signal(SIGQUIT, sigterm_handler);
842157019Sdes	}
843157019Sdes
84492559Sdes	notify_setup();
84592559Sdes
84676262Sgreen	max_fd = MAX(connection_in, connection_out);
84792559Sdes	max_fd = MAX(max_fd, notify_pipe[0]);
84876262Sgreen
84960573Skris	server_init_dispatch();
85060573Skris
85160573Skris	for (;;) {
85260573Skris		process_buffered_input_packets();
85376262Sgreen
854296781Sdes		if (!ssh_packet_is_rekeying(active_state) &&
855296781Sdes		    packet_not_very_much_data_to_write())
85660573Skris			channel_output_poll();
857296781Sdes		if (options.rekey_interval > 0 && compat20 &&
858296781Sdes		    !ssh_packet_is_rekeying(active_state))
859255767Sdes			rekey_timeout_ms = packet_get_rekey_timeout() * 1000;
860255767Sdes		else
861255767Sdes			rekey_timeout_ms = 0;
862255767Sdes
86376262Sgreen		wait_until_can_do_something(&readset, &writeset, &max_fd,
864255767Sdes		    &nalloc, rekey_timeout_ms);
86592559Sdes
866157019Sdes		if (received_sigterm) {
867248619Sdes			logit("Exiting on signal %d", (int)received_sigterm);
868157019Sdes			/* Clean up sessions, utmp, etc. */
869157019Sdes			cleanup_exit(255);
870157019Sdes		}
871157019Sdes
87292559Sdes		collect_children();
873296781Sdes		if (!ssh_packet_is_rekeying(active_state))
87476262Sgreen			channel_after_select(readset, writeset);
87576262Sgreen		process_input(readset);
87676262Sgreen		if (connection_closed)
87776262Sgreen			break;
87876262Sgreen		process_output(writeset);
87960573Skris	}
88092559Sdes	collect_children();
88192559Sdes
882255767Sdes	free(readset);
883255767Sdes	free(writeset);
88476262Sgreen
88592559Sdes	/* free all channels, no more reads and writes */
88692559Sdes	channel_free_all();
88792559Sdes
88892559Sdes	/* free remaining sessions, e.g. remove wtmp entries */
88998684Sdes	session_destroy_all(NULL);
89060573Skris}
89160573Skris
892295367Sdesstatic int
893126277Sdesserver_input_keep_alive(int type, u_int32_t seq, void *ctxt)
89476262Sgreen{
895126277Sdes	debug("Got %d/%u for keepalive", type, seq);
89692559Sdes	/*
89792559Sdes	 * reset timeout, since we got a sane answer from the client.
89876262Sgreen	 * even if this was generated by something other than
89976262Sgreen	 * the bogus CHANNEL_REQUEST we send for keepalives.
90076262Sgreen	 */
901197679Sdes	packet_set_alive_timeouts(0);
902295367Sdes	return 0;
90376262Sgreen}
90476262Sgreen
905295367Sdesstatic int
90692559Sdesserver_input_stdin_data(int type, u_int32_t seq, void *ctxt)
90760573Skris{
90860573Skris	char *data;
90976262Sgreen	u_int data_len;
91060573Skris
91160573Skris	/* Stdin data from the client.  Append it to the buffer. */
91260573Skris	/* Ignore any data if the client has closed stdin. */
91360573Skris	if (fdin == -1)
914295367Sdes		return 0;
91560573Skris	data = packet_get_string(&data_len);
91692559Sdes	packet_check_eom();
91760573Skris	buffer_append(&stdin_buffer, data, data_len);
918264377Sdes	explicit_bzero(data, data_len);
919255767Sdes	free(data);
920295367Sdes	return 0;
92160573Skris}
92260573Skris
923295367Sdesstatic int
92492559Sdesserver_input_eof(int type, u_int32_t seq, void *ctxt)
92560573Skris{
92660573Skris	/*
92760573Skris	 * Eof from the client.  The stdin descriptor to the
92860573Skris	 * program will be closed when all buffered data has
92960573Skris	 * drained.
93060573Skris	 */
93160573Skris	debug("EOF received for stdin.");
93292559Sdes	packet_check_eom();
93360573Skris	stdin_eof = 1;
934295367Sdes	return 0;
93560573Skris}
93660573Skris
937295367Sdesstatic int
93892559Sdesserver_input_window_size(int type, u_int32_t seq, void *ctxt)
93960573Skris{
940162856Sdes	u_int row = packet_get_int();
941162856Sdes	u_int col = packet_get_int();
942162856Sdes	u_int xpixel = packet_get_int();
943162856Sdes	u_int ypixel = packet_get_int();
94460573Skris
94560573Skris	debug("Window change received.");
94692559Sdes	packet_check_eom();
94760573Skris	if (fdin != -1)
94860573Skris		pty_change_window_size(fdin, row, col, xpixel, ypixel);
949295367Sdes	return 0;
95060573Skris}
95160573Skris
95292559Sdesstatic Channel *
953126277Sdesserver_request_direct_tcpip(void)
95460573Skris{
955248619Sdes	Channel *c = NULL;
95660573Skris	char *target, *originator;
957192595Sdes	u_short target_port, originator_port;
95860573Skris
95960573Skris	target = packet_get_string(NULL);
96060573Skris	target_port = packet_get_int();
96160573Skris	originator = packet_get_string(NULL);
96260573Skris	originator_port = packet_get_int();
96392559Sdes	packet_check_eom();
96461209Skris
965181111Sdes	debug("server_request_direct_tcpip: originator %s port %d, target %s "
966181111Sdes	    "port %d", originator, originator_port, target, target_port);
96765668Skris
968248619Sdes	/* XXX fine grained permissions */
969248619Sdes	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 &&
970248619Sdes	    !no_port_forwarding_flag) {
971295367Sdes		c = channel_connect_to_port(target, target_port,
972248619Sdes		    "direct-tcpip", "direct-tcpip");
973248619Sdes	} else {
974248619Sdes		logit("refused local port forward: "
975248619Sdes		    "originator %s port %d, target %s port %d",
976248619Sdes		    originator, originator_port, target, target_port);
977248619Sdes	}
978181111Sdes
979255767Sdes	free(originator);
980255767Sdes	free(target);
981181111Sdes
98292559Sdes	return c;
98360573Skris}
98460573Skris
98592559Sdesstatic Channel *
986295367Sdesserver_request_direct_streamlocal(void)
987295367Sdes{
988295367Sdes	Channel *c = NULL;
989295367Sdes	char *target, *originator;
990295367Sdes	u_short originator_port;
991295367Sdes
992295367Sdes	target = packet_get_string(NULL);
993295367Sdes	originator = packet_get_string(NULL);
994295367Sdes	originator_port = packet_get_int();
995295367Sdes	packet_check_eom();
996295367Sdes
997295367Sdes	debug("server_request_direct_streamlocal: originator %s port %d, target %s",
998295367Sdes	    originator, originator_port, target);
999295367Sdes
1000295367Sdes	/* XXX fine grained permissions */
1001295367Sdes	if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 &&
1002323124Sdes	    !no_port_forwarding_flag) {
1003295367Sdes		c = channel_connect_to_path(target,
1004295367Sdes		    "direct-streamlocal@openssh.com", "direct-streamlocal");
1005295367Sdes	} else {
1006295367Sdes		logit("refused streamlocal port forward: "
1007295367Sdes		    "originator %s port %d, target %s",
1008295367Sdes		    originator, originator_port, target);
1009295367Sdes	}
1010295367Sdes
1011295367Sdes	free(originator);
1012295367Sdes	free(target);
1013295367Sdes
1014295367Sdes	return c;
1015295367Sdes}
1016295367Sdes
1017295367Sdesstatic Channel *
1018157019Sdesserver_request_tun(void)
1019157019Sdes{
1020157019Sdes	Channel *c = NULL;
1021157019Sdes	int mode, tun;
1022157019Sdes	int sock;
1023157019Sdes
1024157019Sdes	mode = packet_get_int();
1025157019Sdes	switch (mode) {
1026157019Sdes	case SSH_TUNMODE_POINTOPOINT:
1027157019Sdes	case SSH_TUNMODE_ETHERNET:
1028157019Sdes		break;
1029157019Sdes	default:
1030157019Sdes		packet_send_debug("Unsupported tunnel device mode.");
1031157019Sdes		return NULL;
1032157019Sdes	}
1033157019Sdes	if ((options.permit_tun & mode) == 0) {
1034157019Sdes		packet_send_debug("Server has rejected tunnel device "
1035157019Sdes		    "forwarding");
1036157019Sdes		return NULL;
1037157019Sdes	}
1038157019Sdes
1039157019Sdes	tun = packet_get_int();
1040157019Sdes	if (forced_tun_device != -1) {
1041162856Sdes		if (tun != SSH_TUNID_ANY && forced_tun_device != tun)
1042157019Sdes			goto done;
1043157019Sdes		tun = forced_tun_device;
1044157019Sdes	}
1045157019Sdes	sock = tun_open(tun, mode);
1046157019Sdes	if (sock < 0)
1047157019Sdes		goto done;
1048294693Sdes	c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
1049294693Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
1050157019Sdes	c->datagram = 1;
1051157019Sdes#if defined(SSH_TUN_FILTER)
1052157019Sdes	if (mode == SSH_TUNMODE_POINTOPOINT)
1053157019Sdes		channel_register_filter(c->self, sys_tun_infilter,
1054181111Sdes		    sys_tun_outfilter, NULL, NULL);
1055157019Sdes#endif
1056157019Sdes
1057157019Sdes done:
1058157019Sdes	if (c == NULL)
1059157019Sdes		packet_send_debug("Failed to open the tunnel device.");
1060157019Sdes	return c;
1061157019Sdes}
1062157019Sdes
1063157019Sdesstatic Channel *
1064126277Sdesserver_request_session(void)
106576262Sgreen{
106692559Sdes	Channel *c;
106776262Sgreen
106876262Sgreen	debug("input_session_request");
106992559Sdes	packet_check_eom();
1070181111Sdes
1071181111Sdes	if (no_more_sessions) {
1072181111Sdes		packet_disconnect("Possible attack: attempt to open a session "
1073181111Sdes		    "after additional sessions disabled");
1074181111Sdes	}
1075181111Sdes
107676262Sgreen	/*
107776262Sgreen	 * A server session has no fd to read or write until a
107876262Sgreen	 * CHANNEL_REQUEST for a shell is made, so we set the type to
107976262Sgreen	 * SSH_CHANNEL_LARVAL.  Additionally, a callback for handling all
108076262Sgreen	 * CHANNEL_REQUEST messages is registered.
108176262Sgreen	 */
1082126277Sdes	c = channel_new("session", SSH_CHANNEL_LARVAL,
108392559Sdes	    -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
1084124211Sdes	    0, "server-session", 1);
1085126277Sdes	if (session_open(the_authctxt, c->self) != 1) {
108692559Sdes		debug("session open failed, free channel %d", c->self);
108792559Sdes		channel_free(c);
108892559Sdes		return NULL;
108976262Sgreen	}
1090157019Sdes	channel_register_cleanup(c->self, session_close_by_channel, 0);
109192559Sdes	return c;
109276262Sgreen}
109376262Sgreen
1094295367Sdesstatic int
109592559Sdesserver_input_channel_open(int type, u_int32_t seq, void *ctxt)
109660573Skris{
109760573Skris	Channel *c = NULL;
109860573Skris	char *ctype;
109960573Skris	int rchan;
110099063Sdes	u_int rmaxpack, rwindow, len;
110160573Skris
110260573Skris	ctype = packet_get_string(&len);
110360573Skris	rchan = packet_get_int();
110460573Skris	rwindow = packet_get_int();
110560573Skris	rmaxpack = packet_get_int();
110660573Skris
110769587Sgreen	debug("server_input_channel_open: ctype %s rchan %d win %d max %d",
110860573Skris	    ctype, rchan, rwindow, rmaxpack);
110960573Skris
111060573Skris	if (strcmp(ctype, "session") == 0) {
1111126277Sdes		c = server_request_session();
111260573Skris	} else if (strcmp(ctype, "direct-tcpip") == 0) {
1113126277Sdes		c = server_request_direct_tcpip();
1114295367Sdes	} else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) {
1115295367Sdes		c = server_request_direct_streamlocal();
1116157019Sdes	} else if (strcmp(ctype, "tun@openssh.com") == 0) {
1117157019Sdes		c = server_request_tun();
111860573Skris	}
111960573Skris	if (c != NULL) {
112076262Sgreen		debug("server_input_channel_open: confirm %s", ctype);
112160573Skris		c->remote_id = rchan;
112260573Skris		c->remote_window = rwindow;
112360573Skris		c->remote_maxpacket = rmaxpack;
112492559Sdes		if (c->type != SSH_CHANNEL_CONNECTING) {
112592559Sdes			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
112692559Sdes			packet_put_int(c->remote_id);
112792559Sdes			packet_put_int(c->self);
112892559Sdes			packet_put_int(c->local_window);
112992559Sdes			packet_put_int(c->local_maxpacket);
113092559Sdes			packet_send();
113192559Sdes		}
113260573Skris	} else {
113376262Sgreen		debug("server_input_channel_open: failure %s", ctype);
113460573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
113560573Skris		packet_put_int(rchan);
113660573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
113792559Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
113892559Sdes			packet_put_cstring("open failed");
113992559Sdes			packet_put_cstring("");
114092559Sdes		}
114160573Skris		packet_send();
114260573Skris	}
1143255767Sdes	free(ctype);
1144295367Sdes	return 0;
114560573Skris}
114660573Skris
1147295367Sdesstatic int
1148295367Sdesserver_input_hostkeys_prove(struct sshbuf **respp)
1149295367Sdes{
1150295367Sdes	struct ssh *ssh = active_state; /* XXX */
1151295367Sdes	struct sshbuf *resp = NULL;
1152295367Sdes	struct sshbuf *sigbuf = NULL;
1153295367Sdes	struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL;
1154295367Sdes	int r, ndx, success = 0;
1155295367Sdes	const u_char *blob;
1156295367Sdes	u_char *sig = 0;
1157295367Sdes	size_t blen, slen;
1158295367Sdes
1159295367Sdes	if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL)
1160295367Sdes		fatal("%s: sshbuf_new", __func__);
1161295367Sdes
1162295367Sdes	while (ssh_packet_remaining(ssh) > 0) {
1163295367Sdes		sshkey_free(key);
1164295367Sdes		key = NULL;
1165295367Sdes		if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 ||
1166295367Sdes		    (r = sshkey_from_blob(blob, blen, &key)) != 0) {
1167295367Sdes			error("%s: couldn't parse key: %s",
1168295367Sdes			    __func__, ssh_err(r));
1169295367Sdes			goto out;
1170295367Sdes		}
1171295367Sdes		/*
1172295367Sdes		 * Better check that this is actually one of our hostkeys
1173295367Sdes		 * before attempting to sign anything with it.
1174295367Sdes		 */
1175295367Sdes		if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) {
1176295367Sdes			error("%s: unknown host %s key",
1177295367Sdes			    __func__, sshkey_type(key));
1178295367Sdes			goto out;
1179295367Sdes		}
1180295367Sdes		/*
1181295367Sdes		 * XXX refactor: make kex->sign just use an index rather
1182295367Sdes		 * than passing in public and private keys
1183295367Sdes		 */
1184295367Sdes		if ((key_prv = get_hostkey_by_index(ndx)) == NULL &&
1185295367Sdes		    (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) {
1186295367Sdes			error("%s: can't retrieve hostkey %d", __func__, ndx);
1187295367Sdes			goto out;
1188295367Sdes		}
1189295367Sdes		sshbuf_reset(sigbuf);
1190295367Sdes		free(sig);
1191295367Sdes		sig = NULL;
1192295367Sdes		if ((r = sshbuf_put_cstring(sigbuf,
1193295367Sdes		    "hostkeys-prove-00@openssh.com")) != 0 ||
1194295367Sdes		    (r = sshbuf_put_string(sigbuf,
1195295367Sdes		    ssh->kex->session_id, ssh->kex->session_id_len)) != 0 ||
1196295367Sdes		    (r = sshkey_puts(key, sigbuf)) != 0 ||
1197295367Sdes		    (r = ssh->kex->sign(key_prv, key_pub, &sig, &slen,
1198296781Sdes		    sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), NULL, 0)) != 0 ||
1199295367Sdes		    (r = sshbuf_put_string(resp, sig, slen)) != 0) {
1200295367Sdes			error("%s: couldn't prepare signature: %s",
1201295367Sdes			    __func__, ssh_err(r));
1202295367Sdes			goto out;
1203295367Sdes		}
1204295367Sdes	}
1205295367Sdes	/* Success */
1206295367Sdes	*respp = resp;
1207295367Sdes	resp = NULL; /* don't free it */
1208295367Sdes	success = 1;
1209295367Sdes out:
1210295367Sdes	free(sig);
1211295367Sdes	sshbuf_free(resp);
1212295367Sdes	sshbuf_free(sigbuf);
1213295367Sdes	sshkey_free(key);
1214295367Sdes	return success;
1215295367Sdes}
1216295367Sdes
1217295367Sdesstatic int
121892559Sdesserver_input_global_request(int type, u_int32_t seq, void *ctxt)
121960573Skris{
122076262Sgreen	char *rtype;
122176262Sgreen	int want_reply;
1222295367Sdes	int r, success = 0, allocated_listen_port = 0;
1223295367Sdes	struct sshbuf *resp = NULL;
122476262Sgreen
122576262Sgreen	rtype = packet_get_string(NULL);
122676262Sgreen	want_reply = packet_get_char();
122776262Sgreen	debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply);
122876262Sgreen
122976262Sgreen	/* -R style forwarding */
123076262Sgreen	if (strcmp(rtype, "tcpip-forward") == 0) {
123176262Sgreen		struct passwd *pw;
1232295367Sdes		struct Forward fwd;
123376262Sgreen
1234126277Sdes		pw = the_authctxt->pw;
1235126277Sdes		if (pw == NULL || !the_authctxt->valid)
1236126277Sdes			fatal("server_input_global_request: no/invalid user");
1237295367Sdes		memset(&fwd, 0, sizeof(fwd));
1238295367Sdes		fwd.listen_host = packet_get_string(NULL);
1239295367Sdes		fwd.listen_port = (u_short)packet_get_int();
124076262Sgreen		debug("server_input_global_request: tcpip-forward listen %s port %d",
1241295367Sdes		    fwd.listen_host, fwd.listen_port);
124276262Sgreen
124376262Sgreen		/* check permissions */
1244248619Sdes		if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
1245192595Sdes		    no_port_forwarding_flag ||
1246323124Sdes		    (!want_reply && fwd.listen_port == 0) ||
1247323124Sdes		    (fwd.listen_port != 0 && fwd.listen_port < IPPORT_RESERVED &&
1248323124Sdes		    pw->pw_uid != 0)) {
124976262Sgreen			success = 0;
125076262Sgreen			packet_send_debug("Server has disabled port forwarding.");
125176262Sgreen		} else {
125276262Sgreen			/* Start listening on the port */
1253295367Sdes			success = channel_setup_remote_fwd_listener(&fwd,
1254295367Sdes			    &allocated_listen_port, &options.fwd_opts);
125576262Sgreen		}
1256295367Sdes		free(fwd.listen_host);
1257295367Sdes		if ((resp = sshbuf_new()) == NULL)
1258295367Sdes			fatal("%s: sshbuf_new", __func__);
1259296781Sdes		if (allocated_listen_port != 0 &&
1260296781Sdes		    (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0)
1261295367Sdes			fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r));
1262137019Sdes	} else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
1263295367Sdes		struct Forward fwd;
1264137019Sdes
1265295367Sdes		memset(&fwd, 0, sizeof(fwd));
1266295367Sdes		fwd.listen_host = packet_get_string(NULL);
1267295367Sdes		fwd.listen_port = (u_short)packet_get_int();
1268137019Sdes		debug("%s: cancel-tcpip-forward addr %s port %d", __func__,
1269295367Sdes		    fwd.listen_host, fwd.listen_port);
1270137019Sdes
1271295367Sdes		success = channel_cancel_rport_listener(&fwd);
1272295367Sdes		free(fwd.listen_host);
1273295367Sdes	} else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) {
1274295367Sdes		struct Forward fwd;
1275295367Sdes
1276295367Sdes		memset(&fwd, 0, sizeof(fwd));
1277295367Sdes		fwd.listen_path = packet_get_string(NULL);
1278295367Sdes		debug("server_input_global_request: streamlocal-forward listen path %s",
1279295367Sdes		    fwd.listen_path);
1280295367Sdes
1281295367Sdes		/* check permissions */
1282295367Sdes		if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0
1283323124Sdes		    || no_port_forwarding_flag) {
1284295367Sdes			success = 0;
1285295367Sdes			packet_send_debug("Server has disabled port forwarding.");
1286295367Sdes		} else {
1287295367Sdes			/* Start listening on the socket */
1288295367Sdes			success = channel_setup_remote_fwd_listener(
1289295367Sdes			    &fwd, NULL, &options.fwd_opts);
1290295367Sdes		}
1291295367Sdes		free(fwd.listen_path);
1292295367Sdes	} else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) {
1293295367Sdes		struct Forward fwd;
1294295367Sdes
1295295367Sdes		memset(&fwd, 0, sizeof(fwd));
1296295367Sdes		fwd.listen_path = packet_get_string(NULL);
1297295367Sdes		debug("%s: cancel-streamlocal-forward path %s", __func__,
1298295367Sdes		    fwd.listen_path);
1299295367Sdes
1300295367Sdes		success = channel_cancel_rport_listener(&fwd);
1301295367Sdes		free(fwd.listen_path);
1302181111Sdes	} else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) {
1303181111Sdes		no_more_sessions = 1;
1304181111Sdes		success = 1;
1305295367Sdes	} else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) {
1306295367Sdes		success = server_input_hostkeys_prove(&resp);
130776262Sgreen	}
130876262Sgreen	if (want_reply) {
130976262Sgreen		packet_start(success ?
131076262Sgreen		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
1311295367Sdes		if (success && resp != NULL)
1312295367Sdes			ssh_packet_put_raw(active_state, sshbuf_ptr(resp),
1313295367Sdes			    sshbuf_len(resp));
131476262Sgreen		packet_send();
131576262Sgreen		packet_write_wait();
131676262Sgreen	}
1317255767Sdes	free(rtype);
1318295367Sdes	sshbuf_free(resp);
1319295367Sdes	return 0;
132076262Sgreen}
1321162856Sdes
1322295367Sdesstatic int
132392559Sdesserver_input_channel_req(int type, u_int32_t seq, void *ctxt)
132492559Sdes{
132592559Sdes	Channel *c;
132692559Sdes	int id, reply, success = 0;
132792559Sdes	char *rtype;
132876262Sgreen
132992559Sdes	id = packet_get_int();
133092559Sdes	rtype = packet_get_string(NULL);
133192559Sdes	reply = packet_get_char();
133292559Sdes
133392559Sdes	debug("server_input_channel_req: channel %d request %s reply %d",
133492559Sdes	    id, rtype, reply);
133592559Sdes
133692559Sdes	if ((c = channel_lookup(id)) == NULL)
133792559Sdes		packet_disconnect("server_input_channel_req: "
133892559Sdes		    "unknown channel %d", id);
1339181111Sdes	if (!strcmp(rtype, "eow@openssh.com")) {
1340181111Sdes		packet_check_eom();
1341181111Sdes		chan_rcvd_eow(c);
1342181111Sdes	} else if ((c->type == SSH_CHANNEL_LARVAL ||
1343181111Sdes	    c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0)
134492559Sdes		success = session_input_channel_req(c, rtype);
1345295367Sdes	if (reply && !(c->flags & CHAN_CLOSE_SENT)) {
134692559Sdes		packet_start(success ?
134792559Sdes		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
134892559Sdes		packet_put_int(c->remote_id);
134992559Sdes		packet_send();
135092559Sdes	}
1351255767Sdes	free(rtype);
1352295367Sdes	return 0;
135392559Sdes}
135492559Sdes
135592559Sdesstatic void
135676262Sgreenserver_init_dispatch_20(void)
135776262Sgreen{
135860573Skris	debug("server_init_dispatch_20");
135960573Skris	dispatch_init(&dispatch_protocol_error);
136060573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
136160573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
136260573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
136360573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
136460573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open);
136560573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
136660573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
136792559Sdes	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req);
136860573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
136976262Sgreen	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request);
137076262Sgreen	/* client_alive */
1371192595Sdes	dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive);
1372192595Sdes	dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive);
1373126277Sdes	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive);
1374126277Sdes	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive);
137576262Sgreen	/* rekeying */
137676262Sgreen	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
137760573Skris}
137892559Sdesstatic void
137976262Sgreenserver_init_dispatch_13(void)
138060573Skris{
138160573Skris	debug("server_init_dispatch_13");
138260573Skris	dispatch_init(NULL);
138360573Skris	dispatch_set(SSH_CMSG_EOF, &server_input_eof);
138460573Skris	dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data);
138560573Skris	dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size);
138660573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
138760573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
138860573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
138960573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
139060573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
139160573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
139260573Skris}
139392559Sdesstatic void
139476262Sgreenserver_init_dispatch_15(void)
139560573Skris{
139660573Skris	server_init_dispatch_13();
139760573Skris	debug("server_init_dispatch_15");
139860573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
139960573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose);
140060573Skris}
140192559Sdesstatic void
140276262Sgreenserver_init_dispatch(void)
140360573Skris{
140460573Skris	if (compat20)
140560573Skris		server_init_dispatch_20();
140660573Skris	else if (compat13)
140760573Skris		server_init_dispatch_13();
140860573Skris	else
140960573Skris		server_init_dispatch_15();
141060573Skris}
1411