[PATCH] ssh: Add option to present certificates on command line

Damien Miller djm at mindrot.org
Thu Jul 30 10:53:24 AEST 2015


Hi,

Thanks for this. Could I ask you to create a bug at
https://bugzilla.mindrot.org/ and attach your patch there? We're pretty
much closed for the 7.0 release ATM but we'll look at it once we're done.

I guess something similar for ssh-add would make sense too...

-d

On Wed, 29 Jul 2015, Meghana Bhat wrote:

> Allow users to specify certificates to be used for authentication on
> the command line with the '-z' argument when running ssh. For
> successful authentication, the key pair associated with the certificate
> must also be presented during the ssh.
> 
> Certificates may also be specified in ssh_config as a
> CertificateFile.
> 
> This option is meant the address the issue mentioned in the following
> exchange:
> http://lists.mindrot.org/pipermail/openssh-unix-dev/2013-September/031629.html
> 
> Patch developed against 6.9p.
> 
> ---
>  readconf.c          |  48 +++++++++++++++++++
>  readconf.h          |   6 +++
>  regress/Makefile    |   1 +
>  regress/ssh-cert.sh | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  ssh.1               |  17 +++++++
>  ssh.c               |  85 +++++++++++++++++++++++++++++++-
>  ssh.h               |   7 +++
>  ssh_config.5        |  33 +++++++++++++
>  sshconnect2.c       |  47 ++++++++++++++++--
>  9 files changed, 375 insertions(+), 5 deletions(-)
>  create mode 100644 regress/ssh-cert.sh
> 
> diff --git a/readconf.c b/readconf.c
> index f1c860b..b34213d 100644
> --- a/readconf.c
> +++ b/readconf.c
> @@ -135,6 +135,7 @@ typedef enum {
>  	oPasswordAuthentication, oRSAAuthentication,
>  	oChallengeResponseAuthentication, oXAuthLocation,
>  	oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
> +	oCertificateFile,
>  	oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
>  	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
>  	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
> @@ -202,6 +203,7 @@ static struct {
>  	{ "identityfile", oIdentityFile },
>  	{ "identityfile2", oIdentityFile },			/* obsolete */
>  	{ "identitiesonly", oIdentitiesOnly },
> +	{ "certificatefile", oCertificateFile },
>  	{ "hostname", oHostName },
>  	{ "hostkeyalias", oHostKeyAlias },
>  	{ "proxycommand", oProxyCommand },
> @@ -366,6 +368,37 @@ clear_forwardings(Options *options)
>  }
>  
>  void
> +add_certificate_file(Options *options, const char *dir, const char *filename,
> +    int userprovided)
> +{
> +	char *path;
> +	int i;
> +
> +	if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
> +		fatal("Too many certificate files specified (max %d)",
> +		    SSH_MAX_CERTIFICATE_FILES);
> +
> +	if (dir == NULL) /* no dir, filename is absolute */
> +		path = xstrdup(filename);
> +	else
> +		(void)xasprintf(&path, "%.100s%.100s", dir, filename);
> +
> +	/* Avoid registering duplicates */
> +	for (i = 0; i < options->num_certificate_files; i++) {
> +		if (options->certificate_file_userprovided[i] == userprovided &&
> +		    strcmp(options->certificate_files[i], path) == 0) {
> +			debug2("%s: ignoring duplicate key %s", __func__, path);
> +			free(path);
> +			return;
> +		}
> +	}
> +
> +	options->certificate_file_userprovided[options->num_certificate_files] =
> +	    userprovided;
> +	options->certificate_files[options->num_certificate_files++] = path;
> +}
> +
> +void
>  add_identity_file(Options *options, const char *dir, const char *filename,
>      int userprovided)
>  {
> @@ -981,6 +1014,20 @@ parse_time:
>  		}
>  		break;
>  
> +	case oCertificateFile:
> +		arg = strdelim(&s);
> +		if (!arg || *arg == '\0')
> +			fatal("%.200s line %d: Missing argument.", filename, linenum);
> +		if (*activep) {
> +			intptr = &options->num_certificate_files;
> +			if (*intptr >= SSH_MAX_CERTIFICATE_FILES)
> +				fatal("%.200s line %d: Too many identity files specified (max %d).",
> +				    filename, linenum, SSH_MAX_CERTIFICATE_FILES);
> +			add_certificate_file(options, NULL,
> +			    arg, flags & SSHCONF_USERCONF);
> +		}
> +		break;
> +
>  	case oXAuthLocation:
>  		charptr=&options->xauth_location;
>  		goto parse_string;
> @@ -1625,6 +1672,7 @@ initialize_options(Options * options)
>  	options->hostkeyalgorithms = NULL;
>  	options->protocol = SSH_PROTO_UNKNOWN;
>  	options->num_identity_files = 0;
> +	options->num_certificate_files = 0; 
>  	options->hostname = NULL;
>  	options->host_key_alias = NULL;
>  	options->proxy_command = NULL;
> diff --git a/readconf.h b/readconf.h
> index bb2d552..f839016 100644
> --- a/readconf.h
> +++ b/readconf.h
> @@ -94,6 +94,11 @@ typedef struct {
>  	char   *identity_files[SSH_MAX_IDENTITY_FILES];
>  	int    identity_file_userprovided[SSH_MAX_IDENTITY_FILES];
>  	struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES];
> +	
> +	int	num_certificate_files; /* Number of extra certificates for ssh. */
> +	char	*certificate_files[SSH_MAX_CERTIFICATE_FILES];
> +	int	certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
> +	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
>  
>  	/* Local TCP/IP forward requests. */
>  	int     num_local_forwards;
> @@ -194,5 +199,6 @@ void	 dump_client_config(Options *o, const char *host);
>  void	 add_local_forward(Options *, const struct Forward *);
>  void	 add_remote_forward(Options *, const struct Forward *);
>  void	 add_identity_file(Options *, const char *, const char *, int);
> +void	 add_certificate_file(Options *, const char *, const char *, int);
>  
>  #endif				/* READCONF_H */
> diff --git a/regress/Makefile b/regress/Makefile
> index cba83f4..67455a8 100644
> --- a/regress/Makefile
> +++ b/regress/Makefile
> @@ -74,6 +74,7 @@ LTESTS= 	connect \
>  		hostkey-agent \
>  		keygen-knownhosts \
>  		hostkey-rotate \
> +		ssh-cert \
>  		principals-command
>  
>  
> diff --git a/regress/ssh-cert.sh b/regress/ssh-cert.sh
> new file mode 100644
> index 0000000..152278b
> --- /dev/null
> +++ b/regress/ssh-cert.sh
> @@ -0,0 +1,136 @@
> +#	$OpenBSD: multicert.sh,v 1.1 2014/12/22 08:06:03 djm Exp $
> +#	Placed in the Public Domain.
> +
> +tid="ssh with certificates"
> +
> +rm -f $OBJ/user_ca_key* $OBJ/user_key*
> +rm -f $OBJ/cert_user_key*
> +
> +# Create a CA key
> +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key1 ||\
> +	fatal "ssh-keygen failed"
> +${SSHKEYGEN} -q -N '' -t ed25519  -f $OBJ/user_ca_key2 ||\
> +	fatal "ssh-keygen failed"
> +
> +# Make some keys and certificates.
> +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key1 || \
> +	fatal "ssh-keygen failed"
> +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key2 || \
> +	fatal "ssh-keygen failed"
> +# Move the certificate to a different address to better control
> +# when it is offered.
> +${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \
> +	-z $$ -n ${USER} $OBJ/user_key1 ||
> +		fail "couldn't sign user_key1 with user_ca_key1"
> +mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1_1.pub
> +${SSHKEYGEN} -q -s $OBJ/user_ca_key2 -I "regress user key for $USER" \
> +	-z $$ -n ${USER} $OBJ/user_key1 ||
> +		fail "couldn't sign user_key1 with user_ca_key2"
> +mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1_2.pub
> +
> +trace 'try with identity files'
> +opts="-F $OBJ/ssh_proxy -oIdentitiesOnly=yes"
> +opts2="$opts -i $OBJ/user_key1 -i $OBJ/user_key2"
> +echo "cert-authority $(cat $OBJ/user_ca_key1.pub)" > $OBJ/authorized_keys_$USER
> +
> +for p in ${SSH_PROTOCOLS}; do
> +	# Just keys should fail
> +	${SSH} $opts2 somehost exit 5$p
> +	r=$?
> +	if [ $r -eq 5$p ]; then
> +		fail "ssh succeeded with no certs in protocol $p"
> +	fi
> +
> +	# Keys with untrusted cert should fail.
> +	opts3="$opts2 -z $OBJ/cert_user_key1_2.pub"
> +	${SSH} $opts3 somehost exit 5$p
> +	r=$?
> +	if [ $r -eq 5$p ]; then
> +		fail "ssh succeeded with bad cert in protocol $p"
> +	fi
> +
> +	# Good cert with bad key should fail.
> +	opts3="$opts -i $OBJ/user_key2 -z $OBJ/cert_user_key1_1.pub"
> +	${SSH} $opts3 somehost exit 5$p
> +	r=$?
> +	if [ $r -eq 5$p ]; then
> +		fail "ssh succeeded with no matching key in protocol $p"
> +	fi
> +
> +	# Keys with one trusted cert, should succeed.
> +	opts3="$opts2 -z $OBJ/cert_user_key1_1.pub"
> +	${SSH} $opts3 somehost exit 5$p
> +	r=$?
> +	if [ $r -ne 5$p ]; then
> +		fail "ssh failed with trusted cert and key in protocol $p"
> +	fi
> +
> +	# Multiple certs and keys, with one trusted cert, should succeed.
> +	opts3="$opts2 -z $OBJ/cert_user_key1_2.pub -z $OBJ/cert_user_key1_1.pub"
> +	${SSH} $opts3 somehost exit 5$p
> +	r=$?
> +	if [ $r -ne 5$p ]; then
> +		fail "ssh failed with multiple certs in protocol $p"
> +	fi
> +
> +	#Keys with trusted certificate specified in config options, should succeed.
> +	opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_1.pub"
> +	${SSH} $opts3 somehost exit 5$p
> +	r=$?
> +	if [ $r -ne 5$p ]; then
> +		fail "ssh failed with trusted cert in config in protocol $p"
> +	fi
> +done
> +
> +#next, using an agent in combination with the keys
> +SSH_AUTH_SOCK=/nonexistent ${SSHADD} -l > /dev/null 2>&1
> +if [ $? -ne 2 ]; then
> +	fatal "ssh-add -l did not fail with exit code 2"
> +fi
> +
> +trace "start agent"
> +eval `${SSHAGENT} -s` > /dev/null
> +r=$?
> +if [ $r -ne 0 ]; then
> +	fatal "could not start ssh-agent: exit code $r"
> +fi
> +
> +# add private keys to agent
> +${SSHADD} -k $OBJ/user_key2 > /dev/null 2>&1
> +if [ $? -ne 0 ]; then
> +	fatal "ssh-add did not succeed with exit code 0"
> +fi
> +${SSHADD} -k $OBJ/user_key1 > /dev/null 2>&1
> +if [ $? -ne 0 ]; then
> +	fatal "ssh-add did not succeed with exit code 0"
> +fi
> +
> +# try ssh with the agent and certificates
> +# note: ssh agent only uses certificates in protocol 2
> +opts="-F $OBJ/ssh_proxy"
> +# with no certificates, shoud fail
> +${SSH} -2 $opts somehost exit 52
> +if [ $? -eq 52 ]; then
> +	fail "ssh connect with agent in protocol 2 succeeded with no cert"
> +fi
> +
> +#with an untrusted certificate, should fail
> +opts="$opts -z $OBJ/cert_user_key1_2.pub"
> +${SSH} -2 $opts somehost exit 52
> +if [ $? -eq 52 ]; then
> +	fail "ssh connect with agent in protocol 2 succeeded with bad cert"
> +fi
> +
> +#with an additional trusted certificate, should succeed
> +opts="$opts -z $OBJ/cert_user_key1_1.pub"
> +${SSH} -2 $opts somehost exit 52
> +if [ $? -ne 52 ]; then
> +	fail "ssh connect with agent in protocol 2 failed with good cert"
> +fi
> +
> +trace "kill agent"
> +${SSHAGENT} -k > /dev/null
> +
> +#cleanup
> +rm -f $OBJ/user_ca_key* $OBJ/user_key*
> +rm -f $OBJ/cert_user_key*
> diff --git a/ssh.1 b/ssh.1
> index 2ea0a20..76a9459 100644
> --- a/ssh.1
> +++ b/ssh.1
> @@ -63,6 +63,7 @@
>  .Op Fl S Ar ctl_path
>  .Op Fl W Ar host : Ns Ar port
>  .Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun
> +.Op Fl z Ar certificate_file
>  .Oo Ar user Ns @ Oc Ns Ar hostname
>  .Op Ar command
>  .Ek
> @@ -468,6 +469,7 @@ For full details of the options listed below, and their possible values, see
>  .It CanonicalizeHostname
>  .It CanonicalizeMaxDots
>  .It CanonicalizePermittedCNAMEs
> +.It CertificateFile
>  .It ChallengeResponseAuthentication
>  .It CheckHostIP
>  .It Cipher
> @@ -768,6 +770,21 @@ Send log information using the
>  .Xr syslog 3
>  system module.
>  By default this information is sent to stderr.
> +.It Fl z Ar certificate_file
> +Selects a file from which certificate information is loaded for public
> +key authentication. For the certificate to be signed, the private key
> +corresponding to 
> +.Ar certificate_file
> +must also be provided for authentication, whether through
> +.Xr ssh_agent 1 .
> +or through an
> +.Ar identity_file
> +specified on the command line or in configuration files.
> +Certificate files may also be specified on a per-host basis in
> +the configuration file. It is possible to have multiple
> +.Fl z
> +options (and multiple certificates specified in
> +configuration files).
>  .El
>  .Pp
>  .Nm
> diff --git a/ssh.c b/ssh.c
> index 3239108..e01790a 100644
> --- a/ssh.c
> +++ b/ssh.c
> @@ -207,7 +207,8 @@ usage(void)
>  "           [-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"
> +"           [-w local_tun[:remote_tun]] [-z certificate_file]\n"
> +"           [user@]hostname [command]\n"
>  	);
>  	exit(255);
>  }
> @@ -215,6 +216,7 @@ usage(void)
>  static int ssh_session(void);
>  static int ssh_session2(void);
>  static void load_public_identity_files(void);
> +static void load_certificate_files(void);
>  static void main_sigchld_handler(int);
>  
>  /* from muxclient.c */
> @@ -595,7 +597,7 @@ main(int ac, char **av)
>  
>   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:KL:MNO:PQ:R:S:TVw:W:XYyz:")) != -1) {
>  		switch (opt) {
>  		case '1':
>  			options.protocol = SSH_PROTO_1;
> @@ -906,6 +908,9 @@ main(int ac, char **av)
>  		case 'F':
>  			config = optarg;
>  			break;
> +		case 'z':
> +			add_certificate_file(&options, NULL, optarg, 1);
> +			break;
>  		default:
>  			usage();
>  		}
> @@ -1013,6 +1018,9 @@ main(int ac, char **av)
>  		options.hostname = xstrdup(host);
>  	}
>  
> +	/* If the user has specified certificate(s), load it now. */
> +	load_certificate_files();
> +
>  	/* If canonicalization requested then try to apply it */
>  	lowercase(host);
>  	if (options.canonicalize_hostname != SSH_CANONICALISE_NO)
> @@ -1353,6 +1361,13 @@ main(int ac, char **av)
>  		}
>  	}
>  
> +	for (i = 0; i < options.num_certificate_files; i++) {
> +		free(options.certificate_files[i]);
> +		options.certificate_files[i] = NULL;
> +	}
> +
> +
> +
>  	exit_status = compat20 ? ssh_session2() : ssh_session();
>  	packet_close();
>  
> @@ -1938,6 +1953,72 @@ ssh_session2(void)
>  	    options.escape_char : SSH_ESCAPECHAR_NONE, id);
>  }
>  
> +/* Load certificate file(s) specified in options. */
> +static void 
> +load_certificate_files(void)
> +{
> +	char *filename, *cp, thishost[NI_MAXHOST];
> +	char *pwdir = NULL, *pwname = NULL;
> +	struct passwd *pw;
> +	int i, n_ids;
> +	struct sshkey *cert;
> +	char *certificate_files[SSH_MAX_CERTIFICATE_FILES];
> +	struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
> +
> +	n_ids = 0;
> +	memset(certificate_files, 0, sizeof(certificate_files));
> +	memset(certificates, 0, sizeof(certificates));
> +	
> +	if ((pw = getpwuid(original_real_uid)) == NULL)
> +		fatal("load_certificate_files: getpwuid failed");
> +	pwname = xstrdup(pw->pw_name);
> +	pwdir = xstrdup(pw->pw_dir);
> +	if (gethostname(thishost, sizeof(thishost)) == -1)
> +		fatal("load_certificate_files: gethostname: %s",
> +		    strerror(errno));
> +	
> +	if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES)
> +		fatal("load_certificate_files: too many certificates");
> +	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);
> +		
> +		cert = key_load_public(filename, NULL);
> +		debug("certificate file %s type %d", filename,
> +		    cert ? cert->type : -1);
> +		free(options.certificate_files[i]);
> +		if (cert == NULL) {
> +			free(filename);
> +			continue;
> +		}
> +		if (!key_is_cert(cert)) {
> +			debug("%s: key %s type %s is not a certificate",
> +			    __func__, filename, key_type(cert));
> +			key_free(cert);
> +			free(filename);
> +			continue;
> +		}
> +
> +		certificate_files[n_ids] = filename;
> +		certificates[n_ids] = cert;
> +		++n_ids;
> +	}
> +	options.num_certificate_files = n_ids;
> +	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));
> +	free(pwdir);
> +}
> +
> +
> +
>  static void
>  load_public_identity_files(void)
>  {
> diff --git a/ssh.h b/ssh.h
> index 4f8da5c..8fb7ba3 100644
> --- a/ssh.h
> +++ b/ssh.h
> @@ -19,6 +19,13 @@
>  #define SSH_DEFAULT_PORT	22
>  
>  /*
> + * Maximum number of certificate files that can be specified
> + * in configuration files or on the command line.
> + */
> +#define SSH_MAX_CERTIFICATE_FILES	100
> +
> +
> +/*
>   * Maximum number of RSA authentication identity files that can be specified
>   * in configuration files or on the command line.
>   */
> diff --git a/ssh_config.5 b/ssh_config.5
> index e514398..17741b7 100644
> --- a/ssh_config.5
> +++ b/ssh_config.5
> @@ -325,6 +325,34 @@ to be canonicalized to names in the
>  or
>  .Dq *.c.example.com
>  domains.
> +.It Cm CertificateFile
> +Specifies a file from which the user's certificate is read.
> +A corresponding private key must be provided separately in order
> +to use this certificate.
> +.Xr ssh 1
> +will attempt to use private keys provided as identity files
> +or in the agent for such authentication.
> +.Pp
> +The file name may use the tilde
> +syntax to refer to a user's home directory or one of the following
> +escape characters:
> +.Ql %d
> +(local user's home directory),
> +.Ql %u
> +(local user name),
> +.Ql %l
> +(local host name),
> +.Ql %h
> +(remote host name) or
> +.Ql %r
> +(remote user name).
> +.Pp
> +It is possible to have multiple certificate files specified in
> +configuration files; these certificates will be tried in sequence.
> +Multiple
> +.Cm CertificateFile
> +directives will add to the list of certificates used for
> +authentication.
>  .It Cm ChallengeResponseAuthentication
>  Specifies whether to use challenge-response authentication.
>  The argument to this keyword must be
> @@ -911,6 +939,11 @@ differs from that of other configuration directives).
>  may be used in conjunction with
>  .Cm IdentitiesOnly
>  to select which identities in an agent are offered during authentication.
> +.Cm IdentityFile
> +may also be used in conjunction with
> +.Cm CertificateFile
> +in order to provide any certificate also needed for authentication with
> +the identity.
>  .It Cm IgnoreUnknown
>  Specifies a pattern-list of unknown options to be ignored if they are
>  encountered in configuration parsing.
> diff --git a/sshconnect2.c b/sshconnect2.c
> index 34dbf9a..fb24b5e 100644
> --- a/sshconnect2.c
> +++ b/sshconnect2.c
> @@ -1016,6 +1016,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
>  	u_int skip = 0;
>  	int ret = -1;
>  	int have_sig = 1;
> +	int i;
>  	char *fp;
>  
>  	if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
> @@ -1053,6 +1054,33 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
>  	}
>  	buffer_put_string(&b, blob, bloblen);
>  
> +	/* If the key is an input certificate, sign its private key instead. 
> +	 * If no such private key exists, return failure and continue with
> +	 * other methods of authentication.
> +	 * Else, just continue with the normal signing process. */
> +	if (key_is_cert(id->key)) {
> +		for (i = 0; i < options.num_certificate_files; i++) {
> +			if (key_equal(id->key, options.certificates[i])) {
> +				Identity *id2;
> +				int matched = 0;
> +				TAILQ_FOREACH(id2, &authctxt->keys, next) {
> +					if (sshkey_equal_public(id->key, id2->key) &&
> +					    id->key->type != id2->key->type) {
> +						id = id2;
> +						matched = 1;
> +						break;
> +					}
> +				}
> +				if (!matched) {
> +					free(blob);
> +					buffer_free(&b);
> +					return 0;
> +				}
> +				break;
> +			}
> +		}
> +	}
> +	
>  	/* generate signature */
>  	ret = identity_sign(id, &signature, &slen,
>  	    buffer_ptr(&b), buffer_len(&b), datafellows);
> @@ -1189,9 +1217,11 @@ load_identity_file(char *filename, int userprovided)
>  
>  /*
>   * try keys in the following order:
> - *	1. agent keys that are found in the config file
> - *	2. other agent keys
> - *	3. keys that are only listed in the config file
> + * 	1. certificates listed in the config file
> + * 	2. other input certificates
> + *	3. agent keys that are found in the config file
> + *	4. other agent keys
> + *	5. keys that are only listed in the config file
>   */
>  static void
>  pubkey_prepare(Authctxt *authctxt)
> @@ -1245,6 +1275,17 @@ pubkey_prepare(Authctxt *authctxt)
>  			free(id);
>  		}
>  	}
> +	/* list of certificates specified by user */
> +	for (i = 0; i < options.num_certificate_files; i++) {
> +		key = options.certificates[i];
> +		if (!key_is_cert(key))
> +			continue;
> +		id = xcalloc(1, sizeof(*id));
> +		id->key = key;
> +		id->filename = xstrdup(options.certificate_files[i]);
> +		id->userprovided = options.certificate_file_userprovided[i];
> +		TAILQ_INSERT_TAIL(preferred, id, next);
> +	}
>  	/* list of keys supported by the agent */
>  	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
>  		if (r != SSH_ERR_AGENT_NOT_PRESENT)
> -- 
> 1.9.1
> 
> _______________________________________________
> openssh-unix-dev mailing list
> openssh-unix-dev at mindrot.org
> https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
> 


More information about the openssh-unix-dev mailing list