diff options
Diffstat (limited to 'ssh.c')
| -rw-r--r-- | ssh.c | 321 | 
1 files changed, 147 insertions, 174 deletions
| @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.475 2018/02/23 15:58:38 markus Exp $ */ +/* $OpenBSD: ssh.c,v 1.490 2018/07/27 05:34:42 dtucker Exp $ */  /*   * Author: Tatu Ylonen <ylo@cs.hut.fi>   * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -87,9 +87,9 @@  #include "cipher.h"  #include "digest.h"  #include "packet.h" -#include "buffer.h" +#include "sshbuf.h"  #include "channels.h" -#include "key.h" +#include "sshkey.h"  #include "authfd.h"  #include "authfile.h"  #include "pathnames.h" @@ -104,7 +104,6 @@  #include "sshpty.h"  #include "match.h"  #include "msg.h" -#include "uidswap.h"  #include "version.h"  #include "ssherr.h"  #include "myproposal.h" @@ -178,12 +177,8 @@ struct sockaddr_storage hostaddr;  /* Private host keys. */  Sensitive sensitive_data; -/* Original real UID. */ -uid_t original_real_uid; -uid_t original_effective_uid; -  /* command to be executed */ -Buffer command; +struct sshbuf *command;  /* Should we execute a command or invoke a subsystem? */  int subsystem_flag = 0; @@ -224,7 +219,7 @@ tilde_expand_paths(char **paths, u_int num_paths)  	char *cp;  	for (i = 0; i < num_paths; i++) { -		cp = tilde_expand_filename(paths[i], original_real_uid); +		cp = tilde_expand_filename(paths[i], getuid());  		free(paths[i]);  		paths[i] = cp;  	} @@ -504,6 +499,30 @@ resolve_canonicalize(char **hostp, int port)  }  /* + * Check the result of hostkey loading, ignoring some errors and + * fatal()ing for others. + */ +static void +check_load(int r, const char *path, const char *message) +{ +	switch (r) { +	case 0: +		break; +	case SSH_ERR_INTERNAL_ERROR: +	case SSH_ERR_ALLOC_FAIL: +		fatal("load %s \"%s\": %s", message, path, ssh_err(r)); +	case SSH_ERR_SYSTEM_ERROR: +		/* Ignore missing files */ +		if (errno == ENOENT) +			break; +		/* FALLTHROUGH */ +	default: +		error("load %s \"%s\": %s", message, path, ssh_err(r)); +		break; +	} +} + +/*   * Read per-user configuration file.  Ignore the system wide config   * file if the user specifies a config file on the command line.   */ @@ -597,35 +616,10 @@ main(int ac, char **av)  	 */  	closefrom(STDERR_FILENO + 1); -	/* -	 * Save the original real uid.  It will be needed later (uid-swapping -	 * may clobber the real uid). -	 */ -	original_real_uid = getuid(); -	original_effective_uid = geteuid(); - -	/* -	 * Use uid-swapping to give up root privileges for the duration of -	 * option processing.  We will re-instantiate the rights when we are -	 * ready to create the privileged port, and will permanently drop -	 * them when the port has been created (actually, when the connection -	 * has been made, as we may need to create the port several times). -	 */ -	PRIV_END; - -#ifdef HAVE_SETRLIMIT -	/* If we are installed setuid root be careful to not drop core. */ -	if (original_real_uid != original_effective_uid) { -		struct rlimit rlim; -		rlim.rlim_cur = rlim.rlim_max = 0; -		if (setrlimit(RLIMIT_CORE, &rlim) < 0) -			fatal("setrlimit failed: %.100s", strerror(errno)); -	} -#endif  	/* Get user data. */ -	pw = getpwuid(original_real_uid); +	pw = getpwuid(getuid());  	if (!pw) { -		logit("No user exists for uid %lu", (u_long)original_real_uid); +		logit("No user exists for uid %lu", (u_long)getuid());  		exit(255);  	}  	/* Take a copy of the returned structure. */ @@ -728,7 +722,6 @@ main(int ac, char **av)  				fatal("Invalid multiplex command.");  			break;  		case 'P':	/* deprecated */ -			options.use_privileged_port = 0;  			break;  		case 'Q':  			cp = NULL; @@ -769,7 +762,7 @@ main(int ac, char **av)  			options.gss_deleg_creds = 1;  			break;  		case 'i': -			p = tilde_expand_filename(optarg, original_real_uid); +			p = tilde_expand_filename(optarg, getuid());  			if (stat(p, &st) < 0)  				fprintf(stderr, "Warning: Identity file %s "  				    "not accessible: %s.\n", p, @@ -1042,7 +1035,8 @@ main(int ac, char **av)  #endif  	/* Initialize the command to execute on remote host. */ -	buffer_init(&command); +	if ((command = sshbuf_new()) == NULL) +		fatal("sshbuf_new failed");  	/*  	 * Save the command to execute on the remote host in a buffer. There @@ -1059,9 +1053,10 @@ main(int ac, char **av)  	} else {  		/* A command has been specified.  Store it into the buffer. */  		for (i = 0; i < ac; i++) { -			if (i) -				buffer_append(&command, " ", 1); -			buffer_append(&command, av[i], strlen(av[i])); +			if ((r = sshbuf_putf(command, "%s%s", +			    i ? " " : "", av[i])) != 0) +				fatal("%s: buffer error: %s", +				    __func__, ssh_err(r));  		}  	} @@ -1171,6 +1166,14 @@ main(int ac, char **av)  	 */  	if (options.jump_host != NULL) {  		char port_s[8]; +		const char *sshbin = argv0; + +		/* +		 * Try to use SSH indicated by argv[0], but fall back to +		 * "ssh" if it appears unavailable. +		 */ +		if (strchr(argv0, '/') != NULL && access(argv0, X_OK) != 0) +			sshbin = "ssh";  		/* Consistency check */  		if (options.proxy_command != NULL) @@ -1179,7 +1182,8 @@ main(int ac, char **av)  		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", +		    "%s%s%s%s%s%s%s%s%s%s%.*s -W '[%%h]:%%p' %s", +		    sshbin,  		    /* Optional "-l user" argument if jump_user set */  		    options.jump_user == NULL ? "" : " -l ",  		    options.jump_user == NULL ? "" : options.jump_user, @@ -1220,16 +1224,12 @@ main(int ac, char **av)  	}  	if (options.connection_attempts <= 0)  		fatal("Invalid number of ConnectionAttempts"); -#ifndef HAVE_CYGWIN -	if (original_effective_uid != 0) -		options.use_privileged_port = 0; -#endif -	if (buffer_len(&command) != 0 && options.remote_command != NULL) +	if (sshbuf_len(command) != 0 && options.remote_command != NULL)  		fatal("Cannot execute command-line and remote command.");  	/* Cannot fork to background if no command. */ -	if (fork_after_authentication_flag && buffer_len(&command) == 0 && +	if (fork_after_authentication_flag && sshbuf_len(command) == 0 &&  	    options.remote_command == NULL && !no_shell_flag)  		fatal("Cannot fork into background without a command "  		    "to execute."); @@ -1242,7 +1242,7 @@ main(int ac, char **av)  		tty_flag = 1;  	/* Allocate a tty by default if no command specified. */ -	if (buffer_len(&command) == 0 && options.remote_command == NULL) +	if (sshbuf_len(command) == 0 && options.remote_command == NULL)  		tty_flag = options.request_tty != REQUEST_TTY_NO;  	/* Force no tty */ @@ -1269,7 +1269,8 @@ main(int ac, char **av)  	strlcpy(shorthost, thishost, sizeof(shorthost));  	shorthost[strcspn(thishost, ".")] = '\0';  	snprintf(portstr, sizeof(portstr), "%d", options.port); -	snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid); +	snprintf(uidstr, sizeof(uidstr), "%llu", +	    (unsigned long long)pw->pw_uid);  	if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||  	    ssh_digest_update(md, thishost, strlen(thishost)) < 0 || @@ -1294,6 +1295,7 @@ main(int ac, char **av)  		    "L", shorthost,  		    "d", pw->pw_dir,  		    "h", host, +		    "i", uidstr,  		    "l", thishost,  		    "n", host_arg,  		    "p", portstr, @@ -1302,18 +1304,19 @@ main(int ac, char **av)  		    (char *)NULL);  		debug3("expanded RemoteCommand: %s", options.remote_command);  		free(cp); -		buffer_append(&command, options.remote_command, -		    strlen(options.remote_command)); +		if ((r = sshbuf_put(command, options.remote_command, +		    strlen(options.remote_command))) != 0) +			fatal("%s: buffer error: %s", __func__, ssh_err(r));  	}  	if (options.control_path != NULL) { -		cp = tilde_expand_filename(options.control_path, -		    original_real_uid); +		cp = tilde_expand_filename(options.control_path, getuid());  		free(options.control_path);  		options.control_path = percent_expand(cp,  		    "C", conn_hash_hex,  		    "L", shorthost,  		    "h", host, +		    "i", uidstr,  		    "l", thishost,  		    "n", host_arg,  		    "p", portstr, @@ -1323,7 +1326,6 @@ main(int ac, char **av)  		    (char *)NULL);  		free(cp);  	} -	free(conn_hash_hex);  	if (config_test) {  		dump_client_config(&options, host); @@ -1357,8 +1359,7 @@ main(int ac, char **av)  	/* Open a connection to the remote host. */  	if (ssh_connect(ssh, host, addrs, &hostaddr, options.port,  	    options.address_family, options.connection_attempts, -	    &timeout_ms, options.tcp_keep_alive, -	    options.use_privileged_port) != 0) +	    &timeout_ms, options.tcp_keep_alive) != 0)   		exit(255);  	if (addrs != NULL) @@ -1373,100 +1374,45 @@ main(int ac, char **av)  		debug3("timeout: %d ms remain after connect", timeout_ms);  	/* -	 * If we successfully made the connection, load the host private key -	 * in case we will need it later for combined rsa-rhosts -	 * authentication. This must be done before releasing extra -	 * privileges, because the file is only readable by root. -	 * If we cannot access the private keys, load the public keys -	 * instead and try to execute the ssh-keysign helper instead. +	 * If we successfully made the connection and we have hostbased auth +	 * enabled, load the public keys so we can later use the ssh-keysign +	 * helper to sign challenges.  	 */  	sensitive_data.nkeys = 0;  	sensitive_data.keys = NULL; -	sensitive_data.external_keysign = 0;  	if (options.hostbased_authentication) { -		sensitive_data.nkeys = 11; +		sensitive_data.nkeys = 10;  		sensitive_data.keys = xcalloc(sensitive_data.nkeys, -		    sizeof(struct sshkey));	/* XXX */ -		for (i = 0; i < sensitive_data.nkeys; i++) -			sensitive_data.keys[i] = NULL; - -		PRIV_START; -#ifdef OPENSSL_HAS_ECC -		sensitive_data.keys[1] = key_load_private_cert(KEY_ECDSA, -		    _PATH_HOST_ECDSA_KEY_FILE, "", NULL); -#endif -		sensitive_data.keys[2] = key_load_private_cert(KEY_ED25519, -		    _PATH_HOST_ED25519_KEY_FILE, "", NULL); -		sensitive_data.keys[3] = key_load_private_cert(KEY_RSA, -		    _PATH_HOST_RSA_KEY_FILE, "", NULL); -		sensitive_data.keys[4] = key_load_private_cert(KEY_DSA, -		    _PATH_HOST_DSA_KEY_FILE, "", NULL); -#ifdef OPENSSL_HAS_ECC -		sensitive_data.keys[5] = key_load_private_type(KEY_ECDSA, -		    _PATH_HOST_ECDSA_KEY_FILE, "", NULL, NULL); -#endif -		sensitive_data.keys[6] = key_load_private_type(KEY_ED25519, -		    _PATH_HOST_ED25519_KEY_FILE, "", NULL, NULL); -		sensitive_data.keys[7] = key_load_private_type(KEY_RSA, -		    _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); -		sensitive_data.keys[8] = key_load_private_type(KEY_DSA, -		    _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); -		sensitive_data.keys[9] = key_load_private_cert(KEY_XMSS, -		    _PATH_HOST_XMSS_KEY_FILE, "", NULL); -		sensitive_data.keys[10] = key_load_private_type(KEY_XMSS, -		    _PATH_HOST_XMSS_KEY_FILE, "", NULL, NULL); -		PRIV_END; - -		if (options.hostbased_authentication == 1 && -		    sensitive_data.keys[0] == NULL && -		    sensitive_data.keys[5] == NULL && -		    sensitive_data.keys[6] == NULL && -		    sensitive_data.keys[7] == NULL && -		    sensitive_data.keys[8] == NULL && -		    sensitive_data.keys[9] == NULL) { -#ifdef OPENSSL_HAS_ECC -			sensitive_data.keys[1] = key_load_cert( -			    _PATH_HOST_ECDSA_KEY_FILE); -#endif -			sensitive_data.keys[2] = key_load_cert( -			    _PATH_HOST_ED25519_KEY_FILE); -			sensitive_data.keys[3] = key_load_cert( -			    _PATH_HOST_RSA_KEY_FILE); -			sensitive_data.keys[4] = key_load_cert( -			    _PATH_HOST_DSA_KEY_FILE); -#ifdef OPENSSL_HAS_ECC -			sensitive_data.keys[5] = key_load_public( -			    _PATH_HOST_ECDSA_KEY_FILE, NULL); -#endif -			sensitive_data.keys[6] = key_load_public( -			    _PATH_HOST_ED25519_KEY_FILE, NULL); -			sensitive_data.keys[7] = key_load_public( -			    _PATH_HOST_RSA_KEY_FILE, NULL); -			sensitive_data.keys[8] = key_load_public( -			    _PATH_HOST_DSA_KEY_FILE, NULL); -			sensitive_data.keys[9] = key_load_cert( -			    _PATH_HOST_XMSS_KEY_FILE); -			sensitive_data.keys[10] = key_load_public( -			    _PATH_HOST_XMSS_KEY_FILE, NULL); -			sensitive_data.external_keysign = 1; +		    sizeof(struct sshkey)); + +		/* XXX check errors? */ +#define L_PUBKEY(p,o) do { \ +	if ((o) >= sensitive_data.nkeys) \ +		fatal("%s pubkey out of array bounds", __func__); \ +	check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \ +	    p, "pubkey"); \ +} while (0) +#define L_CERT(p,o) do { \ +	if ((o) >= sensitive_data.nkeys) \ +		fatal("%s cert out of array bounds", __func__); \ +	check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), p, "cert"); \ +} while (0) + +		if (options.hostbased_authentication == 1) { +			L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0); +			L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1); +			L_CERT(_PATH_HOST_RSA_KEY_FILE, 2); +			L_CERT(_PATH_HOST_DSA_KEY_FILE, 3); +			L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4); +			L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5); +			L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6); +			L_PUBKEY(_PATH_HOST_DSA_KEY_FILE, 7); +			L_CERT(_PATH_HOST_XMSS_KEY_FILE, 8); +			L_PUBKEY(_PATH_HOST_XMSS_KEY_FILE, 9);  		}  	} -	/* -	 * Get rid of any extra privileges that we may have.  We will no -	 * longer need them.  Also, extra privileges could make it very hard -	 * to read identity files and other non-world-readable files from the -	 * user's home directory if it happens to be on a NFS volume where -	 * root is mapped to nobody. -	 */ -	if (original_effective_uid == 0) { -		PRIV_START; -		permanently_set_uid(pw); -	} -	/* -	 * Now that we are back to our own permissions, create ~/.ssh -	 * directory if it doesn't already exist. -	 */ +	/* Create ~/.ssh * directory if it doesn't already exist. */  	if (config == NULL) {  		r = snprintf(buf, sizeof buf, "%s%s%s", pw->pw_dir,  		    strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); @@ -1485,17 +1431,22 @@ main(int ac, char **av)  	/* load options.identity_files */  	load_public_identity_files(pw); -	/* optionally set the SSH_AUTHSOCKET_ENV_NAME varibale */ +	/* optionally set the SSH_AUTHSOCKET_ENV_NAME variable */  	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); +			    getuid()); +			cp = percent_expand(p, +			    "d", pw->pw_dir, +			    "h", host, +			    "i", uidstr, +			    "l", thishost, +			    "r", options.user, +			    "u", pw->pw_name, +			    (char *)NULL);  			setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1);  			free(cp);  			free(p); @@ -1527,7 +1478,7 @@ main(int ac, char **av)  			if (sensitive_data.keys[i] != NULL) {  				/* Destroys contents safely */  				debug3("clear hostkey %d", i); -				key_free(sensitive_data.keys[i]); +				sshkey_free(sensitive_data.keys[i]);  				sensitive_data.keys[i] = NULL;  			}  		} @@ -1537,7 +1488,7 @@ main(int ac, char **av)  		free(options.identity_files[i]);  		options.identity_files[i] = NULL;  		if (options.identity_keys[i]) { -			key_free(options.identity_keys[i]); +			sshkey_free(options.identity_keys[i]);  			options.identity_keys[i] = NULL;  		}  	} @@ -1638,10 +1589,10 @@ ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt)  			logit("Allocated port %u for remote forward to %s:%d",  			    rfwd->allocated_port,  			    rfwd->connect_host, rfwd->connect_port); -			channel_update_permitted_opens(ssh, +			channel_update_permission(ssh,  			    rfwd->handle, rfwd->allocated_port);  		} else { -			channel_update_permitted_opens(ssh, rfwd->handle, -1); +			channel_update_permission(ssh, rfwd->handle, -1);  		}  	} @@ -1830,7 +1781,7 @@ ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg)  	    options.ip_qos_interactive, options.ip_qos_bulk);  	client_session2_setup(ssh, id, tty_flag, subsystem_flag, getenv("TERM"), -	    NULL, fileno(stdin), &command, environ); +	    NULL, fileno(stdin), command, environ);  }  /* open new channel for a session */ @@ -1900,6 +1851,7 @@ ssh_session2(struct ssh *ssh, struct passwd *pw)  		    "L", shorthost,  		    "d", pw->pw_dir,  		    "h", host, +		    "i", uidstr,  		    "l", thishost,  		    "n", host_arg,  		    "p", portstr, @@ -2009,8 +1961,10 @@ load_public_identity_files(struct passwd *pw)  	u_int n_ids, n_certs;  	char *identity_files[SSH_MAX_IDENTITY_FILES];  	struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; +	int identity_file_userprovided[SSH_MAX_IDENTITY_FILES];  	char *certificate_files[SSH_MAX_CERTIFICATE_FILES];  	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; +	int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];  #ifdef ENABLE_PKCS11  	struct sshkey **keys;  	int nkeys; @@ -2019,8 +1973,12 @@ load_public_identity_files(struct passwd *pw)  	n_ids = n_certs = 0;  	memset(identity_files, 0, sizeof(identity_files));  	memset(identity_keys, 0, sizeof(identity_keys)); +	memset(identity_file_userprovided, 0, +	    sizeof(identity_file_userprovided));  	memset(certificate_files, 0, sizeof(certificate_files));  	memset(certificates, 0, sizeof(certificates)); +	memset(certificate_file_userprovided, 0, +	    sizeof(certificate_file_userprovided));  #ifdef ENABLE_PKCS11  	if (options.pkcs11_provider != NULL && @@ -2030,7 +1988,7 @@ load_public_identity_files(struct passwd *pw)  	    &keys)) > 0) {  		for (i = 0; i < nkeys; i++) {  			if (n_ids >= SSH_MAX_IDENTITY_FILES) { -				key_free(keys[i]); +				sshkey_free(keys[i]);  				continue;  			}  			identity_keys[n_ids] = keys[i]; @@ -2041,8 +1999,6 @@ load_public_identity_files(struct passwd *pw)  		free(keys);  	}  #endif /* ENABLE_PKCS11 */ -	if ((pw = getpwuid(original_real_uid)) == NULL) -		fatal("load_public_identity_files: getpwuid failed");  	for (i = 0; i < options.num_identity_files; i++) {  		if (n_ids >= SSH_MAX_IDENTITY_FILES ||  		    strcasecmp(options.identity_files[i], "none") == 0) { @@ -2050,19 +2006,20 @@ load_public_identity_files(struct passwd *pw)  			options.identity_files[i] = NULL;  			continue;  		} -		cp = tilde_expand_filename(options.identity_files[i], -		    original_real_uid); +		cp = tilde_expand_filename(options.identity_files[i], getuid());  		filename = percent_expand(cp, "d", pw->pw_dir,  		    "u", pw->pw_name, "l", thishost, "h", host,  		    "r", options.user, (char *)NULL);  		free(cp); -		public = key_load_public(filename, NULL); +		check_load(sshkey_load_public(filename, &public, NULL), +		    filename, "pubkey");  		debug("identity file %s type %d", filename,  		    public ? public->type : -1);  		free(options.identity_files[i]);  		identity_files[n_ids] = filename;  		identity_keys[n_ids] = public; - +		identity_file_userprovided[n_ids] = +		    options.identity_file_userprovided[i];  		if (++n_ids >= SSH_MAX_IDENTITY_FILES)  			continue; @@ -2073,23 +2030,26 @@ load_public_identity_files(struct passwd *pw)  		if (options.num_certificate_files != 0)  			continue;  		xasprintf(&cp, "%s-cert", filename); -		public = key_load_public(cp, NULL); +		check_load(sshkey_load_public(cp, &public, NULL), +		    filename, "pubkey");  		debug("identity file %s type %d", cp,  		    public ? public->type : -1);  		if (public == NULL) {  			free(cp);  			continue;  		} -		if (!key_is_cert(public)) { +		if (!sshkey_is_cert(public)) {  			debug("%s: key %s type %s is not a certificate", -			    __func__, cp, key_type(public)); -			key_free(public); +			    __func__, cp, sshkey_type(public)); +			sshkey_free(public);  			free(cp);  			continue;  		}  		/* NB. leave filename pointing to private key */  		identity_files[n_ids] = xstrdup(filename);  		identity_keys[n_ids] = public; +		identity_file_userprovided[n_ids] = +		    options.identity_file_userprovided[i];  		n_ids++;  	} @@ -2097,13 +2057,19 @@ load_public_identity_files(struct passwd *pw)  		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", pw->pw_dir, -		    "u", pw->pw_name, "l", thishost, "h", host, -		    "r", options.user, (char *)NULL); +		    getuid()); +		filename = percent_expand(cp, +		    "d", pw->pw_dir, +		    "h", host, +		    "i", uidstr, +		    "l", thishost, +		    "r", options.user, +		    "u", pw->pw_name, +		    (char *)NULL);  		free(cp); -		public = key_load_public(filename, NULL); +		check_load(sshkey_load_public(filename, &public, NULL), +		    filename, "certificate");  		debug("certificate file %s type %d", filename,  		    public ? public->type : -1);  		free(options.certificate_files[i]); @@ -2112,26 +2078,33 @@ load_public_identity_files(struct passwd *pw)  			free(filename);  			continue;  		} -		if (!key_is_cert(public)) { +		if (!sshkey_is_cert(public)) {  			debug("%s: key %s type %s is not a certificate", -			    __func__, filename, key_type(public)); -			key_free(public); +			    __func__, filename, sshkey_type(public)); +			sshkey_free(public);  			free(filename);  			continue;  		}  		certificate_files[n_certs] = filename;  		certificates[n_certs] = public; +		certificate_file_userprovided[n_certs] = +		    options.certificate_file_userprovided[i];  		++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)); +	memcpy(options.identity_file_userprovided, +	    identity_file_userprovided, sizeof(identity_file_userprovided));  	options.num_certificate_files = n_certs;  	memcpy(options.certificate_files,  	    certificate_files, sizeof(certificate_files));  	memcpy(options.certificates, certificates, sizeof(certificates)); +	memcpy(options.certificate_file_userprovided, +	    certificate_file_userprovided, +	    sizeof(certificate_file_userprovided));  }  static void | 
