1296853Sdes/* $OpenBSD: clientloop.c,v 1.284 2016/02/08 10:57:07 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
665668Skris * The main loop for the interactive session (client side).
760573Skris *
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".
1360573Skris *
1460573Skris *
1565668Skris * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
1660573Skris *
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.
3665668Skris *
3765668Skris *
3860573Skris * SSH2 support added by Markus Friedl.
3992555Sdes * Copyright (c) 1999, 2000, 2001 Markus Friedl.  All rights reserved.
4065668Skris *
4165668Skris * Redistribution and use in source and binary forms, with or without
4265668Skris * modification, are permitted provided that the following conditions
4365668Skris * are met:
4465668Skris * 1. Redistributions of source code must retain the above copyright
4565668Skris *    notice, this list of conditions and the following disclaimer.
4665668Skris * 2. Redistributions in binary form must reproduce the above copyright
4765668Skris *    notice, this list of conditions and the following disclaimer in the
4865668Skris *    documentation and/or other materials provided with the distribution.
4965668Skris *
5065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
5165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
5465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
5565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
5965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6057429Smarkm */
6157429Smarkm
6257429Smarkm#include "includes.h"
6357429Smarkm
64295367Sdes#include <sys/param.h>	/* MIN MAX */
65162852Sdes#include <sys/types.h>
66162852Sdes#include <sys/ioctl.h>
67162852Sdes#ifdef HAVE_SYS_STAT_H
68162852Sdes# include <sys/stat.h>
69162852Sdes#endif
70162852Sdes#ifdef HAVE_SYS_TIME_H
71162852Sdes# include <sys/time.h>
72162852Sdes#endif
73162852Sdes#include <sys/socket.h>
74162852Sdes
75162852Sdes#include <ctype.h>
76162852Sdes#include <errno.h>
77162852Sdes#ifdef HAVE_PATHS_H
78162852Sdes#include <paths.h>
79162852Sdes#endif
80162852Sdes#include <signal.h>
81162852Sdes#include <stdarg.h>
82162852Sdes#include <stdio.h>
83162852Sdes#include <stdlib.h>
84162852Sdes#include <string.h>
85162852Sdes#include <termios.h>
86162852Sdes#include <pwd.h>
87162852Sdes#include <unistd.h>
88295367Sdes#include <limits.h>
89162852Sdes
90181111Sdes#include "openbsd-compat/sys-queue.h"
91162852Sdes#include "xmalloc.h"
9276259Sgreen#include "ssh.h"
9376259Sgreen#include "ssh1.h"
9476259Sgreen#include "ssh2.h"
9557429Smarkm#include "packet.h"
9657429Smarkm#include "buffer.h"
9760573Skris#include "compat.h"
9860573Skris#include "channels.h"
9960573Skris#include "dispatch.h"
10076259Sgreen#include "key.h"
101162852Sdes#include "cipher.h"
10276259Sgreen#include "kex.h"
103295367Sdes#include "myproposal.h"
10476259Sgreen#include "log.h"
105295367Sdes#include "misc.h"
10676259Sgreen#include "readconf.h"
10776259Sgreen#include "clientloop.h"
108157016Sdes#include "sshconnect.h"
10976259Sgreen#include "authfd.h"
11076259Sgreen#include "atomicio.h"
111137015Sdes#include "sshpty.h"
112137015Sdes#include "match.h"
113137015Sdes#include "msg.h"
114295367Sdes#include "ssherr.h"
115295367Sdes#include "hostfile.h"
11660573Skris
11769587Sgreen/* import options */
11868700Sgreenextern Options options;
11968700Sgreen
12057429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */
12157429Smarkmextern int stdin_null_flag;
12257429Smarkm
123126274Sdes/* Flag indicating that no shell has been requested */
124126274Sdesextern int no_shell_flag;
125126274Sdes
126137015Sdes/* Control socket */
127204917Sdesextern int muxserver_sock; /* XXX use mux_client_cleanup() instead */
128137015Sdes
12957429Smarkm/*
13057429Smarkm * Name of the host we are connecting to.  This is the name given on the
13157429Smarkm * command line, or the HostName specified for the user-supplied name in a
13257429Smarkm * configuration file.
13357429Smarkm */
13457429Smarkmextern char *host;
13557429Smarkm
13657429Smarkm/*
13757429Smarkm * Flag to indicate that we have received a window change signal which has
13857429Smarkm * not yet been processed.  This will cause a message indicating the new
13957429Smarkm * window size to be sent to the server a little later.  This is volatile
14057429Smarkm * because this is updated in a signal handler.
14157429Smarkm */
14292555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0;
14392555Sdesstatic volatile sig_atomic_t received_signal = 0;
14457429Smarkm
145157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */
14657429Smarkmstatic int in_non_blocking_mode = 0;
14757429Smarkm
148215116Sdes/* Time when backgrounded control master using ControlPersist should exit */
149215116Sdesstatic time_t control_persist_exit_time = 0;
150215116Sdes
15157429Smarkm/* Common data for the client loop code. */
152204917Sdesvolatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
153181111Sdesstatic int escape_char1;	/* Escape character. (proto1 only) */
154181111Sdesstatic int escape_pending1;	/* Last character was an escape (proto1 only) */
15557429Smarkmstatic int last_was_cr;		/* Last character was a newline. */
156181111Sdesstatic int exit_status;		/* Used to store the command exit status. */
157181111Sdesstatic int stdin_eof;		/* EOF has been encountered on stderr. */
15857429Smarkmstatic Buffer stdin_buffer;	/* Buffer for stdin data. */
15957429Smarkmstatic Buffer stdout_buffer;	/* Buffer for stdout data. */
16057429Smarkmstatic Buffer stderr_buffer;	/* Buffer for stderr data. */
161215116Sdesstatic u_int buffer_high;	/* Soft max buffer size. */
16257429Smarkmstatic int connection_in;	/* Connection to server (input). */
16357429Smarkmstatic int connection_out;	/* Connection to server (output). */
16476259Sgreenstatic int need_rekeying;	/* Set to non-zero if rekeying is requested. */
165215116Sdesstatic int session_closed;	/* In SSH2: login session closed. */
166295367Sdesstatic u_int x11_refuse_time;	/* If >0, refuse x11 opens after this time. */
16757429Smarkm
16892555Sdesstatic void client_init_dispatch(void);
16960573Skrisint	session_ident = -1;
17060573Skris
171181111Sdes/* Track escape per proto2 channel */
172181111Sdesstruct escape_filter_ctx {
173181111Sdes	int escape_pending;
174181111Sdes	int escape_char;
175137015Sdes};
176137015Sdes
177181111Sdes/* Context for channel confirmation replies */
178181111Sdesstruct channel_reply_ctx {
179181111Sdes	const char *request_type;
180226046Sdes	int id;
181226046Sdes	enum confirm_action action;
182181111Sdes};
183181111Sdes
184181111Sdes/* Global request success/failure callbacks */
185181111Sdesstruct global_confirm {
186181111Sdes	TAILQ_ENTRY(global_confirm) entry;
187181111Sdes	global_confirm_cb *cb;
188181111Sdes	void *ctx;
189181111Sdes	int ref_count;
190181111Sdes};
191181111SdesTAILQ_HEAD(global_confirms, global_confirm);
192181111Sdesstatic struct global_confirms global_confirms =
193181111Sdes    TAILQ_HEAD_INITIALIZER(global_confirms);
194181111Sdes
195137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *);
196137015Sdes
19757429Smarkm/* Restores stdin to blocking mode. */
19857429Smarkm
19992555Sdesstatic void
20076259Sgreenleave_non_blocking(void)
20157429Smarkm{
20257429Smarkm	if (in_non_blocking_mode) {
203137015Sdes		unset_nonblock(fileno(stdin));
20457429Smarkm		in_non_blocking_mode = 0;
20557429Smarkm	}
20657429Smarkm}
20757429Smarkm
20857429Smarkm/* Puts stdin terminal in non-blocking mode. */
20957429Smarkm
21092555Sdesstatic void
21176259Sgreenenter_non_blocking(void)
21257429Smarkm{
21357429Smarkm	in_non_blocking_mode = 1;
214137015Sdes	set_nonblock(fileno(stdin));
21557429Smarkm}
21657429Smarkm
21757429Smarkm/*
21857429Smarkm * Signal handler for the window change signal (SIGWINCH).  This just sets a
21957429Smarkm * flag indicating that the window has changed.
22057429Smarkm */
221162852Sdes/*ARGSUSED */
22292555Sdesstatic void
22357429Smarkmwindow_change_handler(int sig)
22457429Smarkm{
22557429Smarkm	received_window_change_signal = 1;
22657429Smarkm	signal(SIGWINCH, window_change_handler);
22757429Smarkm}
22857429Smarkm
22957429Smarkm/*
23057429Smarkm * Signal handler for signals that cause the program to terminate.  These
23157429Smarkm * signals must be trapped to restore terminal modes.
23257429Smarkm */
233162852Sdes/*ARGSUSED */
23492555Sdesstatic void
23557429Smarkmsignal_handler(int sig)
23657429Smarkm{
23792555Sdes	received_signal = sig;
23892555Sdes	quit_pending = 1;
23957429Smarkm}
24057429Smarkm
24157429Smarkm/*
24257429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum
24357429Smarkm * available resolution.
24457429Smarkm */
24557429Smarkm
24692555Sdesstatic double
24776259Sgreenget_current_time(void)
24857429Smarkm{
24957429Smarkm	struct timeval tv;
25057429Smarkm	gettimeofday(&tv, NULL);
25157429Smarkm	return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
25257429Smarkm}
25357429Smarkm
254215116Sdes/*
255215116Sdes * Sets control_persist_exit_time to the absolute time when the
256215116Sdes * backgrounded control master should exit due to expiry of the
257215116Sdes * ControlPersist timeout.  Sets it to 0 if we are not a backgrounded
258215116Sdes * control master process, or if there is no ControlPersist timeout.
259215116Sdes */
260215116Sdesstatic void
261215116Sdesset_control_persist_exit_time(void)
262215116Sdes{
263215116Sdes	if (muxserver_sock == -1 || !options.control_persist
264226046Sdes	    || options.control_persist_timeout == 0) {
265215116Sdes		/* not using a ControlPersist timeout */
266215116Sdes		control_persist_exit_time = 0;
267226046Sdes	} else if (channel_still_open()) {
268215116Sdes		/* some client connections are still open */
269215116Sdes		if (control_persist_exit_time > 0)
270215116Sdes			debug2("%s: cancel scheduled exit", __func__);
271215116Sdes		control_persist_exit_time = 0;
272215116Sdes	} else if (control_persist_exit_time <= 0) {
273215116Sdes		/* a client connection has recently closed */
274255767Sdes		control_persist_exit_time = monotime() +
275215116Sdes			(time_t)options.control_persist_timeout;
276215116Sdes		debug2("%s: schedule exit in %d seconds", __func__,
277215116Sdes		    options.control_persist_timeout);
278215116Sdes	}
279215116Sdes	/* else we are already counting down to the timeout */
280215116Sdes}
281215116Sdes
282240075Sdes#define SSH_X11_VALID_DISPLAY_CHARS ":/.-_"
283240075Sdesstatic int
284240075Sdesclient_x11_display_valid(const char *display)
285240075Sdes{
286240075Sdes	size_t i, dlen;
287240075Sdes
288296853Sdes	if (display == NULL)
289296853Sdes		return 0;
290296853Sdes
291240075Sdes	dlen = strlen(display);
292240075Sdes	for (i = 0; i < dlen; i++) {
293262566Sdes		if (!isalnum((u_char)display[i]) &&
294240075Sdes		    strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) {
295240075Sdes			debug("Invalid character '%c' in DISPLAY", display[i]);
296240075Sdes			return 0;
297240075Sdes		}
298240075Sdes	}
299240075Sdes	return 1;
300240075Sdes}
301240075Sdes
302295367Sdes#define SSH_X11_PROTO		"MIT-MAGIC-COOKIE-1"
303295367Sdes#define X11_TIMEOUT_SLACK	60
304296853Sdesint
305149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path,
306215116Sdes    u_int trusted, u_int timeout, char **_proto, char **_data)
307149749Sdes{
308296853Sdes	char cmd[1024], line[512], xdisplay[512];
309296853Sdes	char xauthfile[PATH_MAX], xauthdir[PATH_MAX];
310149749Sdes	static char proto[512], data[512];
311149749Sdes	FILE *f;
312296853Sdes	int got_data = 0, generated = 0, do_unlink = 0, i, r;
313149749Sdes	struct stat st;
314295367Sdes	u_int now, x11_timeout_real;
315149749Sdes
316149749Sdes	*_proto = proto;
317149749Sdes	*_data = data;
318296853Sdes	proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0';
319149749Sdes
320296853Sdes	if (!client_x11_display_valid(display)) {
321296853Sdes		if (display != NULL)
322296853Sdes			logit("DISPLAY \"%s\" invalid; disabling X11 forwarding",
323296853Sdes			    display);
324296853Sdes		return -1;
325296853Sdes	}
326296853Sdes	if (xauth_path != NULL && stat(xauth_path, &st) == -1) {
327149749Sdes		debug("No xauth program.");
328296853Sdes		xauth_path = NULL;
329296853Sdes	}
330296853Sdes
331296853Sdes	if (xauth_path != NULL) {
332149749Sdes		/*
333149749Sdes		 * Handle FamilyLocal case where $DISPLAY does
334149749Sdes		 * not match an authorization entry.  For this we
335149749Sdes		 * just try "xauth list unix:displaynum.screennum".
336149749Sdes		 * XXX: "localhost" match to determine FamilyLocal
337149749Sdes		 *      is not perfect.
338149749Sdes		 */
339149749Sdes		if (strncmp(display, "localhost:", 10) == 0) {
340296853Sdes			if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
341296853Sdes			    display + 10)) < 0 ||
342296853Sdes			    (size_t)r >= sizeof(xdisplay)) {
343296853Sdes				error("%s: display name too long", __func__);
344296853Sdes				return -1;
345296853Sdes			}
346149749Sdes			display = xdisplay;
347149749Sdes		}
348149749Sdes		if (trusted == 0) {
349295367Sdes			/*
350296853Sdes			 * Generate an untrusted X11 auth cookie.
351296853Sdes			 *
352295367Sdes			 * The authentication cookie should briefly outlive
353295367Sdes			 * ssh's willingness to forward X11 connections to
354295367Sdes			 * avoid nasty fail-open behaviour in the X server.
355295367Sdes			 */
356296853Sdes			mktemp_proto(xauthdir, sizeof(xauthdir));
357296853Sdes			if (mkdtemp(xauthdir) == NULL) {
358296853Sdes				error("%s: mkdtemp: %s",
359296853Sdes				    __func__, strerror(errno));
360296853Sdes				return -1;
361296853Sdes			}
362296853Sdes			do_unlink = 1;
363296853Sdes			if ((r = snprintf(xauthfile, sizeof(xauthfile),
364296853Sdes			    "%s/xauthfile", xauthdir)) < 0 ||
365296853Sdes			    (size_t)r >= sizeof(xauthfile)) {
366296853Sdes				error("%s: xauthfile path too long", __func__);
367296853Sdes				unlink(xauthfile);
368296853Sdes				rmdir(xauthdir);
369296853Sdes				return -1;
370296853Sdes			}
371296853Sdes
372295367Sdes			if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK)
373295367Sdes				x11_timeout_real = UINT_MAX;
374295367Sdes			else
375295367Sdes				x11_timeout_real = timeout + X11_TIMEOUT_SLACK;
376296853Sdes			if ((r = snprintf(cmd, sizeof(cmd),
377296853Sdes			    "%s -f %s generate %s " SSH_X11_PROTO
378296853Sdes			    " untrusted timeout %u 2>" _PATH_DEVNULL,
379296853Sdes			    xauth_path, xauthfile, display,
380296853Sdes			    x11_timeout_real)) < 0 ||
381296853Sdes			    (size_t)r >= sizeof(cmd))
382296853Sdes				fatal("%s: cmd too long", __func__);
383296853Sdes			debug2("%s: %s", __func__, cmd);
384296853Sdes			if (x11_refuse_time == 0) {
385296853Sdes				now = monotime() + 1;
386296853Sdes				if (UINT_MAX - timeout < now)
387296853Sdes					x11_refuse_time = UINT_MAX;
388296853Sdes				else
389296853Sdes					x11_refuse_time = now + timeout;
390296853Sdes				channel_set_x11_refuse_time(x11_refuse_time);
391149749Sdes			}
392296853Sdes			if (system(cmd) == 0)
393296853Sdes				generated = 1;
394149749Sdes		}
395181111Sdes
396181111Sdes		/*
397181111Sdes		 * When in untrusted mode, we read the cookie only if it was
398181111Sdes		 * successfully generated as an untrusted one in the step
399181111Sdes		 * above.
400181111Sdes		 */
401181111Sdes		if (trusted || generated) {
402181111Sdes			snprintf(cmd, sizeof(cmd),
403181111Sdes			    "%s %s%s list %s 2>" _PATH_DEVNULL,
404181111Sdes			    xauth_path,
405181111Sdes			    generated ? "-f " : "" ,
406181111Sdes			    generated ? xauthfile : "",
407181111Sdes			    display);
408181111Sdes			debug2("x11_get_proto: %s", cmd);
409181111Sdes			f = popen(cmd, "r");
410181111Sdes			if (f && fgets(line, sizeof(line), f) &&
411181111Sdes			    sscanf(line, "%*s %511s %511s", proto, data) == 2)
412181111Sdes				got_data = 1;
413181111Sdes			if (f)
414181111Sdes				pclose(f);
415296853Sdes		}
416149749Sdes	}
417149749Sdes
418149749Sdes	if (do_unlink) {
419149749Sdes		unlink(xauthfile);
420149749Sdes		rmdir(xauthdir);
421149749Sdes	}
422149749Sdes
423296853Sdes	/* Don't fall back to fake X11 data for untrusted forwarding */
424296853Sdes	if (!trusted && !got_data) {
425296853Sdes		error("Warning: untrusted X11 forwarding setup failed: "
426296853Sdes		    "xauth key data not generated");
427296853Sdes		return -1;
428296853Sdes	}
429296853Sdes
430149749Sdes	/*
431149749Sdes	 * If we didn't get authentication data, just make up some
432149749Sdes	 * data.  The forwarding code will check the validity of the
433149749Sdes	 * response anyway, and substitute this data.  The X11
434149749Sdes	 * server, however, will ignore this fake data and use
435149749Sdes	 * whatever authentication mechanisms it was using otherwise
436149749Sdes	 * for the local connection.
437149749Sdes	 */
438149749Sdes	if (!got_data) {
439149749Sdes		u_int32_t rnd = 0;
440149749Sdes
441149749Sdes		logit("Warning: No xauth data; "
442149749Sdes		    "using fake authentication data for X11 forwarding.");
443149749Sdes		strlcpy(proto, SSH_X11_PROTO, sizeof proto);
444149749Sdes		for (i = 0; i < 16; i++) {
445149749Sdes			if (i % 4 == 0)
446149749Sdes				rnd = arc4random();
447149749Sdes			snprintf(data + 2 * i, sizeof data - 2 * i, "%02x",
448149749Sdes			    rnd & 0xff);
449149749Sdes			rnd >>= 8;
450149749Sdes		}
451149749Sdes	}
452296853Sdes
453296853Sdes	return 0;
454149749Sdes}
455149749Sdes
45657429Smarkm/*
45757429Smarkm * This is called when the interactive is entered.  This checks if there is
45857429Smarkm * an EOF coming on stdin.  We must check this explicitly, as select() does
45957429Smarkm * not appear to wake up when redirecting from /dev/null.
46057429Smarkm */
46157429Smarkm
46292555Sdesstatic void
46376259Sgreenclient_check_initial_eof_on_stdin(void)
46457429Smarkm{
46557429Smarkm	int len;
46657429Smarkm	char buf[1];
46757429Smarkm
46857429Smarkm	/*
46957429Smarkm	 * If standard input is to be "redirected from /dev/null", we simply
47057429Smarkm	 * mark that we have seen an EOF and send an EOF message to the
47157429Smarkm	 * server. Otherwise, we try to read a single character; it appears
47257429Smarkm	 * that for some files, such /dev/null, select() never wakes up for
47357429Smarkm	 * read for this descriptor, which means that we never get EOF.  This
47457429Smarkm	 * way we will get the EOF if stdin comes from /dev/null or similar.
47557429Smarkm	 */
47657429Smarkm	if (stdin_null_flag) {
47757429Smarkm		/* Fake EOF on stdin. */
47857429Smarkm		debug("Sending eof.");
47957429Smarkm		stdin_eof = 1;
48057429Smarkm		packet_start(SSH_CMSG_EOF);
48157429Smarkm		packet_send();
48257429Smarkm	} else {
48357429Smarkm		enter_non_blocking();
48457429Smarkm
48557429Smarkm		/* Check for immediate EOF on stdin. */
48657429Smarkm		len = read(fileno(stdin), buf, 1);
48757429Smarkm		if (len == 0) {
488181111Sdes			/*
489181111Sdes			 * EOF.  Record that we have seen it and send
490181111Sdes			 * EOF to server.
491181111Sdes			 */
49257429Smarkm			debug("Sending eof.");
49357429Smarkm			stdin_eof = 1;
49457429Smarkm			packet_start(SSH_CMSG_EOF);
49557429Smarkm			packet_send();
49657429Smarkm		} else if (len > 0) {
49757429Smarkm			/*
49857429Smarkm			 * Got data.  We must store the data in the buffer,
49957429Smarkm			 * and also process it as an escape character if
50057429Smarkm			 * appropriate.
50157429Smarkm			 */
502181111Sdes			if ((u_char) buf[0] == escape_char1)
503181111Sdes				escape_pending1 = 1;
50476259Sgreen			else
50557429Smarkm				buffer_append(&stdin_buffer, buf, 1);
50657429Smarkm		}
50757429Smarkm		leave_non_blocking();
50857429Smarkm	}
50957429Smarkm}
51057429Smarkm
51157429Smarkm
51257429Smarkm/*
51357429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the
51457429Smarkm * connection.
51557429Smarkm */
51657429Smarkm
51792555Sdesstatic void
51876259Sgreenclient_make_packets_from_stdin_data(void)
51957429Smarkm{
52076259Sgreen	u_int len;
52157429Smarkm
52257429Smarkm	/* Send buffered stdin data to the server. */
52357429Smarkm	while (buffer_len(&stdin_buffer) > 0 &&
52492555Sdes	    packet_not_very_much_data_to_write()) {
52557429Smarkm		len = buffer_len(&stdin_buffer);
52657429Smarkm		/* Keep the packets at reasonable size. */
52757429Smarkm		if (len > packet_get_maxsize())
52857429Smarkm			len = packet_get_maxsize();
52957429Smarkm		packet_start(SSH_CMSG_STDIN_DATA);
53057429Smarkm		packet_put_string(buffer_ptr(&stdin_buffer), len);
53157429Smarkm		packet_send();
53257429Smarkm		buffer_consume(&stdin_buffer, len);
53357429Smarkm		/* If we have a pending EOF, send it now. */
53457429Smarkm		if (stdin_eof && buffer_len(&stdin_buffer) == 0) {
53557429Smarkm			packet_start(SSH_CMSG_EOF);
53657429Smarkm			packet_send();
53757429Smarkm		}
53857429Smarkm	}
53957429Smarkm}
54057429Smarkm
54157429Smarkm/*
54257429Smarkm * Checks if the client window has changed, and sends a packet about it to
54357429Smarkm * the server if so.  The actual change is detected elsewhere (by a software
54457429Smarkm * interrupt on Unix); this just checks the flag and sends a message if
54557429Smarkm * appropriate.
54657429Smarkm */
54757429Smarkm
54892555Sdesstatic void
54976259Sgreenclient_check_window_change(void)
55057429Smarkm{
55160573Skris	struct winsize ws;
55257429Smarkm
55360573Skris	if (! received_window_change_signal)
55460573Skris		return;
55560573Skris	/** XXX race */
55660573Skris	received_window_change_signal = 0;
55757429Smarkm
55869587Sgreen	debug2("client_check_window_change: changed");
55960573Skris
56060573Skris	if (compat20) {
561137015Sdes		channel_send_window_changes();
56260573Skris	} else {
563137015Sdes		if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
564137015Sdes			return;
56560573Skris		packet_start(SSH_CMSG_WINDOW_SIZE);
566162852Sdes		packet_put_int((u_int)ws.ws_row);
567162852Sdes		packet_put_int((u_int)ws.ws_col);
568162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
569162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
57060573Skris		packet_send();
57157429Smarkm	}
57257429Smarkm}
57357429Smarkm
574295367Sdesstatic int
575126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt)
576126274Sdes{
577181111Sdes	struct global_confirm *gc;
578181111Sdes
579181111Sdes	if ((gc = TAILQ_FIRST(&global_confirms)) == NULL)
580295367Sdes		return 0;
581181111Sdes	if (gc->cb != NULL)
582181111Sdes		gc->cb(type, seq, gc->ctx);
583181111Sdes	if (--gc->ref_count <= 0) {
584181111Sdes		TAILQ_REMOVE(&global_confirms, gc, entry);
585264377Sdes		explicit_bzero(gc, sizeof(*gc));
586255767Sdes		free(gc);
587181111Sdes	}
588181111Sdes
589197679Sdes	packet_set_alive_timeouts(0);
590295367Sdes	return 0;
591126274Sdes}
592126274Sdes
593126274Sdesstatic void
594126274Sdesserver_alive_check(void)
595126274Sdes{
596197679Sdes	if (packet_inc_alive_timeouts() > options.server_alive_count_max) {
597221420Sdes		logit("Timeout, server %s not responding.", host);
598164146Sdes		cleanup_exit(255);
599164146Sdes	}
600126274Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
601126274Sdes	packet_put_cstring("keepalive@openssh.com");
602126274Sdes	packet_put_char(1);     /* boolean: want reply */
603126274Sdes	packet_send();
604181111Sdes	/* Insert an empty placeholder to maintain ordering */
605181111Sdes	client_register_global_confirm(NULL, NULL);
606126274Sdes}
607126274Sdes
60857429Smarkm/*
60957429Smarkm * Waits until the client can do something (some data becomes available on
61057429Smarkm * one of the file descriptors).
61157429Smarkm */
61292555Sdesstatic void
61376259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
614137015Sdes    int *maxfdp, u_int *nallocp, int rekeying)
61557429Smarkm{
616126274Sdes	struct timeval tv, *tvp;
617215116Sdes	int timeout_secs;
618255767Sdes	time_t minwait_secs = 0, server_alive_time = 0, now = monotime();
619126274Sdes	int ret;
620126274Sdes
62176259Sgreen	/* Add any selections by the channel mechanism. */
622240075Sdes	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
623240075Sdes	    &minwait_secs, rekeying);
62457429Smarkm
62560573Skris	if (!compat20) {
62660573Skris		/* Read from the connection, unless our buffers are full. */
62760573Skris		if (buffer_len(&stdout_buffer) < buffer_high &&
62860573Skris		    buffer_len(&stderr_buffer) < buffer_high &&
62960573Skris		    channel_not_very_much_buffered_data())
63076259Sgreen			FD_SET(connection_in, *readsetp);
63160573Skris		/*
63260573Skris		 * Read from stdin, unless we have seen EOF or have very much
63360573Skris		 * buffered data to send to the server.
63460573Skris		 */
63560573Skris		if (!stdin_eof && packet_not_very_much_data_to_write())
63676259Sgreen			FD_SET(fileno(stdin), *readsetp);
63760573Skris
63860573Skris		/* Select stdout/stderr if have data in buffer. */
63960573Skris		if (buffer_len(&stdout_buffer) > 0)
64076259Sgreen			FD_SET(fileno(stdout), *writesetp);
64160573Skris		if (buffer_len(&stderr_buffer) > 0)
64276259Sgreen			FD_SET(fileno(stderr), *writesetp);
64360573Skris	} else {
64492555Sdes		/* channel_prepare_select could have closed the last channel */
64592555Sdes		if (session_closed && !channel_still_open() &&
64692555Sdes		    !packet_have_data_to_write()) {
64792555Sdes			/* clear mask since we did not call select() */
64892555Sdes			memset(*readsetp, 0, *nallocp);
64992555Sdes			memset(*writesetp, 0, *nallocp);
65092555Sdes			return;
65192555Sdes		} else {
65292555Sdes			FD_SET(connection_in, *readsetp);
65392555Sdes		}
65460573Skris	}
65557429Smarkm
65657429Smarkm	/* Select server connection if have data to write to the server. */
65757429Smarkm	if (packet_have_data_to_write())
65876259Sgreen		FD_SET(connection_out, *writesetp);
65957429Smarkm
66057429Smarkm	/*
66157429Smarkm	 * Wait for something to happen.  This will suspend the process until
66257429Smarkm	 * some selected descriptor can be read, written, or has some other
663215116Sdes	 * event pending, or a timeout expires.
66457429Smarkm	 */
66557429Smarkm
666215116Sdes	timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */
667255767Sdes	if (options.server_alive_interval > 0 && compat20) {
668215116Sdes		timeout_secs = options.server_alive_interval;
669255767Sdes		server_alive_time = now + options.server_alive_interval;
670255767Sdes	}
671255767Sdes	if (options.rekey_interval > 0 && compat20 && !rekeying)
672255767Sdes		timeout_secs = MIN(timeout_secs, packet_get_rekey_timeout());
673215116Sdes	set_control_persist_exit_time();
674215116Sdes	if (control_persist_exit_time > 0) {
675215116Sdes		timeout_secs = MIN(timeout_secs,
676255767Sdes			control_persist_exit_time - now);
677215116Sdes		if (timeout_secs < 0)
678215116Sdes			timeout_secs = 0;
679215116Sdes	}
680240075Sdes	if (minwait_secs != 0)
681240075Sdes		timeout_secs = MIN(timeout_secs, (int)minwait_secs);
682215116Sdes	if (timeout_secs == INT_MAX)
683126274Sdes		tvp = NULL;
684137015Sdes	else {
685215116Sdes		tv.tv_sec = timeout_secs;
686126274Sdes		tv.tv_usec = 0;
687126274Sdes		tvp = &tv;
688126274Sdes	}
689215116Sdes
690126274Sdes	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
691126274Sdes	if (ret < 0) {
69257429Smarkm		char buf[100];
69376259Sgreen
69476259Sgreen		/*
69576259Sgreen		 * We have to clear the select masks, because we return.
69676259Sgreen		 * We have to return, because the mainloop checks for the flags
69776259Sgreen		 * set by the signal handlers.
69876259Sgreen		 */
69992555Sdes		memset(*readsetp, 0, *nallocp);
70092555Sdes		memset(*writesetp, 0, *nallocp);
70176259Sgreen
70257429Smarkm		if (errno == EINTR)
70357429Smarkm			return;
70457429Smarkm		/* Note: we might still have data in the buffers. */
70557429Smarkm		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
70657429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
70757429Smarkm		quit_pending = 1;
708255767Sdes	} else if (ret == 0) {
709255767Sdes		/*
710255767Sdes		 * Timeout.  Could have been either keepalive or rekeying.
711255767Sdes		 * Keepalive we check here, rekeying is checked in clientloop.
712255767Sdes		 */
713255767Sdes		if (server_alive_time != 0 && server_alive_time <= monotime())
714255767Sdes			server_alive_check();
715255767Sdes	}
716255767Sdes
71757429Smarkm}
71857429Smarkm
71992555Sdesstatic void
72065668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
72157429Smarkm{
72257429Smarkm	/* Flush stdout and stderr buffers. */
72365668Skris	if (buffer_len(bout) > 0)
724181111Sdes		atomicio(vwrite, fileno(stdout), buffer_ptr(bout),
725181111Sdes		    buffer_len(bout));
72665668Skris	if (buffer_len(berr) > 0)
727181111Sdes		atomicio(vwrite, fileno(stderr), buffer_ptr(berr),
728181111Sdes		    buffer_len(berr));
72957429Smarkm
730226046Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
73157429Smarkm
73257429Smarkm	/*
73357429Smarkm	 * Free (and clear) the buffer to reduce the amount of data that gets
73457429Smarkm	 * written to swap.
73557429Smarkm	 */
73665668Skris	buffer_free(bin);
73765668Skris	buffer_free(bout);
73865668Skris	buffer_free(berr);
73957429Smarkm
74057429Smarkm	/* Send the suspend signal to the program itself. */
74157429Smarkm	kill(getpid(), SIGTSTP);
74257429Smarkm
743146998Sdes	/* Reset window sizes in case they have changed */
744146998Sdes	received_window_change_signal = 1;
74557429Smarkm
74657429Smarkm	/* OK, we have been continued by the user. Reinitialize buffers. */
74765668Skris	buffer_init(bin);
74865668Skris	buffer_init(bout);
74965668Skris	buffer_init(berr);
75057429Smarkm
751226046Sdes	enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
75257429Smarkm}
75357429Smarkm
75492555Sdesstatic void
755162852Sdesclient_process_net_input(fd_set *readset)
75657429Smarkm{
757296853Sdes	int len;
758197679Sdes	char buf[SSH_IOBUFSZ];
75957429Smarkm
76057429Smarkm	/*
76157429Smarkm	 * Read input from the server, and add any such data to the buffer of
76257429Smarkm	 * the packet subsystem.
76357429Smarkm	 */
76457429Smarkm	if (FD_ISSET(connection_in, readset)) {
76557429Smarkm		/* Read as much as possible. */
766296853Sdes		len = read(connection_in, buf, sizeof(buf));
767296853Sdes		if (len == 0) {
768181111Sdes			/*
769181111Sdes			 * Received EOF.  The remote host has closed the
770181111Sdes			 * connection.
771181111Sdes			 */
772181111Sdes			snprintf(buf, sizeof buf,
773181111Sdes			    "Connection to %.300s closed by remote host.\r\n",
774181111Sdes			    host);
77557429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
77657429Smarkm			quit_pending = 1;
77757429Smarkm			return;
77857429Smarkm		}
77957429Smarkm		/*
78057429Smarkm		 * There is a kernel bug on Solaris that causes select to
78157429Smarkm		 * sometimes wake up even though there is no data available.
78257429Smarkm		 */
783181111Sdes		if (len < 0 &&
784181111Sdes		    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
78557429Smarkm			len = 0;
78657429Smarkm
78757429Smarkm		if (len < 0) {
788181111Sdes			/*
789181111Sdes			 * An error has encountered.  Perhaps there is a
790181111Sdes			 * network problem.
791181111Sdes			 */
792181111Sdes			snprintf(buf, sizeof buf,
793181111Sdes			    "Read from remote host %.300s: %.100s\r\n",
794181111Sdes			    host, strerror(errno));
79557429Smarkm			buffer_append(&stderr_buffer, buf, strlen(buf));
79657429Smarkm			quit_pending = 1;
79757429Smarkm			return;
79857429Smarkm		}
79957429Smarkm		packet_process_incoming(buf, len);
80057429Smarkm	}
80160573Skris}
80260573Skris
80398675Sdesstatic void
804181111Sdesclient_status_confirm(int type, Channel *c, void *ctx)
805137015Sdes{
806181111Sdes	struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx;
807181111Sdes	char errmsg[256];
808181111Sdes	int tochan;
809137015Sdes
810226046Sdes	/*
811226046Sdes	 * If a TTY was explicitly requested, then a failure to allocate
812226046Sdes	 * one is fatal.
813226046Sdes	 */
814226046Sdes	if (cr->action == CONFIRM_TTY &&
815226046Sdes	    (options.request_tty == REQUEST_TTY_FORCE ||
816226046Sdes	    options.request_tty == REQUEST_TTY_YES))
817226046Sdes		cr->action = CONFIRM_CLOSE;
818226046Sdes
819181111Sdes	/* XXX supress on mux _client_ quietmode */
820181111Sdes	tochan = options.log_level >= SYSLOG_LEVEL_ERROR &&
821204917Sdes	    c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE;
822137015Sdes
823181111Sdes	if (type == SSH2_MSG_CHANNEL_SUCCESS) {
824181111Sdes		debug2("%s request accepted on channel %d",
825181111Sdes		    cr->request_type, c->self);
826181111Sdes	} else if (type == SSH2_MSG_CHANNEL_FAILURE) {
827181111Sdes		if (tochan) {
828181111Sdes			snprintf(errmsg, sizeof(errmsg),
829181111Sdes			    "%s request failed\r\n", cr->request_type);
830181111Sdes		} else {
831181111Sdes			snprintf(errmsg, sizeof(errmsg),
832181111Sdes			    "%s request failed on channel %d",
833181111Sdes			    cr->request_type, c->self);
834181111Sdes		}
835181111Sdes		/* If error occurred on primary session channel, then exit */
836226046Sdes		if (cr->action == CONFIRM_CLOSE && c->self == session_ident)
837181111Sdes			fatal("%s", errmsg);
838226046Sdes		/*
839226046Sdes		 * If error occurred on mux client, append to
840226046Sdes		 * their stderr.
841226046Sdes		 */
842226046Sdes		if (tochan) {
843226046Sdes			buffer_append(&c->extended, errmsg,
844226046Sdes			    strlen(errmsg));
845226046Sdes		} else
846181111Sdes			error("%s", errmsg);
847226046Sdes		if (cr->action == CONFIRM_TTY) {
848226046Sdes			/*
849226046Sdes			 * If a TTY allocation error occurred, then arrange
850226046Sdes			 * for the correct TTY to leave raw mode.
851226046Sdes			 */
852226046Sdes			if (c->self == session_ident)
853226046Sdes				leave_raw_mode(0);
854226046Sdes			else
855226046Sdes				mux_tty_alloc_failed(c);
856226046Sdes		} else if (cr->action == CONFIRM_CLOSE) {
857181111Sdes			chan_read_failed(c);
858181111Sdes			chan_write_failed(c);
859181111Sdes		}
860137015Sdes	}
861255767Sdes	free(cr);
862137015Sdes}
863137015Sdes
864137015Sdesstatic void
865181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx)
866137015Sdes{
867255767Sdes	free(ctx);
868137015Sdes}
869137015Sdes
870226046Sdesvoid
871226046Sdesclient_expect_confirm(int id, const char *request,
872226046Sdes    enum confirm_action action)
873137015Sdes{
874258343Sdes	struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr));
875137015Sdes
876181111Sdes	cr->request_type = request;
877226046Sdes	cr->action = action;
878137015Sdes
879181111Sdes	channel_register_status_confirm(id, client_status_confirm,
880181111Sdes	    client_abandon_status_confirm, cr);
881181111Sdes}
882137015Sdes
883181111Sdesvoid
884181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx)
885181111Sdes{
886181111Sdes	struct global_confirm *gc, *last_gc;
887137015Sdes
888181111Sdes	/* Coalesce identical callbacks */
889181111Sdes	last_gc = TAILQ_LAST(&global_confirms, global_confirms);
890181111Sdes	if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) {
891181111Sdes		if (++last_gc->ref_count >= INT_MAX)
892181111Sdes			fatal("%s: last_gc->ref_count = %d",
893181111Sdes			    __func__, last_gc->ref_count);
894146998Sdes		return;
895146998Sdes	}
896146998Sdes
897258343Sdes	gc = xcalloc(1, sizeof(*gc));
898181111Sdes	gc->cb = cb;
899181111Sdes	gc->ctx = ctx;
900181111Sdes	gc->ref_count = 1;
901181111Sdes	TAILQ_INSERT_TAIL(&global_confirms, gc, entry);
902137015Sdes}
903137015Sdes
904137015Sdesstatic void
90598675Sdesprocess_cmdline(void)
90698675Sdes{
90798675Sdes	void (*handler)(int);
908295367Sdes	char *s, *cmd;
909295367Sdes	int ok, delete = 0, local = 0, remote = 0, dynamic = 0;
910295367Sdes	struct Forward fwd;
91198675Sdes
912264377Sdes	memset(&fwd, 0, sizeof(fwd));
913181111Sdes
914226046Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
91598675Sdes	handler = signal(SIGINT, SIG_IGN);
91698675Sdes	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
91798675Sdes	if (s == NULL)
91898675Sdes		goto out;
919262566Sdes	while (isspace((u_char)*s))
92098675Sdes		s++;
921137015Sdes	if (*s == '-')
922137015Sdes		s++;	/* Skip cmdline '-', if any */
923137015Sdes	if (*s == '\0')
92498675Sdes		goto out;
925137015Sdes
926137015Sdes	if (*s == 'h' || *s == 'H' || *s == '?') {
927137015Sdes		logit("Commands:");
928162852Sdes		logit("      -L[bind_address:]port:host:hostport    "
929162852Sdes		    "Request local forward");
930162852Sdes		logit("      -R[bind_address:]port:host:hostport    "
931162852Sdes		    "Request remote forward");
932192595Sdes		logit("      -D[bind_address:]port                  "
933192595Sdes		    "Request dynamic forward");
934240075Sdes		logit("      -KL[bind_address:]port                 "
935240075Sdes		    "Cancel local forward");
936162852Sdes		logit("      -KR[bind_address:]port                 "
937162852Sdes		    "Cancel remote forward");
938240075Sdes		logit("      -KD[bind_address:]port                 "
939240075Sdes		    "Cancel dynamic forward");
940157016Sdes		if (!options.permit_local_command)
941157016Sdes			goto out;
942162852Sdes		logit("      !args                                  "
943162852Sdes		    "Execute local command");
944137015Sdes		goto out;
945137015Sdes	}
946137015Sdes
947157016Sdes	if (*s == '!' && options.permit_local_command) {
948157016Sdes		s++;
949157016Sdes		ssh_local_cmd(s);
950157016Sdes		goto out;
951157016Sdes	}
952157016Sdes
953137015Sdes	if (*s == 'K') {
954137015Sdes		delete = 1;
955137015Sdes		s++;
956137015Sdes	}
957192595Sdes	if (*s == 'L')
958192595Sdes		local = 1;
959192595Sdes	else if (*s == 'R')
960192595Sdes		remote = 1;
961192595Sdes	else if (*s == 'D')
962192595Sdes		dynamic = 1;
963192595Sdes	else {
964124208Sdes		logit("Invalid command.");
96598675Sdes		goto out;
96698675Sdes	}
967192595Sdes
968240075Sdes	if (delete && !compat20) {
969124208Sdes		logit("Not supported for SSH protocol version 1.");
97098675Sdes		goto out;
97198675Sdes	}
972137015Sdes
973262566Sdes	while (isspace((u_char)*++s))
974181111Sdes		;
97598675Sdes
976204917Sdes	/* XXX update list of forwards in options */
977137015Sdes	if (delete) {
978295367Sdes		/* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */
979295367Sdes		if (!parse_forward(&fwd, s, 1, 0)) {
980295367Sdes			logit("Bad forwarding close specification.");
981137015Sdes			goto out;
982137015Sdes		}
983240075Sdes		if (remote)
984295367Sdes			ok = channel_request_rforward_cancel(&fwd) == 0;
985240075Sdes		else if (dynamic)
986295367Sdes			ok = channel_cancel_lport_listener(&fwd,
987295367Sdes			    0, &options.fwd_opts) > 0;
988240075Sdes		else
989295367Sdes			ok = channel_cancel_lport_listener(&fwd,
990295367Sdes			    CHANNEL_CANCEL_PORT_STATIC,
991295367Sdes			    &options.fwd_opts) > 0;
992240075Sdes		if (!ok) {
993240075Sdes			logit("Unkown port forwarding.");
994240075Sdes			goto out;
995240075Sdes		}
996240075Sdes		logit("Canceled forwarding.");
997137015Sdes	} else {
998192595Sdes		if (!parse_forward(&fwd, s, dynamic, remote)) {
999137015Sdes			logit("Bad forwarding specification.");
1000137015Sdes			goto out;
1001137015Sdes		}
1002192595Sdes		if (local || dynamic) {
1003295367Sdes			if (!channel_setup_local_fwd_listener(&fwd,
1004295367Sdes			    &options.fwd_opts)) {
1005137015Sdes				logit("Port forwarding failed.");
1006137015Sdes				goto out;
1007137015Sdes			}
1008146998Sdes		} else {
1009295367Sdes			if (channel_request_remote_forwarding(&fwd) < 0) {
1010162852Sdes				logit("Port forwarding failed.");
1011162852Sdes				goto out;
1012162852Sdes			}
1013146998Sdes		}
1014137015Sdes		logit("Forwarding port.");
1015137015Sdes	}
1016137015Sdes
101798675Sdesout:
101898675Sdes	signal(SIGINT, handler);
1019226046Sdes	enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
1020255767Sdes	free(cmd);
1021255767Sdes	free(fwd.listen_host);
1022295367Sdes	free(fwd.listen_path);
1023255767Sdes	free(fwd.connect_host);
1024295367Sdes	free(fwd.connect_path);
102598675Sdes}
102698675Sdes
1027248619Sdes/* reasons to suppress output of an escape command in help output */
1028248619Sdes#define SUPPRESS_NEVER		0	/* never suppress, always show */
1029248619Sdes#define SUPPRESS_PROTO1		1	/* don't show in protocol 1 sessions */
1030248619Sdes#define SUPPRESS_MUXCLIENT	2	/* don't show in mux client sessions */
1031248619Sdes#define SUPPRESS_MUXMASTER	4	/* don't show in mux master sessions */
1032248619Sdes#define SUPPRESS_SYSLOG		8	/* don't show when logging to syslog */
1033248619Sdesstruct escape_help_text {
1034248619Sdes	const char *cmd;
1035248619Sdes	const char *text;
1036248619Sdes	unsigned int flags;
1037248619Sdes};
1038248619Sdesstatic struct escape_help_text esc_txt[] = {
1039248619Sdes    {".",  "terminate session", SUPPRESS_MUXMASTER},
1040248619Sdes    {".",  "terminate connection (and any multiplexed sessions)",
1041248619Sdes	SUPPRESS_MUXCLIENT},
1042248619Sdes    {"B",  "send a BREAK to the remote system", SUPPRESS_PROTO1},
1043248619Sdes    {"C",  "open a command line", SUPPRESS_MUXCLIENT},
1044248619Sdes    {"R",  "request rekey", SUPPRESS_PROTO1},
1045248619Sdes    {"V/v",  "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT},
1046248619Sdes    {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT},
1047248619Sdes    {"#",  "list forwarded connections", SUPPRESS_NEVER},
1048248619Sdes    {"&",  "background ssh (when waiting for connections to terminate)",
1049248619Sdes	SUPPRESS_MUXCLIENT},
1050248619Sdes    {"?", "this message", SUPPRESS_NEVER},
1051248619Sdes};
1052248619Sdes
1053248619Sdesstatic void
1054248619Sdesprint_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client,
1055248619Sdes    int using_stderr)
1056248619Sdes{
1057248619Sdes	unsigned int i, suppress_flags;
1058248619Sdes	char string[1024];
1059248619Sdes
1060248619Sdes	snprintf(string, sizeof string, "%c?\r\n"
1061248619Sdes	    "Supported escape sequences:\r\n", escape_char);
1062248619Sdes	buffer_append(b, string, strlen(string));
1063248619Sdes
1064248619Sdes	suppress_flags = (protocol2 ? 0 : SUPPRESS_PROTO1) |
1065248619Sdes	    (mux_client ? SUPPRESS_MUXCLIENT : 0) |
1066248619Sdes	    (mux_client ? 0 : SUPPRESS_MUXMASTER) |
1067248619Sdes	    (using_stderr ? 0 : SUPPRESS_SYSLOG);
1068248619Sdes
1069248619Sdes	for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) {
1070248619Sdes		if (esc_txt[i].flags & suppress_flags)
1071248619Sdes			continue;
1072248619Sdes		snprintf(string, sizeof string, " %c%-3s - %s\r\n",
1073248619Sdes		    escape_char, esc_txt[i].cmd, esc_txt[i].text);
1074248619Sdes		buffer_append(b, string, strlen(string));
1075248619Sdes	}
1076248619Sdes
1077248619Sdes	snprintf(string, sizeof string,
1078248619Sdes	    " %c%c   - send the escape character by typing it twice\r\n"
1079248619Sdes	    "(Note that escapes are only recognized immediately after "
1080248619Sdes	    "newline.)\r\n", escape_char, escape_char);
1081248619Sdes	buffer_append(b, string, strlen(string));
1082248619Sdes}
1083248619Sdes
1084181111Sdes/*
1085181111Sdes * Process the characters one by one, call with c==NULL for proto1 case.
1086181111Sdes */
108792555Sdesstatic int
1088181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
1089181111Sdes    char *buf, int len)
109065668Skris{
109165668Skris	char string[1024];
109265668Skris	pid_t pid;
109365668Skris	int bytes = 0;
109476259Sgreen	u_int i;
109576259Sgreen	u_char ch;
109665668Skris	char *s;
1097181111Sdes	int *escape_pendingp, escape_char;
1098181111Sdes	struct escape_filter_ctx *efc;
109965668Skris
1100181111Sdes	if (c == NULL) {
1101181111Sdes		escape_pendingp = &escape_pending1;
1102181111Sdes		escape_char = escape_char1;
1103181111Sdes	} else {
1104181111Sdes		if (c->filter_ctx == NULL)
1105181111Sdes			return 0;
1106181111Sdes		efc = (struct escape_filter_ctx *)c->filter_ctx;
1107181111Sdes		escape_pendingp = &efc->escape_pending;
1108181111Sdes		escape_char = efc->escape_char;
1109181111Sdes	}
1110181111Sdes
1111149749Sdes	if (len <= 0)
1112149749Sdes		return (0);
1113149749Sdes
1114149749Sdes	for (i = 0; i < (u_int)len; i++) {
111565668Skris		/* Get one character at a time. */
111665668Skris		ch = buf[i];
111765668Skris
1118181111Sdes		if (*escape_pendingp) {
111965668Skris			/* We have previously seen an escape character. */
112065668Skris			/* Clear the flag now. */
1121181111Sdes			*escape_pendingp = 0;
112265668Skris
112365668Skris			/* Process the escaped character. */
112465668Skris			switch (ch) {
112565668Skris			case '.':
112665668Skris				/* Terminate the connection. */
1127181111Sdes				snprintf(string, sizeof string, "%c.\r\n",
1128181111Sdes				    escape_char);
112965668Skris				buffer_append(berr, string, strlen(string));
113065668Skris
1131204917Sdes				if (c && c->ctl_chan != -1) {
1132181111Sdes					chan_read_failed(c);
1133181111Sdes					chan_write_failed(c);
1134255767Sdes					if (c->detach_user)
1135255767Sdes						c->detach_user(c->self, NULL);
1136255767Sdes					c->type = SSH_CHANNEL_ABANDONED;
1137255767Sdes					buffer_clear(&c->input);
1138255767Sdes					chan_ibuf_empty(c);
1139181111Sdes					return 0;
1140181111Sdes				} else
1141181111Sdes					quit_pending = 1;
114265668Skris				return -1;
114365668Skris
114465668Skris			case 'Z' - 64:
1145181111Sdes				/* XXX support this for mux clients */
1146204917Sdes				if (c && c->ctl_chan != -1) {
1147248619Sdes					char b[16];
1148181111Sdes noescape:
1149248619Sdes					if (ch == 'Z' - 64)
1150248619Sdes						snprintf(b, sizeof b, "^Z");
1151248619Sdes					else
1152248619Sdes						snprintf(b, sizeof b, "%c", ch);
1153181111Sdes					snprintf(string, sizeof string,
1154248619Sdes					    "%c%s escape not available to "
1155181111Sdes					    "multiplexed sessions\r\n",
1156248619Sdes					    escape_char, b);
1157181111Sdes					buffer_append(berr, string,
1158181111Sdes					    strlen(string));
1159181111Sdes					continue;
1160181111Sdes				}
1161181111Sdes				/* Suspend the program. Inform the user */
1162181111Sdes				snprintf(string, sizeof string,
1163181111Sdes				    "%c^Z [suspend ssh]\r\n", escape_char);
116465668Skris				buffer_append(berr, string, strlen(string));
116565668Skris
116665668Skris				/* Restore terminal modes and suspend. */
116765668Skris				client_suspend_self(bin, bout, berr);
116865668Skris
116965668Skris				/* We have been continued. */
117065668Skris				continue;
117165668Skris
1172124208Sdes			case 'B':
1173124208Sdes				if (compat20) {
1174124208Sdes					snprintf(string, sizeof string,
1175124208Sdes					    "%cB\r\n", escape_char);
1176124208Sdes					buffer_append(berr, string,
1177124208Sdes					    strlen(string));
1178262566Sdes					channel_request_start(c->self,
1179124208Sdes					    "break", 0);
1180124208Sdes					packet_put_int(1000);
1181124208Sdes					packet_send();
1182124208Sdes				}
1183124208Sdes				continue;
1184124208Sdes
118576259Sgreen			case 'R':
118676259Sgreen				if (compat20) {
118776259Sgreen					if (datafellows & SSH_BUG_NOREKEY)
1188181111Sdes						logit("Server does not "
1189181111Sdes						    "support re-keying");
119076259Sgreen					else
119176259Sgreen						need_rekeying = 1;
119276259Sgreen				}
119376259Sgreen				continue;
119476259Sgreen
1195248619Sdes			case 'V':
1196248619Sdes				/* FALLTHROUGH */
1197248619Sdes			case 'v':
1198248619Sdes				if (c && c->ctl_chan != -1)
1199248619Sdes					goto noescape;
1200248619Sdes				if (!log_is_on_stderr()) {
1201248619Sdes					snprintf(string, sizeof string,
1202248619Sdes					    "%c%c [Logging to syslog]\r\n",
1203248619Sdes					     escape_char, ch);
1204248619Sdes					buffer_append(berr, string,
1205248619Sdes					    strlen(string));
1206248619Sdes					continue;
1207248619Sdes				}
1208248619Sdes				if (ch == 'V' && options.log_level >
1209248619Sdes				    SYSLOG_LEVEL_QUIET)
1210248619Sdes					log_change_level(--options.log_level);
1211248619Sdes				if (ch == 'v' && options.log_level <
1212248619Sdes				    SYSLOG_LEVEL_DEBUG3)
1213248619Sdes					log_change_level(++options.log_level);
1214248619Sdes				snprintf(string, sizeof string,
1215248619Sdes				    "%c%c [LogLevel %s]\r\n", escape_char, ch,
1216248619Sdes				    log_level_name(options.log_level));
1217248619Sdes				buffer_append(berr, string, strlen(string));
1218248619Sdes				continue;
1219248619Sdes
122065668Skris			case '&':
1221204917Sdes				if (c && c->ctl_chan != -1)
1222181111Sdes					goto noescape;
122365668Skris				/*
1224181111Sdes				 * Detach the program (continue to serve
1225181111Sdes				 * connections, but put in background and no
1226181111Sdes				 * more new connections).
122765668Skris				 */
122865668Skris				/* Restore tty modes. */
1229226046Sdes				leave_raw_mode(
1230226046Sdes				    options.request_tty == REQUEST_TTY_FORCE);
123165668Skris
123265668Skris				/* Stop listening for new connections. */
123365668Skris				channel_stop_listening();
123465668Skris
123592555Sdes				snprintf(string, sizeof string,
123692555Sdes				    "%c& [backgrounded]\n", escape_char);
123792555Sdes				buffer_append(berr, string, strlen(string));
123865668Skris
123965668Skris				/* Fork into background. */
124065668Skris				pid = fork();
124165668Skris				if (pid < 0) {
124265668Skris					error("fork: %.100s", strerror(errno));
124365668Skris					continue;
124465668Skris				}
124565668Skris				if (pid != 0) {	/* This is the parent. */
124665668Skris					/* The parent just exits. */
124765668Skris					exit(0);
124865668Skris				}
124965668Skris				/* The child continues serving connections. */
125092555Sdes				if (compat20) {
125192555Sdes					buffer_append(bin, "\004", 1);
125292555Sdes					/* fake EOF on stdin */
125392555Sdes					return -1;
125492555Sdes				} else if (!stdin_eof) {
125592555Sdes					/*
1256181111Sdes					 * Sending SSH_CMSG_EOF alone does not
1257181111Sdes					 * always appear to be enough.  So we
1258181111Sdes					 * try to send an EOF character first.
125992555Sdes					 */
126092555Sdes					packet_start(SSH_CMSG_STDIN_DATA);
126192555Sdes					packet_put_string("\004", 1);
126292555Sdes					packet_send();
126392555Sdes					/* Close stdin. */
126492555Sdes					stdin_eof = 1;
126592555Sdes					if (buffer_len(bin) == 0) {
126692555Sdes						packet_start(SSH_CMSG_EOF);
126792555Sdes						packet_send();
126892555Sdes					}
126992555Sdes				}
127092555Sdes				continue;
127165668Skris
127265668Skris			case '?':
1273248619Sdes				print_escape_help(berr, escape_char, compat20,
1274248619Sdes				    (c && c->ctl_chan != -1),
1275248619Sdes				    log_is_on_stderr());
127665668Skris				continue;
127765668Skris
127865668Skris			case '#':
1279181111Sdes				snprintf(string, sizeof string, "%c#\r\n",
1280181111Sdes				    escape_char);
128165668Skris				buffer_append(berr, string, strlen(string));
128265668Skris				s = channel_open_message();
128365668Skris				buffer_append(berr, s, strlen(s));
1284255767Sdes				free(s);
128565668Skris				continue;
128665668Skris
128798675Sdes			case 'C':
1288204917Sdes				if (c && c->ctl_chan != -1)
1289192595Sdes					goto noescape;
129098675Sdes				process_cmdline();
129198675Sdes				continue;
129298675Sdes
129365668Skris			default:
129465668Skris				if (ch != escape_char) {
129565668Skris					buffer_put_char(bin, escape_char);
129665668Skris					bytes++;
129765668Skris				}
129865668Skris				/* Escaped characters fall through here */
129965668Skris				break;
130065668Skris			}
130165668Skris		} else {
130265668Skris			/*
1303181111Sdes			 * The previous character was not an escape char.
1304181111Sdes			 * Check if this is an escape.
130565668Skris			 */
130665668Skris			if (last_was_cr && ch == escape_char) {
1307181111Sdes				/*
1308181111Sdes				 * It is. Set the flag and continue to
1309181111Sdes				 * next character.
1310181111Sdes				 */
1311181111Sdes				*escape_pendingp = 1;
131265668Skris				continue;
131365668Skris			}
131465668Skris		}
131565668Skris
131665668Skris		/*
131765668Skris		 * Normal character.  Record whether it was a newline,
131865668Skris		 * and append it to the buffer.
131965668Skris		 */
132065668Skris		last_was_cr = (ch == '\r' || ch == '\n');
132165668Skris		buffer_put_char(bin, ch);
132265668Skris		bytes++;
132365668Skris	}
132465668Skris	return bytes;
132565668Skris}
132665668Skris
132792555Sdesstatic void
1328162852Sdesclient_process_input(fd_set *readset)
132960573Skris{
133060573Skris	int len;
1331197679Sdes	char buf[SSH_IOBUFSZ];
133260573Skris
133357429Smarkm	/* Read input from stdin. */
133457429Smarkm	if (FD_ISSET(fileno(stdin), readset)) {
133557429Smarkm		/* Read as much as possible. */
133657429Smarkm		len = read(fileno(stdin), buf, sizeof(buf));
1337181111Sdes		if (len < 0 &&
1338181111Sdes		    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
133976259Sgreen			return;		/* we'll try again later */
134057429Smarkm		if (len <= 0) {
134157429Smarkm			/*
134257429Smarkm			 * Received EOF or error.  They are treated
134357429Smarkm			 * similarly, except that an error message is printed
134457429Smarkm			 * if it was an error condition.
134557429Smarkm			 */
134657429Smarkm			if (len < 0) {
1347181111Sdes				snprintf(buf, sizeof buf, "read: %.100s\r\n",
1348181111Sdes				    strerror(errno));
134957429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
135057429Smarkm			}
135157429Smarkm			/* Mark that we have seen EOF. */
135257429Smarkm			stdin_eof = 1;
135357429Smarkm			/*
135457429Smarkm			 * Send an EOF message to the server unless there is
135557429Smarkm			 * data in the buffer.  If there is data in the
135657429Smarkm			 * buffer, no message will be sent now.  Code
135757429Smarkm			 * elsewhere will send the EOF when the buffer
135857429Smarkm			 * becomes empty if stdin_eof is set.
135957429Smarkm			 */
136057429Smarkm			if (buffer_len(&stdin_buffer) == 0) {
136157429Smarkm				packet_start(SSH_CMSG_EOF);
136257429Smarkm				packet_send();
136357429Smarkm			}
1364181111Sdes		} else if (escape_char1 == SSH_ESCAPECHAR_NONE) {
136557429Smarkm			/*
136657429Smarkm			 * Normal successful read, and no escape character.
136757429Smarkm			 * Just append the data to buffer.
136857429Smarkm			 */
136957429Smarkm			buffer_append(&stdin_buffer, buf, len);
137057429Smarkm		} else {
137157429Smarkm			/*
1372181111Sdes			 * Normal, successful read.  But we have an escape
1373181111Sdes			 * character and have to process the characters one
1374181111Sdes			 * by one.
137557429Smarkm			 */
1376181111Sdes			if (process_escapes(NULL, &stdin_buffer,
1377181111Sdes			    &stdout_buffer, &stderr_buffer, buf, len) == -1)
137865668Skris				return;
137957429Smarkm		}
138057429Smarkm	}
138157429Smarkm}
138257429Smarkm
138392555Sdesstatic void
1384162852Sdesclient_process_output(fd_set *writeset)
138557429Smarkm{
138657429Smarkm	int len;
138757429Smarkm	char buf[100];
138857429Smarkm
138957429Smarkm	/* Write buffered output to stdout. */
139057429Smarkm	if (FD_ISSET(fileno(stdout), writeset)) {
139157429Smarkm		/* Write as much data as possible. */
139257429Smarkm		len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
139357429Smarkm		    buffer_len(&stdout_buffer));
139457429Smarkm		if (len <= 0) {
1395181111Sdes			if (errno == EINTR || errno == EAGAIN ||
1396181111Sdes			    errno == EWOULDBLOCK)
139757429Smarkm				len = 0;
139857429Smarkm			else {
139957429Smarkm				/*
140057429Smarkm				 * An error or EOF was encountered.  Put an
140157429Smarkm				 * error message to stderr buffer.
140257429Smarkm				 */
1403181111Sdes				snprintf(buf, sizeof buf,
1404181111Sdes				    "write stdout: %.50s\r\n", strerror(errno));
140557429Smarkm				buffer_append(&stderr_buffer, buf, strlen(buf));
140657429Smarkm				quit_pending = 1;
140757429Smarkm				return;
140857429Smarkm			}
140957429Smarkm		}
141057429Smarkm		/* Consume printed data from the buffer. */
141157429Smarkm		buffer_consume(&stdout_buffer, len);
141257429Smarkm	}
141357429Smarkm	/* Write buffered output to stderr. */
141457429Smarkm	if (FD_ISSET(fileno(stderr), writeset)) {
141557429Smarkm		/* Write as much data as possible. */
141657429Smarkm		len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
141757429Smarkm		    buffer_len(&stderr_buffer));
141857429Smarkm		if (len <= 0) {
1419181111Sdes			if (errno == EINTR || errno == EAGAIN ||
1420181111Sdes			    errno == EWOULDBLOCK)
142157429Smarkm				len = 0;
142257429Smarkm			else {
1423181111Sdes				/*
1424181111Sdes				 * EOF or error, but can't even print
1425181111Sdes				 * error message.
1426181111Sdes				 */
142757429Smarkm				quit_pending = 1;
142857429Smarkm				return;
142957429Smarkm			}
143057429Smarkm		}
143157429Smarkm		/* Consume printed characters from the buffer. */
143257429Smarkm		buffer_consume(&stderr_buffer, len);
143357429Smarkm	}
143457429Smarkm}
143557429Smarkm
143657429Smarkm/*
143760573Skris * Get packets from the connection input buffer, and process them as long as
143860573Skris * there are packets available.
143960573Skris *
144060573Skris * Any unknown packets received during the actual
144160573Skris * session cause the session to terminate.  This is
144260573Skris * intended to make debugging easier since no
144360573Skris * confirmations are sent.  Any compatible protocol
144460573Skris * extensions must be negotiated during the
144560573Skris * preparatory phase.
144660573Skris */
144760573Skris
144892555Sdesstatic void
144976259Sgreenclient_process_buffered_input_packets(void)
145060573Skris{
1451295367Sdes	dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state);
145260573Skris}
145360573Skris
145465668Skris/* scan buf[] for '~' before sending data to the peer */
145565668Skris
1456181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */
1457181111Sdesvoid *
1458181111Sdesclient_new_escape_filter_ctx(int escape_char)
145965668Skris{
1460181111Sdes	struct escape_filter_ctx *ret;
1461181111Sdes
1462258343Sdes	ret = xcalloc(1, sizeof(*ret));
1463181111Sdes	ret->escape_pending = 0;
1464181111Sdes	ret->escape_char = escape_char;
1465181111Sdes	return (void *)ret;
146665668Skris}
146765668Skris
1468181111Sdes/* Free the escape filter context on channel free */
1469181111Sdesvoid
1470181111Sdesclient_filter_cleanup(int cid, void *ctx)
1471181111Sdes{
1472255767Sdes	free(ctx);
1473181111Sdes}
1474181111Sdes
1475181111Sdesint
1476181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len)
1477181111Sdes{
1478181111Sdes	if (c->extended_usage != CHAN_EXTENDED_WRITE)
1479181111Sdes		return 0;
1480181111Sdes
1481181111Sdes	return process_escapes(c, &c->input, &c->output, &c->extended,
1482181111Sdes	    buf, len);
1483181111Sdes}
1484181111Sdes
148592555Sdesstatic void
148676259Sgreenclient_channel_closed(int id, void *arg)
148776259Sgreen{
148892555Sdes	channel_cancel_cleanup(id);
148976259Sgreen	session_closed = 1;
1490226046Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
149176259Sgreen}
149276259Sgreen
149360573Skris/*
149457429Smarkm * Implements the interactive session with the server.  This is called after
149557429Smarkm * the user has been authenticated, and a command has been started on the
149692555Sdes * remote host.  If escape_char != SSH_ESCAPECHAR_NONE, it is the character
149792555Sdes * used as an escape character for terminating or suspending the session.
149857429Smarkm */
149957429Smarkm
150060573Skrisint
150165668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
150257429Smarkm{
150376259Sgreen	fd_set *readset = NULL, *writeset = NULL;
150457429Smarkm	double start_time, total_time;
1505296853Sdes	int r, max_fd = 0, max_fd2 = 0, len;
1506181111Sdes	u_int64_t ibytes, obytes;
1507137015Sdes	u_int nalloc = 0;
150857429Smarkm	char buf[100];
150957429Smarkm
151057429Smarkm	debug("Entering interactive session.");
151157429Smarkm
1512296853Sdes	if (options.control_master &&
1513296853Sdes	    ! option_clear_or_none(options.control_path)) {
1514296853Sdes		debug("pledge: id");
1515296853Sdes		if (pledge("stdio rpath wpath cpath unix inet dns proc exec id tty",
1516296853Sdes		    NULL) == -1)
1517296853Sdes			fatal("%s pledge(): %s", __func__, strerror(errno));
1518296853Sdes
1519296853Sdes	} else if (options.forward_x11 || options.permit_local_command) {
1520296853Sdes		debug("pledge: exec");
1521296853Sdes		if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty",
1522296853Sdes		    NULL) == -1)
1523296853Sdes			fatal("%s pledge(): %s", __func__, strerror(errno));
1524296853Sdes
1525296853Sdes	} else if (options.update_hostkeys) {
1526296853Sdes		debug("pledge: filesystem full");
1527296853Sdes		if (pledge("stdio rpath wpath cpath unix inet dns proc tty",
1528296853Sdes		    NULL) == -1)
1529296853Sdes			fatal("%s pledge(): %s", __func__, strerror(errno));
1530296853Sdes
1531296853Sdes	} else if (! option_clear_or_none(options.proxy_command)) {
1532296853Sdes		debug("pledge: proc");
1533296853Sdes		if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1)
1534296853Sdes			fatal("%s pledge(): %s", __func__, strerror(errno));
1535296853Sdes
1536296853Sdes	} else {
1537296853Sdes		debug("pledge: network");
1538296853Sdes		if (pledge("stdio unix inet dns tty", NULL) == -1)
1539296853Sdes			fatal("%s pledge(): %s", __func__, strerror(errno));
1540296853Sdes	}
1541296853Sdes
154257429Smarkm	start_time = get_current_time();
154357429Smarkm
154457429Smarkm	/* Initialize variables. */
1545181111Sdes	escape_pending1 = 0;
154657429Smarkm	last_was_cr = 1;
154757429Smarkm	exit_status = -1;
154857429Smarkm	stdin_eof = 0;
154957429Smarkm	buffer_high = 64 * 1024;
155057429Smarkm	connection_in = packet_get_connection_in();
155157429Smarkm	connection_out = packet_get_connection_out();
155276259Sgreen	max_fd = MAX(connection_in, connection_out);
155376259Sgreen
155476259Sgreen	if (!compat20) {
155576259Sgreen		/* enable nonblocking unless tty */
155676259Sgreen		if (!isatty(fileno(stdin)))
155776259Sgreen			set_nonblock(fileno(stdin));
155876259Sgreen		if (!isatty(fileno(stdout)))
155976259Sgreen			set_nonblock(fileno(stdout));
156076259Sgreen		if (!isatty(fileno(stderr)))
156176259Sgreen			set_nonblock(fileno(stderr));
156276259Sgreen		max_fd = MAX(max_fd, fileno(stdin));
156376259Sgreen		max_fd = MAX(max_fd, fileno(stdout));
156476259Sgreen		max_fd = MAX(max_fd, fileno(stderr));
156576259Sgreen	}
156657429Smarkm	quit_pending = 0;
1567181111Sdes	escape_char1 = escape_char_arg;
156857429Smarkm
156957429Smarkm	/* Initialize buffers. */
157057429Smarkm	buffer_init(&stdin_buffer);
157157429Smarkm	buffer_init(&stdout_buffer);
157257429Smarkm	buffer_init(&stderr_buffer);
157357429Smarkm
157460573Skris	client_init_dispatch();
157560573Skris
1576113908Sdes	/*
1577113908Sdes	 * Set signal handlers, (e.g. to restore non-blocking mode)
1578113908Sdes	 * but don't overwrite SIG_IGN, matches behaviour from rsh(1)
1579113908Sdes	 */
1580146998Sdes	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1581146998Sdes		signal(SIGHUP, signal_handler);
1582113908Sdes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1583113908Sdes		signal(SIGINT, signal_handler);
1584113908Sdes	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
1585113908Sdes		signal(SIGQUIT, signal_handler);
1586113908Sdes	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
1587113908Sdes		signal(SIGTERM, signal_handler);
1588146998Sdes	signal(SIGWINCH, window_change_handler);
158957429Smarkm
159057429Smarkm	if (have_pty)
1591226046Sdes		enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
159257429Smarkm
159376259Sgreen	if (compat20) {
159476259Sgreen		session_ident = ssh2_chan_id;
1595226046Sdes		if (session_ident != -1) {
1596226046Sdes			if (escape_char_arg != SSH_ESCAPECHAR_NONE) {
1597226046Sdes				channel_register_filter(session_ident,
1598226046Sdes				    client_simple_escape_filter, NULL,
1599226046Sdes				    client_filter_cleanup,
1600226046Sdes				    client_new_escape_filter_ctx(
1601226046Sdes				    escape_char_arg));
1602226046Sdes			}
160376259Sgreen			channel_register_cleanup(session_ident,
1604157016Sdes			    client_channel_closed, 0);
1605226046Sdes		}
160676259Sgreen	} else {
160776259Sgreen		/* Check if we should immediately send eof on stdin. */
160860573Skris		client_check_initial_eof_on_stdin();
160976259Sgreen	}
161057429Smarkm
161157429Smarkm	/* Main loop of the client for the interactive session mode. */
161257429Smarkm	while (!quit_pending) {
161357429Smarkm
161457429Smarkm		/* Process buffered packets sent by the server. */
161557429Smarkm		client_process_buffered_input_packets();
161657429Smarkm
161776259Sgreen		if (compat20 && session_closed && !channel_still_open())
161860573Skris			break;
161960573Skris
1620296853Sdes		if (ssh_packet_is_rekeying(active_state)) {
162176259Sgreen			debug("rekeying in progress");
1622296853Sdes		} else if (need_rekeying) {
1623296853Sdes			/* manual rekey request */
1624296853Sdes			debug("need rekeying");
1625296853Sdes			if ((r = kex_start_rekex(active_state)) != 0)
1626296853Sdes				fatal("%s: kex_start_rekex: %s", __func__,
1627296853Sdes				    ssh_err(r));
1628296853Sdes			need_rekeying = 0;
162976259Sgreen		} else {
163076259Sgreen			/*
163176259Sgreen			 * Make packets of buffered stdin data, and buffer
163276259Sgreen			 * them for sending to the server.
163376259Sgreen			 */
163476259Sgreen			if (!compat20)
163576259Sgreen				client_make_packets_from_stdin_data();
163657429Smarkm
163776259Sgreen			/*
163876259Sgreen			 * Make packets from buffered channel data, and
163976259Sgreen			 * enqueue them for sending to the server.
164076259Sgreen			 */
164176259Sgreen			if (packet_not_very_much_data_to_write())
164276259Sgreen				channel_output_poll();
164357429Smarkm
164476259Sgreen			/*
164576259Sgreen			 * Check if the window size has changed, and buffer a
164676259Sgreen			 * message about it to the server if so.
164776259Sgreen			 */
164876259Sgreen			client_check_window_change();
164957429Smarkm
165076259Sgreen			if (quit_pending)
165176259Sgreen				break;
165276259Sgreen		}
165357429Smarkm		/*
165457429Smarkm		 * Wait until we have something to do (something becomes
165557429Smarkm		 * available on one of the descriptors).
165657429Smarkm		 */
165792555Sdes		max_fd2 = max_fd;
165876259Sgreen		client_wait_until_can_do_something(&readset, &writeset,
1659296853Sdes		    &max_fd2, &nalloc, ssh_packet_is_rekeying(active_state));
166057429Smarkm
166157429Smarkm		if (quit_pending)
166257429Smarkm			break;
166357429Smarkm
166476259Sgreen		/* Do channel operations unless rekeying in progress. */
1665296853Sdes		if (!ssh_packet_is_rekeying(active_state))
166676259Sgreen			channel_after_select(readset, writeset);
166776259Sgreen
166860573Skris		/* Buffer input from the connection.  */
166976259Sgreen		client_process_net_input(readset);
167057429Smarkm
167160573Skris		if (quit_pending)
167260573Skris			break;
167357429Smarkm
167460573Skris		if (!compat20) {
167560573Skris			/* Buffer data from stdin */
167676259Sgreen			client_process_input(readset);
167760573Skris			/*
167860573Skris			 * Process output to stdout and stderr.  Output to
167960573Skris			 * the connection is processed elsewhere (above).
168060573Skris			 */
168176259Sgreen			client_process_output(writeset);
168260573Skris		}
168360573Skris
1684181111Sdes		/*
1685181111Sdes		 * Send as much buffered packet data as possible to the
1686181111Sdes		 * sender.
1687181111Sdes		 */
168876259Sgreen		if (FD_ISSET(connection_out, writeset))
168957429Smarkm			packet_write_poll();
1690215116Sdes
1691215116Sdes		/*
1692215116Sdes		 * If we are a backgrounded control master, and the
1693215116Sdes		 * timeout has expired without any active client
1694215116Sdes		 * connections, then quit.
1695215116Sdes		 */
1696215116Sdes		if (control_persist_exit_time > 0) {
1697255767Sdes			if (monotime() >= control_persist_exit_time) {
1698215116Sdes				debug("ControlPersist timeout expired");
1699215116Sdes				break;
1700215116Sdes			}
1701215116Sdes		}
170257429Smarkm	}
1703255767Sdes	free(readset);
1704255767Sdes	free(writeset);
170557429Smarkm
170657429Smarkm	/* Terminate the session. */
170757429Smarkm
170857429Smarkm	/* Stop watching for window change. */
1709146998Sdes	signal(SIGWINCH, SIG_DFL);
171057429Smarkm
1711197679Sdes	if (compat20) {
1712197679Sdes		packet_start(SSH2_MSG_DISCONNECT);
1713197679Sdes		packet_put_int(SSH2_DISCONNECT_BY_APPLICATION);
1714197679Sdes		packet_put_cstring("disconnected by user");
1715207319Sdes		packet_put_cstring(""); /* language tag */
1716197679Sdes		packet_send();
1717197679Sdes		packet_write_wait();
1718197679Sdes	}
1719197679Sdes
172092555Sdes	channel_free_all();
172157429Smarkm
172292555Sdes	if (have_pty)
1723226046Sdes		leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
172492555Sdes
172592555Sdes	/* restore blocking io */
172692555Sdes	if (!isatty(fileno(stdin)))
172792555Sdes		unset_nonblock(fileno(stdin));
172892555Sdes	if (!isatty(fileno(stdout)))
172992555Sdes		unset_nonblock(fileno(stdout));
173092555Sdes	if (!isatty(fileno(stderr)))
173192555Sdes		unset_nonblock(fileno(stderr));
173292555Sdes
1733126274Sdes	/*
1734126274Sdes	 * If there was no shell or command requested, there will be no remote
1735126274Sdes	 * exit status to be returned.  In that case, clear error code if the
1736126274Sdes	 * connection was deliberately terminated at this end.
1737126274Sdes	 */
1738126274Sdes	if (no_shell_flag && received_signal == SIGTERM) {
1739126274Sdes		received_signal = 0;
1740126274Sdes		exit_status = 0;
174192555Sdes	}
174292555Sdes
1743126274Sdes	if (received_signal)
1744126274Sdes		fatal("Killed by signal %d.", (int) received_signal);
1745126274Sdes
174657429Smarkm	/*
174757429Smarkm	 * In interactive mode (with pseudo tty) display a message indicating
174857429Smarkm	 * that the connection has been closed.
174957429Smarkm	 */
175057429Smarkm	if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
1751181111Sdes		snprintf(buf, sizeof buf,
1752181111Sdes		    "Connection to %.64s closed.\r\n", host);
175357429Smarkm		buffer_append(&stderr_buffer, buf, strlen(buf));
175457429Smarkm	}
175592555Sdes
175657429Smarkm	/* Output any buffered data for stdout. */
1757221420Sdes	if (buffer_len(&stdout_buffer) > 0) {
1758221420Sdes		len = atomicio(vwrite, fileno(stdout),
1759221420Sdes		    buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer));
1760221420Sdes		if (len < 0 || (u_int)len != buffer_len(&stdout_buffer))
176157429Smarkm			error("Write failed flushing stdout buffer.");
1762221420Sdes		else
1763221420Sdes			buffer_consume(&stdout_buffer, len);
176457429Smarkm	}
176557429Smarkm
176657429Smarkm	/* Output any buffered data for stderr. */
1767221420Sdes	if (buffer_len(&stderr_buffer) > 0) {
1768221420Sdes		len = atomicio(vwrite, fileno(stderr),
1769221420Sdes		    buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer));
1770221420Sdes		if (len < 0 || (u_int)len != buffer_len(&stderr_buffer))
177157429Smarkm			error("Write failed flushing stderr buffer.");
1772221420Sdes		else
1773221420Sdes			buffer_consume(&stderr_buffer, len);
177457429Smarkm	}
177557429Smarkm
177657429Smarkm	/* Clear and free any buffers. */
1777296853Sdes	explicit_bzero(buf, sizeof(buf));
177857429Smarkm	buffer_free(&stdin_buffer);
177957429Smarkm	buffer_free(&stdout_buffer);
178057429Smarkm	buffer_free(&stderr_buffer);
178157429Smarkm
178257429Smarkm	/* Report bytes transferred, and transfer rates. */
178357429Smarkm	total_time = get_current_time() - start_time;
1784295367Sdes	packet_get_bytes(&ibytes, &obytes);
1785181111Sdes	verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds",
1786221420Sdes	    (unsigned long long)obytes, (unsigned long long)ibytes, total_time);
178757429Smarkm	if (total_time > 0)
1788181111Sdes		verbose("Bytes per second: sent %.1f, received %.1f",
1789181111Sdes		    obytes / total_time, ibytes / total_time);
179057429Smarkm	/* Return the exit status of the program. */
179157429Smarkm	debug("Exit status %d", exit_status);
179257429Smarkm	return exit_status;
179357429Smarkm}
179460573Skris
179560573Skris/*********/
179660573Skris
1797295367Sdesstatic int
179892555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt)
179960573Skris{
180076259Sgreen	u_int data_len;
180160573Skris	char *data = packet_get_string(&data_len);
180292555Sdes	packet_check_eom();
180360573Skris	buffer_append(&stdout_buffer, data, data_len);
1804264377Sdes	explicit_bzero(data, data_len);
1805255767Sdes	free(data);
1806295367Sdes	return 0;
180760573Skris}
1808295367Sdesstatic int
180992555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt)
181060573Skris{
181176259Sgreen	u_int data_len;
181260573Skris	char *data = packet_get_string(&data_len);
181392555Sdes	packet_check_eom();
181460573Skris	buffer_append(&stderr_buffer, data, data_len);
1815264377Sdes	explicit_bzero(data, data_len);
1816255767Sdes	free(data);
1817295367Sdes	return 0;
181860573Skris}
1819295367Sdesstatic int
182092555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt)
182160573Skris{
182260573Skris	exit_status = packet_get_int();
182392555Sdes	packet_check_eom();
182460573Skris	/* Acknowledge the exit. */
182560573Skris	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
182660573Skris	packet_send();
182760573Skris	/*
182860573Skris	 * Must wait for packet to be sent since we are
182960573Skris	 * exiting the loop.
183060573Skris	 */
183160573Skris	packet_write_wait();
183260573Skris	/* Flag that we want to exit. */
183360573Skris	quit_pending = 1;
1834295367Sdes	return 0;
183560573Skris}
1836295367Sdes
1837295367Sdesstatic int
1838126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt)
1839126274Sdes{
1840126274Sdes	Channel *c = NULL;
1841295367Sdes	int r, remote_id, sock;
184260573Skris
1843126274Sdes	/* Read the remote channel number from the message. */
1844126274Sdes	remote_id = packet_get_int();
1845126274Sdes	packet_check_eom();
1846126274Sdes
1847126274Sdes	/*
1848126274Sdes	 * Get a connection to the local authentication agent (this may again
1849126274Sdes	 * get forwarded).
1850126274Sdes	 */
1851295367Sdes	if ((r = ssh_get_authentication_socket(&sock)) != 0 &&
1852295367Sdes	    r != SSH_ERR_AGENT_NOT_PRESENT)
1853295367Sdes		debug("%s: ssh_get_authentication_socket: %s",
1854295367Sdes		    __func__, ssh_err(r));
1855126274Sdes
1856295367Sdes
1857126274Sdes	/*
1858126274Sdes	 * If we could not connect the agent, send an error message back to
1859126274Sdes	 * the server. This should never happen unless the agent dies,
1860126274Sdes	 * because authentication forwarding is only enabled if we have an
1861126274Sdes	 * agent.
1862126274Sdes	 */
1863126274Sdes	if (sock >= 0) {
1864126274Sdes		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
1865126274Sdes		    -1, 0, 0, 0, "authentication agent connection", 1);
1866126274Sdes		c->remote_id = remote_id;
1867126274Sdes		c->force_drain = 1;
1868126274Sdes	}
1869126274Sdes	if (c == NULL) {
1870126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
1871126274Sdes		packet_put_int(remote_id);
1872126274Sdes	} else {
1873126274Sdes		/* Send a confirmation to the remote host. */
1874126274Sdes		debug("Forwarding authentication connection.");
1875126274Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
1876126274Sdes		packet_put_int(remote_id);
1877126274Sdes		packet_put_int(c->self);
1878126274Sdes	}
1879126274Sdes	packet_send();
1880295367Sdes	return 0;
1881126274Sdes}
1882126274Sdes
188392555Sdesstatic Channel *
188476259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan)
188576259Sgreen{
1886106121Sdes	Channel *c = NULL;
188776259Sgreen	char *listen_address, *originator_address;
1888192595Sdes	u_short listen_port, originator_port;
188976259Sgreen
189076259Sgreen	/* Get rest of the packet */
189176259Sgreen	listen_address = packet_get_string(NULL);
189276259Sgreen	listen_port = packet_get_int();
189376259Sgreen	originator_address = packet_get_string(NULL);
189476259Sgreen	originator_port = packet_get_int();
189592555Sdes	packet_check_eom();
189676259Sgreen
1897295367Sdes	debug("%s: listen %s port %d, originator %s port %d", __func__,
1898295367Sdes	    listen_address, listen_port, originator_address, originator_port);
189976259Sgreen
1900295367Sdes	c = channel_connect_by_listen_address(listen_address, listen_port,
1901181111Sdes	    "forwarded-tcpip", originator_address);
1902181111Sdes
1903255767Sdes	free(originator_address);
1904255767Sdes	free(listen_address);
190576259Sgreen	return c;
190676259Sgreen}
190776259Sgreen
1908106121Sdesstatic Channel *
1909295367Sdesclient_request_forwarded_streamlocal(const char *request_type, int rchan)
1910295367Sdes{
1911295367Sdes	Channel *c = NULL;
1912295367Sdes	char *listen_path;
1913295367Sdes
1914295367Sdes	/* Get the remote path. */
1915295367Sdes	listen_path = packet_get_string(NULL);
1916295367Sdes	/* XXX: Skip reserved field for now. */
1917295367Sdes	if (packet_get_string_ptr(NULL) == NULL)
1918295367Sdes		fatal("%s: packet_get_string_ptr failed", __func__);
1919295367Sdes	packet_check_eom();
1920295367Sdes
1921295367Sdes	debug("%s: %s", __func__, listen_path);
1922295367Sdes
1923295367Sdes	c = channel_connect_by_listen_path(listen_path,
1924295367Sdes	    "forwarded-streamlocal@openssh.com", "forwarded-streamlocal");
1925295367Sdes	free(listen_path);
1926295367Sdes	return c;
1927295367Sdes}
1928295367Sdes
1929295367Sdesstatic Channel *
193076259Sgreenclient_request_x11(const char *request_type, int rchan)
193176259Sgreen{
193276259Sgreen	Channel *c = NULL;
193376259Sgreen	char *originator;
1934192595Sdes	u_short originator_port;
193592555Sdes	int sock;
193676259Sgreen
193776259Sgreen	if (!options.forward_x11) {
193876259Sgreen		error("Warning: ssh server tried X11 forwarding.");
1939181111Sdes		error("Warning: this is probably a break-in attempt by a "
1940181111Sdes		    "malicious server.");
194176259Sgreen		return NULL;
194276259Sgreen	}
1943295367Sdes	if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) {
1944215116Sdes		verbose("Rejected X11 connection after ForwardX11Timeout "
1945215116Sdes		    "expired");
1946215116Sdes		return NULL;
1947215116Sdes	}
194876259Sgreen	originator = packet_get_string(NULL);
194976259Sgreen	if (datafellows & SSH_BUG_X11FWD) {
195076259Sgreen		debug2("buggy server: x11 request w/o originator_port");
195176259Sgreen		originator_port = 0;
195276259Sgreen	} else {
195376259Sgreen		originator_port = packet_get_int();
195476259Sgreen	}
195592555Sdes	packet_check_eom();
195676259Sgreen	/* XXX check permission */
195776259Sgreen	debug("client_request_x11: request from %s %d", originator,
195876259Sgreen	    originator_port);
1959255767Sdes	free(originator);
196076259Sgreen	sock = x11_connect_display();
196192555Sdes	if (sock < 0)
196292555Sdes		return NULL;
1963294693Sdes	c = channel_new("x11",
1964294693Sdes	    SSH_CHANNEL_X11_OPEN, sock, sock, -1,
1965294693Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
196692555Sdes	c->force_drain = 1;
196776259Sgreen	return c;
196876259Sgreen}
196976259Sgreen
1970106121Sdesstatic Channel *
197176259Sgreenclient_request_agent(const char *request_type, int rchan)
197276259Sgreen{
197376259Sgreen	Channel *c = NULL;
1974295367Sdes	int r, sock;
197576259Sgreen
197676259Sgreen	if (!options.forward_agent) {
197776259Sgreen		error("Warning: ssh server tried agent forwarding.");
1978181111Sdes		error("Warning: this is probably a break-in attempt by a "
1979181111Sdes		    "malicious server.");
198076259Sgreen		return NULL;
198176259Sgreen	}
1982295367Sdes	if ((r = ssh_get_authentication_socket(&sock)) != 0) {
1983295367Sdes		if (r != SSH_ERR_AGENT_NOT_PRESENT)
1984295367Sdes			debug("%s: ssh_get_authentication_socket: %s",
1985295367Sdes			    __func__, ssh_err(r));
198692555Sdes		return NULL;
1987295367Sdes	}
1988294693Sdes	c = channel_new("authentication agent connection",
1989294693Sdes	    SSH_CHANNEL_OPEN, sock, sock, -1,
1990294693Sdes	    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
1991294693Sdes	    "authentication agent connection", 1);
199292555Sdes	c->force_drain = 1;
199376259Sgreen	return c;
199476259Sgreen}
199576259Sgreen
1996181111Sdesint
1997181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun)
1998181111Sdes{
1999181111Sdes	Channel *c;
2000181111Sdes	int fd;
2001181111Sdes
2002181111Sdes	if (tun_mode == SSH_TUNMODE_NO)
2003181111Sdes		return 0;
2004181111Sdes
2005181111Sdes	if (!compat20) {
2006192595Sdes		error("Tunnel forwarding is not supported for protocol 1");
2007181111Sdes		return -1;
2008181111Sdes	}
2009181111Sdes
2010181111Sdes	debug("Requesting tun unit %d in mode %d", local_tun, tun_mode);
2011181111Sdes
2012181111Sdes	/* Open local tunnel device */
2013181111Sdes	if ((fd = tun_open(local_tun, tun_mode)) == -1) {
2014181111Sdes		error("Tunnel device open failed.");
2015181111Sdes		return -1;
2016181111Sdes	}
2017181111Sdes
2018294693Sdes	c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
2019294693Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
2020181111Sdes	c->datagram = 1;
2021181111Sdes
2022181111Sdes#if defined(SSH_TUN_FILTER)
2023181111Sdes	if (options.tun_open == SSH_TUNMODE_POINTOPOINT)
2024181111Sdes		channel_register_filter(c->self, sys_tun_infilter,
2025181111Sdes		    sys_tun_outfilter, NULL, NULL);
2026181111Sdes#endif
2027181111Sdes
2028181111Sdes	packet_start(SSH2_MSG_CHANNEL_OPEN);
2029181111Sdes	packet_put_cstring("tun@openssh.com");
2030181111Sdes	packet_put_int(c->self);
2031181111Sdes	packet_put_int(c->local_window_max);
2032181111Sdes	packet_put_int(c->local_maxpacket);
2033181111Sdes	packet_put_int(tun_mode);
2034181111Sdes	packet_put_int(remote_tun);
2035181111Sdes	packet_send();
2036181111Sdes
2037181111Sdes	return 0;
2038181111Sdes}
2039181111Sdes
204060573Skris/* XXXX move to generic input handler */
2041295367Sdesstatic int
204292555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt)
204360573Skris{
204460573Skris	Channel *c = NULL;
204560573Skris	char *ctype;
204660573Skris	int rchan;
204799060Sdes	u_int rmaxpack, rwindow, len;
204860573Skris
204960573Skris	ctype = packet_get_string(&len);
205060573Skris	rchan = packet_get_int();
205160573Skris	rwindow = packet_get_int();
205260573Skris	rmaxpack = packet_get_int();
205360573Skris
205460573Skris	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
205560573Skris	    ctype, rchan, rwindow, rmaxpack);
205660573Skris
205776259Sgreen	if (strcmp(ctype, "forwarded-tcpip") == 0) {
205876259Sgreen		c = client_request_forwarded_tcpip(ctype, rchan);
2059295367Sdes	} else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) {
2060295367Sdes		c = client_request_forwarded_streamlocal(ctype, rchan);
206176259Sgreen	} else if (strcmp(ctype, "x11") == 0) {
206276259Sgreen		c = client_request_x11(ctype, rchan);
206376259Sgreen	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
206476259Sgreen		c = client_request_agent(ctype, rchan);
206560573Skris	}
206660573Skris/* XXX duplicate : */
206760573Skris	if (c != NULL) {
206860573Skris		debug("confirm %s", ctype);
206960573Skris		c->remote_id = rchan;
207060573Skris		c->remote_window = rwindow;
207160573Skris		c->remote_maxpacket = rmaxpack;
207292555Sdes		if (c->type != SSH_CHANNEL_CONNECTING) {
207392555Sdes			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
207492555Sdes			packet_put_int(c->remote_id);
207592555Sdes			packet_put_int(c->self);
207692555Sdes			packet_put_int(c->local_window);
207792555Sdes			packet_put_int(c->local_maxpacket);
207892555Sdes			packet_send();
207992555Sdes		}
208060573Skris	} else {
208160573Skris		debug("failure %s", ctype);
208260573Skris		packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
208360573Skris		packet_put_int(rchan);
208460573Skris		packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
208592555Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
208692555Sdes			packet_put_cstring("open failed");
208792555Sdes			packet_put_cstring("");
208892555Sdes		}
208960573Skris		packet_send();
209060573Skris	}
2091255767Sdes	free(ctype);
2092295367Sdes	return 0;
209360573Skris}
2094295367Sdes
2095295367Sdesstatic int
209692555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt)
209776259Sgreen{
209876259Sgreen	Channel *c = NULL;
2099137015Sdes	int exitval, id, reply, success = 0;
210076259Sgreen	char *rtype;
210160573Skris
210276259Sgreen	id = packet_get_int();
210376259Sgreen	rtype = packet_get_string(NULL);
210476259Sgreen	reply = packet_get_char();
210576259Sgreen
210676259Sgreen	debug("client_input_channel_req: channel %d rtype %s reply %d",
210776259Sgreen	    id, rtype, reply);
210876259Sgreen
2109137015Sdes	if (id == -1) {
2110137015Sdes		error("client_input_channel_req: request for channel -1");
2111137015Sdes	} else if ((c = channel_lookup(id)) == NULL) {
2112181111Sdes		error("client_input_channel_req: channel %d: "
2113181111Sdes		    "unknown channel", id);
2114181111Sdes	} else if (strcmp(rtype, "eow@openssh.com") == 0) {
2115181111Sdes		packet_check_eom();
2116181111Sdes		chan_rcvd_eow(c);
211776259Sgreen	} else if (strcmp(rtype, "exit-status") == 0) {
2118137015Sdes		exitval = packet_get_int();
2119204917Sdes		if (c->ctl_chan != -1) {
2120204917Sdes			mux_exit_message(c, exitval);
2121137015Sdes			success = 1;
2122204917Sdes		} else if (id == session_ident) {
2123204917Sdes			/* Record exit value of local session */
2124204917Sdes			success = 1;
2125137015Sdes			exit_status = exitval;
2126137015Sdes		} else {
2127204917Sdes			/* Probably for a mux channel that has already closed */
2128204917Sdes			debug("%s: no sink for exit-status on channel %d",
2129204917Sdes			    __func__, id);
2130137015Sdes		}
213192555Sdes		packet_check_eom();
213276259Sgreen	}
2133295367Sdes	if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) {
213476259Sgreen		packet_start(success ?
213576259Sgreen		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
2136192595Sdes		packet_put_int(c->remote_id);
213776259Sgreen		packet_send();
213876259Sgreen	}
2139255767Sdes	free(rtype);
2140295367Sdes	return 0;
214176259Sgreen}
2142295367Sdes
2143295367Sdesstruct hostkeys_update_ctx {
2144295367Sdes	/* The hostname and (optionally) IP address string for the server */
2145295367Sdes	char *host_str, *ip_str;
2146295367Sdes
2147295367Sdes	/*
2148295367Sdes	 * Keys received from the server and a flag for each indicating
2149295367Sdes	 * whether they already exist in known_hosts.
2150295367Sdes	 * keys_seen is filled in by hostkeys_find() and later (for new
2151295367Sdes	 * keys) by client_global_hostkeys_private_confirm().
2152295367Sdes	 */
2153295367Sdes	struct sshkey **keys;
2154295367Sdes	int *keys_seen;
2155295367Sdes	size_t nkeys;
2156295367Sdes
2157295367Sdes	size_t nnew;
2158295367Sdes
2159295367Sdes	/*
2160295367Sdes	 * Keys that are in known_hosts, but were not present in the update
2161295367Sdes	 * from the server (i.e. scheduled to be deleted).
2162295367Sdes	 * Filled in by hostkeys_find().
2163295367Sdes	 */
2164295367Sdes	struct sshkey **old_keys;
2165295367Sdes	size_t nold;
2166295367Sdes};
2167295367Sdes
216892555Sdesstatic void
2169295367Sdeshostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx)
2170295367Sdes{
2171295367Sdes	size_t i;
2172295367Sdes
2173295367Sdes	if (ctx == NULL)
2174295367Sdes		return;
2175295367Sdes	for (i = 0; i < ctx->nkeys; i++)
2176295367Sdes		sshkey_free(ctx->keys[i]);
2177295367Sdes	free(ctx->keys);
2178295367Sdes	free(ctx->keys_seen);
2179295367Sdes	for (i = 0; i < ctx->nold; i++)
2180295367Sdes		sshkey_free(ctx->old_keys[i]);
2181295367Sdes	free(ctx->old_keys);
2182295367Sdes	free(ctx->host_str);
2183295367Sdes	free(ctx->ip_str);
2184295367Sdes	free(ctx);
2185295367Sdes}
2186295367Sdes
2187295367Sdesstatic int
2188295367Sdeshostkeys_find(struct hostkey_foreach_line *l, void *_ctx)
2189295367Sdes{
2190295367Sdes	struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx;
2191295367Sdes	size_t i;
2192295367Sdes	struct sshkey **tmp;
2193295367Sdes
2194295367Sdes	if (l->status != HKF_STATUS_MATCHED || l->key == NULL ||
2195295367Sdes	    l->key->type == KEY_RSA1)
2196295367Sdes		return 0;
2197295367Sdes
2198295367Sdes	/* Mark off keys we've already seen for this host */
2199295367Sdes	for (i = 0; i < ctx->nkeys; i++) {
2200295367Sdes		if (sshkey_equal(l->key, ctx->keys[i])) {
2201295367Sdes			debug3("%s: found %s key at %s:%ld", __func__,
2202295367Sdes			    sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum);
2203295367Sdes			ctx->keys_seen[i] = 1;
2204295367Sdes			return 0;
2205295367Sdes		}
2206295367Sdes	}
2207295367Sdes	/* This line contained a key that not offered by the server */
2208295367Sdes	debug3("%s: deprecated %s key at %s:%ld", __func__,
2209295367Sdes	    sshkey_ssh_name(l->key), l->path, l->linenum);
2210295367Sdes	if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1,
2211295367Sdes	    sizeof(*ctx->old_keys))) == NULL)
2212295367Sdes		fatal("%s: reallocarray failed nold = %zu",
2213295367Sdes		    __func__, ctx->nold);
2214295367Sdes	ctx->old_keys = tmp;
2215295367Sdes	ctx->old_keys[ctx->nold++] = l->key;
2216295367Sdes	l->key = NULL;
2217295367Sdes
2218295367Sdes	return 0;
2219295367Sdes}
2220295367Sdes
2221295367Sdesstatic void
2222295367Sdesupdate_known_hosts(struct hostkeys_update_ctx *ctx)
2223295367Sdes{
2224295367Sdes	int r, was_raw = 0;
2225295367Sdes	int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ?
2226295367Sdes	    SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE;
2227295367Sdes	char *fp, *response;
2228295367Sdes	size_t i;
2229295367Sdes
2230295367Sdes	for (i = 0; i < ctx->nkeys; i++) {
2231295367Sdes		if (ctx->keys_seen[i] != 2)
2232295367Sdes			continue;
2233295367Sdes		if ((fp = sshkey_fingerprint(ctx->keys[i],
2234295367Sdes		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
2235295367Sdes			fatal("%s: sshkey_fingerprint failed", __func__);
2236295367Sdes		do_log2(loglevel, "Learned new hostkey: %s %s",
2237295367Sdes		    sshkey_type(ctx->keys[i]), fp);
2238295367Sdes		free(fp);
2239295367Sdes	}
2240295367Sdes	for (i = 0; i < ctx->nold; i++) {
2241295367Sdes		if ((fp = sshkey_fingerprint(ctx->old_keys[i],
2242295367Sdes		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
2243295367Sdes			fatal("%s: sshkey_fingerprint failed", __func__);
2244295367Sdes		do_log2(loglevel, "Deprecating obsolete hostkey: %s %s",
2245295367Sdes		    sshkey_type(ctx->old_keys[i]), fp);
2246295367Sdes		free(fp);
2247295367Sdes	}
2248295367Sdes	if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) {
2249295367Sdes		if (get_saved_tio() != NULL) {
2250295367Sdes			leave_raw_mode(1);
2251295367Sdes			was_raw = 1;
2252295367Sdes		}
2253295367Sdes		response = NULL;
2254295367Sdes		for (i = 0; !quit_pending && i < 3; i++) {
2255295367Sdes			free(response);
2256295367Sdes			response = read_passphrase("Accept updated hostkeys? "
2257295367Sdes			    "(yes/no): ", RP_ECHO);
2258295367Sdes			if (strcasecmp(response, "yes") == 0)
2259295367Sdes				break;
2260295367Sdes			else if (quit_pending || response == NULL ||
2261295367Sdes			    strcasecmp(response, "no") == 0) {
2262295367Sdes				options.update_hostkeys = 0;
2263295367Sdes				break;
2264295367Sdes			} else {
2265295367Sdes				do_log2(loglevel, "Please enter "
2266295367Sdes				    "\"yes\" or \"no\"");
2267295367Sdes			}
2268295367Sdes		}
2269295367Sdes		if (quit_pending || i >= 3 || response == NULL)
2270295367Sdes			options.update_hostkeys = 0;
2271295367Sdes		free(response);
2272295367Sdes		if (was_raw)
2273295367Sdes			enter_raw_mode(1);
2274295367Sdes	}
2275295367Sdes
2276295367Sdes	/*
2277295367Sdes	 * Now that all the keys are verified, we can go ahead and replace
2278295367Sdes	 * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't
2279295367Sdes	 * cancel the operation).
2280295367Sdes	 */
2281295367Sdes	if (options.update_hostkeys != 0 &&
2282295367Sdes	    (r = hostfile_replace_entries(options.user_hostfiles[0],
2283295367Sdes	    ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys,
2284295367Sdes	    options.hash_known_hosts, 0,
2285295367Sdes	    options.fingerprint_hash)) != 0)
2286295367Sdes		error("%s: hostfile_replace_entries failed: %s",
2287295367Sdes		    __func__, ssh_err(r));
2288295367Sdes}
2289295367Sdes
2290295367Sdesstatic void
2291295367Sdesclient_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx)
2292295367Sdes{
2293295367Sdes	struct ssh *ssh = active_state; /* XXX */
2294295367Sdes	struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx;
2295295367Sdes	size_t i, ndone;
2296295367Sdes	struct sshbuf *signdata;
2297295367Sdes	int r;
2298295367Sdes	const u_char *sig;
2299295367Sdes	size_t siglen;
2300295367Sdes
2301295367Sdes	if (ctx->nnew == 0)
2302295367Sdes		fatal("%s: ctx->nnew == 0", __func__); /* sanity */
2303295367Sdes	if (type != SSH2_MSG_REQUEST_SUCCESS) {
2304295367Sdes		error("Server failed to confirm ownership of "
2305295367Sdes		    "private host keys");
2306295367Sdes		hostkeys_update_ctx_free(ctx);
2307295367Sdes		return;
2308295367Sdes	}
2309295367Sdes	if ((signdata = sshbuf_new()) == NULL)
2310295367Sdes		fatal("%s: sshbuf_new failed", __func__);
2311295367Sdes	/* Don't want to accidentally accept an unbound signature */
2312295367Sdes	if (ssh->kex->session_id_len == 0)
2313295367Sdes		fatal("%s: ssh->kex->session_id_len == 0", __func__);
2314295367Sdes	/*
2315295367Sdes	 * Expect a signature for each of the ctx->nnew private keys we
2316295367Sdes	 * haven't seen before. They will be in the same order as the
2317295367Sdes	 * ctx->keys where the corresponding ctx->keys_seen[i] == 0.
2318295367Sdes	 */
2319295367Sdes	for (ndone = i = 0; i < ctx->nkeys; i++) {
2320295367Sdes		if (ctx->keys_seen[i])
2321295367Sdes			continue;
2322295367Sdes		/* Prepare data to be signed: session ID, unique string, key */
2323295367Sdes		sshbuf_reset(signdata);
2324295367Sdes		if ( (r = sshbuf_put_cstring(signdata,
2325295367Sdes		    "hostkeys-prove-00@openssh.com")) != 0 ||
2326295367Sdes		    (r = sshbuf_put_string(signdata, ssh->kex->session_id,
2327295367Sdes		    ssh->kex->session_id_len)) != 0 ||
2328295367Sdes		    (r = sshkey_puts(ctx->keys[i], signdata)) != 0)
2329295367Sdes			fatal("%s: failed to prepare signature: %s",
2330295367Sdes			    __func__, ssh_err(r));
2331295367Sdes		/* Extract and verify signature */
2332295367Sdes		if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) {
2333295367Sdes			error("%s: couldn't parse message: %s",
2334295367Sdes			    __func__, ssh_err(r));
2335295367Sdes			goto out;
2336295367Sdes		}
2337295367Sdes		if ((r = sshkey_verify(ctx->keys[i], sig, siglen,
2338295367Sdes		    sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) {
2339295367Sdes			error("%s: server gave bad signature for %s key %zu",
2340295367Sdes			    __func__, sshkey_type(ctx->keys[i]), i);
2341295367Sdes			goto out;
2342295367Sdes		}
2343295367Sdes		/* Key is good. Mark it as 'seen' */
2344295367Sdes		ctx->keys_seen[i] = 2;
2345295367Sdes		ndone++;
2346295367Sdes	}
2347295367Sdes	if (ndone != ctx->nnew)
2348295367Sdes		fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__,
2349295367Sdes		    ndone, ctx->nnew);  /* Shouldn't happen */
2350295367Sdes	ssh_packet_check_eom(ssh);
2351295367Sdes
2352295367Sdes	/* Make the edits to known_hosts */
2353295367Sdes	update_known_hosts(ctx);
2354295367Sdes out:
2355295367Sdes	hostkeys_update_ctx_free(ctx);
2356295367Sdes}
2357295367Sdes
2358295367Sdes/*
2359295367Sdes * Handle hostkeys-00@openssh.com global request to inform the client of all
2360295367Sdes * the server's hostkeys. The keys are checked against the user's
2361295367Sdes * HostkeyAlgorithms preference before they are accepted.
2362295367Sdes */
2363295367Sdesstatic int
2364295367Sdesclient_input_hostkeys(void)
2365295367Sdes{
2366295367Sdes	struct ssh *ssh = active_state; /* XXX */
2367295367Sdes	const u_char *blob = NULL;
2368295367Sdes	size_t i, len = 0;
2369295367Sdes	struct sshbuf *buf = NULL;
2370295367Sdes	struct sshkey *key = NULL, **tmp;
2371295367Sdes	int r;
2372295367Sdes	char *fp;
2373295367Sdes	static int hostkeys_seen = 0; /* XXX use struct ssh */
2374295367Sdes	extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */
2375295367Sdes	struct hostkeys_update_ctx *ctx = NULL;
2376295367Sdes
2377295367Sdes	if (hostkeys_seen)
2378295367Sdes		fatal("%s: server already sent hostkeys", __func__);
2379295367Sdes	if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK &&
2380295367Sdes	    options.batch_mode)
2381295367Sdes		return 1; /* won't ask in batchmode, so don't even try */
2382295367Sdes	if (!options.update_hostkeys || options.num_user_hostfiles <= 0)
2383295367Sdes		return 1;
2384295367Sdes
2385295367Sdes	ctx = xcalloc(1, sizeof(*ctx));
2386295367Sdes	while (ssh_packet_remaining(ssh) > 0) {
2387295367Sdes		sshkey_free(key);
2388295367Sdes		key = NULL;
2389295367Sdes		if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) {
2390295367Sdes			error("%s: couldn't parse message: %s",
2391295367Sdes			    __func__, ssh_err(r));
2392295367Sdes			goto out;
2393295367Sdes		}
2394295367Sdes		if ((r = sshkey_from_blob(blob, len, &key)) != 0) {
2395295367Sdes			error("%s: parse key: %s", __func__, ssh_err(r));
2396295367Sdes			goto out;
2397295367Sdes		}
2398295367Sdes		fp = sshkey_fingerprint(key, options.fingerprint_hash,
2399295367Sdes		    SSH_FP_DEFAULT);
2400295367Sdes		debug3("%s: received %s key %s", __func__,
2401295367Sdes		    sshkey_type(key), fp);
2402295367Sdes		free(fp);
2403295367Sdes
2404295367Sdes		/* Check that the key is accepted in HostkeyAlgorithms */
2405295367Sdes		if (match_pattern_list(sshkey_ssh_name(key),
2406295367Sdes		    options.hostkeyalgorithms ? options.hostkeyalgorithms :
2407295367Sdes		    KEX_DEFAULT_PK_ALG, 0) != 1) {
2408295367Sdes			debug3("%s: %s key not permitted by HostkeyAlgorithms",
2409295367Sdes			    __func__, sshkey_ssh_name(key));
2410295367Sdes			continue;
2411295367Sdes		}
2412295367Sdes		/* Skip certs */
2413295367Sdes		if (sshkey_is_cert(key)) {
2414295367Sdes			debug3("%s: %s key is a certificate; skipping",
2415295367Sdes			    __func__, sshkey_ssh_name(key));
2416295367Sdes			continue;
2417295367Sdes		}
2418295367Sdes		/* Ensure keys are unique */
2419295367Sdes		for (i = 0; i < ctx->nkeys; i++) {
2420295367Sdes			if (sshkey_equal(key, ctx->keys[i])) {
2421295367Sdes				error("%s: received duplicated %s host key",
2422295367Sdes				    __func__, sshkey_ssh_name(key));
2423295367Sdes				goto out;
2424295367Sdes			}
2425295367Sdes		}
2426295367Sdes		/* Key is good, record it */
2427295367Sdes		if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1,
2428295367Sdes		    sizeof(*ctx->keys))) == NULL)
2429295367Sdes			fatal("%s: reallocarray failed nkeys = %zu",
2430295367Sdes			    __func__, ctx->nkeys);
2431295367Sdes		ctx->keys = tmp;
2432295367Sdes		ctx->keys[ctx->nkeys++] = key;
2433295367Sdes		key = NULL;
2434295367Sdes	}
2435295367Sdes
2436295367Sdes	if (ctx->nkeys == 0) {
2437295367Sdes		debug("%s: server sent no hostkeys", __func__);
2438295367Sdes		goto out;
2439295367Sdes	}
2440295367Sdes
2441295367Sdes	if ((ctx->keys_seen = calloc(ctx->nkeys,
2442295367Sdes	    sizeof(*ctx->keys_seen))) == NULL)
2443295367Sdes		fatal("%s: calloc failed", __func__);
2444295367Sdes
2445295367Sdes	get_hostfile_hostname_ipaddr(host,
2446295367Sdes	    options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL,
2447295367Sdes	    options.port, &ctx->host_str,
2448295367Sdes	    options.check_host_ip ? &ctx->ip_str : NULL);
2449295367Sdes
2450295367Sdes	/* Find which keys we already know about. */
2451295367Sdes	if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find,
2452295367Sdes	    ctx, ctx->host_str, ctx->ip_str,
2453295367Sdes	    HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) {
2454295367Sdes		error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r));
2455295367Sdes		goto out;
2456295367Sdes	}
2457295367Sdes
2458295367Sdes	/* Figure out if we have any new keys to add */
2459295367Sdes	ctx->nnew = 0;
2460295367Sdes	for (i = 0; i < ctx->nkeys; i++) {
2461295367Sdes		if (!ctx->keys_seen[i])
2462295367Sdes			ctx->nnew++;
2463295367Sdes	}
2464295367Sdes
2465295367Sdes	debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove",
2466295367Sdes	    __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold);
2467295367Sdes
2468295367Sdes	if (ctx->nnew == 0 && ctx->nold != 0) {
2469295367Sdes		/* We have some keys to remove. Just do it. */
2470295367Sdes		update_known_hosts(ctx);
2471295367Sdes	} else if (ctx->nnew != 0) {
2472295367Sdes		/*
2473295367Sdes		 * We have received hitherto-unseen keys from the server.
2474295367Sdes		 * Ask the server to confirm ownership of the private halves.
2475295367Sdes		 */
2476295367Sdes		debug3("%s: asking server to prove ownership for %zu keys",
2477295367Sdes		    __func__, ctx->nnew);
2478295367Sdes		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
2479295367Sdes		    (r = sshpkt_put_cstring(ssh,
2480295367Sdes		    "hostkeys-prove-00@openssh.com")) != 0 ||
2481295367Sdes		    (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */
2482295367Sdes			fatal("%s: cannot prepare packet: %s",
2483295367Sdes			    __func__, ssh_err(r));
2484295367Sdes		if ((buf = sshbuf_new()) == NULL)
2485295367Sdes			fatal("%s: sshbuf_new", __func__);
2486295367Sdes		for (i = 0; i < ctx->nkeys; i++) {
2487295367Sdes			if (ctx->keys_seen[i])
2488295367Sdes				continue;
2489295367Sdes			sshbuf_reset(buf);
2490295367Sdes			if ((r = sshkey_putb(ctx->keys[i], buf)) != 0)
2491295367Sdes				fatal("%s: sshkey_putb: %s",
2492295367Sdes				    __func__, ssh_err(r));
2493295367Sdes			if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
2494295367Sdes				fatal("%s: sshpkt_put_string: %s",
2495295367Sdes				    __func__, ssh_err(r));
2496295367Sdes		}
2497295367Sdes		if ((r = sshpkt_send(ssh)) != 0)
2498295367Sdes			fatal("%s: sshpkt_send: %s", __func__, ssh_err(r));
2499295367Sdes		client_register_global_confirm(
2500295367Sdes		    client_global_hostkeys_private_confirm, ctx);
2501295367Sdes		ctx = NULL;  /* will be freed in callback */
2502295367Sdes	}
2503295367Sdes
2504295367Sdes	/* Success */
2505295367Sdes out:
2506295367Sdes	hostkeys_update_ctx_free(ctx);
2507295367Sdes	sshkey_free(key);
2508295367Sdes	sshbuf_free(buf);
2509295367Sdes	/*
2510295367Sdes	 * NB. Return success for all cases. The server doesn't need to know
2511295367Sdes	 * what the client does with its hosts file.
2512295367Sdes	 */
2513295367Sdes	return 1;
2514295367Sdes}
2515295367Sdes
2516295367Sdesstatic int
251792555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt)
251892555Sdes{
251992555Sdes	char *rtype;
252092555Sdes	int want_reply;
252192555Sdes	int success = 0;
252276259Sgreen
2523295367Sdes	rtype = packet_get_cstring(NULL);
252492555Sdes	want_reply = packet_get_char();
2525126274Sdes	debug("client_input_global_request: rtype %s want_reply %d",
2526126274Sdes	    rtype, want_reply);
2527295367Sdes	if (strcmp(rtype, "hostkeys-00@openssh.com") == 0)
2528295367Sdes		success = client_input_hostkeys();
252992555Sdes	if (want_reply) {
253092555Sdes		packet_start(success ?
253192555Sdes		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
253292555Sdes		packet_send();
253392555Sdes		packet_write_wait();
253492555Sdes	}
2535255767Sdes	free(rtype);
2536295367Sdes	return 0;
253792555Sdes}
253892555Sdes
2539137015Sdesvoid
2540137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem,
2541181111Sdes    const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env)
2542137015Sdes{
2543137015Sdes	int len;
2544146998Sdes	Channel *c = NULL;
2545137015Sdes
2546137015Sdes	debug2("%s: id %d", __func__, id);
2547137015Sdes
2548146998Sdes	if ((c = channel_lookup(id)) == NULL)
2549146998Sdes		fatal("client_session2_setup: channel %d: unknown channel", id);
2550146998Sdes
2551221420Sdes	packet_set_interactive(want_tty,
2552221420Sdes	    options.ip_qos_interactive, options.ip_qos_bulk);
2553221420Sdes
2554137015Sdes	if (want_tty) {
2555137015Sdes		struct winsize ws;
2556137015Sdes
2557137015Sdes		/* Store window size in the packet. */
2558137015Sdes		if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0)
2559137015Sdes			memset(&ws, 0, sizeof(ws));
2560137015Sdes
2561181111Sdes		channel_request_start(id, "pty-req", 1);
2562226046Sdes		client_expect_confirm(id, "PTY allocation", CONFIRM_TTY);
2563137015Sdes		packet_put_cstring(term != NULL ? term : "");
2564162852Sdes		packet_put_int((u_int)ws.ws_col);
2565162852Sdes		packet_put_int((u_int)ws.ws_row);
2566162852Sdes		packet_put_int((u_int)ws.ws_xpixel);
2567162852Sdes		packet_put_int((u_int)ws.ws_ypixel);
2568181111Sdes		if (tiop == NULL)
2569181111Sdes			tiop = get_saved_tio();
2570181111Sdes		tty_make_modes(-1, tiop);
2571137015Sdes		packet_send();
2572137015Sdes		/* XXX wait for reply */
2573146998Sdes		c->client_tty = 1;
2574137015Sdes	}
2575137015Sdes
2576137015Sdes	/* Transfer any environment variables from client to server */
2577137015Sdes	if (options.num_send_env != 0 && env != NULL) {
2578137015Sdes		int i, j, matched;
2579137015Sdes		char *name, *val;
2580137015Sdes
2581137015Sdes		debug("Sending environment.");
2582137015Sdes		for (i = 0; env[i] != NULL; i++) {
2583137015Sdes			/* Split */
2584137015Sdes			name = xstrdup(env[i]);
2585137015Sdes			if ((val = strchr(name, '=')) == NULL) {
2586255767Sdes				free(name);
2587137015Sdes				continue;
2588137015Sdes			}
2589137015Sdes			*val++ = '\0';
2590137015Sdes
2591137015Sdes			matched = 0;
2592137015Sdes			for (j = 0; j < options.num_send_env; j++) {
2593137015Sdes				if (match_pattern(name, options.send_env[j])) {
2594137015Sdes					matched = 1;
2595137015Sdes					break;
2596137015Sdes				}
2597137015Sdes			}
2598137015Sdes			if (!matched) {
2599137015Sdes				debug3("Ignored env %s", name);
2600255767Sdes				free(name);
2601137015Sdes				continue;
2602137015Sdes			}
2603137015Sdes
2604137015Sdes			debug("Sending env %s = %s", name, val);
2605137015Sdes			channel_request_start(id, "env", 0);
2606137015Sdes			packet_put_cstring(name);
2607137015Sdes			packet_put_cstring(val);
2608137015Sdes			packet_send();
2609255767Sdes			free(name);
2610137015Sdes		}
2611137015Sdes	}
2612137015Sdes
2613137015Sdes	len = buffer_len(cmd);
2614137015Sdes	if (len > 0) {
2615137015Sdes		if (len > 900)
2616137015Sdes			len = 900;
2617137015Sdes		if (want_subsystem) {
2618181111Sdes			debug("Sending subsystem: %.*s",
2619181111Sdes			    len, (u_char*)buffer_ptr(cmd));
2620181111Sdes			channel_request_start(id, "subsystem", 1);
2621226046Sdes			client_expect_confirm(id, "subsystem", CONFIRM_CLOSE);
2622137015Sdes		} else {
2623181111Sdes			debug("Sending command: %.*s",
2624181111Sdes			    len, (u_char*)buffer_ptr(cmd));
2625181111Sdes			channel_request_start(id, "exec", 1);
2626226046Sdes			client_expect_confirm(id, "exec", CONFIRM_CLOSE);
2627137015Sdes		}
2628137015Sdes		packet_put_string(buffer_ptr(cmd), buffer_len(cmd));
2629137015Sdes		packet_send();
2630137015Sdes	} else {
2631181111Sdes		channel_request_start(id, "shell", 1);
2632226046Sdes		client_expect_confirm(id, "shell", CONFIRM_CLOSE);
2633137015Sdes		packet_send();
2634137015Sdes	}
2635137015Sdes}
2636137015Sdes
263792555Sdesstatic void
263876259Sgreenclient_init_dispatch_20(void)
263960573Skris{
264060573Skris	dispatch_init(&dispatch_protocol_error);
264198675Sdes
264260573Skris	dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
264360573Skris	dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
264460573Skris	dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
264560573Skris	dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
264660573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open);
264760573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
264860573Skris	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
264976259Sgreen	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req);
265060573Skris	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
2651181111Sdes	dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm);
2652181111Sdes	dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm);
265392555Sdes	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request);
265476259Sgreen
265576259Sgreen	/* rekeying */
265676259Sgreen	dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
265798675Sdes
265898675Sdes	/* global request reply messages */
265998675Sdes	dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply);
266098675Sdes	dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply);
266160573Skris}
2662181111Sdes
266392555Sdesstatic void
266476259Sgreenclient_init_dispatch_13(void)
266560573Skris{
266660573Skris	dispatch_init(NULL);
266760573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
266860573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
266960573Skris	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
267060573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
267160573Skris	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
267260573Skris	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
267360573Skris	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
267460573Skris	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
267560573Skris	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
267668700Sgreen
267768700Sgreen	dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ?
2678126274Sdes	    &client_input_agent_open : &deny_input_open);
267968700Sgreen	dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ?
268069587Sgreen	    &x11_input_open : &deny_input_open);
268160573Skris}
2682181111Sdes
268392555Sdesstatic void
268476259Sgreenclient_init_dispatch_15(void)
268560573Skris{
268660573Skris	client_init_dispatch_13();
268760573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
268860573Skris	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
268960573Skris}
2690181111Sdes
269192555Sdesstatic void
269276259Sgreenclient_init_dispatch(void)
269360573Skris{
269460573Skris	if (compat20)
269560573Skris		client_init_dispatch_20();
269660573Skris	else if (compat13)
269760573Skris		client_init_dispatch_13();
269860573Skris	else
269960573Skris		client_init_dispatch_15();
270060573Skris}
2701126274Sdes
2702226046Sdesvoid
2703226046Sdesclient_stop_mux(void)
2704226046Sdes{
2705226046Sdes	if (options.control_path != NULL && muxserver_sock != -1)
2706226046Sdes		unlink(options.control_path);
2707226046Sdes	/*
2708248619Sdes	 * If we are in persist mode, or don't have a shell, signal that we
2709248619Sdes	 * should close when all active channels are closed.
2710226046Sdes	 */
2711248619Sdes	if (options.control_persist || no_shell_flag) {
2712226046Sdes		session_closed = 1;
2713226046Sdes		setproctitle("[stopped mux]");
2714226046Sdes	}
2715226046Sdes}
2716226046Sdes
2717126274Sdes/* client specific fatal cleanup */
2718126274Sdesvoid
2719126274Sdescleanup_exit(int i)
2720126274Sdes{
2721226046Sdes	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
2722126274Sdes	leave_non_blocking();
2723181111Sdes	if (options.control_path != NULL && muxserver_sock != -1)
2724137015Sdes		unlink(options.control_path);
2725221420Sdes	ssh_kill_proxy_command();
2726126274Sdes	_exit(i);
2727126274Sdes}
2728