]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: ssh client support for U2F/FIDO keys
authordjm@openbsd.org <djm@openbsd.org>
Thu, 31 Oct 2019 21:18:28 +0000 (21:18 +0000)
committerDamien Miller <djm@mindrot.org>
Thu, 31 Oct 2019 22:46:09 +0000 (09:46 +1100)
OpenBSD-Commit-ID: eb2cfa6cf7419a1895e06e398ea6d41516c5b0bc

readconf.c
readconf.h
ssh.c
sshconnect2.c

index f78b4d6fef57c3a03c65b189fbb8682a0bedde63..f181945804d6316bb30ab29d2ab434d533934289 100644 (file)
@@ -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@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@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);
index 8e36bf32adb34293c2a9d2d2a64f63ee64db44bb..51d540b88bea774a0b6033cd2715b3b7c5222524 100644 (file)
@@ -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@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 ee51823cd833397c4ffb5abe1d1ac2bc1e095fab..4736b5dd0cfc6586e580eb7909ba7e85459a40ae 100644 (file)
--- 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@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@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) {
index 87fa70a40cf2204248416faeb07214dca54016a7..62f0c3e76ef02c00c65c07067a921fecde111194 100644 (file)
@@ -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;