diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-12-30 09:21:16 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-12-30 20:58:19 +1100 |
commit | 14cea36df397677b8f8568204300ef654114fd76 (patch) | |
tree | 4c9b0bf5108df396f8d9eaff576537e7c9cc50e9 /ssh-sk.c | |
parent | 2fe05fcb4a2695f190b4fcf27770b655586ab349 (diff) |
upstream: resident keys support in SK API
Adds a sk_load_resident_keys() function to the security key
API that accepts a security key provider and a PIN and returns
a list of keys.
Implement support for this in the usbhid middleware.
feedback and ok markus@
OpenBSD-Commit-ID: 67e984e4e87f4999ce447a6178c4249a9174eff0
Diffstat (limited to 'ssh-sk.c')
-rw-r--r-- | ssh-sk.c | 116 |
1 files changed, 112 insertions, 4 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.c,v 1.19 2019/12/30 09:20:36 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.c,v 1.20 2019/12/30 09:21:16 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -60,6 +60,10 @@ struct sshsk_provider { | |||
60 | const char *application, | 60 | const char *application, |
61 | const uint8_t *key_handle, size_t key_handle_len, | 61 | const uint8_t *key_handle, size_t key_handle_len, |
62 | uint8_t flags, struct sk_sign_response **sign_response); | 62 | uint8_t flags, struct sk_sign_response **sign_response); |
63 | |||
64 | /* Enumerate resident keys */ | ||
65 | int (*sk_load_resident_keys)(const char *pin, | ||
66 | struct sk_resident_key ***rks, size_t *nrks); | ||
63 | }; | 67 | }; |
64 | 68 | ||
65 | /* Built-in version */ | 69 | /* Built-in version */ |
@@ -70,6 +74,8 @@ int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
70 | const char *application, | 74 | const char *application, |
71 | const uint8_t *key_handle, size_t key_handle_len, | 75 | const uint8_t *key_handle, size_t key_handle_len, |
72 | uint8_t flags, struct sk_sign_response **sign_response); | 76 | uint8_t flags, struct sk_sign_response **sign_response); |
77 | int ssh_sk_load_resident_keys(const char *pin, | ||
78 | struct sk_resident_key ***rks, size_t *nrks); | ||
73 | 79 | ||
74 | static void | 80 | static void |
75 | sshsk_free(struct sshsk_provider *p) | 81 | sshsk_free(struct sshsk_provider *p) |
@@ -101,6 +107,7 @@ sshsk_open(const char *path) | |||
101 | #ifdef ENABLE_SK_INTERNAL | 107 | #ifdef ENABLE_SK_INTERNAL |
102 | ret->sk_enroll = ssh_sk_enroll; | 108 | ret->sk_enroll = ssh_sk_enroll; |
103 | ret->sk_sign = ssh_sk_sign; | 109 | ret->sk_sign = ssh_sk_sign; |
110 | ret->sk_load_resident_keys = ssh_sk_load_resident_keys; | ||
104 | #else | 111 | #else |
105 | error("internal security key support not enabled"); | 112 | error("internal security key support not enabled"); |
106 | #endif | 113 | #endif |
@@ -136,6 +143,12 @@ sshsk_open(const char *path) | |||
136 | path, dlerror()); | 143 | path, dlerror()); |
137 | goto fail; | 144 | goto fail; |
138 | } | 145 | } |
146 | if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, | ||
147 | "sk_load_resident_keys")) == NULL) { | ||
148 | error("Security key provider %s dlsym(sk_load_resident_keys) " | ||
149 | "failed: %s", path, dlerror()); | ||
150 | goto fail; | ||
151 | } | ||
139 | /* success */ | 152 | /* success */ |
140 | return ret; | 153 | return ret; |
141 | fail: | 154 | fail: |
@@ -264,9 +277,7 @@ sshsk_key_from_response(int alg, const char *application, uint8_t flags, | |||
264 | *keyp = NULL; | 277 | *keyp = NULL; |
265 | 278 | ||
266 | /* Check response validity */ | 279 | /* Check response validity */ |
267 | if (resp->public_key == NULL || resp->key_handle == NULL || | 280 | if (resp->public_key == NULL || resp->key_handle == NULL) { |
268 | resp->signature == NULL || | ||
269 | (resp->attestation_cert == NULL && resp->attestation_cert_len != 0)) { | ||
270 | error("%s: sk_enroll response invalid", __func__); | 281 | error("%s: sk_enroll response invalid", __func__); |
271 | r = SSH_ERR_INVALID_FORMAT; | 282 | r = SSH_ERR_INVALID_FORMAT; |
272 | goto out; | 283 | goto out; |
@@ -595,4 +606,101 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
595 | sshbuf_free(inner_sig); | 606 | sshbuf_free(inner_sig); |
596 | return r; | 607 | return r; |
597 | } | 608 | } |
609 | |||
610 | static void | ||
611 | sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) | ||
612 | { | ||
613 | size_t i; | ||
614 | |||
615 | if (nrks == 0 || rks == NULL) | ||
616 | return; | ||
617 | for (i = 0; i < nrks; i++) { | ||
618 | free(rks[i]->application); | ||
619 | freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); | ||
620 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); | ||
621 | freezero(rks[i]->key.signature, rks[i]->key.signature_len); | ||
622 | freezero(rks[i]->key.attestation_cert, | ||
623 | rks[i]->key.attestation_cert_len); | ||
624 | freezero(rks[i], sizeof(**rks)); | ||
625 | } | ||
626 | free(rks); | ||
627 | } | ||
628 | |||
629 | int | ||
630 | sshsk_load_resident(const char *provider_path, const char *pin, | ||
631 | struct sshkey ***keysp, size_t *nkeysp) | ||
632 | { | ||
633 | struct sshsk_provider *skp = NULL; | ||
634 | int r = SSH_ERR_INTERNAL_ERROR; | ||
635 | struct sk_resident_key **rks = NULL; | ||
636 | size_t i, nrks = 0, nkeys = 0; | ||
637 | struct sshkey *key = NULL, **keys = NULL, **tmp; | ||
638 | uint8_t flags; | ||
639 | |||
640 | debug("%s: provider \"%s\"%s", __func__, provider_path, | ||
641 | (pin != NULL && *pin != '\0') ? ", have-pin": ""); | ||
642 | |||
643 | if (keysp == NULL || nkeysp == NULL) | ||
644 | return SSH_ERR_INVALID_ARGUMENT; | ||
645 | *keysp = NULL; | ||
646 | *nkeysp = 0; | ||
647 | |||
648 | if ((skp = sshsk_open(provider_path)) == NULL) { | ||
649 | r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ | ||
650 | goto out; | ||
651 | } | ||
652 | if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) { | ||
653 | error("Security key provider %s returned failure %d", | ||
654 | provider_path, r); | ||
655 | r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */ | ||
656 | goto out; | ||
657 | } | ||
658 | for (i = 0; i < nrks; i++) { | ||
659 | debug3("%s: rk %zu: slot = %zu, alg = %d, application = \"%s\"", | ||
660 | __func__, i, rks[i]->slot, rks[i]->alg, | ||
661 | rks[i]->application); | ||
662 | /* XXX need better filter here */ | ||
663 | if (strncmp(rks[i]->application, "ssh:", 4) != 0) | ||
664 | continue; | ||
665 | switch (rks[i]->alg) { | ||
666 | case SSH_SK_ECDSA: | ||
667 | case SSH_SK_ED25519: | ||
668 | break; | ||
669 | default: | ||
670 | continue; | ||
671 | } | ||
672 | /* XXX where to get flags? */ | ||
673 | flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; | ||
674 | if ((r = sshsk_key_from_response(rks[i]->alg, | ||
675 | rks[i]->application, flags, &rks[i]->key, &key)) != 0) | ||
676 | goto out; | ||
677 | if ((tmp = recallocarray(keys, nkeys, nkeys + 1, | ||
678 | sizeof(*tmp))) == NULL) { | ||
679 | error("%s: recallocarray failed", __func__); | ||
680 | r = SSH_ERR_ALLOC_FAIL; | ||
681 | goto out; | ||
682 | } | ||
683 | keys = tmp; | ||
684 | keys[nkeys++] = key; | ||
685 | key = NULL; | ||
686 | /* XXX synthesise comment */ | ||
687 | } | ||
688 | /* success */ | ||
689 | *keysp = keys; | ||
690 | *nkeysp = nkeys; | ||
691 | keys = NULL; | ||
692 | nkeys = 0; | ||
693 | r = 0; | ||
694 | out: | ||
695 | sshsk_free(skp); | ||
696 | sshsk_free_sk_resident_keys(rks, nrks); | ||
697 | sshkey_free(key); | ||
698 | if (nkeys != 0) { | ||
699 | for (i = 0; i < nkeys; i++) | ||
700 | sshkey_free(keys[i]); | ||
701 | free(keys); | ||
702 | } | ||
703 | return r; | ||
704 | } | ||
705 | |||
598 | #endif /* ENABLE_SK */ | 706 | #endif /* ENABLE_SK */ |