diff options
author | djm@openbsd.org <djm@openbsd.org> | 2020-08-27 01:06:18 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2020-08-27 11:28:36 +1000 |
commit | 9b8ad93824c682ce841f53f3b5762cef4e7cc4dc (patch) | |
tree | d4523956d4623b19bf5904d1b92afeb2307f69d3 /ssh-keygen.c | |
parent | 1196d7f49d4fbc90f37e550de3056561613b0960 (diff) |
upstream: support for user-verified FIDO keys
FIDO2 supports a notion of "user verification" where the user is
required to demonstrate their identity to the token before particular
operations (e.g. signing). Typically this is done by authenticating
themselves using a PIN that has been set on the token.
This adds support for generating and using user verified keys where
the verification happens via PIN (other options might be added in the
future, but none are in common use now). Practically, this adds
another key generation option "verify-required" that yields a key that
requires a PIN before each authentication.
feedback markus@ and Pedro Martelletto; ok markus@
OpenBSD-Commit-ID: 57fd461e4366f87c47502c5614ec08573e6d6a15
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r-- | ssh-keygen.c | 55 |
1 files changed, 39 insertions, 16 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index cc092368e..89ef9a143 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.415 2020/08/03 02:53:51 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.416 2020/08/27 01:06:18 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -589,7 +589,7 @@ do_convert_private_ssh2(struct sshbuf *b) | |||
589 | 589 | ||
590 | /* try the key */ | 590 | /* try the key */ |
591 | if (sshkey_sign(key, &sig, &slen, data, sizeof(data), | 591 | if (sshkey_sign(key, &sig, &slen, data, sizeof(data), |
592 | NULL, NULL, 0) != 0 || | 592 | NULL, NULL, NULL, 0) != 0 || |
593 | sshkey_verify(key, sig, slen, data, sizeof(data), | 593 | sshkey_verify(key, sig, slen, data, sizeof(data), |
594 | NULL, 0, NULL) != 0) { | 594 | NULL, 0, NULL) != 0) { |
595 | sshkey_free(key); | 595 | sshkey_free(key); |
@@ -1727,7 +1727,8 @@ load_pkcs11_key(char *path) | |||
1727 | static int | 1727 | static int |
1728 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, | 1728 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, |
1729 | const u_char *data, size_t datalen, | 1729 | const u_char *data, size_t datalen, |
1730 | const char *alg, const char *provider, u_int compat, void *ctx) | 1730 | const char *alg, const char *provider, const char *pin, |
1731 | u_int compat, void *ctx) | ||
1731 | { | 1732 | { |
1732 | int *agent_fdp = (int *)ctx; | 1733 | int *agent_fdp = (int *)ctx; |
1733 | 1734 | ||
@@ -1744,7 +1745,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1744 | u_int n; | 1745 | u_int n; |
1745 | struct sshkey *ca, *public; | 1746 | struct sshkey *ca, *public; |
1746 | char valid[64], *otmp, *tmp, *cp, *out, *comment; | 1747 | char valid[64], *otmp, *tmp, *cp, *out, *comment; |
1747 | char *ca_fp = NULL, **plist = NULL; | 1748 | char *ca_fp = NULL, **plist = NULL, *pin = NULL; |
1748 | struct ssh_identitylist *agent_ids; | 1749 | struct ssh_identitylist *agent_ids; |
1749 | size_t j; | 1750 | size_t j; |
1750 | struct notifier_ctx *notifier = NULL; | 1751 | struct notifier_ctx *notifier = NULL; |
@@ -1785,6 +1786,12 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1785 | } else { | 1786 | } else { |
1786 | /* CA key is assumed to be a private key on the filesystem */ | 1787 | /* CA key is assumed to be a private key on the filesystem */ |
1787 | ca = load_identity(tmp, NULL); | 1788 | ca = load_identity(tmp, NULL); |
1789 | if (sshkey_is_sk(ca) && | ||
1790 | (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { | ||
1791 | if ((pin = read_passphrase("Enter PIN for CA key: ", | ||
1792 | RP_ALLOW_STDIN)) == NULL) | ||
1793 | fatal("%s: couldn't read PIN", __func__); | ||
1794 | } | ||
1788 | } | 1795 | } |
1789 | free(tmp); | 1796 | free(tmp); |
1790 | 1797 | ||
@@ -1844,7 +1851,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1844 | 1851 | ||
1845 | if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { | 1852 | if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { |
1846 | if ((r = sshkey_certify_custom(public, ca, | 1853 | if ((r = sshkey_certify_custom(public, ca, |
1847 | key_type_name, sk_provider, agent_signer, | 1854 | key_type_name, sk_provider, NULL, agent_signer, |
1848 | &agent_fd)) != 0) | 1855 | &agent_fd)) != 0) |
1849 | fatal("Couldn't certify key %s via agent: %s", | 1856 | fatal("Couldn't certify key %s via agent: %s", |
1850 | tmp, ssh_err(r)); | 1857 | tmp, ssh_err(r)); |
@@ -1856,7 +1863,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1856 | sshkey_type(ca), ca_fp); | 1863 | sshkey_type(ca), ca_fp); |
1857 | } | 1864 | } |
1858 | r = sshkey_certify(public, ca, key_type_name, | 1865 | r = sshkey_certify(public, ca, key_type_name, |
1859 | sk_provider); | 1866 | sk_provider, pin); |
1860 | notify_complete(notifier); | 1867 | notify_complete(notifier); |
1861 | if (r != 0) | 1868 | if (r != 0) |
1862 | fatal("Couldn't certify key %s: %s", | 1869 | fatal("Couldn't certify key %s: %s", |
@@ -1890,6 +1897,8 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1890 | if (cert_serial_autoinc) | 1897 | if (cert_serial_autoinc) |
1891 | cert_serial++; | 1898 | cert_serial++; |
1892 | } | 1899 | } |
1900 | if (pin != NULL) | ||
1901 | freezero(pin, strlen(pin)); | ||
1893 | free(ca_fp); | 1902 | free(ca_fp); |
1894 | #ifdef ENABLE_PKCS11 | 1903 | #ifdef ENABLE_PKCS11 |
1895 | pkcs11_terminate(); | 1904 | pkcs11_terminate(); |
@@ -2526,6 +2535,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2526 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | 2535 | struct sshbuf *sigbuf = NULL, *abuf = NULL; |
2527 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; | 2536 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; |
2528 | char *wfile = NULL, *asig = NULL, *fp = NULL; | 2537 | char *wfile = NULL, *asig = NULL, *fp = NULL; |
2538 | char *pin = NULL, *prompt = NULL; | ||
2529 | 2539 | ||
2530 | if (!quiet) { | 2540 | if (!quiet) { |
2531 | if (fd == STDIN_FILENO) | 2541 | if (fd == STDIN_FILENO) |
@@ -2533,17 +2543,25 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2533 | else | 2543 | else |
2534 | fprintf(stderr, "Signing file %s\n", filename); | 2544 | fprintf(stderr, "Signing file %s\n", filename); |
2535 | } | 2545 | } |
2536 | if (signer == NULL && sshkey_is_sk(signkey) && | 2546 | if (signer == NULL && sshkey_is_sk(signkey)) { |
2537 | (signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | 2547 | if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { |
2538 | if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, | 2548 | xasprintf(&prompt, "Enter PIN for %s key: ", |
2539 | SSH_FP_DEFAULT)) == NULL) | 2549 | sshkey_type(signkey)); |
2540 | fatal("%s: sshkey_fingerprint failed", __func__); | 2550 | if ((pin = read_passphrase(prompt, |
2541 | fprintf(stderr, "Confirm user presence for key %s %s\n", | 2551 | RP_ALLOW_STDIN)) == NULL) |
2542 | sshkey_type(signkey), fp); | 2552 | fatal("%s: couldn't read PIN", __func__); |
2543 | free(fp); | 2553 | } |
2554 | if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | ||
2555 | if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, | ||
2556 | SSH_FP_DEFAULT)) == NULL) | ||
2557 | fatal("%s: fingerprint failed", __func__); | ||
2558 | fprintf(stderr, "Confirm user presence for key %s %s\n", | ||
2559 | sshkey_type(signkey), fp); | ||
2560 | free(fp); | ||
2561 | } | ||
2544 | } | 2562 | } |
2545 | if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, fd, sig_namespace, | 2563 | if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, pin, |
2546 | &sigbuf, signer, signer_ctx)) != 0) { | 2564 | fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) { |
2547 | error("Signing %s failed: %s", filename, ssh_err(r)); | 2565 | error("Signing %s failed: %s", filename, ssh_err(r)); |
2548 | goto out; | 2566 | goto out; |
2549 | } | 2567 | } |
@@ -2591,7 +2609,10 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2591 | r = 0; | 2609 | r = 0; |
2592 | out: | 2610 | out: |
2593 | free(wfile); | 2611 | free(wfile); |
2612 | free(prompt); | ||
2594 | free(asig); | 2613 | free(asig); |
2614 | if (pin != NULL) | ||
2615 | freezero(pin, strlen(pin)); | ||
2595 | sshbuf_free(abuf); | 2616 | sshbuf_free(abuf); |
2596 | sshbuf_free(sigbuf); | 2617 | sshbuf_free(sigbuf); |
2597 | if (wfd != -1) | 2618 | if (wfd != -1) |
@@ -3554,6 +3575,8 @@ main(int argc, char **argv) | |||
3554 | for (i = 0; i < nopts; i++) { | 3575 | for (i = 0; i < nopts; i++) { |
3555 | if (strcasecmp(opts[i], "no-touch-required") == 0) { | 3576 | if (strcasecmp(opts[i], "no-touch-required") == 0) { |
3556 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; | 3577 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; |
3578 | } else if (strcasecmp(opts[i], "verify-required") == 0) { | ||
3579 | sk_flags |= SSH_SK_USER_VERIFICATION_REQD; | ||
3557 | } else if (strcasecmp(opts[i], "resident") == 0) { | 3580 | } else if (strcasecmp(opts[i], "resident") == 0) { |
3558 | sk_flags |= SSH_SK_RESIDENT_KEY; | 3581 | sk_flags |= SSH_SK_RESIDENT_KEY; |
3559 | } else if (strncasecmp(opts[i], "device=", 7) == 0) { | 3582 | } else if (strncasecmp(opts[i], "device=", 7) == 0) { |