Merge openssh 7.5p1

BUG: 35443510
Change-Id: I2558fb80234e145c13ccb7a51859f2a5fd2e0324
diff --git a/ssh.c b/ssh.c
index 67c1ebf..32b27bb 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.420 2015/07/30 00:01:34 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.451 2017/03/10 04:07:20 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -67,6 +67,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <limits.h>
+#include <locale.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -105,10 +106,10 @@
 #include "match.h"
 #include "msg.h"
 #include "uidswap.h"
-#include "roaming.h"
 #include "version.h"
 #include "ssherr.h"
 #include "myproposal.h"
+#include "utf8.h"
 
 #ifdef ENABLE_PKCS11
 #include "ssh-pkcs11.h"
@@ -152,10 +153,6 @@
  */
 int fork_after_authentication_flag = 0;
 
-/* forward stdio to remote host and port */
-char *stdio_forward_host = NULL;
-int stdio_forward_port = 0;
-
 /*
  * General data structure for command line options and options configurable
  * in configuration files.  See readconf.h.
@@ -204,11 +201,10 @@
 "usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n"
 "           [-D [bind_address:]port] [-E log_file] [-e escape_char]\n"
 "           [-F configfile] [-I pkcs11] [-i identity_file]\n"
-"           [-L address] [-l login_name] [-m mac_spec]\n"
-"           [-O ctl_cmd] [-o option] [-p port]\n"
-"           [-Q cipher | cipher-auth | mac | kex | key]\n"
-"           [-R address] [-S ctl_path] [-W host:port]\n"
-"           [-w local_tun[:remote_tun]] [user@]hostname [command]\n"
+"           [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n"
+"           [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n"
+"           [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
+"           [user@]hostname [command]\n"
 	);
 	exit(255);
 }
@@ -218,10 +214,6 @@
 static void load_public_identity_files(void);
 static void main_sigchld_handler(int);
 
-/* from muxclient.c */
-void muxclient(const char *);
-void muxserver_listen(void);
-
 /* ~/ expand a list of paths. NB. assumes path[n] is heap-allocated. */
 static void
 tilde_expand_paths(char **paths, u_int num_paths)
@@ -252,7 +244,7 @@
 	if (port <= 0)
 		port = default_ssh_port();
 
-	snprintf(strport, sizeof strport, "%u", port);
+	snprintf(strport, sizeof strport, "%d", port);
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = options.address_family == -1 ?
 	    AF_UNSPEC : options.address_family;
@@ -337,7 +329,7 @@
  * NB. this function must operate with a options having undefined members.
  */
 static int
-check_follow_cname(char **namep, const char *cname)
+check_follow_cname(int direct, char **namep, const char *cname)
 {
 	int i;
 	struct allowed_cname *rule;
@@ -349,9 +341,9 @@
 		return 0;
 	/*
 	 * Don't attempt to canonicalize names that will be interpreted by
-	 * a proxy unless the user specifically requests so.
+	 * a proxy or jump host unless the user specifically requests so.
 	 */
-	if (!option_clear_or_none(options.proxy_command) &&
+	if (!direct &&
 	    options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
 		return 0;
 	debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname);
@@ -378,7 +370,7 @@
 static struct addrinfo *
 resolve_canonicalize(char **hostp, int port)
 {
-	int i, ndots;
+	int i, direct, ndots;
 	char *cp, *fullhost, newname[NI_MAXHOST];
 	struct addrinfo *addrs;
 
@@ -389,7 +381,9 @@
 	 * Don't attempt to canonicalize names that will be interpreted by
 	 * a proxy unless the user specifically requests so.
 	 */
-	if (!option_clear_or_none(options.proxy_command) &&
+	direct = option_clear_or_none(options.proxy_command) &&
+	    options.jump_host == NULL;
+	if (!direct &&
 	    options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
 		return NULL;
 
@@ -406,6 +400,17 @@
 		return addrs;
 	}
 
+	/* If domain name is anchored, then resolve it now */
+	if ((*hostp)[strlen(*hostp) - 1] == '.') {
+		debug3("%s: name is fully qualified", __func__);
+		fullhost = xstrdup(*hostp);
+		if ((addrs = resolve_host(fullhost, port, 0,
+		    newname, sizeof(newname))) != NULL)
+			goto found;
+		free(fullhost);
+		goto notfound;
+	}
+
 	/* Don't apply canonicalization to sufficiently-qualified hostnames */
 	ndots = 0;
 	for (cp = *hostp; *cp != '\0'; cp++) {
@@ -429,10 +434,11 @@
 			free(fullhost);
 			continue;
 		}
+ found:
 		/* Remove trailing '.' */
 		fullhost[strlen(fullhost) - 1] = '\0';
 		/* Follow CNAME if requested */
-		if (!check_follow_cname(&fullhost, newname)) {
+		if (!check_follow_cname(direct, &fullhost, newname)) {
 			debug("Canonicalized hostname \"%s\" => \"%s\"",
 			    *hostp, fullhost);
 		}
@@ -440,6 +446,7 @@
 		*hostp = fullhost;
 		return addrs;
 	}
+ notfound:
 	if (!options.canonicalize_fallback_local)
 		fatal("%s: Could not resolve host \"%s\"", __progname, *hostp);
 	debug2("%s: host %s not found in any suffix", __func__, *hostp);
@@ -503,10 +510,11 @@
 int
 main(int ac, char **av)
 {
-	int i, r, opt, exit_status, use_syslog, config_test = 0;
+	struct ssh *ssh = NULL;
+	int i, r, opt, exit_status, use_syslog, direct, config_test = 0;
 	char *p, *cp, *line, *argv0, buf[PATH_MAX], *host_arg, *logfile;
 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
-	char cname[NI_MAXHOST];
+	char cname[NI_MAXHOST], uidstr[32], *conn_hash_hex;
 	struct stat st;
 	struct passwd *pw;
 	int timeout_ms;
@@ -516,8 +524,8 @@
 	struct addrinfo *addrs = NULL;
 	struct ssh_digest_ctx *md;
 	u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
-	char *conn_hash_hex;
 
+	ssh_malloc_init();	/* must be called before any mallocs */
 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
 	sanitise_stdfd();
 
@@ -582,6 +590,8 @@
 	 */
 	umask(022);
 
+	msetlocale();
+
 	/*
 	 * Initialize option structure to indicate that no values have been
 	 * set.
@@ -596,7 +606,7 @@
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
-	    "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
+	    "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -627,7 +637,7 @@
 			use_syslog = 1;
 			break;
 		case 'E':
-			logfile = xstrdup(optarg);
+			logfile = optarg;
 			break;
 		case 'G':
 			config_test = 1;
@@ -640,7 +650,7 @@
 			options.fwd_opts.gateway_ports = 1;
 			break;
 		case 'O':
-			if (stdio_forward_host != NULL)
+			if (options.stdio_forward_host != NULL)
 				fatal("Cannot specify multiplexing "
 				    "command with -W");
 			else if (muxclient_command != 0)
@@ -655,6 +665,8 @@
 				muxclient_command = SSHMUX_COMMAND_STOP;
 			else if (strcmp(optarg, "cancel") == 0)
 				muxclient_command = SSHMUX_COMMAND_CANCEL_FWD;
+			else if (strcmp(optarg, "proxy") == 0)
+				muxclient_command = SSHMUX_COMMAND_PROXY;
 			else
 				fatal("Invalid multiplex command.");
 			break;
@@ -672,11 +684,11 @@
 			else if (strcmp(optarg, "kex") == 0)
 				cp = kex_alg_list('\n');
 			else if (strcmp(optarg, "key") == 0)
-				cp = key_alg_list(0, 0);
+				cp = sshkey_alg_list(0, 0, 0, '\n');
 			else if (strcmp(optarg, "key-cert") == 0)
-				cp = key_alg_list(1, 0);
+				cp = sshkey_alg_list(1, 0, 0, '\n');
 			else if (strcmp(optarg, "key-plain") == 0)
-				cp = key_alg_list(0, 1);
+				cp = sshkey_alg_list(0, 1, 0, '\n');
 			else if (strcmp(optarg, "protocol-version") == 0) {
 #ifdef WITH_SSH1
 				cp = xstrdup("1\n2");
@@ -704,21 +716,32 @@
 			options.gss_deleg_creds = 1;
 			break;
 		case 'i':
-			if (stat(optarg, &st) < 0) {
+			p = tilde_expand_filename(optarg, original_real_uid);
+			if (stat(p, &st) < 0)
 				fprintf(stderr, "Warning: Identity file %s "
-				    "not accessible: %s.\n", optarg,
+				    "not accessible: %s.\n", p,
 				    strerror(errno));
-				break;
-			}
-			add_identity_file(&options, NULL, optarg, 1);
+			else
+				add_identity_file(&options, NULL, p, 1);
+			free(p);
 			break;
 		case 'I':
 #ifdef ENABLE_PKCS11
+			free(options.pkcs11_provider);
 			options.pkcs11_provider = xstrdup(optarg);
 #else
 			fprintf(stderr, "no support for PKCS#11.\n");
 #endif
 			break;
+		case 'J':
+			if (options.jump_host != NULL)
+				fatal("Only a single -J option permitted");
+			if (options.proxy_command != NULL)
+				fatal("Cannot specify -J with ProxyCommand");
+			if (parse_jump(optarg, &options, 1) == -1)
+				fatal("Invalid -J argument");
+			options.proxy_command = xstrdup("none");
+			break;
 		case 't':
 			if (options.request_tty == REQUEST_TTY_YES)
 				options.request_tty = REQUEST_TTY_FORCE;
@@ -730,8 +753,10 @@
 				debug_flag = 1;
 				options.log_level = SYSLOG_LEVEL_DEBUG1;
 			} else {
-				if (options.log_level < SYSLOG_LEVEL_DEBUG3)
+				if (options.log_level < SYSLOG_LEVEL_DEBUG3) {
+					debug_flag++;
 					options.log_level++;
+				}
 			}
 			break;
 		case 'V':
@@ -757,13 +782,13 @@
 			}
 			break;
 		case 'W':
-			if (stdio_forward_host != NULL)
+			if (options.stdio_forward_host != NULL)
 				fatal("stdio forward already specified");
 			if (muxclient_command != 0)
 				fatal("Cannot specify stdio forward with -O");
 			if (parse_forward(&fwd, optarg, 1, 0)) {
-				stdio_forward_host = fwd.listen_host;
-				stdio_forward_port = fwd.listen_port;
+				options.stdio_forward_host = fwd.listen_host;
+				options.stdio_forward_port = fwd.listen_port;
 				free(fwd.connect_host);
 			} else {
 				fprintf(stderr,
@@ -773,8 +798,6 @@
 			}
 			options.request_tty = REQUEST_TTY_NO;
 			no_shell_flag = 1;
-			options.clear_forwardings = 1;
-			options.exit_on_forward_failure = 1;
 			break;
 		case 'q':
 			options.log_level = SYSLOG_LEVEL_QUIET;
@@ -798,6 +821,7 @@
 			if (ciphers_valid(*optarg == '+' ?
 			    optarg + 1 : optarg)) {
 				/* SSH2 only */
+				free(options.ciphers);
 				options.ciphers = xstrdup(optarg);
 				options.cipher = SSH_CIPHER_INVALID;
 				break;
@@ -817,9 +841,10 @@
 				options.ciphers = xstrdup(KEX_CLIENT_ENCRYPT);
 			break;
 		case 'm':
-			if (mac_valid(optarg))
+			if (mac_valid(optarg)) {
+				free(options.macs);
 				options.macs = xstrdup(optarg);
-			else {
+			} else {
 				fprintf(stderr, "Unknown mac type '%s'\n",
 				    optarg);
 				exit(255);
@@ -897,8 +922,7 @@
 			subsystem_flag = 1;
 			break;
 		case 'S':
-			if (options.control_path != NULL)
-				free(options.control_path);
+			free(options.control_path);
 			options.control_path = xstrdup(optarg);
 			break;
 		case 'b':
@@ -980,10 +1004,8 @@
 	 */
 	if (use_syslog && logfile != NULL)
 		fatal("Can't specify both -y and -E");
-	if (logfile != NULL) {
+	if (logfile != NULL)
 		log_redirect_stderr_to(logfile);
-		free(logfile);
-	}
 	log_init(argv0,
 	    options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
 	    SYSLOG_FACILITY_USER, !use_syslog);
@@ -1032,9 +1054,10 @@
 	 * has specifically requested canonicalisation for this case via
 	 * CanonicalizeHostname=always
 	 */
-	if (addrs == NULL && options.num_permitted_cnames != 0 &&
-	    (option_clear_or_none(options.proxy_command) ||
-            options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) {
+	direct = option_clear_or_none(options.proxy_command) &&
+	    options.jump_host == NULL;
+	if (addrs == NULL && options.num_permitted_cnames != 0 && (direct ||
+	    options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) {
 		if ((addrs = resolve_host(host, options.port,
 		    option_clear_or_none(options.proxy_command),
 		    cname, sizeof(cname))) == NULL) {
@@ -1042,7 +1065,7 @@
 			if (option_clear_or_none(options.proxy_command))
 				cleanup_exit(255); /* logged in resolve_host */
 		} else
-			check_follow_cname(&host, cname);
+			check_follow_cname(direct, &host, cname);
 	}
 
 	/*
@@ -1067,6 +1090,41 @@
 	/* Fill configuration defaults. */
 	fill_default_options(&options);
 
+	/*
+	 * If ProxyJump option specified, then construct a ProxyCommand now.
+	 */
+	if (options.jump_host != NULL) {
+		char port_s[8];
+
+		/* Consistency check */
+		if (options.proxy_command != NULL)
+			fatal("inconsistent options: ProxyCommand+ProxyJump");
+		/* Never use FD passing for ProxyJump */
+		options.proxy_use_fdpass = 0;
+		snprintf(port_s, sizeof(port_s), "%d", options.jump_port);
+		xasprintf(&options.proxy_command,
+		    "ssh%s%s%s%s%s%s%s%s%s%.*s -W '[%%h]:%%p' %s",
+		    /* Optional "-l user" argument if jump_user set */
+		    options.jump_user == NULL ? "" : " -l ",
+		    options.jump_user == NULL ? "" : options.jump_user,
+		    /* Optional "-p port" argument if jump_port set */
+		    options.jump_port <= 0 ? "" : " -p ",
+		    options.jump_port <= 0 ? "" : port_s,
+		    /* Optional additional jump hosts ",..." */
+		    options.jump_extra == NULL ? "" : " -J ",
+		    options.jump_extra == NULL ? "" : options.jump_extra,
+		    /* Optional "-F" argumment if -F specified */
+		    config == NULL ? "" : " -F ",
+		    config == NULL ? "" : config,
+		    /* Optional "-v" arguments if -v set */
+		    debug_flag ? " -" : "",
+		    debug_flag, "vvv",
+		    /* Mandatory hostname */
+		    options.jump_host);
+		debug("Setting implicit ProxyCommand from ProxyJump: %s",
+		    options.proxy_command);
+	}
+
 	if (options.port == 0)
 		options.port = default_ssh_port();
 	channel_set_af(options.address_family);
@@ -1084,6 +1142,8 @@
 		    "disabling");
 		options.update_hostkeys = 0;
 	}
+	if (options.connection_attempts <= 0)
+		fatal("Invalid number of ConnectionAttempts");
 #ifndef HAVE_CYGWIN
 	if (original_effective_uid != 0)
 		options.use_privileged_port = 0;
@@ -1101,7 +1161,8 @@
 		tty_flag = options.request_tty != REQUEST_TTY_NO;
 
 	/* Force no tty */
-	if (options.request_tty == REQUEST_TTY_NO || muxclient_command != 0)
+	if (options.request_tty == REQUEST_TTY_NO ||
+	    (muxclient_command && muxclient_command != SSHMUX_COMMAND_PROXY))
 		tty_flag = 0;
 	/* Do not allocate a tty if stdin is not a tty. */
 	if ((!isatty(fileno(stdin)) || stdin_null_flag) &&
@@ -1122,6 +1183,7 @@
 	strlcpy(shorthost, thishost, sizeof(shorthost));
 	shorthost[strcspn(thishost, ".")] = '\0';
 	snprintf(portstr, sizeof(portstr), "%d", options.port);
+	snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid);
 
 	if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
 	    ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
@@ -1164,6 +1226,7 @@
 		    "p", portstr,
 		    "r", options.user,
 		    "u", pw->pw_name,
+		    "i", uidstr,
 		    (char *)NULL);
 		free(cp);
 	}
@@ -1176,14 +1239,23 @@
 
 	if (muxclient_command != 0 && options.control_path == NULL)
 		fatal("No ControlPath specified for \"-O\" command");
-	if (options.control_path != NULL)
-		muxclient(options.control_path);
+	if (options.control_path != NULL) {
+		int sock;
+		if ((sock = muxclient(options.control_path)) >= 0) {
+			packet_set_connection(sock, sock);
+			ssh = active_state; /* XXX */
+			enable_compat20();	/* XXX */
+			packet_set_mux();
+			goto skip_connect;
+		}
+	}
 
 	/*
 	 * If hostname canonicalisation was not enabled, then we may not
 	 * have yet resolved the hostname. Do so now.
 	 */
 	if (addrs == NULL && options.proxy_command == NULL) {
+		debug2("resolving \"%s\" port %d", host, options.port);
 		if ((addrs = resolve_host(host, options.port, 1,
 		    cname, sizeof(cname))) == NULL)
 			cleanup_exit(255); /* resolve_host logs the error */
@@ -1204,6 +1276,8 @@
 	packet_set_timeout(options.server_alive_interval,
 	    options.server_alive_count_max);
 
+	ssh = active_state; /* XXX */
+
 	if (timeout_ms > 0)
 		debug3("timeout: %d ms remain after connect", timeout_ms);
 
@@ -1227,8 +1301,10 @@
 			sensitive_data.keys[i] = NULL;
 
 		PRIV_START;
+#if WITH_SSH1
 		sensitive_data.keys[0] = key_load_private_type(KEY_RSA1,
 		    _PATH_HOST_KEY_FILE, "", NULL, NULL);
+#endif
 #ifdef OPENSSL_HAS_ECC
 		sensitive_data.keys[1] = key_load_private_cert(KEY_ECDSA,
 		    _PATH_HOST_ECDSA_KEY_FILE, "", NULL);
@@ -1314,6 +1390,23 @@
 	/* load options.identity_files */
 	load_public_identity_files();
 
+	/* optionally set the SSH_AUTHSOCKET_ENV_NAME varibale */
+	if (options.identity_agent &&
+	    strcmp(options.identity_agent, SSH_AUTHSOCKET_ENV_NAME) != 0) {
+		if (strcmp(options.identity_agent, "none") == 0) {
+			unsetenv(SSH_AUTHSOCKET_ENV_NAME);
+		} else {
+			p = tilde_expand_filename(options.identity_agent,
+			    original_real_uid);
+			cp = percent_expand(p, "d", pw->pw_dir,
+			    "u", pw->pw_name, "l", thishost, "h", host,
+			    "r", options.user, (char *)NULL);
+			setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1);
+			free(cp);
+			free(p);
+		}
+	}
+
 	/* Expand ~ in known host file names. */
 	tilde_expand_paths(options.system_hostfiles,
 	    options.num_system_hostfiles);
@@ -1328,7 +1421,7 @@
 
 	if (packet_connection_is_on_socket()) {
 		verbose("Authenticated to %s ([%s]:%d).", host,
-		    get_remote_ipaddr(), get_remote_port());
+		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
 	} else {
 		verbose("Authenticated to %s (via proxy).", host);
 	}
@@ -1353,7 +1446,12 @@
 			options.identity_keys[i] = NULL;
 		}
 	}
+	for (i = 0; i < options.num_certificate_files; i++) {
+		free(options.certificate_files[i]);
+		options.certificate_files[i] = NULL;
+	}
 
+ skip_connect:
 	exit_status = compat20 ? ssh_session2() : ssh_session();
 	packet_close();
 
@@ -1370,7 +1468,7 @@
 control_persist_detach(void)
 {
 	pid_t pid;
-	int devnull;
+	int devnull, keep_stderr;
 
 	debug("%s: backgrounding master process", __func__);
 
@@ -1401,8 +1499,10 @@
 		error("%s: open(\"/dev/null\"): %s", __func__,
 		    strerror(errno));
 	} else {
+		keep_stderr = log_is_on_stderr() && debug_flag;
 		if (dup2(devnull, STDIN_FILENO) == -1 ||
-		    dup2(devnull, STDOUT_FILENO) == -1)
+		    dup2(devnull, STDOUT_FILENO) == -1 ||
+		    (!keep_stderr && dup2(devnull, STDERR_FILENO) == -1))
 			error("%s: dup2: %s", __func__, strerror(errno));
 		if (devnull > STDERR_FILENO)
 			close(devnull);
@@ -1494,18 +1594,19 @@
 	Channel *c;
 	int in, out;
 
-	if (stdio_forward_host == NULL)
+	if (options.stdio_forward_host == NULL)
 		return;
 	if (!compat20)
 		fatal("stdio forwarding require Protocol 2");
 
-	debug3("%s: %s:%d", __func__, stdio_forward_host, stdio_forward_port);
+	debug3("%s: %s:%d", __func__, options.stdio_forward_host,
+	    options.stdio_forward_port);
 
 	if ((in = dup(STDIN_FILENO)) < 0 ||
 	    (out = dup(STDOUT_FILENO)) < 0)
 		fatal("channel_connect_stdio_fwd: dup() in/out failed");
-	if ((c = channel_connect_stdio_fwd(stdio_forward_host,
-	    stdio_forward_port, in, out)) == NULL)
+	if ((c = channel_connect_stdio_fwd(options.stdio_forward_host,
+	    options.stdio_forward_port, in, out)) == NULL)
 		fatal("%s: channel_connect_stdio_fwd failed", __func__);
 	channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0);
 	channel_register_open_confirm(c->self, ssh_stdio_confirm, NULL);
@@ -1604,6 +1705,7 @@
 	struct winsize ws;
 	char *cp;
 	const char *display;
+	char *proto = NULL, *data = NULL;
 
 	/* Enable compression if requested. */
 	if (options.compression) {
@@ -1674,13 +1776,9 @@
 	display = getenv("DISPLAY");
 	if (display == NULL && options.forward_x11)
 		debug("X11 forwarding requested but DISPLAY not set");
-	if (options.forward_x11 && display != NULL) {
-		char *proto, *data;
-		/* Get reasonable local authentication information. */
-		client_x11_get_proto(display, options.xauth_location,
-		    options.forward_x11_trusted,
-		    options.forward_x11_timeout,
-		    &proto, &data);
+	if (options.forward_x11 && client_x11_get_proto(display,
+	    options.xauth_location, options.forward_x11_trusted,
+	    options.forward_x11_timeout, &proto, &data) == 0) {
 		/* Request forwarding with authentication spoofing. */
 		debug("Requesting X11 forwarding with authentication "
 		    "spoofing.");
@@ -1770,6 +1868,7 @@
 	extern char **environ;
 	const char *display;
 	int interactive = tty_flag;
+	char *proto = NULL, *data = NULL;
 
 	if (!success)
 		return; /* No need for error message, channels code sens one */
@@ -1777,12 +1876,9 @@
 	display = getenv("DISPLAY");
 	if (display == NULL && options.forward_x11)
 		debug("X11 forwarding requested but DISPLAY not set");
-	if (options.forward_x11 && display != NULL) {
-		char *proto, *data;
-		/* Get reasonable local authentication information. */
-		client_x11_get_proto(display, options.xauth_location,
-		    options.forward_x11_trusted,
-		    options.forward_x11_timeout, &proto, &data);
+	if (options.forward_x11 && client_x11_get_proto(display,
+	    options.xauth_location, options.forward_x11_trusted,
+	    options.forward_x11_timeout, &proto, &data) == 0) {
 		/* Request forwarding with authentication spoofing. */
 		debug("Requesting X11 forwarding with authentication "
 		    "spoofing.");
@@ -1866,7 +1962,8 @@
 	ssh_init_forwarding();
 
 	/* Start listening for multiplex clients */
-	muxserver_listen();
+	if (!packet_get_mux())
+		muxserver_listen();
 
  	/*
 	 * If we are in control persist mode and have a working mux listen
@@ -1936,25 +2033,30 @@
 	    options.escape_char : SSH_ESCAPECHAR_NONE, id);
 }
 
+/* Loads all IdentityFile and CertificateFile keys */
 static void
 load_public_identity_files(void)
 {
 	char *filename, *cp, thishost[NI_MAXHOST];
 	char *pwdir = NULL, *pwname = NULL;
-	int i = 0;
 	Key *public;
 	struct passwd *pw;
-	u_int n_ids;
+	int i;
+	u_int n_ids, n_certs;
 	char *identity_files[SSH_MAX_IDENTITY_FILES];
 	Key *identity_keys[SSH_MAX_IDENTITY_FILES];
+	char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
+	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
 #ifdef ENABLE_PKCS11
 	Key **keys;
 	int nkeys;
 #endif /* PKCS11 */
 
-	n_ids = 0;
+	n_ids = n_certs = 0;
 	memset(identity_files, 0, sizeof(identity_files));
 	memset(identity_keys, 0, sizeof(identity_keys));
+	memset(certificate_files, 0, sizeof(certificate_files));
+	memset(certificates, 0, sizeof(certificates));
 
 #ifdef ENABLE_PKCS11
 	if (options.pkcs11_provider != NULL &&
@@ -1986,6 +2088,7 @@
 		if (n_ids >= SSH_MAX_IDENTITY_FILES ||
 		    strcasecmp(options.identity_files[i], "none") == 0) {
 			free(options.identity_files[i]);
+			options.identity_files[i] = NULL;
 			continue;
 		}
 		cp = tilde_expand_filename(options.identity_files[i],
@@ -2004,7 +2107,12 @@
 		if (++n_ids >= SSH_MAX_IDENTITY_FILES)
 			continue;
 
-		/* Try to add the certificate variant too */
+		/*
+		 * If no certificates have been explicitly listed then try
+		 * to add the default certificate variant too.
+		 */
+		if (options.num_certificate_files != 0)
+			continue;
 		xasprintf(&cp, "%s-cert", filename);
 		public = key_load_public(cp, NULL);
 		debug("identity file %s type %d", cp,
@@ -2020,15 +2128,52 @@
 			free(cp);
 			continue;
 		}
-		identity_keys[n_ids] = public;
-		/* point to the original path, most likely the private key */
+		/* NB. leave filename pointing to private key */
 		identity_files[n_ids] = xstrdup(filename);
+		identity_keys[n_ids] = public;
 		n_ids++;
 	}
+
+	if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES)
+		fatal("%s: too many certificates", __func__);
+	for (i = 0; i < options.num_certificate_files; i++) {
+		cp = tilde_expand_filename(options.certificate_files[i],
+		    original_real_uid);
+		filename = percent_expand(cp, "d", pwdir,
+		    "u", pwname, "l", thishost, "h", host,
+		    "r", options.user, (char *)NULL);
+		free(cp);
+
+		public = key_load_public(filename, NULL);
+		debug("certificate file %s type %d", filename,
+		    public ? public->type : -1);
+		free(options.certificate_files[i]);
+		options.certificate_files[i] = NULL;
+		if (public == NULL) {
+			free(filename);
+			continue;
+		}
+		if (!key_is_cert(public)) {
+			debug("%s: key %s type %s is not a certificate",
+			    __func__, filename, key_type(public));
+			key_free(public);
+			free(filename);
+			continue;
+		}
+		certificate_files[n_certs] = filename;
+		certificates[n_certs] = public;
+		++n_certs;
+	}
+
 	options.num_identity_files = n_ids;
 	memcpy(options.identity_files, identity_files, sizeof(identity_files));
 	memcpy(options.identity_keys, identity_keys, sizeof(identity_keys));
 
+	options.num_certificate_files = n_certs;
+	memcpy(options.certificate_files,
+	    certificate_files, sizeof(certificate_files));
+	memcpy(options.certificates, certificates, sizeof(certificates));
+
 	explicit_bzero(pwname, strlen(pwname));
 	free(pwname);
 	explicit_bzero(pwdir, strlen(pwdir));