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/sshconnect.c b/sshconnect.c
index 0ee7266..9e51506 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.234 2011/05/24 07:15:47 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.259 2015/01/28 22:36:00 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -15,6 +15,7 @@
 
 #include "includes.h"
 
+#include <sys/param.h>	/* roundup */
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -54,16 +55,20 @@
 #include "sshconnect.h"
 #include "hostfile.h"
 #include "log.h"
+#include "misc.h"
 #include "readconf.h"
 #include "atomicio.h"
-#include "misc.h"
 #include "dns.h"
 #include "roaming.h"
+#include "monitor_fdpass.h"
 #include "ssh2.h"
 #include "version.h"
+#include "authfile.h"
+#include "ssherr.h"
 
 char *client_version_string = NULL;
 char *server_version_string = NULL;
+Key *previous_host_key = NULL;
 
 static int matching_host_key_dns = 0;
 
@@ -78,40 +83,122 @@
 static int show_other_keys(struct hostkeys *, Key *);
 static void warn_changed_key(Key *);
 
+/* Expand a proxy command */
+static char *
+expand_proxy_command(const char *proxy_command, const char *user,
+    const char *host, int port)
+{
+	char *tmp, *ret, strport[NI_MAXSERV];
+
+	snprintf(strport, sizeof strport, "%d", port);
+	xasprintf(&tmp, "exec %s", proxy_command);
+	ret = percent_expand(tmp, "h", host, "p", strport,
+	    "r", options.user, (char *)NULL);
+	free(tmp);
+	return ret;
+}
+
+/*
+ * Connect to the given ssh server using a proxy command that passes a
+ * a connected fd back to us.
+ */
+static int
+ssh_proxy_fdpass_connect(const char *host, u_short port,
+    const char *proxy_command)
+{
+	char *command_string;
+	int sp[2], sock;
+	pid_t pid;
+	char *shell;
+
+	if ((shell = getenv("SHELL")) == NULL)
+		shell = _PATH_BSHELL;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
+		fatal("Could not create socketpair to communicate with "
+		    "proxy dialer: %.100s", strerror(errno));
+
+	command_string = expand_proxy_command(proxy_command, options.user,
+	    host, port);
+	debug("Executing proxy dialer command: %.500s", command_string);
+
+	/* Fork and execute the proxy command. */
+	if ((pid = fork()) == 0) {
+		char *argv[10];
+
+		/* Child.  Permanently give up superuser privileges. */
+		permanently_drop_suid(original_real_uid);
+
+		close(sp[1]);
+		/* Redirect stdin and stdout. */
+		if (sp[0] != 0) {
+			if (dup2(sp[0], 0) < 0)
+				perror("dup2 stdin");
+		}
+		if (sp[0] != 1) {
+			if (dup2(sp[0], 1) < 0)
+				perror("dup2 stdout");
+		}
+		if (sp[0] >= 2)
+			close(sp[0]);
+
+		/*
+		 * Stderr is left as it is so that error messages get
+		 * printed on the user's terminal.
+		 */
+		argv[0] = shell;
+		argv[1] = "-c";
+		argv[2] = command_string;
+		argv[3] = NULL;
+
+		/*
+		 * Execute the proxy command.
+		 * Note that we gave up any extra privileges above.
+		 */
+		execv(argv[0], argv);
+		perror(argv[0]);
+		exit(1);
+	}
+	/* Parent. */
+	if (pid < 0)
+		fatal("fork failed: %.100s", strerror(errno));
+	close(sp[0]);
+	free(command_string);
+
+	if ((sock = mm_receive_fd(sp[1])) == -1)
+		fatal("proxy dialer did not pass back a connection");
+
+	while (waitpid(pid, NULL, 0) == -1)
+		if (errno != EINTR)
+			fatal("Couldn't wait for child: %s", strerror(errno));
+
+	/* Set the connection file descriptors. */
+	packet_set_connection(sock, sock);
+
+	return 0;
+}
+
 /*
  * Connect to the given ssh server using a proxy command.
  */
 static int
 ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
 {
-	char *command_string, *tmp;
+	char *command_string;
 	int pin[2], pout[2];
 	pid_t pid;
-	char *shell, strport[NI_MAXSERV];
+	char *shell;
 
 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
 		shell = _PATH_BSHELL;
 
-	/* Convert the port number into a string. */
-	snprintf(strport, sizeof strport, "%hu", port);
-
-	/*
-	 * Build the final command string in the buffer by making the
-	 * appropriate substitutions to the given proxy command.
-	 *
-	 * Use "exec" to avoid "sh -c" processes on some platforms
-	 * (e.g. Solaris)
-	 */
-	xasprintf(&tmp, "exec %s", proxy_command);
-	command_string = percent_expand(tmp, "h", host, "p", strport,
-	    "r", options.user, (char *)NULL);
-	xfree(tmp);
-
 	/* Create pipes for communicating with the proxy. */
 	if (pipe(pin) < 0 || pipe(pout) < 0)
 		fatal("Could not create pipes to communicate with the proxy: %.100s",
 		    strerror(errno));
 
+	command_string = expand_proxy_command(proxy_command, options.user,
+	    host, port);
 	debug("Executing proxy command: %.500s", command_string);
 
 	/* Fork and execute the proxy command. */
@@ -159,12 +246,10 @@
 	close(pout[1]);
 
 	/* Free the command name. */
-	xfree(command_string);
+	free(command_string);
 
 	/* Set the connection file descriptors. */
 	packet_set_connection(pout[0], pin[1]);
-	packet_set_timeout(options.server_alive_interval,
-	    options.server_alive_count_max);
 
 	/* Indicate OK return */
 	return 0;
@@ -187,55 +272,59 @@
 static int
 ssh_create_socket(int privileged, struct addrinfo *ai)
 {
-	int sock, gaierr;
-	struct addrinfo hints, *res;
+	int sock, r, gaierr;
+	struct addrinfo hints, *res = NULL;
 
-	/*
-	 * If we are running as root and want to connect to a privileged
-	 * port, bind our own socket to a privileged port.
-	 */
-	if (privileged) {
-		int p = IPPORT_RESERVED - 1;
-		PRIV_START;
-		sock = rresvport_af(&p, ai->ai_family);
-		PRIV_END;
-		if (sock < 0)
-			error("rresvport: af=%d %.100s", ai->ai_family,
-			    strerror(errno));
-		else
-			debug("Allocated local port %d.", p);
-		return sock;
-	}
 	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 	if (sock < 0) {
-		error("socket: %.100s", strerror(errno));
+		error("socket: %s", strerror(errno));
 		return -1;
 	}
 	fcntl(sock, F_SETFD, FD_CLOEXEC);
 
 	/* Bind the socket to an alternative local IP address */
-	if (options.bind_address == NULL)
+	if (options.bind_address == NULL && !privileged)
 		return sock;
 
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = ai->ai_family;
-	hints.ai_socktype = ai->ai_socktype;
-	hints.ai_protocol = ai->ai_protocol;
-	hints.ai_flags = AI_PASSIVE;
-	gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
-	if (gaierr) {
-		error("getaddrinfo: %s: %s", options.bind_address,
-		    ssh_gai_strerror(gaierr));
-		close(sock);
-		return -1;
+	if (options.bind_address) {
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_family = ai->ai_family;
+		hints.ai_socktype = ai->ai_socktype;
+		hints.ai_protocol = ai->ai_protocol;
+		hints.ai_flags = AI_PASSIVE;
+		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
+		if (gaierr) {
+			error("getaddrinfo: %s: %s", options.bind_address,
+			    ssh_gai_strerror(gaierr));
+			close(sock);
+			return -1;
+		}
 	}
-	if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
-		error("bind: %s: %s", options.bind_address, strerror(errno));
-		close(sock);
+	/*
+	 * If we are running as root and want to connect to a privileged
+	 * port, bind our own socket to a privileged port.
+	 */
+	if (privileged) {
+		PRIV_START;
+		r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
+		PRIV_END;
+		if (r < 0) {
+			error("bindresvport_sa: af=%d %s", ai->ai_family,
+			    strerror(errno));
+			goto fail;
+		}
+	} else {
+		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+			error("bind: %s: %s", options.bind_address,
+			    strerror(errno));
+ fail:
+			close(sock);
+			freeaddrinfo(res);
+			return -1;
+		}
+	}
+	if (res != NULL)
 		freeaddrinfo(res);
-		return -1;
-	}
-	freeaddrinfo(res);
 	return sock;
 }
 
@@ -308,7 +397,7 @@
 		fatal("Bogus return (%d) from select()", rc);
 	}
 
-	xfree(fdset);
+	free(fdset);
 
  done:
  	if (result == 0 && *timeoutp > 0) {
@@ -333,33 +422,18 @@
  * and %p substituted for host and port, respectively) to use to contact
  * the daemon.
  */
-int
-ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
-    u_short port, int family, int connection_attempts, int *timeout_ms,
-    int want_keepalive, int needpriv, const char *proxy_command)
+static int
+ssh_connect_direct(const char *host, struct addrinfo *aitop,
+    struct sockaddr_storage *hostaddr, u_short port, int family,
+    int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
 {
-	int gaierr;
 	int on = 1;
 	int sock = -1, attempt;
 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
-	struct addrinfo hints, *ai, *aitop;
+	struct addrinfo *ai;
 
 	debug2("ssh_connect: needpriv %d", needpriv);
 
-	/* If a proxy command is given, connect using it. */
-	if (proxy_command != NULL)
-		return ssh_proxy_connect(host, port, proxy_command);
-
-	/* No proxy command. */
-
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = family;
-	hints.ai_socktype = SOCK_STREAM;
-	snprintf(strport, sizeof strport, "%u", port);
-	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
-		fatal("%s: Could not resolve hostname %.100s: %s", __progname,
-		    host, ssh_gai_strerror(gaierr));
-
 	for (attempt = 0; attempt < connection_attempts; attempt++) {
 		if (attempt > 0) {
 			/* Sleep a moment before retrying. */
@@ -371,7 +445,8 @@
 		 * sequence until the connection succeeds.
 		 */
 		for (ai = aitop; ai; ai = ai->ai_next) {
-			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+			if (ai->ai_family != AF_INET &&
+			    ai->ai_family != AF_INET6)
 				continue;
 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
 			    ntop, sizeof(ntop), strport, sizeof(strport),
@@ -404,8 +479,6 @@
 			break;	/* Successful connection. */
 	}
 
-	freeaddrinfo(aitop);
-
 	/* Return failure if we didn't get a successful connection. */
 	if (sock == -1) {
 		error("ssh: connect to host %s port %s: %s",
@@ -423,12 +496,46 @@
 
 	/* Set the connection. */
 	packet_set_connection(sock, sock);
-	packet_set_timeout(options.server_alive_interval,
-	    options.server_alive_count_max);
 
 	return 0;
 }
 
+int
+ssh_connect(const char *host, struct addrinfo *addrs,
+    struct sockaddr_storage *hostaddr, u_short port, int family,
+    int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
+{
+	if (options.proxy_command == NULL) {
+		return ssh_connect_direct(host, addrs, hostaddr, port, family,
+		    connection_attempts, timeout_ms, want_keepalive, needpriv);
+	} else if (strcmp(options.proxy_command, "-") == 0) {
+		packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
+		return 0; /* Always succeeds */
+	} else if (options.proxy_use_fdpass) {
+		return ssh_proxy_fdpass_connect(host, port,
+		    options.proxy_command);
+	}
+	return ssh_proxy_connect(host, port, options.proxy_command);
+}
+
+static void
+send_client_banner(int connection_out, int minor1)
+{
+	/* Send our own protocol version identification. */
+	if (compat20) {
+		xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n",
+		    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION);
+	} else {
+		xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n",
+		    PROTOCOL_MAJOR_1, minor1, SSH_VERSION);
+	}
+	if (roaming_atomicio(vwrite, connection_out, client_version_string,
+	    strlen(client_version_string)) != strlen(client_version_string))
+		fatal("write: %.100s", strerror(errno));
+	chop(client_version_string);
+	debug("Local version string %.100s", client_version_string);
+}
+
 /*
  * Waits for the server identification string, and sends our own
  * identification string.
@@ -440,7 +547,7 @@
 	int remote_major, remote_minor, mismatch;
 	int connection_in = packet_get_connection_in();
 	int connection_out = packet_get_connection_out();
-	int minor1 = PROTOCOL_MINOR_1;
+	int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0;
 	u_int i, n;
 	size_t len;
 	int fdsetsz, remaining, rc;
@@ -450,6 +557,16 @@
 	fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
 	fdset = xcalloc(1, fdsetsz);
 
+	/*
+	 * If we are SSH2-only then we can send the banner immediately and
+	 * save a round-trip.
+	 */
+	if (options.protocol == SSH_PROTO_2) {
+		enable_compat20();
+		send_client_banner(connection_out, 0);
+		client_banner_sent = 1;
+	}
+
 	/* Read other side's version identification. */
 	remaining = timeout_ms;
 	for (n = 0;;) {
@@ -499,7 +616,7 @@
 		debug("ssh_exchange_identification: %s", buf);
 	}
 	server_version_string = xstrdup(buf);
-	xfree(fdset);
+	free(fdset);
 
 	/*
 	 * Check that the versions match.  In future this might accept
@@ -511,7 +628,7 @@
 	debug("Remote protocol version %d.%d, remote software version %.100s",
 	    remote_major, remote_minor, remote_version);
 
-	compat_datafellows(remote_version);
+	active_state->compat = compat_datafellows(remote_version);
 	mismatch = 0;
 
 	switch (remote_major) {
@@ -552,18 +669,15 @@
 		fatal("Protocol major versions differ: %d vs. %d",
 		    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
 		    remote_major);
-	/* Send our own protocol version identification. */
-	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s",
-	    compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
-	    compat20 ? PROTOCOL_MINOR_2 : minor1,
-	    SSH_VERSION, compat20 ? "\r\n" : "\n");
-	if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf))
-	    != strlen(buf))
-		fatal("write: %.100s", strerror(errno));
-	client_version_string = xstrdup(buf);
-	chop(client_version_string);
+	if ((datafellows & SSH_BUG_DERIVEKEY) != 0)
+		fatal("Server version \"%.100s\" uses unsafe key agreement; "
+		    "refusing connection", remote_version);
+	if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
+		logit("Server version \"%.100s\" uses unsafe RSA signature "
+		    "scheme; disabling use of RSA keys", remote_version);
+	if (!client_banner_sent)
+		send_client_banner(connection_out, minor1);
 	chop(server_version_string);
-	debug("Local version string %.100s", client_version_string);
 }
 
 /* defaults to 'no' */
@@ -584,8 +698,7 @@
 			ret = 0;
 		if (p && strncasecmp(p, "yes", 3) == 0)
 			ret = 1;
-		if (p)
-			xfree(p);
+		free(p);
 		if (ret != -1)
 			return ret;
 	}
@@ -600,7 +713,7 @@
 		error("%s", reason);
 		return 0;
 	}
-	if (buffer_len(&host_key->cert->critical) != 0) {
+	if (buffer_len(host_key->cert->critical) != 0) {
 		error("Certificate for %s contains unsupported "
 		    "critical options(s)", host);
 		return 0;
@@ -657,7 +770,7 @@
 		if (options.proxy_command == NULL) {
 			if (getnameinfo(hostaddr, addrlen,
 			    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
-			fatal("check_host_key: getnameinfo failed");
+			fatal("%s: getnameinfo failed", __func__);
 			*hostfile_ipaddr = put_host_port(ntop, port);
 		} else {
 			*hostfile_ipaddr = xstrdup("<no hostip for proxy "
@@ -705,6 +818,7 @@
 	int len, cancelled_forwarding = 0;
 	int local = sockaddr_is_local(hostaddr);
 	int r, want_cert = key_is_cert(host_key), host_ip_differ = 0;
+	int hostkey_trusted = 0; /* Known or explicitly accepted by user */
 	struct hostkeys *host_hostkeys, *ip_hostkeys;
 	u_int i;
 
@@ -805,13 +919,17 @@
 				    "key for IP address '%.128s' to the list "
 				    "of known hosts.", type, ip);
 		} else if (options.visual_host_key) {
-			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
-			ra = key_fingerprint(host_key, SSH_FP_MD5,
-			    SSH_FP_RANDOMART);
+			fp = sshkey_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_DEFAULT);
+			ra = sshkey_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_RANDOMART);
+			if (fp == NULL || ra == NULL)
+				fatal("%s: sshkey_fingerprint fail", __func__);
 			logit("Host key fingerprint is %s\n%s\n", fp, ra);
-			xfree(ra);
-			xfree(fp);
+			free(ra);
+			free(fp);
 		}
+		hostkey_trusted = 1;
 		break;
 	case HOST_NEW:
 		if (options.host_key_alias == NULL && port != 0 &&
@@ -846,9 +964,12 @@
 			else
 				snprintf(msg1, sizeof(msg1), ".");
 			/* The default */
-			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
-			ra = key_fingerprint(host_key, SSH_FP_MD5,
-			    SSH_FP_RANDOMART);
+			fp = sshkey_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_DEFAULT);
+			ra = sshkey_fingerprint(host_key,
+			    options.fingerprint_hash, SSH_FP_RANDOMART);
+			if (fp == NULL || ra == NULL)
+				fatal("%s: sshkey_fingerprint fail", __func__);
 			msg2[0] = '\0';
 			if (options.verify_host_key_dns) {
 				if (matching_host_key_dns)
@@ -870,10 +991,11 @@
 			    options.visual_host_key ? "\n" : "",
 			    options.visual_host_key ? ra : "",
 			    msg2);
-			xfree(ra);
-			xfree(fp);
+			free(ra);
+			free(fp);
 			if (!confirm(msg))
 				goto fail;
+			hostkey_trusted = 1; /* user explicitly confirmed */
 		}
 		/*
 		 * If not in strict mode, add the key automatically to the
@@ -1072,8 +1194,14 @@
 		}
 	}
 
-	xfree(ip);
-	xfree(host);
+	if (!hostkey_trusted && options.update_hostkeys) {
+		debug("%s: hostkey not known or explicitly trusted: "
+		    "disabling UpdateHostkeys", __func__);
+		options.update_hostkeys = 0;
+	}
+
+	free(ip);
+	free(host);
 	if (host_hostkeys != NULL)
 		free_hostkeys(host_hostkeys);
 	if (ip_hostkeys != NULL)
@@ -1095,8 +1223,8 @@
 	}
 	if (raw_key != NULL)
 		key_free(raw_key);
-	xfree(ip);
-	xfree(host);
+	free(ip);
+	free(host);
 	if (host_hostkeys != NULL)
 		free_hostkeys(host_hostkeys);
 	if (ip_hostkeys != NULL)
@@ -1108,36 +1236,89 @@
 int
 verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
 {
-	int flags = 0;
-	char *fp;
+	int r = -1, flags = 0;
+	char *fp = NULL;
+	struct sshkey *plain = NULL;
 
-	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
-	debug("Server host key: %s %s", key_type(host_key), fp);
-	xfree(fp);
+	if ((fp = sshkey_fingerprint(host_key,
+	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
+		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
+		r = -1;
+		goto out;
+	}
 
-	/* XXX certs are not yet supported for DNS */
-	if (!key_is_cert(host_key) && options.verify_host_key_dns &&
-	    verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
-		if (flags & DNS_VERIFY_FOUND) {
+	debug("Server host key: %s %s",
+	    compat20 ? sshkey_ssh_name(host_key) : sshkey_type(host_key), fp);
 
-			if (options.verify_host_key_dns == 1 &&
-			    flags & DNS_VERIFY_MATCH &&
-			    flags & DNS_VERIFY_SECURE)
-				return 0;
+	if (sshkey_equal(previous_host_key, host_key)) {
+		debug2("%s: server host key %s %s matches cached key",
+		    __func__, sshkey_type(host_key), fp);
+		r = 0;
+		goto out;
+	}
 
-			if (flags & DNS_VERIFY_MATCH) {
-				matching_host_key_dns = 1;
-			} else {
-				warn_changed_key(host_key);
-				error("Update the SSHFP RR in DNS with the new "
-				    "host key to get rid of this message.");
-			}
+	/* Check in RevokedHostKeys file if specified */
+	if (options.revoked_host_keys != NULL) {
+		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
+		switch (r) {
+		case 0:
+			break; /* not revoked */
+		case SSH_ERR_KEY_REVOKED:
+			error("Host key %s %s revoked by file %s",
+			    sshkey_type(host_key), fp,
+			    options.revoked_host_keys);
+			r = -1;
+			goto out;
+		default:
+			error("Error checking host key %s %s in "
+			    "revoked keys file %s: %s", sshkey_type(host_key),
+			    fp, options.revoked_host_keys, ssh_err(r));
+			r = -1;
+			goto out;
 		}
 	}
 
-	return check_host_key(host, hostaddr, options.port, host_key, RDRW,
+	if (options.verify_host_key_dns) {
+		/*
+		 * XXX certs are not yet supported for DNS, so downgrade
+		 * them and try the plain key.
+		 */
+		if ((r = sshkey_from_private(host_key, &plain)) != 0)
+			goto out;
+		if (sshkey_is_cert(plain))
+			sshkey_drop_cert(plain);
+		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
+			if (flags & DNS_VERIFY_FOUND) {
+				if (options.verify_host_key_dns == 1 &&
+				    flags & DNS_VERIFY_MATCH &&
+				    flags & DNS_VERIFY_SECURE) {
+					r = 0;
+					goto out;
+				}
+				if (flags & DNS_VERIFY_MATCH) {
+					matching_host_key_dns = 1;
+				} else {
+					warn_changed_key(plain);
+					error("Update the SSHFP RR in DNS "
+					    "with the new host key to get rid "
+					    "of this message.");
+				}
+			}
+		}
+	}
+	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
 	    options.user_hostfiles, options.num_user_hostfiles,
 	    options.system_hostfiles, options.num_system_hostfiles);
+
+out:
+	sshkey_free(plain);
+	free(fp);
+	if (r == 0 && host_key != NULL) {
+		key_free(previous_host_key);
+		previous_host_key = key_from_private(host_key);
+	}
+
+	return r;
 }
 
 /*
@@ -1151,7 +1332,7 @@
 ssh_login(Sensitive *sensitive, const char *orighost,
     struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
 {
-	char *host, *cp;
+	char *host;
 	char *server_user, *local_user;
 
 	local_user = xstrdup(pw->pw_name);
@@ -1159,9 +1340,7 @@
 
 	/* Convert the user-supplied hostname into all lowercase. */
 	host = xstrdup(orighost);
-	for (cp = host; *cp; cp++)
-		if (isupper(*cp))
-			*cp = (char)tolower(*cp);
+	lowercase(host);
 
 	/* Exchange protocol version identification strings with the server. */
 	ssh_exchange_identification(timeout_ms);
@@ -1175,10 +1354,14 @@
 		ssh_kex2(host, hostaddr, port);
 		ssh_userauth2(local_user, server_user, host, sensitive);
 	} else {
+#ifdef WITH_SSH1
 		ssh_kex(host, hostaddr);
 		ssh_userauth1(local_user, server_user, host, sensitive);
+#else
+		fatal("ssh1 is not unsupported");
+#endif
 	}
-	xfree(local_user);
+	free(local_user);
 }
 
 void
@@ -1195,15 +1378,22 @@
 	padded = xcalloc(1, size);
 	strlcpy(padded, password, size);
 	packet_put_string(padded, size);
-	memset(padded, 0, size);
-	xfree(padded);
+	explicit_bzero(padded, size);
+	free(padded);
 }
 
 /* print all known host keys for a given host, but skip keys of given type */
 static int
 show_other_keys(struct hostkeys *hostkeys, Key *key)
 {
-	int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1};
+	int type[] = {
+		KEY_RSA1,
+		KEY_RSA,
+		KEY_DSA,
+		KEY_ECDSA,
+		KEY_ED25519,
+		-1
+	};
 	int i, ret = 0;
 	char *fp, *ra;
 	const struct hostkey_entry *found;
@@ -1213,8 +1403,12 @@
 			continue;
 		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
 			continue;
-		fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX);
-		ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART);
+		fp = sshkey_fingerprint(found->key,
+		    options.fingerprint_hash, SSH_FP_DEFAULT);
+		ra = sshkey_fingerprint(found->key,
+		    options.fingerprint_hash, SSH_FP_RANDOMART);
+		if (fp == NULL || ra == NULL)
+			fatal("%s: sshkey_fingerprint fail", __func__);
 		logit("WARNING: %s key found for host %s\n"
 		    "in %s:%lu\n"
 		    "%s key fingerprint %s.",
@@ -1223,8 +1417,8 @@
 		    key_type(found->key), fp);
 		if (options.visual_host_key)
 			logit("%s", ra);
-		xfree(ra);
-		xfree(fp);
+		free(ra);
+		free(fp);
 		ret = 1;
 	}
 	return ret;
@@ -1235,7 +1429,10 @@
 {
 	char *fp;
 
-	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+	fp = sshkey_fingerprint(host_key, options.fingerprint_hash,
+	    SSH_FP_DEFAULT);
+	if (fp == NULL)
+		fatal("%s: sshkey_fingerprint fail", __func__);
 
 	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
 	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
@@ -1247,7 +1444,7 @@
 	    key_type(host_key), fp);
 	error("Please contact your system administrator.");
 
-	xfree(fp);
+	free(fp);
 }
 
 /*