diff options
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r-- | ssh-keygen.c | 828 |
1 files changed, 603 insertions, 225 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index 8c829cad6..0d6ed1fff 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.355 2019/10/03 17:07:50 jmc Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.398 2020/02/07 03:27:54 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 |
@@ -63,6 +63,8 @@ | |||
63 | #include "utf8.h" | 63 | #include "utf8.h" |
64 | #include "authfd.h" | 64 | #include "authfd.h" |
65 | #include "sshsig.h" | 65 | #include "sshsig.h" |
66 | #include "ssh-sk.h" | ||
67 | #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */ | ||
66 | 68 | ||
67 | #ifdef WITH_OPENSSL | 69 | #ifdef WITH_OPENSSL |
68 | # define DEFAULT_KEY_TYPE_NAME "rsa" | 70 | # define DEFAULT_KEY_TYPE_NAME "rsa" |
@@ -118,11 +120,12 @@ static u_int64_t cert_valid_from = 0; | |||
118 | static u_int64_t cert_valid_to = ~0ULL; | 120 | static u_int64_t cert_valid_to = ~0ULL; |
119 | 121 | ||
120 | /* Certificate options */ | 122 | /* Certificate options */ |
121 | #define CERTOPT_X_FWD (1) | 123 | #define CERTOPT_X_FWD (1) |
122 | #define CERTOPT_AGENT_FWD (1<<1) | 124 | #define CERTOPT_AGENT_FWD (1<<1) |
123 | #define CERTOPT_PORT_FWD (1<<2) | 125 | #define CERTOPT_PORT_FWD (1<<2) |
124 | #define CERTOPT_PTY (1<<3) | 126 | #define CERTOPT_PTY (1<<3) |
125 | #define CERTOPT_USER_RC (1<<4) | 127 | #define CERTOPT_USER_RC (1<<4) |
128 | #define CERTOPT_NO_REQUIRE_USER_PRESENCE (1<<5) | ||
126 | #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ | 129 | #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ |
127 | CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) | 130 | CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) |
128 | static u_int32_t certflags_flags = CERTOPT_DEFAULT; | 131 | static u_int32_t certflags_flags = CERTOPT_DEFAULT; |
@@ -150,16 +153,16 @@ static char *key_type_name = NULL; | |||
150 | /* Load key from this PKCS#11 provider */ | 153 | /* Load key from this PKCS#11 provider */ |
151 | static char *pkcs11provider = NULL; | 154 | static char *pkcs11provider = NULL; |
152 | 155 | ||
156 | /* FIDO/U2F provider to use */ | ||
157 | static char *sk_provider = NULL; | ||
158 | |||
153 | /* Format for writing private keys */ | 159 | /* Format for writing private keys */ |
154 | static int private_key_format = SSHKEY_PRIVATE_OPENSSH; | 160 | static int private_key_format = SSHKEY_PRIVATE_OPENSSH; |
155 | 161 | ||
156 | /* Cipher for new-format private keys */ | 162 | /* Cipher for new-format private keys */ |
157 | static char *openssh_format_cipher = NULL; | 163 | static char *openssh_format_cipher = NULL; |
158 | 164 | ||
159 | /* | 165 | /* Number of KDF rounds to derive new format keys. */ |
160 | * Number of KDF rounds to derive new format keys / | ||
161 | * number of primality trials when screening moduli. | ||
162 | */ | ||
163 | static int rounds = 0; | 166 | static int rounds = 0; |
164 | 167 | ||
165 | /* argv0 */ | 168 | /* argv0 */ |
@@ -269,6 +272,10 @@ ask_filename(struct passwd *pw, const char *prompt) | |||
269 | case KEY_ECDSA: | 272 | case KEY_ECDSA: |
270 | name = _PATH_SSH_CLIENT_ID_ECDSA; | 273 | name = _PATH_SSH_CLIENT_ID_ECDSA; |
271 | break; | 274 | break; |
275 | case KEY_ECDSA_SK_CERT: | ||
276 | case KEY_ECDSA_SK: | ||
277 | name = _PATH_SSH_CLIENT_ID_ECDSA_SK; | ||
278 | break; | ||
272 | #endif | 279 | #endif |
273 | case KEY_RSA_CERT: | 280 | case KEY_RSA_CERT: |
274 | case KEY_RSA: | 281 | case KEY_RSA: |
@@ -278,6 +285,10 @@ ask_filename(struct passwd *pw, const char *prompt) | |||
278 | case KEY_ED25519_CERT: | 285 | case KEY_ED25519_CERT: |
279 | name = _PATH_SSH_CLIENT_ID_ED25519; | 286 | name = _PATH_SSH_CLIENT_ID_ED25519; |
280 | break; | 287 | break; |
288 | case KEY_ED25519_SK: | ||
289 | case KEY_ED25519_SK_CERT: | ||
290 | name = _PATH_SSH_CLIENT_ID_ED25519_SK; | ||
291 | break; | ||
281 | case KEY_XMSS: | 292 | case KEY_XMSS: |
282 | case KEY_XMSS_CERT: | 293 | case KEY_XMSS_CERT: |
283 | name = _PATH_SSH_CLIENT_ID_XMSS; | 294 | name = _PATH_SSH_CLIENT_ID_XMSS; |
@@ -391,6 +402,16 @@ do_convert_to_pem(struct sshkey *k) | |||
391 | if (!PEM_write_RSAPublicKey(stdout, k->rsa)) | 402 | if (!PEM_write_RSAPublicKey(stdout, k->rsa)) |
392 | fatal("PEM_write_RSAPublicKey failed"); | 403 | fatal("PEM_write_RSAPublicKey failed"); |
393 | break; | 404 | break; |
405 | case KEY_DSA: | ||
406 | if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) | ||
407 | fatal("PEM_write_DSA_PUBKEY failed"); | ||
408 | break; | ||
409 | #ifdef OPENSSL_HAS_ECC | ||
410 | case KEY_ECDSA: | ||
411 | if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) | ||
412 | fatal("PEM_write_EC_PUBKEY failed"); | ||
413 | break; | ||
414 | #endif | ||
394 | default: | 415 | default: |
395 | fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); | 416 | fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); |
396 | } | 417 | } |
@@ -568,8 +589,10 @@ do_convert_private_ssh2(struct sshbuf *b) | |||
568 | error("%s: remaining bytes in key blob %d", __func__, rlen); | 589 | error("%s: remaining bytes in key blob %d", __func__, rlen); |
569 | 590 | ||
570 | /* try the key */ | 591 | /* try the key */ |
571 | if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 || | 592 | if (sshkey_sign(key, &sig, &slen, data, sizeof(data), |
572 | sshkey_verify(key, sig, slen, data, sizeof(data), NULL, 0) != 0) { | 593 | NULL, NULL, 0) != 0 || |
594 | sshkey_verify(key, sig, slen, data, sizeof(data), | ||
595 | NULL, 0, NULL) != 0) { | ||
573 | sshkey_free(key); | 596 | sshkey_free(key); |
574 | free(sig); | 597 | free(sig); |
575 | return NULL; | 598 | return NULL; |
@@ -651,6 +674,7 @@ do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) | |||
651 | *k = do_convert_private_ssh2(buf); | 674 | *k = do_convert_private_ssh2(buf); |
652 | else if ((r = sshkey_fromb(buf, k)) != 0) | 675 | else if ((r = sshkey_fromb(buf, k)) != 0) |
653 | fatal("decode blob failed: %s", ssh_err(r)); | 676 | fatal("decode blob failed: %s", ssh_err(r)); |
677 | sshbuf_free(buf); | ||
654 | fclose(fp); | 678 | fclose(fp); |
655 | } | 679 | } |
656 | 680 | ||
@@ -807,13 +831,13 @@ do_download(struct passwd *pw) | |||
807 | int i, nkeys; | 831 | int i, nkeys; |
808 | enum sshkey_fp_rep rep; | 832 | enum sshkey_fp_rep rep; |
809 | int fptype; | 833 | int fptype; |
810 | char *fp, *ra; | 834 | char *fp, *ra, **comments = NULL; |
811 | 835 | ||
812 | fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; | 836 | fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; |
813 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; | 837 | rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; |
814 | 838 | ||
815 | pkcs11_init(1); | 839 | pkcs11_init(1); |
816 | nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys); | 840 | nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments); |
817 | if (nkeys <= 0) | 841 | if (nkeys <= 0) |
818 | fatal("cannot read public key from pkcs11"); | 842 | fatal("cannot read public key from pkcs11"); |
819 | for (i = 0; i < nkeys; i++) { | 843 | for (i = 0; i < nkeys; i++) { |
@@ -831,10 +855,13 @@ do_download(struct passwd *pw) | |||
831 | free(fp); | 855 | free(fp); |
832 | } else { | 856 | } else { |
833 | (void) sshkey_write(keys[i], stdout); /* XXX check */ | 857 | (void) sshkey_write(keys[i], stdout); /* XXX check */ |
834 | fprintf(stdout, "\n"); | 858 | fprintf(stdout, "%s%s\n", |
859 | *(comments[i]) == '\0' ? "" : " ", comments[i]); | ||
835 | } | 860 | } |
861 | free(comments[i]); | ||
836 | sshkey_free(keys[i]); | 862 | sshkey_free(keys[i]); |
837 | } | 863 | } |
864 | free(comments); | ||
838 | free(keys); | 865 | free(keys); |
839 | pkcs11_terminate(); | 866 | pkcs11_terminate(); |
840 | exit(0); | 867 | exit(0); |
@@ -1247,8 +1274,10 @@ known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) | |||
1247 | if (fp == NULL || ra == NULL) | 1274 | if (fp == NULL || ra == NULL) |
1248 | fatal("%s: sshkey_fingerprint failed", | 1275 | fatal("%s: sshkey_fingerprint failed", |
1249 | __func__); | 1276 | __func__); |
1250 | mprintf("%s %s %s %s\n", ctx->host, | 1277 | mprintf("%s %s %s%s%s\n", ctx->host, |
1251 | sshkey_type(l->key), fp, l->comment); | 1278 | sshkey_type(l->key), fp, |
1279 | l->comment[0] ? " " : "", | ||
1280 | l->comment); | ||
1252 | if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) | 1281 | if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) |
1253 | printf("%s\n", ra); | 1282 | printf("%s\n", ra); |
1254 | free(ra); | 1283 | free(ra); |
@@ -1650,6 +1679,9 @@ prepare_options_buf(struct sshbuf *c, int which) | |||
1650 | (certflags_flags & CERTOPT_USER_RC) != 0) | 1679 | (certflags_flags & CERTOPT_USER_RC) != 0) |
1651 | add_flag_option(c, "permit-user-rc"); | 1680 | add_flag_option(c, "permit-user-rc"); |
1652 | if ((which & OPTIONS_CRITICAL) != 0 && | 1681 | if ((which & OPTIONS_CRITICAL) != 0 && |
1682 | (certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) | ||
1683 | add_flag_option(c, "no-touch-required"); | ||
1684 | if ((which & OPTIONS_CRITICAL) != 0 && | ||
1653 | certflags_src_addr != NULL) | 1685 | certflags_src_addr != NULL) |
1654 | add_string_option(c, "source-address", certflags_src_addr); | 1686 | add_string_option(c, "source-address", certflags_src_addr); |
1655 | for (i = 0; i < ncert_userext; i++) { | 1687 | for (i = 0; i < ncert_userext; i++) { |
@@ -1676,7 +1708,8 @@ load_pkcs11_key(char *path) | |||
1676 | fatal("Couldn't load CA public key \"%s\": %s", | 1708 | fatal("Couldn't load CA public key \"%s\": %s", |
1677 | path, ssh_err(r)); | 1709 | path, ssh_err(r)); |
1678 | 1710 | ||
1679 | nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys); | 1711 | nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, |
1712 | &keys, NULL); | ||
1680 | debug3("%s: %d keys", __func__, nkeys); | 1713 | debug3("%s: %d keys", __func__, nkeys); |
1681 | if (nkeys <= 0) | 1714 | if (nkeys <= 0) |
1682 | fatal("cannot read public key from pkcs11"); | 1715 | fatal("cannot read public key from pkcs11"); |
@@ -1699,7 +1732,7 @@ load_pkcs11_key(char *path) | |||
1699 | static int | 1732 | static int |
1700 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, | 1733 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, |
1701 | const u_char *data, size_t datalen, | 1734 | const u_char *data, size_t datalen, |
1702 | const char *alg, u_int compat, void *ctx) | 1735 | const char *alg, const char *provider, u_int compat, void *ctx) |
1703 | { | 1736 | { |
1704 | int *agent_fdp = (int *)ctx; | 1737 | int *agent_fdp = (int *)ctx; |
1705 | 1738 | ||
@@ -1715,10 +1748,12 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1715 | int r, i, fd, found, agent_fd = -1; | 1748 | int r, i, fd, found, agent_fd = -1; |
1716 | u_int n; | 1749 | u_int n; |
1717 | struct sshkey *ca, *public; | 1750 | struct sshkey *ca, *public; |
1718 | char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL; | 1751 | char valid[64], *otmp, *tmp, *cp, *out, *comment; |
1752 | char *ca_fp = NULL, **plist = NULL; | ||
1719 | FILE *f; | 1753 | FILE *f; |
1720 | struct ssh_identitylist *agent_ids; | 1754 | struct ssh_identitylist *agent_ids; |
1721 | size_t j; | 1755 | size_t j; |
1756 | struct notifier_ctx *notifier = NULL; | ||
1722 | 1757 | ||
1723 | #ifdef ENABLE_PKCS11 | 1758 | #ifdef ENABLE_PKCS11 |
1724 | pkcs11_init(1); | 1759 | pkcs11_init(1); |
@@ -1759,11 +1794,16 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1759 | } | 1794 | } |
1760 | free(tmp); | 1795 | free(tmp); |
1761 | 1796 | ||
1762 | if (key_type_name != NULL && | 1797 | if (key_type_name != NULL) { |
1763 | sshkey_type_from_name(key_type_name) != ca->type) { | 1798 | if (sshkey_type_from_name(key_type_name) != ca->type) { |
1764 | fatal("CA key type %s doesn't match specified %s", | 1799 | fatal("CA key type %s doesn't match specified %s", |
1765 | sshkey_ssh_name(ca), key_type_name); | 1800 | sshkey_ssh_name(ca), key_type_name); |
1801 | } | ||
1802 | } else if (ca->type == KEY_RSA) { | ||
1803 | /* Default to a good signature algorithm */ | ||
1804 | key_type_name = "rsa-sha2-512"; | ||
1766 | } | 1805 | } |
1806 | ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); | ||
1767 | 1807 | ||
1768 | for (i = 0; i < argc; i++) { | 1808 | for (i = 0; i < argc; i++) { |
1769 | /* Split list of principals */ | 1809 | /* Split list of principals */ |
@@ -1785,9 +1825,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1785 | if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) | 1825 | if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) |
1786 | fatal("%s: unable to open \"%s\": %s", | 1826 | fatal("%s: unable to open \"%s\": %s", |
1787 | __func__, tmp, ssh_err(r)); | 1827 | __func__, tmp, ssh_err(r)); |
1788 | if (public->type != KEY_RSA && public->type != KEY_DSA && | 1828 | if (sshkey_is_cert(public)) |
1789 | public->type != KEY_ECDSA && public->type != KEY_ED25519 && | ||
1790 | public->type != KEY_XMSS) | ||
1791 | fatal("%s: key \"%s\" type %s cannot be certified", | 1829 | fatal("%s: key \"%s\" type %s cannot be certified", |
1792 | __func__, tmp, sshkey_type(public)); | 1830 | __func__, tmp, sshkey_type(public)); |
1793 | 1831 | ||
@@ -1811,11 +1849,21 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1811 | 1849 | ||
1812 | if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { | 1850 | if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { |
1813 | if ((r = sshkey_certify_custom(public, ca, | 1851 | if ((r = sshkey_certify_custom(public, ca, |
1814 | key_type_name, agent_signer, &agent_fd)) != 0) | 1852 | key_type_name, sk_provider, agent_signer, |
1853 | &agent_fd)) != 0) | ||
1815 | fatal("Couldn't certify key %s via agent: %s", | 1854 | fatal("Couldn't certify key %s via agent: %s", |
1816 | tmp, ssh_err(r)); | 1855 | tmp, ssh_err(r)); |
1817 | } else { | 1856 | } else { |
1818 | if ((sshkey_certify(public, ca, key_type_name)) != 0) | 1857 | if (sshkey_is_sk(ca) && |
1858 | (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | ||
1859 | notifier = notify_start(0, | ||
1860 | "Confirm user presence for key %s %s", | ||
1861 | sshkey_type(ca), ca_fp); | ||
1862 | } | ||
1863 | r = sshkey_certify(public, ca, key_type_name, | ||
1864 | sk_provider); | ||
1865 | notify_complete(notifier); | ||
1866 | if (r != 0) | ||
1819 | fatal("Couldn't certify key %s: %s", | 1867 | fatal("Couldn't certify key %s: %s", |
1820 | tmp, ssh_err(r)); | 1868 | tmp, ssh_err(r)); |
1821 | } | 1869 | } |
@@ -1853,6 +1901,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1853 | if (cert_serial_autoinc) | 1901 | if (cert_serial_autoinc) |
1854 | cert_serial++; | 1902 | cert_serial++; |
1855 | } | 1903 | } |
1904 | free(ca_fp); | ||
1856 | #ifdef ENABLE_PKCS11 | 1905 | #ifdef ENABLE_PKCS11 |
1857 | pkcs11_terminate(); | 1906 | pkcs11_terminate(); |
1858 | #endif | 1907 | #endif |
@@ -1951,6 +2000,10 @@ add_cert_option(char *opt) | |||
1951 | certflags_flags &= ~CERTOPT_USER_RC; | 2000 | certflags_flags &= ~CERTOPT_USER_RC; |
1952 | else if (strcasecmp(opt, "permit-user-rc") == 0) | 2001 | else if (strcasecmp(opt, "permit-user-rc") == 0) |
1953 | certflags_flags |= CERTOPT_USER_RC; | 2002 | certflags_flags |= CERTOPT_USER_RC; |
2003 | else if (strcasecmp(opt, "touch-required") == 0) | ||
2004 | certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE; | ||
2005 | else if (strcasecmp(opt, "no-touch-required") == 0) | ||
2006 | certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE; | ||
1954 | else if (strncasecmp(opt, "force-command=", 14) == 0) { | 2007 | else if (strncasecmp(opt, "force-command=", 14) == 0) { |
1955 | val = opt + 14; | 2008 | val = opt + 14; |
1956 | if (*val == '\0') | 2009 | if (*val == '\0') |
@@ -2004,9 +2057,10 @@ show_options(struct sshbuf *optbuf, int in_critical) | |||
2004 | strcmp(name, "permit-agent-forwarding") == 0 || | 2057 | strcmp(name, "permit-agent-forwarding") == 0 || |
2005 | strcmp(name, "permit-port-forwarding") == 0 || | 2058 | strcmp(name, "permit-port-forwarding") == 0 || |
2006 | strcmp(name, "permit-pty") == 0 || | 2059 | strcmp(name, "permit-pty") == 0 || |
2007 | strcmp(name, "permit-user-rc") == 0)) | 2060 | strcmp(name, "permit-user-rc") == 0 || |
2061 | strcmp(name, "no-touch-required") == 0)) { | ||
2008 | printf("\n"); | 2062 | printf("\n"); |
2009 | else if (in_critical && | 2063 | } else if (in_critical && |
2010 | (strcmp(name, "force-command") == 0 || | 2064 | (strcmp(name, "force-command") == 0 || |
2011 | strcmp(name, "source-address") == 0)) { | 2065 | strcmp(name, "source-address") == 0)) { |
2012 | if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) | 2066 | if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) |
@@ -2135,15 +2189,10 @@ static void | |||
2135 | load_krl(const char *path, struct ssh_krl **krlp) | 2189 | load_krl(const char *path, struct ssh_krl **krlp) |
2136 | { | 2190 | { |
2137 | struct sshbuf *krlbuf; | 2191 | struct sshbuf *krlbuf; |
2138 | int r, fd; | 2192 | int r; |
2139 | 2193 | ||
2140 | if ((krlbuf = sshbuf_new()) == NULL) | 2194 | if ((r = sshbuf_load_file(path, &krlbuf)) != 0) |
2141 | fatal("sshbuf_new failed"); | ||
2142 | if ((fd = open(path, O_RDONLY)) == -1) | ||
2143 | fatal("open %s: %s", path, strerror(errno)); | ||
2144 | if ((r = sshkey_load_file(fd, krlbuf)) != 0) | ||
2145 | fatal("Unable to load KRL: %s", ssh_err(r)); | 2195 | fatal("Unable to load KRL: %s", ssh_err(r)); |
2146 | close(fd); | ||
2147 | /* XXX check sigs */ | 2196 | /* XXX check sigs */ |
2148 | if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 || | 2197 | if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 || |
2149 | *krlp == NULL) | 2198 | *krlp == NULL) |
@@ -2345,7 +2394,7 @@ do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path, | |||
2345 | struct ssh_krl *krl; | 2394 | struct ssh_krl *krl; |
2346 | struct stat sb; | 2395 | struct stat sb; |
2347 | struct sshkey *ca = NULL; | 2396 | struct sshkey *ca = NULL; |
2348 | int fd, i, r, wild_ca = 0; | 2397 | int i, r, wild_ca = 0; |
2349 | char *tmp; | 2398 | char *tmp; |
2350 | struct sshbuf *kbuf; | 2399 | struct sshbuf *kbuf; |
2351 | 2400 | ||
@@ -2387,12 +2436,8 @@ do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path, | |||
2387 | fatal("sshbuf_new failed"); | 2436 | fatal("sshbuf_new failed"); |
2388 | if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0) | 2437 | if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0) |
2389 | fatal("Couldn't generate KRL"); | 2438 | fatal("Couldn't generate KRL"); |
2390 | if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) | 2439 | if ((r = sshbuf_write_file(identity_file, kbuf)) != 0) |
2391 | fatal("open %s: %s", identity_file, strerror(errno)); | ||
2392 | if (atomicio(vwrite, fd, sshbuf_mutable_ptr(kbuf), sshbuf_len(kbuf)) != | ||
2393 | sshbuf_len(kbuf)) | ||
2394 | fatal("write %s: %s", identity_file, strerror(errno)); | 2440 | fatal("write %s: %s", identity_file, strerror(errno)); |
2395 | close(fd); | ||
2396 | sshbuf_free(kbuf); | 2441 | sshbuf_free(kbuf); |
2397 | ssh_krl_free(krl); | 2442 | ssh_krl_free(krl); |
2398 | sshkey_free(ca); | 2443 | sshkey_free(ca); |
@@ -2488,8 +2533,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2488 | { | 2533 | { |
2489 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | 2534 | struct sshbuf *sigbuf = NULL, *abuf = NULL; |
2490 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; | 2535 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; |
2491 | char *wfile = NULL; | 2536 | char *wfile = NULL, *asig = NULL, *fp = NULL; |
2492 | char *asig = NULL; | ||
2493 | 2537 | ||
2494 | if (!quiet) { | 2538 | if (!quiet) { |
2495 | if (fd == STDIN_FILENO) | 2539 | if (fd == STDIN_FILENO) |
@@ -2497,7 +2541,16 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2497 | else | 2541 | else |
2498 | fprintf(stderr, "Signing file %s\n", filename); | 2542 | fprintf(stderr, "Signing file %s\n", filename); |
2499 | } | 2543 | } |
2500 | if ((r = sshsig_sign_fd(signkey, NULL, fd, sig_namespace, | 2544 | if (signer == NULL && sshkey_is_sk(signkey) && |
2545 | (signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | ||
2546 | if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, | ||
2547 | SSH_FP_DEFAULT)) == NULL) | ||
2548 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2549 | fprintf(stderr, "Confirm user presence for key %s %s\n", | ||
2550 | sshkey_type(signkey), fp); | ||
2551 | free(fp); | ||
2552 | } | ||
2553 | if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, fd, sig_namespace, | ||
2501 | &sigbuf, signer, signer_ctx)) != 0) { | 2554 | &sigbuf, signer, signer_ctx)) != 0) { |
2502 | error("Signing %s failed: %s", filename, ssh_err(r)); | 2555 | error("Signing %s failed: %s", filename, ssh_err(r)); |
2503 | goto out; | 2556 | goto out; |
@@ -2555,7 +2608,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2555 | } | 2608 | } |
2556 | 2609 | ||
2557 | static int | 2610 | static int |
2558 | sign(const char *keypath, const char *sig_namespace, int argc, char **argv) | 2611 | sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv) |
2559 | { | 2612 | { |
2560 | int i, fd = -1, r, ret = -1; | 2613 | int i, fd = -1, r, ret = -1; |
2561 | int agent_fd = -1; | 2614 | int agent_fd = -1; |
@@ -2626,38 +2679,37 @@ done: | |||
2626 | } | 2679 | } |
2627 | 2680 | ||
2628 | static int | 2681 | static int |
2629 | verify(const char *signature, const char *sig_namespace, const char *principal, | 2682 | sig_verify(const char *signature, const char *sig_namespace, |
2630 | const char *allowed_keys, const char *revoked_keys) | 2683 | const char *principal, const char *allowed_keys, const char *revoked_keys) |
2631 | { | 2684 | { |
2632 | int r, ret = -1, sigfd = -1; | 2685 | int r, ret = -1; |
2633 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | 2686 | struct sshbuf *sigbuf = NULL, *abuf = NULL; |
2634 | struct sshkey *sign_key = NULL; | 2687 | struct sshkey *sign_key = NULL; |
2635 | char *fp = NULL; | 2688 | char *fp = NULL; |
2689 | struct sshkey_sig_details *sig_details = NULL; | ||
2636 | 2690 | ||
2637 | if ((abuf = sshbuf_new()) == NULL) | 2691 | memset(&sig_details, 0, sizeof(sig_details)); |
2638 | fatal("%s: sshbuf_new() failed", __func__); | 2692 | if ((r = sshbuf_load_file(signature, &abuf)) != 0) { |
2639 | |||
2640 | if ((sigfd = open(signature, O_RDONLY)) < 0) { | ||
2641 | error("Couldn't open signature file %s", signature); | ||
2642 | goto done; | ||
2643 | } | ||
2644 | |||
2645 | if ((r = sshkey_load_file(sigfd, abuf)) != 0) { | ||
2646 | error("Couldn't read signature file: %s", ssh_err(r)); | 2693 | error("Couldn't read signature file: %s", ssh_err(r)); |
2647 | goto done; | 2694 | goto done; |
2648 | } | 2695 | } |
2696 | |||
2649 | if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { | 2697 | if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { |
2650 | error("%s: sshsig_armor: %s", __func__, ssh_err(r)); | 2698 | error("%s: sshsig_armor: %s", __func__, ssh_err(r)); |
2651 | return r; | 2699 | goto done; |
2652 | } | 2700 | } |
2653 | if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, | 2701 | if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, |
2654 | &sign_key)) != 0) | 2702 | &sign_key, &sig_details)) != 0) |
2655 | goto done; /* sshsig_verify() prints error */ | 2703 | goto done; /* sshsig_verify() prints error */ |
2656 | 2704 | ||
2657 | if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, | 2705 | if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, |
2658 | SSH_FP_DEFAULT)) == NULL) | 2706 | SSH_FP_DEFAULT)) == NULL) |
2659 | fatal("%s: sshkey_fingerprint failed", __func__); | 2707 | fatal("%s: sshkey_fingerprint failed", __func__); |
2660 | debug("Valid (unverified) signature from key %s", fp); | 2708 | debug("Valid (unverified) signature from key %s", fp); |
2709 | if (sig_details != NULL) { | ||
2710 | debug2("%s: signature details: counter = %u, flags = 0x%02x", | ||
2711 | __func__, sig_details->sk_counter, sig_details->sk_flags); | ||
2712 | } | ||
2661 | free(fp); | 2713 | free(fp); |
2662 | fp = NULL; | 2714 | fp = NULL; |
2663 | 2715 | ||
@@ -2697,21 +2749,312 @@ done: | |||
2697 | printf("Could not verify signature.\n"); | 2749 | printf("Could not verify signature.\n"); |
2698 | } | 2750 | } |
2699 | } | 2751 | } |
2700 | if (sigfd != -1) | ||
2701 | close(sigfd); | ||
2702 | sshbuf_free(sigbuf); | 2752 | sshbuf_free(sigbuf); |
2703 | sshbuf_free(abuf); | 2753 | sshbuf_free(abuf); |
2704 | sshkey_free(sign_key); | 2754 | sshkey_free(sign_key); |
2755 | sshkey_sig_details_free(sig_details); | ||
2705 | free(fp); | 2756 | free(fp); |
2706 | return ret; | 2757 | return ret; |
2707 | } | 2758 | } |
2708 | 2759 | ||
2760 | static int | ||
2761 | sig_find_principals(const char *signature, const char *allowed_keys) { | ||
2762 | int r, ret = -1; | ||
2763 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | ||
2764 | struct sshkey *sign_key = NULL; | ||
2765 | char *principals = NULL, *cp, *tmp; | ||
2766 | |||
2767 | if ((r = sshbuf_load_file(signature, &abuf)) != 0) { | ||
2768 | error("Couldn't read signature file: %s", ssh_err(r)); | ||
2769 | goto done; | ||
2770 | } | ||
2771 | if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { | ||
2772 | error("%s: sshsig_armor: %s", __func__, ssh_err(r)); | ||
2773 | goto done; | ||
2774 | } | ||
2775 | if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) { | ||
2776 | error("%s: sshsig_get_pubkey: %s", | ||
2777 | __func__, ssh_err(r)); | ||
2778 | goto done; | ||
2779 | } | ||
2780 | if ((r = sshsig_find_principals(allowed_keys, sign_key, | ||
2781 | &principals)) != 0) { | ||
2782 | error("%s: sshsig_get_principal: %s", | ||
2783 | __func__, ssh_err(r)); | ||
2784 | goto done; | ||
2785 | } | ||
2786 | ret = 0; | ||
2787 | done: | ||
2788 | if (ret == 0 ) { | ||
2789 | /* Emit matching principals one per line */ | ||
2790 | tmp = principals; | ||
2791 | while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0') | ||
2792 | puts(cp); | ||
2793 | } else { | ||
2794 | fprintf(stderr, "No principal matched.\n"); | ||
2795 | } | ||
2796 | sshbuf_free(sigbuf); | ||
2797 | sshbuf_free(abuf); | ||
2798 | sshkey_free(sign_key); | ||
2799 | free(principals); | ||
2800 | return ret; | ||
2801 | } | ||
2802 | |||
2803 | static void | ||
2804 | do_moduli_gen(const char *out_file, char **opts, size_t nopts) | ||
2805 | { | ||
2806 | #ifdef WITH_OPENSSL | ||
2807 | /* Moduli generation/screening */ | ||
2808 | u_int32_t memory = 0; | ||
2809 | BIGNUM *start = NULL; | ||
2810 | int moduli_bits = 0; | ||
2811 | FILE *out; | ||
2812 | size_t i; | ||
2813 | const char *errstr; | ||
2814 | |||
2815 | /* Parse options */ | ||
2816 | for (i = 0; i < nopts; i++) { | ||
2817 | if (strncmp(opts[i], "memory=", 7) == 0) { | ||
2818 | memory = (u_int32_t)strtonum(opts[i]+7, 1, | ||
2819 | UINT_MAX, &errstr); | ||
2820 | if (errstr) { | ||
2821 | fatal("Memory limit is %s: %s", | ||
2822 | errstr, opts[i]+7); | ||
2823 | } | ||
2824 | } else if (strncmp(opts[i], "start=", 6) == 0) { | ||
2825 | /* XXX - also compare length against bits */ | ||
2826 | if (BN_hex2bn(&start, opts[i]+6) == 0) | ||
2827 | fatal("Invalid start point."); | ||
2828 | } else if (strncmp(opts[i], "bits=", 5) == 0) { | ||
2829 | moduli_bits = (int)strtonum(opts[i]+5, 1, | ||
2830 | INT_MAX, &errstr); | ||
2831 | if (errstr) { | ||
2832 | fatal("Invalid number: %s (%s)", | ||
2833 | opts[i]+12, errstr); | ||
2834 | } | ||
2835 | } else { | ||
2836 | fatal("Option \"%s\" is unsupported for moduli " | ||
2837 | "generation", opts[i]); | ||
2838 | } | ||
2839 | } | ||
2840 | |||
2841 | if ((out = fopen(out_file, "w")) == NULL) { | ||
2842 | fatal("Couldn't open modulus candidate file \"%s\": %s", | ||
2843 | out_file, strerror(errno)); | ||
2844 | } | ||
2845 | setvbuf(out, NULL, _IOLBF, 0); | ||
2846 | |||
2847 | if (moduli_bits == 0) | ||
2848 | moduli_bits = DEFAULT_BITS; | ||
2849 | if (gen_candidates(out, memory, moduli_bits, start) != 0) | ||
2850 | fatal("modulus candidate generation failed"); | ||
2851 | #else /* WITH_OPENSSL */ | ||
2852 | fatal("Moduli generation is not supported"); | ||
2853 | #endif /* WITH_OPENSSL */ | ||
2854 | } | ||
2855 | |||
2856 | static void | ||
2857 | do_moduli_screen(const char *out_file, char **opts, size_t nopts) | ||
2858 | { | ||
2859 | #ifdef WITH_OPENSSL | ||
2860 | /* Moduli generation/screening */ | ||
2861 | char *checkpoint = NULL; | ||
2862 | u_int32_t generator_wanted = 0; | ||
2863 | unsigned long start_lineno = 0, lines_to_process = 0; | ||
2864 | int prime_tests = 0; | ||
2865 | FILE *out, *in = stdin; | ||
2866 | size_t i; | ||
2867 | const char *errstr; | ||
2868 | |||
2869 | /* Parse options */ | ||
2870 | for (i = 0; i < nopts; i++) { | ||
2871 | if (strncmp(opts[i], "lines=", 6) == 0) { | ||
2872 | lines_to_process = strtoul(opts[i]+6, NULL, 10); | ||
2873 | } else if (strncmp(opts[i], "start-line=", 11) == 0) { | ||
2874 | start_lineno = strtoul(opts[i]+11, NULL, 10); | ||
2875 | } else if (strncmp(opts[i], "checkpoint=", 11) == 0) { | ||
2876 | checkpoint = xstrdup(opts[i]+11); | ||
2877 | } else if (strncmp(opts[i], "generator=", 10) == 0) { | ||
2878 | generator_wanted = (u_int32_t)strtonum( | ||
2879 | opts[i]+10, 1, UINT_MAX, &errstr); | ||
2880 | if (errstr != NULL) { | ||
2881 | fatal("Generator invalid: %s (%s)", | ||
2882 | opts[i]+10, errstr); | ||
2883 | } | ||
2884 | } else if (strncmp(opts[i], "prime-tests=", 12) == 0) { | ||
2885 | prime_tests = (int)strtonum(opts[i]+12, 1, | ||
2886 | INT_MAX, &errstr); | ||
2887 | if (errstr) { | ||
2888 | fatal("Invalid number: %s (%s)", | ||
2889 | opts[i]+12, errstr); | ||
2890 | } | ||
2891 | } else { | ||
2892 | fatal("Option \"%s\" is unsupported for moduli " | ||
2893 | "screening", opts[i]); | ||
2894 | } | ||
2895 | } | ||
2896 | |||
2897 | if (have_identity && strcmp(identity_file, "-") != 0) { | ||
2898 | if ((in = fopen(identity_file, "r")) == NULL) { | ||
2899 | fatal("Couldn't open modulus candidate " | ||
2900 | "file \"%s\": %s", identity_file, | ||
2901 | strerror(errno)); | ||
2902 | } | ||
2903 | } | ||
2904 | |||
2905 | if ((out = fopen(out_file, "a")) == NULL) { | ||
2906 | fatal("Couldn't open moduli file \"%s\": %s", | ||
2907 | out_file, strerror(errno)); | ||
2908 | } | ||
2909 | setvbuf(out, NULL, _IOLBF, 0); | ||
2910 | if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests, | ||
2911 | generator_wanted, checkpoint, | ||
2912 | start_lineno, lines_to_process) != 0) | ||
2913 | fatal("modulus screening failed"); | ||
2914 | #else /* WITH_OPENSSL */ | ||
2915 | fatal("Moduli screening is not supported"); | ||
2916 | #endif /* WITH_OPENSSL */ | ||
2917 | } | ||
2918 | |||
2919 | static char * | ||
2920 | private_key_passphrase(void) | ||
2921 | { | ||
2922 | char *passphrase1, *passphrase2; | ||
2923 | |||
2924 | /* Ask for a passphrase (twice). */ | ||
2925 | if (identity_passphrase) | ||
2926 | passphrase1 = xstrdup(identity_passphrase); | ||
2927 | else if (identity_new_passphrase) | ||
2928 | passphrase1 = xstrdup(identity_new_passphrase); | ||
2929 | else { | ||
2930 | passphrase_again: | ||
2931 | passphrase1 = | ||
2932 | read_passphrase("Enter passphrase (empty for no " | ||
2933 | "passphrase): ", RP_ALLOW_STDIN); | ||
2934 | passphrase2 = read_passphrase("Enter same passphrase again: ", | ||
2935 | RP_ALLOW_STDIN); | ||
2936 | if (strcmp(passphrase1, passphrase2) != 0) { | ||
2937 | /* | ||
2938 | * The passphrases do not match. Clear them and | ||
2939 | * retry. | ||
2940 | */ | ||
2941 | freezero(passphrase1, strlen(passphrase1)); | ||
2942 | freezero(passphrase2, strlen(passphrase2)); | ||
2943 | printf("Passphrases do not match. Try again.\n"); | ||
2944 | goto passphrase_again; | ||
2945 | } | ||
2946 | /* Clear the other copy of the passphrase. */ | ||
2947 | freezero(passphrase2, strlen(passphrase2)); | ||
2948 | } | ||
2949 | return passphrase1; | ||
2950 | } | ||
2951 | |||
2952 | static const char * | ||
2953 | skip_ssh_url_preamble(const char *s) | ||
2954 | { | ||
2955 | if (strncmp(s, "ssh://", 6) == 0) | ||
2956 | return s + 6; | ||
2957 | else if (strncmp(s, "ssh:", 4) == 0) | ||
2958 | return s + 4; | ||
2959 | return s; | ||
2960 | } | ||
2961 | |||
2962 | static int | ||
2963 | do_download_sk(const char *skprovider, const char *device) | ||
2964 | { | ||
2965 | struct sshkey **keys; | ||
2966 | size_t nkeys, i; | ||
2967 | int r, ok = -1; | ||
2968 | char *fp, *pin, *pass = NULL, *path, *pubpath; | ||
2969 | const char *ext; | ||
2970 | |||
2971 | if (skprovider == NULL) | ||
2972 | fatal("Cannot download keys without provider"); | ||
2973 | |||
2974 | pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); | ||
2975 | if ((r = sshsk_load_resident(skprovider, device, pin, | ||
2976 | &keys, &nkeys)) != 0) { | ||
2977 | freezero(pin, strlen(pin)); | ||
2978 | error("Unable to load resident keys: %s", ssh_err(r)); | ||
2979 | return -1; | ||
2980 | } | ||
2981 | if (nkeys == 0) | ||
2982 | logit("No keys to download"); | ||
2983 | freezero(pin, strlen(pin)); | ||
2984 | |||
2985 | for (i = 0; i < nkeys; i++) { | ||
2986 | if (keys[i]->type != KEY_ECDSA_SK && | ||
2987 | keys[i]->type != KEY_ED25519_SK) { | ||
2988 | error("Unsupported key type %s (%d)", | ||
2989 | sshkey_type(keys[i]), keys[i]->type); | ||
2990 | continue; | ||
2991 | } | ||
2992 | if ((fp = sshkey_fingerprint(keys[i], | ||
2993 | fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
2994 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2995 | debug("%s: key %zu: %s %s %s (flags 0x%02x)", __func__, i, | ||
2996 | sshkey_type(keys[i]), fp, keys[i]->sk_application, | ||
2997 | keys[i]->sk_flags); | ||
2998 | ext = skip_ssh_url_preamble(keys[i]->sk_application); | ||
2999 | xasprintf(&path, "id_%s_rk%s%s", | ||
3000 | keys[i]->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk", | ||
3001 | *ext == '\0' ? "" : "_", ext); | ||
3002 | |||
3003 | /* If the file already exists, ask the user to confirm. */ | ||
3004 | if (!confirm_overwrite(path)) { | ||
3005 | free(path); | ||
3006 | break; | ||
3007 | } | ||
3008 | |||
3009 | /* Save the key with the application string as the comment */ | ||
3010 | if (pass == NULL) | ||
3011 | pass = private_key_passphrase(); | ||
3012 | if ((r = sshkey_save_private(keys[i], path, pass, | ||
3013 | keys[i]->sk_application, private_key_format, | ||
3014 | openssh_format_cipher, rounds)) != 0) { | ||
3015 | error("Saving key \"%s\" failed: %s", | ||
3016 | path, ssh_err(r)); | ||
3017 | free(path); | ||
3018 | break; | ||
3019 | } | ||
3020 | if (!quiet) { | ||
3021 | printf("Saved %s key%s%s to %s\n", | ||
3022 | sshkey_type(keys[i]), | ||
3023 | *ext != '\0' ? " " : "", | ||
3024 | *ext != '\0' ? keys[i]->sk_application : "", | ||
3025 | path); | ||
3026 | } | ||
3027 | |||
3028 | /* Save public key too */ | ||
3029 | xasprintf(&pubpath, "%s.pub", path); | ||
3030 | free(path); | ||
3031 | if ((r = sshkey_save_public(keys[i], pubpath, | ||
3032 | keys[i]->sk_application)) != 0) { | ||
3033 | free(pubpath); | ||
3034 | error("Saving public key \"%s\" failed: %s", | ||
3035 | pubpath, ssh_err(r)); | ||
3036 | break; | ||
3037 | } | ||
3038 | free(pubpath); | ||
3039 | } | ||
3040 | |||
3041 | if (i >= nkeys) | ||
3042 | ok = 0; /* success */ | ||
3043 | if (pass != NULL) | ||
3044 | freezero(pass, strlen(pass)); | ||
3045 | for (i = 0; i < nkeys; i++) | ||
3046 | sshkey_free(keys[i]); | ||
3047 | free(keys); | ||
3048 | return ok ? 0 : -1; | ||
3049 | } | ||
3050 | |||
2709 | static void | 3051 | static void |
2710 | usage(void) | 3052 | usage(void) |
2711 | { | 3053 | { |
2712 | fprintf(stderr, | 3054 | fprintf(stderr, |
2713 | "usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]\n" | 3055 | "usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]\n" |
2714 | " [-N new_passphrase] [-t dsa | ecdsa | ed25519 | rsa]\n" | 3056 | " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n" |
3057 | " [-N new_passphrase] [-O option] [-w provider]\n" | ||
2715 | " ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]\n" | 3058 | " ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]\n" |
2716 | " [-P old_passphrase]\n" | 3059 | " [-P old_passphrase]\n" |
2717 | " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" | 3060 | " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" |
@@ -2727,12 +3070,12 @@ usage(void) | |||
2727 | fprintf(stderr, | 3070 | fprintf(stderr, |
2728 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" | 3071 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" |
2729 | " ssh-keygen -H [-f known_hosts_file]\n" | 3072 | " ssh-keygen -H [-f known_hosts_file]\n" |
3073 | " ssh-keygen -K [-w provider]\n" | ||
2730 | " ssh-keygen -R hostname [-f known_hosts_file]\n" | 3074 | " ssh-keygen -R hostname [-f known_hosts_file]\n" |
2731 | " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" | 3075 | " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" |
2732 | #ifdef WITH_OPENSSL | 3076 | #ifdef WITH_OPENSSL |
2733 | " ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n" | 3077 | " ssh-keygen -M generate [-O option] output_file\n" |
2734 | " ssh-keygen -f input_file -T output_file [-v] [-a rounds] [-J num_lines]\n" | 3078 | " ssh-keygen -M screen [-f input_file] [-O option] output_file\n" |
2735 | " [-j start_line] [-K checkpt] [-W generator]\n" | ||
2736 | #endif | 3079 | #endif |
2737 | " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n" | 3080 | " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n" |
2738 | " [-n principals] [-O option] [-V validity_interval]\n" | 3081 | " [-n principals] [-O option] [-V validity_interval]\n" |
@@ -2742,6 +3085,7 @@ usage(void) | |||
2742 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" | 3085 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" |
2743 | " file ...\n" | 3086 | " file ...\n" |
2744 | " ssh-keygen -Q -f krl_file file ...\n" | 3087 | " ssh-keygen -Q -f krl_file file ...\n" |
3088 | " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n" | ||
2745 | " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" | 3089 | " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" |
2746 | " ssh-keygen -Y sign -f key_file -n namespace file ...\n" | 3090 | " ssh-keygen -Y sign -f key_file -n namespace file ...\n" |
2747 | " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" | 3091 | " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" |
@@ -2755,32 +3099,29 @@ usage(void) | |||
2755 | int | 3099 | int |
2756 | main(int argc, char **argv) | 3100 | main(int argc, char **argv) |
2757 | { | 3101 | { |
2758 | char dotsshdir[PATH_MAX], comment[1024], *passphrase1, *passphrase2; | 3102 | char dotsshdir[PATH_MAX], comment[1024], *passphrase; |
2759 | char *rr_hostname = NULL, *ep, *fp, *ra; | 3103 | char *rr_hostname = NULL, *ep, *fp, *ra; |
2760 | struct sshkey *private, *public; | 3104 | struct sshkey *private, *public; |
2761 | struct passwd *pw; | 3105 | struct passwd *pw; |
2762 | struct stat st; | 3106 | struct stat st; |
2763 | int r, opt, type, fd; | 3107 | int r, opt, type; |
2764 | int change_passphrase = 0, change_comment = 0, show_cert = 0; | 3108 | int change_passphrase = 0, change_comment = 0, show_cert = 0; |
2765 | int find_host = 0, delete_host = 0, hash_hosts = 0; | 3109 | int find_host = 0, delete_host = 0, hash_hosts = 0; |
2766 | int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; | 3110 | int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; |
2767 | int prefer_agent = 0, convert_to = 0, convert_from = 0; | 3111 | int prefer_agent = 0, convert_to = 0, convert_from = 0; |
2768 | int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; | 3112 | int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; |
3113 | int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; | ||
2769 | unsigned long long cert_serial = 0; | 3114 | unsigned long long cert_serial = 0; |
2770 | char *identity_comment = NULL, *ca_key_path = NULL; | 3115 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; |
3116 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; | ||
3117 | char *sk_attestaion_path = NULL; | ||
3118 | struct sshbuf *challenge = NULL, *attest = NULL; | ||
3119 | size_t i, nopts = 0; | ||
2771 | u_int32_t bits = 0; | 3120 | u_int32_t bits = 0; |
2772 | FILE *f; | 3121 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; |
2773 | const char *errstr; | 3122 | const char *errstr; |
2774 | int log_level = SYSLOG_LEVEL_INFO; | 3123 | int log_level = SYSLOG_LEVEL_INFO; |
2775 | char *sign_op = NULL; | 3124 | char *sign_op = NULL; |
2776 | #ifdef WITH_OPENSSL | ||
2777 | /* Moduli generation/screening */ | ||
2778 | char out_file[PATH_MAX], *checkpoint = NULL; | ||
2779 | u_int32_t memory = 0, generator_wanted = 0; | ||
2780 | int do_gen_candidates = 0, do_screen_candidates = 0; | ||
2781 | unsigned long start_lineno = 0, lines_to_process = 0; | ||
2782 | BIGNUM *start = NULL; | ||
2783 | #endif | ||
2784 | 3125 | ||
2785 | extern int optind; | 3126 | extern int optind; |
2786 | extern char *optarg; | 3127 | extern char *optarg; |
@@ -2803,10 +3144,12 @@ main(int argc, char **argv) | |||
2803 | if (gethostname(hostname, sizeof(hostname)) == -1) | 3144 | if (gethostname(hostname, sizeof(hostname)) == -1) |
2804 | fatal("gethostname: %s", strerror(errno)); | 3145 | fatal("gethostname: %s", strerror(errno)); |
2805 | 3146 | ||
2806 | /* Remaining characters: dw */ | 3147 | sk_provider = getenv("SSH_SK_PROVIDER"); |
2807 | while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvxy" | 3148 | |
2808 | "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Y:Z:" | 3149 | /* Remaining characters: dGjJSTWx */ |
2809 | "a:b:f:g:j:m:n:r:s:t:z:")) != -1) { | 3150 | while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy" |
3151 | "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" | ||
3152 | "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { | ||
2810 | switch (opt) { | 3153 | switch (opt) { |
2811 | case 'A': | 3154 | case 'A': |
2812 | gen_all_hostkeys = 1; | 3155 | gen_all_hostkeys = 1; |
@@ -2884,6 +3227,9 @@ main(int argc, char **argv) | |||
2884 | case 'g': | 3227 | case 'g': |
2885 | print_generic = 1; | 3228 | print_generic = 1; |
2886 | break; | 3229 | break; |
3230 | case 'K': | ||
3231 | download_sk = 1; | ||
3232 | break; | ||
2887 | case 'P': | 3233 | case 'P': |
2888 | identity_passphrase = optarg; | 3234 | identity_passphrase = optarg; |
2889 | break; | 3235 | break; |
@@ -2894,7 +3240,9 @@ main(int argc, char **argv) | |||
2894 | check_krl = 1; | 3240 | check_krl = 1; |
2895 | break; | 3241 | break; |
2896 | case 'O': | 3242 | case 'O': |
2897 | add_cert_option(optarg); | 3243 | opts = xrecallocarray(opts, nopts, nopts + 1, |
3244 | sizeof(*opts)); | ||
3245 | opts[nopts++] = xstrdup(optarg); | ||
2898 | break; | 3246 | break; |
2899 | case 'Z': | 3247 | case 'Z': |
2900 | openssh_format_cipher = optarg; | 3248 | openssh_format_cipher = optarg; |
@@ -2906,7 +3254,6 @@ main(int argc, char **argv) | |||
2906 | quiet = 1; | 3254 | quiet = 1; |
2907 | break; | 3255 | break; |
2908 | case 'e': | 3256 | case 'e': |
2909 | case 'x': | ||
2910 | /* export key */ | 3257 | /* export key */ |
2911 | convert_to = 1; | 3258 | convert_to = 1; |
2912 | break; | 3259 | break; |
@@ -2964,6 +3311,9 @@ main(int argc, char **argv) | |||
2964 | case 'Y': | 3311 | case 'Y': |
2965 | sign_op = optarg; | 3312 | sign_op = optarg; |
2966 | break; | 3313 | break; |
3314 | case 'w': | ||
3315 | sk_provider = optarg; | ||
3316 | break; | ||
2967 | case 'z': | 3317 | case 'z': |
2968 | errno = 0; | 3318 | errno = 0; |
2969 | if (*optarg == '+') { | 3319 | if (*optarg == '+') { |
@@ -2975,56 +3325,25 @@ main(int argc, char **argv) | |||
2975 | (errno == ERANGE && cert_serial == ULLONG_MAX)) | 3325 | (errno == ERANGE && cert_serial == ULLONG_MAX)) |
2976 | fatal("Invalid serial number \"%s\"", optarg); | 3326 | fatal("Invalid serial number \"%s\"", optarg); |
2977 | break; | 3327 | break; |
2978 | #ifdef WITH_OPENSSL | ||
2979 | /* Moduli generation/screening */ | ||
2980 | case 'G': | ||
2981 | do_gen_candidates = 1; | ||
2982 | if (strlcpy(out_file, optarg, sizeof(out_file)) >= | ||
2983 | sizeof(out_file)) | ||
2984 | fatal("Output filename too long"); | ||
2985 | break; | ||
2986 | case 'J': | ||
2987 | lines_to_process = strtoul(optarg, NULL, 10); | ||
2988 | break; | ||
2989 | case 'j': | ||
2990 | start_lineno = strtoul(optarg, NULL, 10); | ||
2991 | break; | ||
2992 | case 'K': | ||
2993 | if (strlen(optarg) >= PATH_MAX) | ||
2994 | fatal("Checkpoint filename too long"); | ||
2995 | checkpoint = xstrdup(optarg); | ||
2996 | break; | ||
2997 | case 'M': | 3328 | case 'M': |
2998 | memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, | 3329 | if (strcmp(optarg, "generate") == 0) |
2999 | &errstr); | 3330 | do_gen_candidates = 1; |
3000 | if (errstr) | 3331 | else if (strcmp(optarg, "screen") == 0) |
3001 | fatal("Memory limit is %s: %s", errstr, optarg); | 3332 | do_screen_candidates = 1; |
3333 | else | ||
3334 | fatal("Unsupported moduli option %s", optarg); | ||
3002 | break; | 3335 | break; |
3003 | case 'S': | ||
3004 | /* XXX - also compare length against bits */ | ||
3005 | if (BN_hex2bn(&start, optarg) == 0) | ||
3006 | fatal("Invalid start point."); | ||
3007 | break; | ||
3008 | case 'T': | ||
3009 | do_screen_candidates = 1; | ||
3010 | if (strlcpy(out_file, optarg, sizeof(out_file)) >= | ||
3011 | sizeof(out_file)) | ||
3012 | fatal("Output filename too long"); | ||
3013 | break; | ||
3014 | case 'W': | ||
3015 | generator_wanted = (u_int32_t)strtonum(optarg, 1, | ||
3016 | UINT_MAX, &errstr); | ||
3017 | if (errstr != NULL) | ||
3018 | fatal("Desired generator invalid: %s (%s)", | ||
3019 | optarg, errstr); | ||
3020 | break; | ||
3021 | #endif /* WITH_OPENSSL */ | ||
3022 | case '?': | 3336 | case '?': |
3023 | default: | 3337 | default: |
3024 | usage(); | 3338 | usage(); |
3025 | } | 3339 | } |
3026 | } | 3340 | } |
3027 | 3341 | ||
3342 | #ifdef ENABLE_SK_INTERNAL | ||
3343 | if (sk_provider == NULL) | ||
3344 | sk_provider = "internal"; | ||
3345 | #endif | ||
3346 | |||
3028 | /* reinit */ | 3347 | /* reinit */ |
3029 | log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); | 3348 | log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); |
3030 | 3349 | ||
@@ -3032,27 +3351,47 @@ main(int argc, char **argv) | |||
3032 | argc -= optind; | 3351 | argc -= optind; |
3033 | 3352 | ||
3034 | if (sign_op != NULL) { | 3353 | if (sign_op != NULL) { |
3035 | if (cert_principals == NULL || *cert_principals == '\0') { | 3354 | if (strncmp(sign_op, "find-principals", 15) == 0) { |
3036 | error("Too few arguments for sign/verify: " | 3355 | if (ca_key_path == NULL) { |
3037 | "missing namespace"); | 3356 | error("Too few arguments for find-principals:" |
3038 | exit(1); | 3357 | "missing signature file"); |
3039 | } | 3358 | exit(1); |
3040 | if (strncmp(sign_op, "sign", 4) == 0) { | 3359 | } |
3360 | if (!have_identity) { | ||
3361 | error("Too few arguments for find-principals:" | ||
3362 | "missing allowed keys file"); | ||
3363 | exit(1); | ||
3364 | } | ||
3365 | return sig_find_principals(ca_key_path, identity_file); | ||
3366 | } else if (strncmp(sign_op, "sign", 4) == 0) { | ||
3367 | if (cert_principals == NULL || | ||
3368 | *cert_principals == '\0') { | ||
3369 | error("Too few arguments for sign: " | ||
3370 | "missing namespace"); | ||
3371 | exit(1); | ||
3372 | } | ||
3041 | if (!have_identity) { | 3373 | if (!have_identity) { |
3042 | error("Too few arguments for sign: " | 3374 | error("Too few arguments for sign: " |
3043 | "missing key"); | 3375 | "missing key"); |
3044 | exit(1); | 3376 | exit(1); |
3045 | } | 3377 | } |
3046 | return sign(identity_file, cert_principals, argc, argv); | 3378 | return sig_sign(identity_file, cert_principals, |
3379 | argc, argv); | ||
3047 | } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { | 3380 | } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { |
3048 | if (ca_key_path == NULL) { | 3381 | if (ca_key_path == NULL) { |
3049 | error("Too few arguments for check-novalidate: " | 3382 | error("Too few arguments for check-novalidate: " |
3050 | "missing signature file"); | 3383 | "missing signature file"); |
3051 | exit(1); | 3384 | exit(1); |
3052 | } | 3385 | } |
3053 | return verify(ca_key_path, cert_principals, | 3386 | return sig_verify(ca_key_path, cert_principals, |
3054 | NULL, NULL, NULL); | 3387 | NULL, NULL, NULL); |
3055 | } else if (strncmp(sign_op, "verify", 6) == 0) { | 3388 | } else if (strncmp(sign_op, "verify", 6) == 0) { |
3389 | if (cert_principals == NULL || | ||
3390 | *cert_principals == '\0') { | ||
3391 | error("Too few arguments for verify: " | ||
3392 | "missing namespace"); | ||
3393 | exit(1); | ||
3394 | } | ||
3056 | if (ca_key_path == NULL) { | 3395 | if (ca_key_path == NULL) { |
3057 | error("Too few arguments for verify: " | 3396 | error("Too few arguments for verify: " |
3058 | "missing signature file"); | 3397 | "missing signature file"); |
@@ -3068,9 +3407,10 @@ main(int argc, char **argv) | |||
3068 | "missing principal ID"); | 3407 | "missing principal ID"); |
3069 | exit(1); | 3408 | exit(1); |
3070 | } | 3409 | } |
3071 | return verify(ca_key_path, cert_principals, | 3410 | return sig_verify(ca_key_path, cert_principals, |
3072 | cert_key_id, identity_file, rr_hostname); | 3411 | cert_key_id, identity_file, rr_hostname); |
3073 | } | 3412 | } |
3413 | error("Unsupported operation for -Y: \"%s\"", sign_op); | ||
3074 | usage(); | 3414 | usage(); |
3075 | /* NOTREACHED */ | 3415 | /* NOTREACHED */ |
3076 | } | 3416 | } |
@@ -3080,7 +3420,8 @@ main(int argc, char **argv) | |||
3080 | error("Too few arguments."); | 3420 | error("Too few arguments."); |
3081 | usage(); | 3421 | usage(); |
3082 | } | 3422 | } |
3083 | } else if (argc > 0 && !gen_krl && !check_krl) { | 3423 | } else if (argc > 0 && !gen_krl && !check_krl && |
3424 | !do_gen_candidates && !do_screen_candidates) { | ||
3084 | error("Too many arguments."); | 3425 | error("Too many arguments."); |
3085 | usage(); | 3426 | usage(); |
3086 | } | 3427 | } |
@@ -3104,6 +3445,8 @@ main(int argc, char **argv) | |||
3104 | if (ca_key_path != NULL) { | 3445 | if (ca_key_path != NULL) { |
3105 | if (cert_key_id == NULL) | 3446 | if (cert_key_id == NULL) |
3106 | fatal("Must specify key id (-I) when certifying"); | 3447 | fatal("Must specify key id (-I) when certifying"); |
3448 | for (i = 0; i < nopts; i++) | ||
3449 | add_cert_option(opts[i]); | ||
3107 | do_ca_sign(pw, ca_key_path, prefer_agent, | 3450 | do_ca_sign(pw, ca_key_path, prefer_agent, |
3108 | cert_serial, cert_serial_autoinc, argc, argv); | 3451 | cert_serial, cert_serial_autoinc, argc, argv); |
3109 | } | 3452 | } |
@@ -3115,6 +3458,17 @@ main(int argc, char **argv) | |||
3115 | } | 3458 | } |
3116 | if (pkcs11provider != NULL) | 3459 | if (pkcs11provider != NULL) |
3117 | do_download(pw); | 3460 | do_download(pw); |
3461 | if (download_sk) { | ||
3462 | for (i = 0; i < nopts; i++) { | ||
3463 | if (strncasecmp(opts[i], "device=", 7) == 0) { | ||
3464 | sk_device = xstrdup(opts[i] + 7); | ||
3465 | } else { | ||
3466 | fatal("Option \"%s\" is unsupported for " | ||
3467 | "FIDO authenticator download", opts[i]); | ||
3468 | } | ||
3469 | } | ||
3470 | return do_download_sk(sk_provider, sk_device); | ||
3471 | } | ||
3118 | if (print_fingerprint || print_bubblebabble) | 3472 | if (print_fingerprint || print_bubblebabble) |
3119 | do_fingerprint(pw); | 3473 | do_fingerprint(pw); |
3120 | if (change_passphrase) | 3474 | if (change_passphrase) |
@@ -3164,47 +3518,20 @@ main(int argc, char **argv) | |||
3164 | } | 3518 | } |
3165 | } | 3519 | } |
3166 | 3520 | ||
3167 | #ifdef WITH_OPENSSL | 3521 | if (do_gen_candidates || do_screen_candidates) { |
3522 | if (argc <= 0) | ||
3523 | fatal("No output file specified"); | ||
3524 | else if (argc > 1) | ||
3525 | fatal("Too many output files specified"); | ||
3526 | } | ||
3168 | if (do_gen_candidates) { | 3527 | if (do_gen_candidates) { |
3169 | FILE *out = fopen(out_file, "w"); | 3528 | do_moduli_gen(argv[0], opts, nopts); |
3170 | 3529 | return 0; | |
3171 | if (out == NULL) { | ||
3172 | error("Couldn't open modulus candidate file \"%s\": %s", | ||
3173 | out_file, strerror(errno)); | ||
3174 | return (1); | ||
3175 | } | ||
3176 | if (bits == 0) | ||
3177 | bits = DEFAULT_BITS; | ||
3178 | if (gen_candidates(out, memory, bits, start) != 0) | ||
3179 | fatal("modulus candidate generation failed"); | ||
3180 | |||
3181 | return (0); | ||
3182 | } | 3530 | } |
3183 | |||
3184 | if (do_screen_candidates) { | 3531 | if (do_screen_candidates) { |
3185 | FILE *in; | 3532 | do_moduli_screen(argv[0], opts, nopts); |
3186 | FILE *out = fopen(out_file, "a"); | 3533 | return 0; |
3187 | |||
3188 | if (have_identity && strcmp(identity_file, "-") != 0) { | ||
3189 | if ((in = fopen(identity_file, "r")) == NULL) { | ||
3190 | fatal("Couldn't open modulus candidate " | ||
3191 | "file \"%s\": %s", identity_file, | ||
3192 | strerror(errno)); | ||
3193 | } | ||
3194 | } else | ||
3195 | in = stdin; | ||
3196 | |||
3197 | if (out == NULL) { | ||
3198 | fatal("Couldn't open moduli file \"%s\": %s", | ||
3199 | out_file, strerror(errno)); | ||
3200 | } | ||
3201 | if (prime_test(in, out, rounds == 0 ? 100 : rounds, | ||
3202 | generator_wanted, checkpoint, | ||
3203 | start_lineno, lines_to_process) != 0) | ||
3204 | fatal("modulus screening failed"); | ||
3205 | return (0); | ||
3206 | } | 3534 | } |
3207 | #endif | ||
3208 | 3535 | ||
3209 | if (gen_all_hostkeys) { | 3536 | if (gen_all_hostkeys) { |
3210 | do_gen_all_hostkeys(pw); | 3537 | do_gen_all_hostkeys(pw); |
@@ -3220,8 +3547,78 @@ main(int argc, char **argv) | |||
3220 | if (!quiet) | 3547 | if (!quiet) |
3221 | printf("Generating public/private %s key pair.\n", | 3548 | printf("Generating public/private %s key pair.\n", |
3222 | key_type_name); | 3549 | key_type_name); |
3223 | if ((r = sshkey_generate(type, bits, &private)) != 0) | 3550 | switch (type) { |
3224 | fatal("sshkey_generate failed"); | 3551 | case KEY_ECDSA_SK: |
3552 | case KEY_ED25519_SK: | ||
3553 | for (i = 0; i < nopts; i++) { | ||
3554 | if (strcasecmp(opts[i], "no-touch-required") == 0) { | ||
3555 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; | ||
3556 | } else if (strcasecmp(opts[i], "resident") == 0) { | ||
3557 | sk_flags |= SSH_SK_RESIDENT_KEY; | ||
3558 | } else if (strncasecmp(opts[i], "device=", 7) == 0) { | ||
3559 | sk_device = xstrdup(opts[i] + 7); | ||
3560 | } else if (strncasecmp(opts[i], "user=", 5) == 0) { | ||
3561 | sk_user = xstrdup(opts[i] + 5); | ||
3562 | } else if (strncasecmp(opts[i], "challenge=", 10) == 0) { | ||
3563 | if ((r = sshbuf_load_file(opts[i] + 10, | ||
3564 | &challenge)) != 0) { | ||
3565 | fatal("Unable to load FIDO enrollment " | ||
3566 | "challenge \"%s\": %s", | ||
3567 | opts[i] + 10, ssh_err(r)); | ||
3568 | } | ||
3569 | } else if (strncasecmp(opts[i], | ||
3570 | "write-attestation=", 18) == 0) { | ||
3571 | sk_attestaion_path = opts[i] + 18; | ||
3572 | } else if (strncasecmp(opts[i], | ||
3573 | "application=", 12) == 0) { | ||
3574 | sk_application = xstrdup(opts[i] + 12); | ||
3575 | if (strncmp(sk_application, "ssh:", 4) != 0) { | ||
3576 | fatal("FIDO application string must " | ||
3577 | "begin with \"ssh:\""); | ||
3578 | } | ||
3579 | } else { | ||
3580 | fatal("Option \"%s\" is unsupported for " | ||
3581 | "FIDO authenticator enrollment", opts[i]); | ||
3582 | } | ||
3583 | } | ||
3584 | if (!quiet) { | ||
3585 | printf("You may need to touch your authenticator " | ||
3586 | "to authorize key generation.\n"); | ||
3587 | } | ||
3588 | passphrase = NULL; | ||
3589 | if ((attest = sshbuf_new()) == NULL) | ||
3590 | fatal("sshbuf_new failed"); | ||
3591 | for (i = 0 ; ; i++) { | ||
3592 | fflush(stdout); | ||
3593 | r = sshsk_enroll(type, sk_provider, sk_device, | ||
3594 | sk_application == NULL ? "ssh:" : sk_application, | ||
3595 | sk_user, sk_flags, passphrase, challenge, | ||
3596 | &private, attest); | ||
3597 | if (r == 0) | ||
3598 | break; | ||
3599 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) | ||
3600 | fatal("Key enrollment failed: %s", ssh_err(r)); | ||
3601 | else if (i > 0) | ||
3602 | error("PIN incorrect"); | ||
3603 | if (passphrase != NULL) { | ||
3604 | freezero(passphrase, strlen(passphrase)); | ||
3605 | passphrase = NULL; | ||
3606 | } | ||
3607 | if (i >= 3) | ||
3608 | fatal("Too many incorrect PINs"); | ||
3609 | passphrase = read_passphrase("Enter PIN for " | ||
3610 | "authenticator: ", RP_ALLOW_STDIN); | ||
3611 | } | ||
3612 | if (passphrase != NULL) { | ||
3613 | freezero(passphrase, strlen(passphrase)); | ||
3614 | passphrase = NULL; | ||
3615 | } | ||
3616 | break; | ||
3617 | default: | ||
3618 | if ((r = sshkey_generate(type, bits, &private)) != 0) | ||
3619 | fatal("sshkey_generate failed"); | ||
3620 | break; | ||
3621 | } | ||
3225 | if ((r = sshkey_from_private(private, &public)) != 0) | 3622 | if ((r = sshkey_from_private(private, &public)) != 0) |
3226 | fatal("sshkey_from_private failed: %s\n", ssh_err(r)); | 3623 | fatal("sshkey_from_private failed: %s\n", ssh_err(r)); |
3227 | 3624 | ||
@@ -3246,35 +3643,9 @@ main(int argc, char **argv) | |||
3246 | /* If the file already exists, ask the user to confirm. */ | 3643 | /* If the file already exists, ask the user to confirm. */ |
3247 | if (!confirm_overwrite(identity_file)) | 3644 | if (!confirm_overwrite(identity_file)) |
3248 | exit(1); | 3645 | exit(1); |
3249 | /* Ask for a passphrase (twice). */ | ||
3250 | if (identity_passphrase) | ||
3251 | passphrase1 = xstrdup(identity_passphrase); | ||
3252 | else if (identity_new_passphrase) | ||
3253 | passphrase1 = xstrdup(identity_new_passphrase); | ||
3254 | else { | ||
3255 | passphrase_again: | ||
3256 | passphrase1 = | ||
3257 | read_passphrase("Enter passphrase (empty for no " | ||
3258 | "passphrase): ", RP_ALLOW_STDIN); | ||
3259 | passphrase2 = read_passphrase("Enter same passphrase again: ", | ||
3260 | RP_ALLOW_STDIN); | ||
3261 | if (strcmp(passphrase1, passphrase2) != 0) { | ||
3262 | /* | ||
3263 | * The passphrases do not match. Clear them and | ||
3264 | * retry. | ||
3265 | */ | ||
3266 | explicit_bzero(passphrase1, strlen(passphrase1)); | ||
3267 | explicit_bzero(passphrase2, strlen(passphrase2)); | ||
3268 | free(passphrase1); | ||
3269 | free(passphrase2); | ||
3270 | printf("Passphrases do not match. Try again.\n"); | ||
3271 | goto passphrase_again; | ||
3272 | } | ||
3273 | /* Clear the other copy of the passphrase. */ | ||
3274 | explicit_bzero(passphrase2, strlen(passphrase2)); | ||
3275 | free(passphrase2); | ||
3276 | } | ||
3277 | 3646 | ||
3647 | /* Determine the passphrase for the private key */ | ||
3648 | passphrase = private_key_passphrase(); | ||
3278 | if (identity_comment) { | 3649 | if (identity_comment) { |
3279 | strlcpy(comment, identity_comment, sizeof(comment)); | 3650 | strlcpy(comment, identity_comment, sizeof(comment)); |
3280 | } else { | 3651 | } else { |
@@ -3283,35 +3654,26 @@ passphrase_again: | |||
3283 | } | 3654 | } |
3284 | 3655 | ||
3285 | /* Save the key with the given passphrase and comment. */ | 3656 | /* Save the key with the given passphrase and comment. */ |
3286 | if ((r = sshkey_save_private(private, identity_file, passphrase1, | 3657 | if ((r = sshkey_save_private(private, identity_file, passphrase, |
3287 | comment, private_key_format, openssh_format_cipher, rounds)) != 0) { | 3658 | comment, private_key_format, openssh_format_cipher, rounds)) != 0) { |
3288 | error("Saving key \"%s\" failed: %s", | 3659 | error("Saving key \"%s\" failed: %s", |
3289 | identity_file, ssh_err(r)); | 3660 | identity_file, ssh_err(r)); |
3290 | explicit_bzero(passphrase1, strlen(passphrase1)); | 3661 | freezero(passphrase, strlen(passphrase)); |
3291 | free(passphrase1); | ||
3292 | exit(1); | 3662 | exit(1); |
3293 | } | 3663 | } |
3294 | /* Clear the passphrase. */ | 3664 | freezero(passphrase, strlen(passphrase)); |
3295 | explicit_bzero(passphrase1, strlen(passphrase1)); | ||
3296 | free(passphrase1); | ||
3297 | |||
3298 | /* Clear the private key and the random number generator. */ | ||
3299 | sshkey_free(private); | 3665 | sshkey_free(private); |
3300 | 3666 | ||
3301 | if (!quiet) | 3667 | if (!quiet) { |
3302 | printf("Your identification has been saved in %s.\n", identity_file); | 3668 | printf("Your identification has been saved in %s\n", |
3669 | identity_file); | ||
3670 | } | ||
3303 | 3671 | ||
3304 | strlcat(identity_file, ".pub", sizeof(identity_file)); | 3672 | strlcat(identity_file, ".pub", sizeof(identity_file)); |
3305 | if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) | 3673 | if ((r = sshkey_save_public(public, identity_file, comment)) != 0) { |
3306 | fatal("Unable to save public key to %s: %s", | 3674 | fatal("Unable to save public key to %s: %s", |
3307 | identity_file, strerror(errno)); | 3675 | identity_file, strerror(errno)); |
3308 | if ((f = fdopen(fd, "w")) == NULL) | 3676 | } |
3309 | fatal("fdopen %s failed: %s", identity_file, strerror(errno)); | ||
3310 | if ((r = sshkey_write(public, f)) != 0) | ||
3311 | error("write key failed: %s", ssh_err(r)); | ||
3312 | fprintf(f, " %s\n", comment); | ||
3313 | if (ferror(f) || fclose(f) != 0) | ||
3314 | fatal("write public failed: %s", strerror(errno)); | ||
3315 | 3677 | ||
3316 | if (!quiet) { | 3678 | if (!quiet) { |
3317 | fp = sshkey_fingerprint(public, fingerprint_hash, | 3679 | fp = sshkey_fingerprint(public, fingerprint_hash, |
@@ -3320,7 +3682,7 @@ passphrase_again: | |||
3320 | SSH_FP_RANDOMART); | 3682 | SSH_FP_RANDOMART); |
3321 | if (fp == NULL || ra == NULL) | 3683 | if (fp == NULL || ra == NULL) |
3322 | fatal("sshkey_fingerprint failed"); | 3684 | fatal("sshkey_fingerprint failed"); |
3323 | printf("Your public key has been saved in %s.\n", | 3685 | printf("Your public key has been saved in %s\n", |
3324 | identity_file); | 3686 | identity_file); |
3325 | printf("The key fingerprint is:\n"); | 3687 | printf("The key fingerprint is:\n"); |
3326 | printf("%s %s\n", fp, comment); | 3688 | printf("%s %s\n", fp, comment); |
@@ -3330,6 +3692,22 @@ passphrase_again: | |||
3330 | free(fp); | 3692 | free(fp); |
3331 | } | 3693 | } |
3332 | 3694 | ||
3695 | if (sk_attestaion_path != NULL) { | ||
3696 | if (attest == NULL || sshbuf_len(attest) == 0) { | ||
3697 | fatal("Enrollment did not return attestation " | ||
3698 | "certificate"); | ||
3699 | } | ||
3700 | if ((r = sshbuf_write_file(sk_attestaion_path, attest)) != 0) { | ||
3701 | fatal("Unable to write attestation certificate " | ||
3702 | "\"%s\": %s", sk_attestaion_path, ssh_err(r)); | ||
3703 | } | ||
3704 | if (!quiet) { | ||
3705 | printf("Your FIDO attestation certificate has been " | ||
3706 | "saved in %s\n", sk_attestaion_path); | ||
3707 | } | ||
3708 | } | ||
3709 | sshbuf_free(attest); | ||
3333 | sshkey_free(public); | 3710 | sshkey_free(public); |
3711 | |||
3334 | exit(0); | 3712 | exit(0); |
3335 | } | 3713 | } |