[openssh-commits] [openssh] 07/14: upstream: ssh client support for U2F/FIDO keys

git+noreply at mindrot.org git+noreply at mindrot.org
Fri Nov 1 09:47:16 AEDT 2019


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 884416bdb10468f1252e4d7c13d51b43dccba7f6
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Thu Oct 31 21:18:28 2019 +0000

    upstream: ssh client support for U2F/FIDO keys
    
    OpenBSD-Commit-ID: eb2cfa6cf7419a1895e06e398ea6d41516c5b0bc
---
 readconf.c    |  17 +++++++--
 readconf.h    |   3 +-
 ssh.c         |  18 +++++++++-
 sshconnect2.c | 111 +++++++++++++++++++++++++++++++++++++++++++++-------------
 4 files changed, 120 insertions(+), 29 deletions(-)

diff --git a/readconf.c b/readconf.c
index f78b4d6f..f1819458 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.309 2019/09/06 14:45:34 naddy Exp $ */
+/* $OpenBSD: readconf.c,v 1.310 2019/10/31 21:18:28 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -174,6 +174,7 @@ typedef enum {
 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
 	oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
 	oPubkeyAcceptedKeyTypes, oCASignatureAlgorithms, oProxyJump,
+	oSecurityKeyProvider,
 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
 } OpCodes;
 
@@ -214,6 +215,7 @@ static struct {
 	{ "smartcarddevice", oUnsupported },
 	{ "pkcs11provider", oUnsupported },
 #endif
+	{ "securitykeyprovider", oSecurityKeyProvider },
 	{ "rsaauthentication", oUnsupported },
 	{ "rhostsrsaauthentication", oUnsupported },
 	{ "compressionlevel", oUnsupported },
@@ -1146,6 +1148,10 @@ parse_char_array:
 		charptr = &options->pkcs11_provider;
 		goto parse_string;
 
+	case oSecurityKeyProvider:
+		charptr = &options->sk_provider;
+		goto parse_string;
+
 	case oProxyCommand:
 		charptr = &options->proxy_command;
 		/* Ignore ProxyCommand if ProxyJump already specified */
@@ -1906,6 +1912,7 @@ initialize_options(Options * options)
 	options->bind_address = NULL;
 	options->bind_interface = NULL;
 	options->pkcs11_provider = NULL;
+	options->sk_provider = NULL;
 	options->enable_ssh_keysign = - 1;
 	options->no_host_authentication_for_localhost = - 1;
 	options->identities_only = - 1;
@@ -2043,6 +2050,8 @@ fill_default_options(Options * options)
 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0);
 #ifdef OPENSSL_HAS_ECC
 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
+		add_identity_file(options, "~/",
+		    _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
 #endif
 		add_identity_file(options, "~/",
 		    _PATH_SSH_CLIENT_ID_ED25519, 0);
@@ -2118,6 +2127,8 @@ fill_default_options(Options * options)
 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
 	if (options->update_hostkeys == -1)
 		options->update_hostkeys = 0;
+	if (options->sk_provider == NULL)
+		options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
 
 	/* Expand KEX name lists */
 	all_cipher = cipher_alg_list(',', 0);
@@ -2135,7 +2146,7 @@ fill_default_options(Options * options)
 	ASSEMBLE(macs, KEX_CLIENT_MAC, all_mac);
 	ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, all_kex);
 	ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key);
-	ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key);
+	ASSEMBLE(pubkey_key_types, PUBKEY_DEFAULT_PK_ALG, all_key);
 	ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig);
 #undef ASSEMBLE
 	free(all_cipher);
@@ -2157,6 +2168,7 @@ fill_default_options(Options * options)
 	CLEAR_ON_NONE(options->control_path);
 	CLEAR_ON_NONE(options->revoked_host_keys);
 	CLEAR_ON_NONE(options->pkcs11_provider);
+	CLEAR_ON_NONE(options->sk_provider);
 	if (options->jump_host != NULL &&
 	    strcmp(options->jump_host, "none") == 0 &&
 	    options->jump_port == 0 && options->jump_user == NULL) {
@@ -2673,6 +2685,7 @@ dump_client_config(Options *o, const char *host)
 #ifdef ENABLE_PKCS11
 	dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
 #endif
+	dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
 	dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
 	dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types);
 	dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
diff --git a/readconf.h b/readconf.h
index 8e36bf32..51d540b8 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.129 2018/11/23 05:08:07 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.130 2019/10/31 21:18:28 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -82,6 +82,7 @@ typedef struct {
 	char   *bind_address;	/* local socket address for connection to sshd */
 	char   *bind_interface;	/* local interface for bind address */
 	char   *pkcs11_provider; /* PKCS#11 provider */
+	char   *sk_provider; /* Security key provider */
 	int	verify_host_key_dns;	/* Verify host key using DNS */
 
 	int     num_identity_files;	/* Number of files for RSA/DSA identities. */
diff --git a/ssh.c b/ssh.c
index ee51823c..4736b5dd 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.507 2019/09/13 04:27:35 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.508 2019/10/31 21:18:28 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -1344,6 +1344,22 @@ main(int ac, char **av)
 		exit(0);
 	}
 
+	/* Expand SecurityKeyProvider if it refers to an environment variable */
+	if (options.sk_provider != NULL && *options.sk_provider == '$' &&
+	    strlen(options.sk_provider) > 1) {
+		if ((cp = getenv(options.sk_provider + 1)) == NULL) {
+			debug("Security key provider %s did not resolve; "
+			    "disabling", options.sk_provider);
+			free(options.sk_provider);
+			options.sk_provider = NULL;
+		} else {
+			debug2("resolved SecurityKeyProvider %s => %s",
+			    options.sk_provider, cp);
+			free(options.sk_provider);
+			options.sk_provider = xstrdup(cp);
+		}
+	}
+
 	if (muxclient_command != 0 && options.control_path == NULL)
 		fatal("No ControlPath specified for \"-O\" command");
 	if (options.control_path != NULL) {
diff --git a/sshconnect2.c b/sshconnect2.c
index 87fa70a4..62f0c3e7 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.308 2019/08/05 11:50:33 dtucker Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.309 2019/10/31 21:18:28 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2008 Damien Miller.  All rights reserved.
@@ -72,6 +72,7 @@
 #include "hostfile.h"
 #include "ssherr.h"
 #include "utf8.h"
+#include "ssh-sk.h"
 
 #ifdef GSSAPI
 #include "ssh-gss.h"
@@ -601,17 +602,23 @@ static char *
 format_identity(Identity *id)
 {
 	char *fp = NULL, *ret = NULL;
+	const char *note = "";
 
 	if (id->key != NULL) {
 	     fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
 		    SSH_FP_DEFAULT);
 	}
+	if (id->key) {
+		if ((id->key->flags & SSHKEY_FLAG_EXT) != 0)
+			note = " token";
+		else if (sshkey_type_plain(id->key->type) == KEY_ECDSA_SK)
+			note = " security-key";
+	}
 	xasprintf(&ret, "%s %s%s%s%s%s%s",
 	    id->filename,
 	    id->key ? sshkey_type(id->key) : "", id->key ? " " : "",
 	    fp ? fp : "",
-	    id->userprovided ? " explicit" : "",
-	    (id->key && (id->key->flags & SSHKEY_FLAG_EXT)) ? " token" : "",
+	    id->userprovided ? " explicit" : "", note,
 	    id->agent_fd != -1 ? " agent" : "");
 	free(fp);
 	return ret;
@@ -1140,8 +1147,11 @@ static int
 identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
     const u_char *data, size_t datalen, u_int compat, const char *alg)
 {
-	struct sshkey *prv;
-	int r;
+	struct sshkey *sign_key = NULL, *prv = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+
+	*sigp = NULL;
+	*lenp = 0;
 
 	/* The agent supports this key. */
 	if (id->key != NULL && id->agent_fd != -1) {
@@ -1155,27 +1165,46 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
 	 */
 	if (id->key != NULL &&
 	    (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
-		if ((r = sshkey_sign(id->key, sigp, lenp, data, datalen,
-		    alg, compat)) != 0)
-			return r;
-		/*
-		 * PKCS#11 tokens may not support all signature algorithms,
-		 * so check what we get back.
-		 */
-		if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0)
-			return r;
-		return 0;
+		sign_key = id->key;
+	} else {
+		/* Load the private key from the file. */
+		if ((prv = load_identity_file(id)) == NULL)
+			return SSH_ERR_KEY_NOT_FOUND;
+		if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
+			error("%s: private key %s contents do not match public",
+			   __func__, id->filename);
+			r = SSH_ERR_KEY_NOT_FOUND;
+			goto out;
+		}
+		sign_key = prv;
 	}
 
-	/* Load the private key from the file. */
-	if ((prv = load_identity_file(id)) == NULL)
-		return SSH_ERR_KEY_NOT_FOUND;
-	if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
-		error("%s: private key %s contents do not match public",
-		   __func__, id->filename);
-		return SSH_ERR_KEY_NOT_FOUND;
+	if (sshkey_type_plain(sign_key->type) == KEY_ECDSA_SK) {
+		if (options.sk_provider == NULL) {
+			/* Shouldn't happen here; checked in pubkey_prepare() */
+			fatal("%s: missing SecurityKeyProvider", __func__);
+		}
+		if ((r = sshsk_ecdsa_sign(options.sk_provider, sign_key,
+		    sigp, lenp, data, datalen, compat)) != 0) {
+			debug("%s: sshsk_ecdsa_sign: %s", __func__, ssh_err(r));
+			goto out;
+		}
+	} else if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen,
+	    alg, compat)) != 0) {
+		debug("%s: sshkey_sign: %s", __func__, ssh_err(r));
+		goto out;
 	}
-	r = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat);
+	/*
+	 * PKCS#11 tokens may not support all signature algorithms,
+	 * so check what we get back.
+	 */
+	if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) {
+		debug("%s: sshkey_check_sigtype: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	/* success */
+	r = 0;
+ out:
 	sshkey_free(prv);
 	return r;
 }
@@ -1450,6 +1479,15 @@ load_identity_file(Identity *id)
 			quit = 1;
 			break;
 		}
+		if (private != NULL &&
+		    sshkey_type_plain(private->type) == KEY_ECDSA_SK &&
+		    options.sk_provider == NULL) {
+			debug("key \"%s\" is a security key, but no "
+			    "provider specified", id->filename);
+			sshkey_free(private);
+			private = NULL;
+			quit = 1;
+		}
 		if (!quit && private != NULL && id->agent_fd == -1 &&
 		    !(id->key && id->isprivate))
 			maybe_add_key_to_agent(id->filename, private, comment,
@@ -1520,8 +1558,20 @@ pubkey_prepare(Authctxt *authctxt)
 	/* list of keys stored in the filesystem and PKCS#11 */
 	for (i = 0; i < options.num_identity_files; i++) {
 		key = options.identity_keys[i];
-		if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER)
+		if (key && key->cert &&
+		    key->cert->type != SSH2_CERT_TYPE_USER) {
+			debug("%s: ignoring certificate %s: not a user "
+			    "certificate",  __func__,
+			    options.identity_files[i]);
 			continue;
+		}
+		if (key && sshkey_type_plain(key->type) == KEY_ECDSA_SK &&
+		    options.sk_provider == NULL) {
+			debug("%s: ignoring security key %s as no "
+			    "SecurityKeyProvider has been specified",
+			    __func__, options.identity_files[i]);
+			continue;
+		}
 		options.identity_keys[i] = NULL;
 		id = xcalloc(1, sizeof(*id));
 		id->agent_fd = -1;
@@ -1534,8 +1584,19 @@ pubkey_prepare(Authctxt *authctxt)
 	for (i = 0; i < options.num_certificate_files; i++) {
 		key = options.certificates[i];
 		if (!sshkey_is_cert(key) || key->cert == NULL ||
-		    key->cert->type != SSH2_CERT_TYPE_USER)
+		    key->cert->type != SSH2_CERT_TYPE_USER) {
+			debug("%s: ignoring certificate %s: not a user "
+			    "certificate",  __func__,
+			    options.identity_files[i]);
 			continue;
+		}
+		if (key && sshkey_type_plain(key->type) == KEY_ECDSA_SK &&
+		    options.sk_provider == NULL) {
+			debug("%s: ignoring security key certificate %s as no "
+			    "SecurityKeyProvider has been specified",
+			    __func__, options.identity_files[i]);
+			continue;
+		}
 		id = xcalloc(1, sizeof(*id));
 		id->agent_fd = -1;
 		id->key = key;

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list