external/openssh: update to 6.8p1.

In preparation for some updates to external/openssh to make it work with
BoringSSL, this change updates the code to a recent version. The current
version (5.9p1) is coming up on four years old now.

  * Confirmed that f5c67b478bef9992de9e9ec91ce10af4f6205e0d matches
    OpenSSH 5.9p1 exactly (save for the removal of the scard
    subdirectory).

  * Downloaded openssh-6.8p1.tar.gz (SHA256:
    3ff64ce73ee124480b5bf767b9830d7d3c03bbcb6abe716b78f0192c37ce160e)
    and verified with PGP signature. (I've verified Damien's key in
    person previously.)

  * Applied changes between f5c67b478bef9992de9e9ec91ce10af4f6205e0d and
    OpenSSH 5.9p1 to 6.8p1 and updated the build as best I can. The
    ugliest change is probably the duplication of umac.c to umac128.c
    because Android conditionally compiles that file twice. See the
    comment in those files.

Change-Id: I63cb07a8118afb5a377f116087a0882914cea486
diff --git a/clientloop.c b/clientloop.c
index c19b01f..a9c8a90 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.236 2011/06/22 22:08:42 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.272 2015/02/25 19:54:02 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -61,9 +61,9 @@
 
 #include "includes.h"
 
+#include <sys/param.h>	/* MIN MAX */
 #include <sys/types.h>
 #include <sys/ioctl.h>
-#include <sys/param.h>
 #ifdef HAVE_SYS_STAT_H
 # include <sys/stat.h>
 #endif
@@ -85,6 +85,7 @@
 #include <termios.h>
 #include <pwd.h>
 #include <unistd.h>
+#include <limits.h>
 
 #include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
@@ -100,16 +101,18 @@
 #include "cipher.h"
 #include "kex.h"
 #include "log.h"
+#include "misc.h"
 #include "readconf.h"
 #include "clientloop.h"
 #include "sshconnect.h"
 #include "authfd.h"
 #include "atomicio.h"
 #include "sshpty.h"
-#include "misc.h"
 #include "match.h"
 #include "msg.h"
 #include "roaming.h"
+#include "ssherr.h"
+#include "hostfile.h"
 
 /* import options */
 extern Options options;
@@ -191,9 +194,6 @@
 static struct global_confirms global_confirms =
     TAILQ_HEAD_INITIALIZER(global_confirms);
 
-/*XXX*/
-extern Kex *xxx_kex;
-
 void ssh_process_session2_setup(int, int, int, Buffer *);
 
 /* Restores stdin to blocking mode. */
@@ -273,7 +273,7 @@
 		control_persist_exit_time = 0;
 	} else if (control_persist_exit_time <= 0) {
 		/* a client connection has recently closed */
-		control_persist_exit_time = time(NULL) +
+		control_persist_exit_time = monotime() +
 			(time_t)options.control_persist_timeout;
 		debug2("%s: schedule exit in %d seconds", __func__,
 		    options.control_persist_timeout);
@@ -281,6 +281,23 @@
 	/* else we are already counting down to the timeout */
 }
 
+#define SSH_X11_VALID_DISPLAY_CHARS ":/.-_"
+static int
+client_x11_display_valid(const char *display)
+{
+	size_t i, dlen;
+
+	dlen = strlen(display);
+	for (i = 0; i < dlen; i++) {
+		if (!isalnum((u_char)display[i]) &&
+		    strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) {
+			debug("Invalid character '%c' in DISPLAY", display[i]);
+			return 0;
+		}
+	}
+	return 1;
+}
+
 #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1"
 void
 client_x11_get_proto(const char *display, const char *xauth_path,
@@ -303,6 +320,9 @@
 
 	if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) {
 		debug("No xauth program.");
+	} else if (!client_x11_display_valid(display)) {
+		logit("DISPLAY '%s' invalid, falling back to fake xauth data",
+		    display);
 	} else {
 		if (display == NULL) {
 			debug("x11_get_proto: DISPLAY not set");
@@ -321,12 +341,12 @@
 			display = xdisplay;
 		}
 		if (trusted == 0) {
-			xauthdir = xmalloc(MAXPATHLEN);
-			xauthfile = xmalloc(MAXPATHLEN);
-			mktemp_proto(xauthdir, MAXPATHLEN);
+			xauthdir = xmalloc(PATH_MAX);
+			xauthfile = xmalloc(PATH_MAX);
+			mktemp_proto(xauthdir, PATH_MAX);
 			if (mkdtemp(xauthdir) != NULL) {
 				do_unlink = 1;
-				snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile",
+				snprintf(xauthfile, PATH_MAX, "%s/xauthfile",
 				    xauthdir);
 				snprintf(cmd, sizeof(cmd),
 				    "%s -f %s generate %s " SSH_X11_PROTO
@@ -336,7 +356,7 @@
 				if (system(cmd) == 0)
 					generated = 1;
 				if (x11_refuse_time == 0) {
-					now = time(NULL) + 1;
+					now = monotime() + 1;
 					if (UINT_MAX - timeout < now)
 						x11_refuse_time = UINT_MAX;
 					else
@@ -373,10 +393,8 @@
 		unlink(xauthfile);
 		rmdir(xauthdir);
 	}
-	if (xauthdir)
-		xfree(xauthdir);
-	if (xauthfile)
-		xfree(xauthfile);
+	free(xauthdir);
+	free(xauthfile);
 
 	/*
 	 * If we didn't get authentication data, just make up some
@@ -520,22 +538,23 @@
 	}
 }
 
-static void
+static int
 client_global_request_reply(int type, u_int32_t seq, void *ctxt)
 {
 	struct global_confirm *gc;
 
 	if ((gc = TAILQ_FIRST(&global_confirms)) == NULL)
-		return;
+		return 0;
 	if (gc->cb != NULL)
 		gc->cb(type, seq, gc->ctx);
 	if (--gc->ref_count <= 0) {
 		TAILQ_REMOVE(&global_confirms, gc, entry);
-		bzero(gc, sizeof(*gc));
-		xfree(gc);
+		explicit_bzero(gc, sizeof(*gc));
+		free(gc);
 	}
 
 	packet_set_alive_timeouts(0);
+	return 0;
 }
 
 static void
@@ -563,10 +582,12 @@
 {
 	struct timeval tv, *tvp;
 	int timeout_secs;
+	time_t minwait_secs = 0, server_alive_time = 0, now = monotime();
 	int ret;
 
 	/* Add any selections by the channel mechanism. */
-	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
+	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
+	    &minwait_secs, rekeying);
 
 	if (!compat20) {
 		/* Read from the connection, unless our buffers are full. */
@@ -610,15 +631,21 @@
 	 */
 
 	timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */
-	if (options.server_alive_interval > 0 && compat20)
+	if (options.server_alive_interval > 0 && compat20) {
 		timeout_secs = options.server_alive_interval;
+		server_alive_time = now + options.server_alive_interval;
+	}
+	if (options.rekey_interval > 0 && compat20 && !rekeying)
+		timeout_secs = MIN(timeout_secs, packet_get_rekey_timeout());
 	set_control_persist_exit_time();
 	if (control_persist_exit_time > 0) {
 		timeout_secs = MIN(timeout_secs,
-			control_persist_exit_time - time(NULL));
+			control_persist_exit_time - now);
 		if (timeout_secs < 0)
 			timeout_secs = 0;
 	}
+	if (minwait_secs != 0)
+		timeout_secs = MIN(timeout_secs, (int)minwait_secs);
 	if (timeout_secs == INT_MAX)
 		tvp = NULL;
 	else {
@@ -645,8 +672,15 @@
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
-	} else if (ret == 0)
-		server_alive_check();
+	} else if (ret == 0) {
+		/*
+		 * Timeout.  Could have been either keepalive or rekeying.
+		 * Keepalive we check here, rekeying is checked in clientloop.
+		 */
+		if (server_alive_time != 0 && server_alive_time <= monotime())
+			server_alive_check();
+	}
+
 }
 
 static void
@@ -791,20 +825,20 @@
 			chan_write_failed(c);
 		}
 	}
-	xfree(cr);
+	free(cr);
 }
 
 static void
 client_abandon_status_confirm(Channel *c, void *ctx)
 {
-	xfree(ctx);
+	free(ctx);
 }
 
 void
 client_expect_confirm(int id, const char *request,
     enum confirm_action action)
 {
-	struct channel_reply_ctx *cr = xmalloc(sizeof(*cr));
+	struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr));
 
 	cr->request_type = request;
 	cr->action = action;
@@ -827,7 +861,7 @@
 		return;
 	}
 
-	gc = xmalloc(sizeof(*gc));
+	gc = xcalloc(1, sizeof(*gc));
 	gc->cb = cb;
 	gc->ctx = ctx;
 	gc->ref_count = 1;
@@ -838,21 +872,18 @@
 process_cmdline(void)
 {
 	void (*handler)(int);
-	char *s, *cmd, *cancel_host;
-	int delete = 0;
-	int local = 0, remote = 0, dynamic = 0;
-	int cancel_port;
-	Forward fwd;
+	char *s, *cmd;
+	int ok, delete = 0, local = 0, remote = 0, dynamic = 0;
+	struct Forward fwd;
 
-	bzero(&fwd, sizeof(fwd));
-	fwd.listen_host = fwd.connect_host = NULL;
+	memset(&fwd, 0, sizeof(fwd));
 
 	leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
 	handler = signal(SIGINT, SIG_IGN);
 	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
 	if (s == NULL)
 		goto out;
-	while (isspace(*s))
+	while (isspace((u_char)*s))
 		s++;
 	if (*s == '-')
 		s++;	/* Skip cmdline '-', if any */
@@ -867,8 +898,12 @@
 		    "Request remote forward");
 		logit("      -D[bind_address:]port                  "
 		    "Request dynamic forward");
+		logit("      -KL[bind_address:]port                 "
+		    "Cancel local forward");
 		logit("      -KR[bind_address:]port                 "
 		    "Cancel remote forward");
+		logit("      -KD[bind_address:]port                 "
+		    "Cancel dynamic forward");
 		if (!options.permit_local_command)
 			goto out;
 		logit("      !args                                  "
@@ -897,67 +932,120 @@
 		goto out;
 	}
 
-	if ((local || dynamic) && delete) {
-		logit("Not supported.");
-		goto out;
-	}
-	if (remote && delete && !compat20) {
+	if (delete && !compat20) {
 		logit("Not supported for SSH protocol version 1.");
 		goto out;
 	}
 
-	while (isspace(*++s))
+	while (isspace((u_char)*++s))
 		;
 
 	/* XXX update list of forwards in options */
 	if (delete) {
-		cancel_port = 0;
-		cancel_host = hpdelim(&s);	/* may be NULL */
-		if (s != NULL) {
-			cancel_port = a2port(s);
-			cancel_host = cleanhostname(cancel_host);
-		} else {
-			cancel_port = a2port(cancel_host);
-			cancel_host = NULL;
-		}
-		if (cancel_port <= 0) {
-			logit("Bad forwarding close port");
+		/* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */
+		if (!parse_forward(&fwd, s, 1, 0)) {
+			logit("Bad forwarding close specification.");
 			goto out;
 		}
-		channel_request_rforward_cancel(cancel_host, cancel_port);
+		if (remote)
+			ok = channel_request_rforward_cancel(&fwd) == 0;
+		else if (dynamic)
+			ok = channel_cancel_lport_listener(&fwd,
+			    0, &options.fwd_opts) > 0;
+		else
+			ok = channel_cancel_lport_listener(&fwd,
+			    CHANNEL_CANCEL_PORT_STATIC,
+			    &options.fwd_opts) > 0;
+		if (!ok) {
+			logit("Unkown port forwarding.");
+			goto out;
+		}
+		logit("Canceled forwarding.");
 	} else {
 		if (!parse_forward(&fwd, s, dynamic, remote)) {
 			logit("Bad forwarding specification.");
 			goto out;
 		}
 		if (local || dynamic) {
-			if (channel_setup_local_fwd_listener(fwd.listen_host,
-			    fwd.listen_port, fwd.connect_host,
-			    fwd.connect_port, options.gateway_ports) < 0) {
+			if (!channel_setup_local_fwd_listener(&fwd,
+			    &options.fwd_opts)) {
 				logit("Port forwarding failed.");
 				goto out;
 			}
 		} else {
-			if (channel_request_remote_forwarding(fwd.listen_host,
-			    fwd.listen_port, fwd.connect_host,
-			    fwd.connect_port) < 0) {
+			if (channel_request_remote_forwarding(&fwd) < 0) {
 				logit("Port forwarding failed.");
 				goto out;
 			}
 		}
-
 		logit("Forwarding port.");
 	}
 
 out:
 	signal(SIGINT, handler);
 	enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE);
-	if (cmd)
-		xfree(cmd);
-	if (fwd.listen_host != NULL)
-		xfree(fwd.listen_host);
-	if (fwd.connect_host != NULL)
-		xfree(fwd.connect_host);
+	free(cmd);
+	free(fwd.listen_host);
+	free(fwd.listen_path);
+	free(fwd.connect_host);
+	free(fwd.connect_path);
+}
+
+/* reasons to suppress output of an escape command in help output */
+#define SUPPRESS_NEVER		0	/* never suppress, always show */
+#define SUPPRESS_PROTO1		1	/* don't show in protocol 1 sessions */
+#define SUPPRESS_MUXCLIENT	2	/* don't show in mux client sessions */
+#define SUPPRESS_MUXMASTER	4	/* don't show in mux master sessions */
+#define SUPPRESS_SYSLOG		8	/* don't show when logging to syslog */
+struct escape_help_text {
+	const char *cmd;
+	const char *text;
+	unsigned int flags;
+};
+static struct escape_help_text esc_txt[] = {
+    {".",  "terminate session", SUPPRESS_MUXMASTER},
+    {".",  "terminate connection (and any multiplexed sessions)",
+	SUPPRESS_MUXCLIENT},
+    {"B",  "send a BREAK to the remote system", SUPPRESS_PROTO1},
+    {"C",  "open a command line", SUPPRESS_MUXCLIENT},
+    {"R",  "request rekey", SUPPRESS_PROTO1},
+    {"V/v",  "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT},
+    {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT},
+    {"#",  "list forwarded connections", SUPPRESS_NEVER},
+    {"&",  "background ssh (when waiting for connections to terminate)",
+	SUPPRESS_MUXCLIENT},
+    {"?", "this message", SUPPRESS_NEVER},
+};
+
+static void
+print_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client,
+    int using_stderr)
+{
+	unsigned int i, suppress_flags;
+	char string[1024];
+
+	snprintf(string, sizeof string, "%c?\r\n"
+	    "Supported escape sequences:\r\n", escape_char);
+	buffer_append(b, string, strlen(string));
+
+	suppress_flags = (protocol2 ? 0 : SUPPRESS_PROTO1) |
+	    (mux_client ? SUPPRESS_MUXCLIENT : 0) |
+	    (mux_client ? 0 : SUPPRESS_MUXMASTER) |
+	    (using_stderr ? 0 : SUPPRESS_SYSLOG);
+
+	for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) {
+		if (esc_txt[i].flags & suppress_flags)
+			continue;
+		snprintf(string, sizeof string, " %c%-3s - %s\r\n",
+		    escape_char, esc_txt[i].cmd, esc_txt[i].text);
+		buffer_append(b, string, strlen(string));
+	}
+
+	snprintf(string, sizeof string,
+	    " %c%c   - send the escape character by typing it twice\r\n"
+	    "(Note that escapes are only recognized immediately after "
+	    "newline.)\r\n", escape_char, escape_char);
+	buffer_append(b, string, strlen(string));
 }
 
 /* 
@@ -1010,6 +1098,11 @@
 				if (c && c->ctl_chan != -1) {
 					chan_read_failed(c);
 					chan_write_failed(c);
+					if (c->detach_user)
+						c->detach_user(c->self, NULL);
+					c->type = SSH_CHANNEL_ABANDONED;
+					buffer_clear(&c->input);
+					chan_ibuf_empty(c);
 					return 0;
 				} else
 					quit_pending = 1;
@@ -1018,11 +1111,16 @@
 			case 'Z' - 64:
 				/* XXX support this for mux clients */
 				if (c && c->ctl_chan != -1) {
+					char b[16];
  noescape:
+					if (ch == 'Z' - 64)
+						snprintf(b, sizeof b, "^Z");
+					else
+						snprintf(b, sizeof b, "%c", ch);
 					snprintf(string, sizeof string,
-					    "%c%c escape not available to "
+					    "%c%s escape not available to "
 					    "multiplexed sessions\r\n",
-					    escape_char, ch);
+					    escape_char, b);
 					buffer_append(berr, string,
 					    strlen(string));
 					continue;
@@ -1044,7 +1142,7 @@
 					    "%cB\r\n", escape_char);
 					buffer_append(berr, string,
 					    strlen(string));
-					channel_request_start(session_ident,
+					channel_request_start(c->self,
 					    "break", 0);
 					packet_put_int(1000);
 					packet_send();
@@ -1061,6 +1159,31 @@
 				}
 				continue;
 
+			case 'V':
+				/* FALLTHROUGH */
+			case 'v':
+				if (c && c->ctl_chan != -1)
+					goto noescape;
+				if (!log_is_on_stderr()) {
+					snprintf(string, sizeof string,
+					    "%c%c [Logging to syslog]\r\n",
+					     escape_char, ch);
+					buffer_append(berr, string,
+					    strlen(string));
+					continue;
+				}
+				if (ch == 'V' && options.log_level >
+				    SYSLOG_LEVEL_QUIET)
+					log_change_level(--options.log_level);
+				if (ch == 'v' && options.log_level <
+				    SYSLOG_LEVEL_DEBUG3)
+					log_change_level(++options.log_level);
+				snprintf(string, sizeof string,
+				    "%c%c [LogLevel %s]\r\n", escape_char, ch,
+				    log_level_name(options.log_level));
+				buffer_append(berr, string, strlen(string));
+				continue;
+
 			case '&':
 				if (c && c->ctl_chan != -1)
 					goto noescape;
@@ -1114,43 +1237,9 @@
 				continue;
 
 			case '?':
-				if (c && c->ctl_chan != -1) {
-					snprintf(string, sizeof string,
-"%c?\r\n\
-Supported escape sequences:\r\n\
-  %c.  - terminate session\r\n\
-  %cB  - send a BREAK to the remote system\r\n\
-  %cR  - Request rekey (SSH protocol 2 only)\r\n\
-  %c#  - list forwarded connections\r\n\
-  %c?  - this message\r\n\
-  %c%c  - send the escape character by typing it twice\r\n\
-(Note that escapes are only recognized immediately after newline.)\r\n",
-					    escape_char, escape_char,
-					    escape_char, escape_char,
-					    escape_char, escape_char,
-					    escape_char, escape_char);
-				} else {
-					snprintf(string, sizeof string,
-"%c?\r\n\
-Supported escape sequences:\r\n\
-  %c.  - terminate connection (and any multiplexed sessions)\r\n\
-  %cB  - send a BREAK to the remote system\r\n\
-  %cC  - open a command line\r\n\
-  %cR  - Request rekey (SSH protocol 2 only)\r\n\
-  %c^Z - suspend ssh\r\n\
-  %c#  - list forwarded connections\r\n\
-  %c&  - background ssh (when waiting for connections to terminate)\r\n\
-  %c?  - this message\r\n\
-  %c%c  - send the escape character by typing it twice\r\n\
-(Note that escapes are only recognized immediately after newline.)\r\n",
-					    escape_char, escape_char,
-					    escape_char, escape_char,
-					    escape_char, escape_char,
-					    escape_char, escape_char,
-					    escape_char, escape_char,
-					    escape_char);
-				}
-				buffer_append(berr, string, strlen(string));
+				print_escape_help(berr, escape_char, compat20,
+				    (c && c->ctl_chan != -1),
+				    log_is_on_stderr());
 				continue;
 
 			case '#':
@@ -1159,7 +1248,7 @@
 				buffer_append(berr, string, strlen(string));
 				s = channel_open_message();
 				buffer_append(berr, s, strlen(s));
-				xfree(s);
+				free(s);
 				continue;
 
 			case 'C':
@@ -1326,8 +1415,7 @@
 static void
 client_process_buffered_input_packets(void)
 {
-	dispatch_run(DISPATCH_NONBLOCK, &quit_pending,
-	    compat20 ? xxx_kex : NULL);
+	dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state);
 }
 
 /* scan buf[] for '~' before sending data to the peer */
@@ -1338,7 +1426,7 @@
 {
 	struct escape_filter_ctx *ret;
 
-	ret = xmalloc(sizeof(*ret));
+	ret = xcalloc(1, sizeof(*ret));
 	ret->escape_pending = 0;
 	ret->escape_char = escape_char;
 	return (void *)ret;
@@ -1348,7 +1436,7 @@
 void
 client_filter_cleanup(int cid, void *ctx)
 {
-	xfree(ctx);
+	free(ctx);
 }
 
 int
@@ -1381,7 +1469,7 @@
 {
 	fd_set *readset = NULL, *writeset = NULL;
 	double start_time, total_time;
-	int max_fd = 0, max_fd2 = 0, len, rekeying = 0;
+	int r, max_fd = 0, max_fd2 = 0, len, rekeying = 0;
 	u_int64_t ibytes, obytes;
 	u_int nalloc = 0;
 	char buf[100];
@@ -1466,7 +1554,7 @@
 		if (compat20 && session_closed && !channel_still_open())
 			break;
 
-		rekeying = (xxx_kex != NULL && !xxx_kex->done);
+		rekeying = (active_state->kex != NULL && !active_state->kex->done);
 
 		if (rekeying) {
 			debug("rekeying in progress");
@@ -1510,8 +1598,10 @@
 			channel_after_select(readset, writeset);
 			if (need_rekeying || packet_need_rekeying()) {
 				debug("need rekeying");
-				xxx_kex->done = 0;
-				kex_send_kexinit(xxx_kex);
+				active_state->kex->done = 0;
+				if ((r = kex_send_kexinit(active_state)) != 0)
+					fatal("%s: kex_send_kexinit: %s",
+					    __func__, ssh_err(r));
 				need_rekeying = 0;
 			}
 		}
@@ -1553,16 +1643,14 @@
 		 * connections, then quit.
 		 */
 		if (control_persist_exit_time > 0) {
-			if (time(NULL) >= control_persist_exit_time) {
+			if (monotime() >= control_persist_exit_time) {
 				debug("ControlPersist timeout expired");
 				break;
 			}
 		}
 	}
-	if (readset)
-		xfree(readset);
-	if (writeset)
-		xfree(writeset);
+	free(readset);
+	free(writeset);
 
 	/* Terminate the session. */
 
@@ -1642,8 +1730,7 @@
 
 	/* Report bytes transferred, and transfer rates. */
 	total_time = get_current_time() - start_time;
-	packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
-	packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
+	packet_get_bytes(&ibytes, &obytes);
 	verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds",
 	    (unsigned long long)obytes, (unsigned long long)ibytes, total_time);
 	if (total_time > 0)
@@ -1656,27 +1743,29 @@
 
 /*********/
 
-static void
+static int
 client_input_stdout_data(int type, u_int32_t seq, void *ctxt)
 {
 	u_int data_len;
 	char *data = packet_get_string(&data_len);
 	packet_check_eom();
 	buffer_append(&stdout_buffer, data, data_len);
-	memset(data, 0, data_len);
-	xfree(data);
+	explicit_bzero(data, data_len);
+	free(data);
+	return 0;
 }
-static void
+static int
 client_input_stderr_data(int type, u_int32_t seq, void *ctxt)
 {
 	u_int data_len;
 	char *data = packet_get_string(&data_len);
 	packet_check_eom();
 	buffer_append(&stderr_buffer, data, data_len);
-	memset(data, 0, data_len);
-	xfree(data);
+	explicit_bzero(data, data_len);
+	free(data);
+	return 0;
 }
-static void
+static int
 client_input_exit_status(int type, u_int32_t seq, void *ctxt)
 {
 	exit_status = packet_get_int();
@@ -1691,12 +1780,14 @@
 	packet_write_wait();
 	/* Flag that we want to exit. */
 	quit_pending = 1;
+	return 0;
 }
-static void
+
+static int
 client_input_agent_open(int type, u_int32_t seq, void *ctxt)
 {
 	Channel *c = NULL;
-	int remote_id, sock;
+	int r, remote_id, sock;
 
 	/* Read the remote channel number from the message. */
 	remote_id = packet_get_int();
@@ -1706,7 +1797,11 @@
 	 * Get a connection to the local authentication agent (this may again
 	 * get forwarded).
 	 */
-	sock = ssh_get_authentication_socket();
+	if ((r = ssh_get_authentication_socket(&sock)) != 0 &&
+	    r != SSH_ERR_AGENT_NOT_PRESENT)
+		debug("%s: ssh_get_authentication_socket: %s",
+		    __func__, ssh_err(r));
+
 
 	/*
 	 * If we could not connect the agent, send an error message back to
@@ -1731,6 +1826,7 @@
 		packet_put_int(c->self);
 	}
 	packet_send();
+	return 0;
 }
 
 static Channel *
@@ -1747,15 +1843,35 @@
 	originator_port = packet_get_int();
 	packet_check_eom();
 
-	debug("client_request_forwarded_tcpip: listen %s port %d, "
-	    "originator %s port %d", listen_address, listen_port,
-	    originator_address, originator_port);
+	debug("%s: listen %s port %d, originator %s port %d", __func__,
+	    listen_address, listen_port, originator_address, originator_port);
 
-	c = channel_connect_by_listen_address(listen_port,
+	c = channel_connect_by_listen_address(listen_address, listen_port,
 	    "forwarded-tcpip", originator_address);
 
-	xfree(originator_address);
-	xfree(listen_address);
+	free(originator_address);
+	free(listen_address);
+	return c;
+}
+
+static Channel *
+client_request_forwarded_streamlocal(const char *request_type, int rchan)
+{
+	Channel *c = NULL;
+	char *listen_path;
+
+	/* Get the remote path. */
+	listen_path = packet_get_string(NULL);
+	/* XXX: Skip reserved field for now. */
+	if (packet_get_string_ptr(NULL) == NULL)
+		fatal("%s: packet_get_string_ptr failed", __func__);
+	packet_check_eom();
+
+	debug("%s: %s", __func__, listen_path);
+
+	c = channel_connect_by_listen_path(listen_path,
+	    "forwarded-streamlocal@openssh.com", "forwarded-streamlocal");
+	free(listen_path);
 	return c;
 }
 
@@ -1773,7 +1889,7 @@
 		    "malicious server.");
 		return NULL;
 	}
-	if (x11_refuse_time != 0 && time(NULL) >= x11_refuse_time) {
+	if (x11_refuse_time != 0 && monotime() >= x11_refuse_time) {
 		verbose("Rejected X11 connection after ForwardX11Timeout "
 		    "expired");
 		return NULL;
@@ -1789,7 +1905,7 @@
 	/* XXX check permission */
 	debug("client_request_x11: request from %s %d", originator,
 	    originator_port);
-	xfree(originator);
+	free(originator);
 	sock = x11_connect_display();
 	if (sock < 0)
 		return NULL;
@@ -1804,7 +1920,7 @@
 client_request_agent(const char *request_type, int rchan)
 {
 	Channel *c = NULL;
-	int sock;
+	int r, sock;
 
 	if (!options.forward_agent) {
 		error("Warning: ssh server tried agent forwarding.");
@@ -1812,9 +1928,12 @@
 		    "malicious server.");
 		return NULL;
 	}
-	sock = ssh_get_authentication_socket();
-	if (sock < 0)
+	if ((r = ssh_get_authentication_socket(&sock)) != 0) {
+		if (r != SSH_ERR_AGENT_NOT_PRESENT)
+			debug("%s: ssh_get_authentication_socket: %s",
+			    __func__, ssh_err(r));
 		return NULL;
+	}
 	c = channel_new("authentication agent connection",
 	    SSH_CHANNEL_OPEN, sock, sock, -1,
 	    CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
@@ -1868,7 +1987,7 @@
 }
 
 /* XXXX move to generic input handler */
-static void
+static int
 client_input_channel_open(int type, u_int32_t seq, void *ctxt)
 {
 	Channel *c = NULL;
@@ -1886,6 +2005,8 @@
 
 	if (strcmp(ctype, "forwarded-tcpip") == 0) {
 		c = client_request_forwarded_tcpip(ctype, rchan);
+	} else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) {
+		c = client_request_forwarded_streamlocal(ctype, rchan);
 	} else if (strcmp(ctype, "x11") == 0) {
 		c = client_request_x11(ctype, rchan);
 	} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) {
@@ -1916,9 +2037,11 @@
 		}
 		packet_send();
 	}
-	xfree(ctype);
+	free(ctype);
+	return 0;
 }
-static void
+
+static int
 client_input_channel_req(int type, u_int32_t seq, void *ctxt)
 {
 	Channel *c = NULL;
@@ -1956,32 +2079,410 @@
 		}
 		packet_check_eom();
 	}
-	if (reply && c != NULL) {
+	if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) {
 		packet_start(success ?
 		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
 		packet_put_int(c->remote_id);
 		packet_send();
 	}
-	xfree(rtype);
+	free(rtype);
+	return 0;
 }
+
+struct hostkeys_update_ctx {
+	/* The hostname and (optionally) IP address string for the server */
+	char *host_str, *ip_str;
+
+	/*
+	 * Keys received from the server and a flag for each indicating
+	 * whether they already exist in known_hosts.
+	 * keys_seen is filled in by hostkeys_find() and later (for new
+	 * keys) by client_global_hostkeys_private_confirm().
+	 */
+	struct sshkey **keys;
+	int *keys_seen;
+	size_t nkeys;
+
+	size_t nnew;
+
+	/*
+	 * Keys that are in known_hosts, but were not present in the update
+	 * from the server (i.e. scheduled to be deleted).
+	 * Filled in by hostkeys_find().
+	 */
+	struct sshkey **old_keys;
+	size_t nold;
+};
+
 static void
+hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx)
+{
+	size_t i;
+
+	if (ctx == NULL)
+		return;
+	for (i = 0; i < ctx->nkeys; i++)
+		sshkey_free(ctx->keys[i]);
+	free(ctx->keys);
+	free(ctx->keys_seen);
+	for (i = 0; i < ctx->nold; i++)
+		sshkey_free(ctx->old_keys[i]);
+	free(ctx->old_keys);
+	free(ctx->host_str);
+	free(ctx->ip_str);
+	free(ctx);
+}
+
+static int
+hostkeys_find(struct hostkey_foreach_line *l, void *_ctx)
+{
+	struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx;
+	size_t i;
+	struct sshkey **tmp;
+
+	if (l->status != HKF_STATUS_MATCHED || l->key == NULL ||
+	    l->key->type == KEY_RSA1)
+		return 0;
+
+	/* Mark off keys we've already seen for this host */
+	for (i = 0; i < ctx->nkeys; i++) {
+		if (sshkey_equal(l->key, ctx->keys[i])) {
+			debug3("%s: found %s key at %s:%ld", __func__,
+			    sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum);
+			ctx->keys_seen[i] = 1;
+			return 0;
+		}
+	}
+	/* This line contained a key that not offered by the server */
+	debug3("%s: deprecated %s key at %s:%ld", __func__,
+	    sshkey_ssh_name(l->key), l->path, l->linenum);
+	if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1,
+	    sizeof(*ctx->old_keys))) == NULL)
+		fatal("%s: reallocarray failed nold = %zu",
+		    __func__, ctx->nold);
+	ctx->old_keys = tmp;
+	ctx->old_keys[ctx->nold++] = l->key;
+	l->key = NULL;
+
+	return 0;
+}
+
+static void
+update_known_hosts(struct hostkeys_update_ctx *ctx)
+{
+	int r, was_raw = 0;
+	int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ?
+	    SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE;
+	char *fp, *response;
+	size_t i;
+
+	for (i = 0; i < ctx->nkeys; i++) {
+		if (ctx->keys_seen[i] != 2)
+			continue;
+		if ((fp = sshkey_fingerprint(ctx->keys[i],
+		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
+			fatal("%s: sshkey_fingerprint failed", __func__);
+		do_log2(loglevel, "Learned new hostkey: %s %s",
+		    sshkey_type(ctx->keys[i]), fp);
+		free(fp);
+	}
+	for (i = 0; i < ctx->nold; i++) {
+		if ((fp = sshkey_fingerprint(ctx->old_keys[i],
+		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
+			fatal("%s: sshkey_fingerprint failed", __func__);
+		do_log2(loglevel, "Deprecating obsolete hostkey: %s %s",
+		    sshkey_type(ctx->old_keys[i]), fp);
+		free(fp);
+	}
+	if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) {
+		if (get_saved_tio() != NULL) {
+			leave_raw_mode(1);
+			was_raw = 1;
+		}
+		response = NULL;
+		for (i = 0; !quit_pending && i < 3; i++) {
+			free(response);
+			response = read_passphrase("Accept updated hostkeys? "
+			    "(yes/no): ", RP_ECHO);
+			if (strcasecmp(response, "yes") == 0)
+				break;
+			else if (quit_pending || response == NULL ||
+			    strcasecmp(response, "no") == 0) {
+				options.update_hostkeys = 0;
+				break;
+			} else {
+				do_log2(loglevel, "Please enter "
+				    "\"yes\" or \"no\"");
+			}
+		}
+		if (quit_pending || i >= 3 || response == NULL)
+			options.update_hostkeys = 0;
+		free(response);
+		if (was_raw)
+			enter_raw_mode(1);
+	}
+
+	/*
+	 * Now that all the keys are verified, we can go ahead and replace
+	 * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't
+	 * cancel the operation).
+	 */
+	if (options.update_hostkeys != 0 &&
+	    (r = hostfile_replace_entries(options.user_hostfiles[0],
+	    ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys,
+	    options.hash_known_hosts, 0,
+	    options.fingerprint_hash)) != 0)
+		error("%s: hostfile_replace_entries failed: %s",
+		    __func__, ssh_err(r));
+}
+
+static void
+client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx)
+{
+	struct ssh *ssh = active_state; /* XXX */
+	struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx;
+	size_t i, ndone;
+	struct sshbuf *signdata;
+	int r;
+	const u_char *sig;
+	size_t siglen;
+
+	if (ctx->nnew == 0)
+		fatal("%s: ctx->nnew == 0", __func__); /* sanity */
+	if (type != SSH2_MSG_REQUEST_SUCCESS) {
+		error("Server failed to confirm ownership of "
+		    "private host keys");
+		hostkeys_update_ctx_free(ctx);
+		return;
+	}
+	if ((signdata = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	/* Don't want to accidentally accept an unbound signature */
+	if (ssh->kex->session_id_len == 0)
+		fatal("%s: ssh->kex->session_id_len == 0", __func__);
+	/*
+	 * Expect a signature for each of the ctx->nnew private keys we
+	 * haven't seen before. They will be in the same order as the
+	 * ctx->keys where the corresponding ctx->keys_seen[i] == 0.
+	 */
+	for (ndone = i = 0; i < ctx->nkeys; i++) {
+		if (ctx->keys_seen[i])
+			continue;
+		/* Prepare data to be signed: session ID, unique string, key */
+		sshbuf_reset(signdata);
+		if ( (r = sshbuf_put_cstring(signdata,
+		    "hostkeys-prove-00@openssh.com")) != 0 ||
+		    (r = sshbuf_put_string(signdata, ssh->kex->session_id,
+		    ssh->kex->session_id_len)) != 0 ||
+		    (r = sshkey_puts(ctx->keys[i], signdata)) != 0)
+			fatal("%s: failed to prepare signature: %s",
+			    __func__, ssh_err(r));
+		/* Extract and verify signature */
+		if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) {
+			error("%s: couldn't parse message: %s",
+			    __func__, ssh_err(r));
+			goto out;
+		}
+		if ((r = sshkey_verify(ctx->keys[i], sig, siglen,
+		    sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) {
+			error("%s: server gave bad signature for %s key %zu",
+			    __func__, sshkey_type(ctx->keys[i]), i);
+			goto out;
+		}
+		/* Key is good. Mark it as 'seen' */
+		ctx->keys_seen[i] = 2;
+		ndone++;
+	}
+	if (ndone != ctx->nnew)
+		fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__,
+		    ndone, ctx->nnew);  /* Shouldn't happen */
+	ssh_packet_check_eom(ssh);
+
+	/* Make the edits to known_hosts */
+	update_known_hosts(ctx);
+ out:
+	hostkeys_update_ctx_free(ctx);
+}
+
+/*
+ * Handle hostkeys-00@openssh.com global request to inform the client of all
+ * the server's hostkeys. The keys are checked against the user's
+ * HostkeyAlgorithms preference before they are accepted.
+ */
+static int
+client_input_hostkeys(void)
+{
+	struct ssh *ssh = active_state; /* XXX */
+	const u_char *blob = NULL;
+	size_t i, len = 0;
+	struct sshbuf *buf = NULL;
+	struct sshkey *key = NULL, **tmp;
+	int r;
+	char *fp;
+	static int hostkeys_seen = 0; /* XXX use struct ssh */
+	extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */
+	struct hostkeys_update_ctx *ctx = NULL;
+
+	if (hostkeys_seen)
+		fatal("%s: server already sent hostkeys", __func__);
+	if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK &&
+	    options.batch_mode)
+		return 1; /* won't ask in batchmode, so don't even try */
+	if (!options.update_hostkeys || options.num_user_hostfiles <= 0)
+		return 1;
+
+	ctx = xcalloc(1, sizeof(*ctx));
+	while (ssh_packet_remaining(ssh) > 0) {
+		sshkey_free(key);
+		key = NULL;
+		if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) {
+			error("%s: couldn't parse message: %s",
+			    __func__, ssh_err(r));
+			goto out;
+		}
+		if ((r = sshkey_from_blob(blob, len, &key)) != 0) {
+			error("%s: parse key: %s", __func__, ssh_err(r));
+			goto out;
+		}
+		fp = sshkey_fingerprint(key, options.fingerprint_hash,
+		    SSH_FP_DEFAULT);
+		debug3("%s: received %s key %s", __func__,
+		    sshkey_type(key), fp);
+		free(fp);
+		/* Check that the key is accepted in HostkeyAlgorithms */
+		if (options.hostkeyalgorithms != NULL &&
+		    match_pattern_list(sshkey_ssh_name(key),
+		    options.hostkeyalgorithms,
+		    strlen(options.hostkeyalgorithms), 0) != 1) {
+			debug3("%s: %s key not permitted by HostkeyAlgorithms",
+			    __func__, sshkey_ssh_name(key));
+			continue;
+		}
+		/* Skip certs */
+		if (sshkey_is_cert(key)) {
+			debug3("%s: %s key is a certificate; skipping",
+			    __func__, sshkey_ssh_name(key));
+			continue;
+		}
+		/* Ensure keys are unique */
+		for (i = 0; i < ctx->nkeys; i++) {
+			if (sshkey_equal(key, ctx->keys[i])) {
+				error("%s: received duplicated %s host key",
+				    __func__, sshkey_ssh_name(key));
+				goto out;
+			}
+		}
+		/* Key is good, record it */
+		if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1,
+		    sizeof(*ctx->keys))) == NULL)
+			fatal("%s: reallocarray failed nkeys = %zu",
+			    __func__, ctx->nkeys);
+		ctx->keys = tmp;
+		ctx->keys[ctx->nkeys++] = key;
+		key = NULL;
+	}
+
+	if (ctx->nkeys == 0) {
+		debug("%s: server sent no hostkeys", __func__);
+		goto out;
+	}
+
+	if ((ctx->keys_seen = calloc(ctx->nkeys,
+	    sizeof(*ctx->keys_seen))) == NULL)
+		fatal("%s: calloc failed", __func__);
+
+	get_hostfile_hostname_ipaddr(host,
+	    options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL,
+	    options.port, &ctx->host_str,
+	    options.check_host_ip ? &ctx->ip_str : NULL);
+
+	/* Find which keys we already know about. */
+	if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find,
+	    ctx, ctx->host_str, ctx->ip_str,
+	    HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) {
+		error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r));
+		goto out;
+	}
+
+	/* Figure out if we have any new keys to add */
+	ctx->nnew = 0;
+	for (i = 0; i < ctx->nkeys; i++) {
+		if (!ctx->keys_seen[i])
+			ctx->nnew++;
+	}
+
+	debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove",
+	    __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold);
+
+	if (ctx->nnew == 0 && ctx->nold != 0) {
+		/* We have some keys to remove. Just do it. */
+		update_known_hosts(ctx);
+	} else if (ctx->nnew != 0) {
+		/*
+		 * We have received hitherto-unseen keys from the server.
+		 * Ask the server to confirm ownership of the private halves.
+		 */
+		debug3("%s: asking server to prove ownership for %zu keys",
+		    __func__, ctx->nnew);
+		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+		    (r = sshpkt_put_cstring(ssh,
+		    "hostkeys-prove-00@openssh.com")) != 0 ||
+		    (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */
+			fatal("%s: cannot prepare packet: %s",
+			    __func__, ssh_err(r));
+		if ((buf = sshbuf_new()) == NULL)
+			fatal("%s: sshbuf_new", __func__);
+		for (i = 0; i < ctx->nkeys; i++) {
+			if (ctx->keys_seen[i])
+				continue;
+			sshbuf_reset(buf);
+			if ((r = sshkey_putb(ctx->keys[i], buf)) != 0)
+				fatal("%s: sshkey_putb: %s",
+				    __func__, ssh_err(r));
+			if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
+				fatal("%s: sshpkt_put_string: %s",
+				    __func__, ssh_err(r));
+		}
+		if ((r = sshpkt_send(ssh)) != 0)
+			fatal("%s: sshpkt_send: %s", __func__, ssh_err(r));
+		client_register_global_confirm(
+		    client_global_hostkeys_private_confirm, ctx);
+		ctx = NULL;  /* will be freed in callback */
+	}
+
+	/* Success */
+ out:
+	hostkeys_update_ctx_free(ctx);
+	sshkey_free(key);
+	sshbuf_free(buf);
+	/*
+	 * NB. Return success for all cases. The server doesn't need to know
+	 * what the client does with its hosts file.
+	 */
+	return 1;
+}
+
+static int
 client_input_global_request(int type, u_int32_t seq, void *ctxt)
 {
 	char *rtype;
 	int want_reply;
 	int success = 0;
 
-	rtype = packet_get_string(NULL);
+	rtype = packet_get_cstring(NULL);
 	want_reply = packet_get_char();
 	debug("client_input_global_request: rtype %s want_reply %d",
 	    rtype, want_reply);
+	if (strcmp(rtype, "hostkeys-00@openssh.com") == 0)
+		success = client_input_hostkeys();
 	if (want_reply) {
 		packet_start(success ?
 		    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
 		packet_send();
 		packet_write_wait();
 	}
-	xfree(rtype);
+	free(rtype);
+	return 0;
 }
 
 void
@@ -2031,7 +2532,7 @@
 			/* Split */
 			name = xstrdup(env[i]);
 			if ((val = strchr(name, '=')) == NULL) {
-				xfree(name);
+				free(name);
 				continue;
 			}
 			*val++ = '\0';
@@ -2045,7 +2546,7 @@
 			}
 			if (!matched) {
 				debug3("Ignored env %s", name);
-				xfree(name);
+				free(name);
 				continue;
 			}
 
@@ -2054,7 +2555,7 @@
 			packet_put_cstring(name);
 			packet_put_cstring(val);
 			packet_send();
-			xfree(name);
+			free(name);
 		}
 	}
 
@@ -2153,10 +2654,10 @@
 	if (options.control_path != NULL && muxserver_sock != -1)
 		unlink(options.control_path);
 	/*
-	 * If we are in persist mode, signal that we should close when all
-	 * active channels are closed.
+	 * If we are in persist mode, or don't have a shell, signal that we
+	 * should close when all active channels are closed.
 	 */
-	if (options.control_persist) {
+	if (options.control_persist || no_shell_flag) {
 		session_closed = 1;
 		setproctitle("[stopped mux]");
 	}