diff options
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r-- | ssh-keygen.c | 336 |
1 files changed, 195 insertions, 141 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index 2c5c75db7..a12b79a56 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.409.2.1 2020/05/18 19:02:13 benno Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.420 2020/09/09 03:08:01 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 |
@@ -133,13 +133,13 @@ static char *certflags_command = NULL; | |||
133 | static char *certflags_src_addr = NULL; | 133 | static char *certflags_src_addr = NULL; |
134 | 134 | ||
135 | /* Arbitrary extensions specified by user */ | 135 | /* Arbitrary extensions specified by user */ |
136 | struct cert_userext { | 136 | struct cert_ext { |
137 | char *key; | 137 | char *key; |
138 | char *val; | 138 | char *val; |
139 | int crit; | 139 | int crit; |
140 | }; | 140 | }; |
141 | static struct cert_userext *cert_userext; | 141 | static struct cert_ext *cert_ext; |
142 | static size_t ncert_userext; | 142 | static size_t ncert_ext; |
143 | 143 | ||
144 | /* Conversion to/from various formats */ | 144 | /* Conversion to/from various formats */ |
145 | enum { | 145 | enum { |
@@ -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); |
@@ -815,10 +815,14 @@ do_print_public(struct passwd *pw) | |||
815 | prv = load_identity(identity_file, &comment); | 815 | prv = load_identity(identity_file, &comment); |
816 | if ((r = sshkey_write(prv, stdout)) != 0) | 816 | if ((r = sshkey_write(prv, stdout)) != 0) |
817 | error("sshkey_write failed: %s", ssh_err(r)); | 817 | error("sshkey_write failed: %s", ssh_err(r)); |
818 | sshkey_free(prv); | ||
819 | if (comment != NULL && *comment != '\0') | 818 | if (comment != NULL && *comment != '\0') |
820 | fprintf(stdout, " %s", comment); | 819 | fprintf(stdout, " %s", comment); |
821 | fprintf(stdout, "\n"); | 820 | fprintf(stdout, "\n"); |
821 | if (sshkey_is_sk(prv)) { | ||
822 | debug("sk_application: \"%s\", sk_flags 0x%02x", | ||
823 | prv->sk_application, prv->sk_flags); | ||
824 | } | ||
825 | sshkey_free(prv); | ||
822 | free(comment); | 826 | free(comment); |
823 | exit(0); | 827 | exit(0); |
824 | } | 828 | } |
@@ -1292,6 +1296,7 @@ do_known_hosts(struct passwd *pw, const char *name, int find_host, | |||
1292 | int r, fd, oerrno, inplace = 0; | 1296 | int r, fd, oerrno, inplace = 0; |
1293 | struct known_hosts_ctx ctx; | 1297 | struct known_hosts_ctx ctx; |
1294 | u_int foreach_options; | 1298 | u_int foreach_options; |
1299 | struct stat sb; | ||
1295 | 1300 | ||
1296 | if (!have_identity) { | 1301 | if (!have_identity) { |
1297 | cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); | 1302 | cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); |
@@ -1301,6 +1306,8 @@ do_known_hosts(struct passwd *pw, const char *name, int find_host, | |||
1301 | free(cp); | 1306 | free(cp); |
1302 | have_identity = 1; | 1307 | have_identity = 1; |
1303 | } | 1308 | } |
1309 | if (stat(identity_file, &sb) != 0) | ||
1310 | fatal("Cannot stat %s: %s", identity_file, strerror(errno)); | ||
1304 | 1311 | ||
1305 | memset(&ctx, 0, sizeof(ctx)); | 1312 | memset(&ctx, 0, sizeof(ctx)); |
1306 | ctx.out = stdout; | 1313 | ctx.out = stdout; |
@@ -1327,6 +1334,7 @@ do_known_hosts(struct passwd *pw, const char *name, int find_host, | |||
1327 | unlink(tmp); | 1334 | unlink(tmp); |
1328 | fatal("fdopen: %s", strerror(oerrno)); | 1335 | fatal("fdopen: %s", strerror(oerrno)); |
1329 | } | 1336 | } |
1337 | fchmod(fd, sb.st_mode & 0644); | ||
1330 | inplace = 1; | 1338 | inplace = 1; |
1331 | } | 1339 | } |
1332 | /* XXX support identity_file == "-" for stdin */ | 1340 | /* XXX support identity_file == "-" for stdin */ |
@@ -1597,31 +1605,32 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1597 | } | 1605 | } |
1598 | 1606 | ||
1599 | static void | 1607 | static void |
1600 | add_flag_option(struct sshbuf *c, const char *name) | 1608 | cert_ext_add(const char *key, const char *value, int iscrit) |
1601 | { | 1609 | { |
1602 | int r; | 1610 | cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext)); |
1603 | 1611 | cert_ext[ncert_ext].key = xstrdup(key); | |
1604 | debug3("%s: %s", __func__, name); | 1612 | cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value); |
1605 | if ((r = sshbuf_put_cstring(c, name)) != 0 || | 1613 | cert_ext[ncert_ext].crit = iscrit; |
1606 | (r = sshbuf_put_string(c, NULL, 0)) != 0) | 1614 | ncert_ext++; |
1607 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
1608 | } | 1615 | } |
1609 | 1616 | ||
1610 | static void | 1617 | /* qsort(3) comparison function for certificate extensions */ |
1611 | add_string_option(struct sshbuf *c, const char *name, const char *value) | 1618 | static int |
1619 | cert_ext_cmp(const void *_a, const void *_b) | ||
1612 | { | 1620 | { |
1613 | struct sshbuf *b; | 1621 | const struct cert_ext *a = (const struct cert_ext *)_a; |
1622 | const struct cert_ext *b = (const struct cert_ext *)_b; | ||
1614 | int r; | 1623 | int r; |
1615 | 1624 | ||
1616 | debug3("%s: %s=%s", __func__, name, value); | 1625 | if (a->crit != b->crit) |
1617 | if ((b = sshbuf_new()) == NULL) | 1626 | return (a->crit < b->crit) ? -1 : 1; |
1618 | fatal("%s: sshbuf_new failed", __func__); | 1627 | if ((r = strcmp(a->key, b->key)) != 0) |
1619 | if ((r = sshbuf_put_cstring(b, value)) != 0 || | 1628 | return r; |
1620 | (r = sshbuf_put_cstring(c, name)) != 0 || | 1629 | if ((a->val == NULL) != (b->val == NULL)) |
1621 | (r = sshbuf_put_stringb(c, b)) != 0) | 1630 | return (a->val == NULL) ? -1 : 1; |
1622 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 1631 | if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0) |
1623 | 1632 | return r; | |
1624 | sshbuf_free(b); | 1633 | return 0; |
1625 | } | 1634 | } |
1626 | 1635 | ||
1627 | #define OPTIONS_CRITICAL 1 | 1636 | #define OPTIONS_CRITICAL 1 |
@@ -1629,44 +1638,62 @@ add_string_option(struct sshbuf *c, const char *name, const char *value) | |||
1629 | static void | 1638 | static void |
1630 | prepare_options_buf(struct sshbuf *c, int which) | 1639 | prepare_options_buf(struct sshbuf *c, int which) |
1631 | { | 1640 | { |
1641 | struct sshbuf *b; | ||
1632 | size_t i; | 1642 | size_t i; |
1643 | int r; | ||
1644 | const struct cert_ext *ext; | ||
1633 | 1645 | ||
1646 | if ((b = sshbuf_new()) == NULL) | ||
1647 | fatal("%s: sshbuf_new failed", __func__); | ||
1634 | sshbuf_reset(c); | 1648 | sshbuf_reset(c); |
1635 | if ((which & OPTIONS_CRITICAL) != 0 && | 1649 | for (i = 0; i < ncert_ext; i++) { |
1636 | certflags_command != NULL) | 1650 | ext = &cert_ext[i]; |
1637 | add_string_option(c, "force-command", certflags_command); | 1651 | if ((ext->crit && (which & OPTIONS_EXTENSIONS)) || |
1638 | if ((which & OPTIONS_EXTENSIONS) != 0 && | 1652 | (!ext->crit && (which & OPTIONS_CRITICAL))) |
1639 | (certflags_flags & CERTOPT_X_FWD) != 0) | ||
1640 | add_flag_option(c, "permit-X11-forwarding"); | ||
1641 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1642 | (certflags_flags & CERTOPT_AGENT_FWD) != 0) | ||
1643 | add_flag_option(c, "permit-agent-forwarding"); | ||
1644 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1645 | (certflags_flags & CERTOPT_PORT_FWD) != 0) | ||
1646 | add_flag_option(c, "permit-port-forwarding"); | ||
1647 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1648 | (certflags_flags & CERTOPT_PTY) != 0) | ||
1649 | add_flag_option(c, "permit-pty"); | ||
1650 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1651 | (certflags_flags & CERTOPT_USER_RC) != 0) | ||
1652 | add_flag_option(c, "permit-user-rc"); | ||
1653 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1654 | (certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) | ||
1655 | add_flag_option(c, "no-touch-required"); | ||
1656 | if ((which & OPTIONS_CRITICAL) != 0 && | ||
1657 | certflags_src_addr != NULL) | ||
1658 | add_string_option(c, "source-address", certflags_src_addr); | ||
1659 | for (i = 0; i < ncert_userext; i++) { | ||
1660 | if ((cert_userext[i].crit && (which & OPTIONS_EXTENSIONS)) || | ||
1661 | (!cert_userext[i].crit && (which & OPTIONS_CRITICAL))) | ||
1662 | continue; | 1653 | continue; |
1663 | if (cert_userext[i].val == NULL) | 1654 | if (ext->val == NULL) { |
1664 | add_flag_option(c, cert_userext[i].key); | 1655 | /* flag option */ |
1665 | else { | 1656 | debug3("%s: %s", __func__, ext->key); |
1666 | add_string_option(c, cert_userext[i].key, | 1657 | if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || |
1667 | cert_userext[i].val); | 1658 | (r = sshbuf_put_string(c, NULL, 0)) != 0) |
1659 | fatal("%s: buffer: %s", __func__, ssh_err(r)); | ||
1660 | } else { | ||
1661 | /* key/value option */ | ||
1662 | debug3("%s: %s=%s", __func__, ext->key, ext->val); | ||
1663 | sshbuf_reset(b); | ||
1664 | if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || | ||
1665 | (r = sshbuf_put_cstring(b, ext->val)) != 0 || | ||
1666 | (r = sshbuf_put_stringb(c, b)) != 0) | ||
1667 | fatal("%s: buffer: %s", __func__, ssh_err(r)); | ||
1668 | } | 1668 | } |
1669 | } | 1669 | } |
1670 | sshbuf_free(b); | ||
1671 | } | ||
1672 | |||
1673 | static void | ||
1674 | finalise_cert_exts(void) | ||
1675 | { | ||
1676 | /* critical options */ | ||
1677 | if (certflags_command != NULL) | ||
1678 | cert_ext_add("force-command", certflags_command, 1); | ||
1679 | if (certflags_src_addr != NULL) | ||
1680 | cert_ext_add("source-address", certflags_src_addr, 1); | ||
1681 | /* extensions */ | ||
1682 | if ((certflags_flags & CERTOPT_X_FWD) != 0) | ||
1683 | cert_ext_add("permit-X11-forwarding", NULL, 0); | ||
1684 | if ((certflags_flags & CERTOPT_AGENT_FWD) != 0) | ||
1685 | cert_ext_add("permit-agent-forwarding", NULL, 0); | ||
1686 | if ((certflags_flags & CERTOPT_PORT_FWD) != 0) | ||
1687 | cert_ext_add("permit-port-forwarding", NULL, 0); | ||
1688 | if ((certflags_flags & CERTOPT_PTY) != 0) | ||
1689 | cert_ext_add("permit-pty", NULL, 0); | ||
1690 | if ((certflags_flags & CERTOPT_USER_RC) != 0) | ||
1691 | cert_ext_add("permit-user-rc", NULL, 0); | ||
1692 | if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) | ||
1693 | cert_ext_add("no-touch-required", NULL, 0); | ||
1694 | /* order lexically by key */ | ||
1695 | if (ncert_ext > 0) | ||
1696 | qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp); | ||
1670 | } | 1697 | } |
1671 | 1698 | ||
1672 | static struct sshkey * | 1699 | static struct sshkey * |
@@ -1704,7 +1731,8 @@ load_pkcs11_key(char *path) | |||
1704 | static int | 1731 | static int |
1705 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, | 1732 | agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, |
1706 | const u_char *data, size_t datalen, | 1733 | const u_char *data, size_t datalen, |
1707 | const char *alg, const char *provider, u_int compat, void *ctx) | 1734 | const char *alg, const char *provider, const char *pin, |
1735 | u_int compat, void *ctx) | ||
1708 | { | 1736 | { |
1709 | int *agent_fdp = (int *)ctx; | 1737 | int *agent_fdp = (int *)ctx; |
1710 | 1738 | ||
@@ -1721,7 +1749,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1721 | u_int n; | 1749 | u_int n; |
1722 | struct sshkey *ca, *public; | 1750 | struct sshkey *ca, *public; |
1723 | char valid[64], *otmp, *tmp, *cp, *out, *comment; | 1751 | char valid[64], *otmp, *tmp, *cp, *out, *comment; |
1724 | char *ca_fp = NULL, **plist = NULL; | 1752 | char *ca_fp = NULL, **plist = NULL, *pin = NULL; |
1725 | struct ssh_identitylist *agent_ids; | 1753 | struct ssh_identitylist *agent_ids; |
1726 | size_t j; | 1754 | size_t j; |
1727 | struct notifier_ctx *notifier = NULL; | 1755 | struct notifier_ctx *notifier = NULL; |
@@ -1762,6 +1790,12 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1762 | } else { | 1790 | } else { |
1763 | /* CA key is assumed to be a private key on the filesystem */ | 1791 | /* CA key is assumed to be a private key on the filesystem */ |
1764 | ca = load_identity(tmp, NULL); | 1792 | ca = load_identity(tmp, NULL); |
1793 | if (sshkey_is_sk(ca) && | ||
1794 | (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { | ||
1795 | if ((pin = read_passphrase("Enter PIN for CA key: ", | ||
1796 | RP_ALLOW_STDIN)) == NULL) | ||
1797 | fatal("%s: couldn't read PIN", __func__); | ||
1798 | } | ||
1765 | } | 1799 | } |
1766 | free(tmp); | 1800 | free(tmp); |
1767 | 1801 | ||
@@ -1776,6 +1810,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1776 | } | 1810 | } |
1777 | ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); | 1811 | ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); |
1778 | 1812 | ||
1813 | finalise_cert_exts(); | ||
1779 | for (i = 0; i < argc; i++) { | 1814 | for (i = 0; i < argc; i++) { |
1780 | /* Split list of principals */ | 1815 | /* Split list of principals */ |
1781 | n = 0; | 1816 | n = 0; |
@@ -1820,7 +1855,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1820 | 1855 | ||
1821 | if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { | 1856 | if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { |
1822 | if ((r = sshkey_certify_custom(public, ca, | 1857 | if ((r = sshkey_certify_custom(public, ca, |
1823 | key_type_name, sk_provider, agent_signer, | 1858 | key_type_name, sk_provider, NULL, agent_signer, |
1824 | &agent_fd)) != 0) | 1859 | &agent_fd)) != 0) |
1825 | fatal("Couldn't certify key %s via agent: %s", | 1860 | fatal("Couldn't certify key %s via agent: %s", |
1826 | tmp, ssh_err(r)); | 1861 | tmp, ssh_err(r)); |
@@ -1832,7 +1867,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1832 | sshkey_type(ca), ca_fp); | 1867 | sshkey_type(ca), ca_fp); |
1833 | } | 1868 | } |
1834 | r = sshkey_certify(public, ca, key_type_name, | 1869 | r = sshkey_certify(public, ca, key_type_name, |
1835 | sk_provider); | 1870 | sk_provider, pin); |
1836 | notify_complete(notifier); | 1871 | notify_complete(notifier); |
1837 | if (r != 0) | 1872 | if (r != 0) |
1838 | fatal("Couldn't certify key %s: %s", | 1873 | fatal("Couldn't certify key %s: %s", |
@@ -1866,6 +1901,8 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1866 | if (cert_serial_autoinc) | 1901 | if (cert_serial_autoinc) |
1867 | cert_serial++; | 1902 | cert_serial++; |
1868 | } | 1903 | } |
1904 | if (pin != NULL) | ||
1905 | freezero(pin, strlen(pin)); | ||
1869 | free(ca_fp); | 1906 | free(ca_fp); |
1870 | #ifdef ENABLE_PKCS11 | 1907 | #ifdef ENABLE_PKCS11 |
1871 | pkcs11_terminate(); | 1908 | pkcs11_terminate(); |
@@ -1990,13 +2027,8 @@ add_cert_option(char *opt) | |||
1990 | val = xstrdup(strchr(opt, ':') + 1); | 2027 | val = xstrdup(strchr(opt, ':') + 1); |
1991 | if ((cp = strchr(val, '=')) != NULL) | 2028 | if ((cp = strchr(val, '=')) != NULL) |
1992 | *cp++ = '\0'; | 2029 | *cp++ = '\0'; |
1993 | cert_userext = xreallocarray(cert_userext, ncert_userext + 1, | 2030 | cert_ext_add(val, cp, iscrit); |
1994 | sizeof(*cert_userext)); | 2031 | free(val); |
1995 | cert_userext[ncert_userext].key = val; | ||
1996 | cert_userext[ncert_userext].val = cp == NULL ? | ||
1997 | NULL : xstrdup(cp); | ||
1998 | cert_userext[ncert_userext].crit = iscrit; | ||
1999 | ncert_userext++; | ||
2000 | } else | 2032 | } else |
2001 | fatal("Unsupported certificate option \"%s\"", opt); | 2033 | fatal("Unsupported certificate option \"%s\"", opt); |
2002 | } | 2034 | } |
@@ -2004,7 +2036,7 @@ add_cert_option(char *opt) | |||
2004 | static void | 2036 | static void |
2005 | show_options(struct sshbuf *optbuf, int in_critical) | 2037 | show_options(struct sshbuf *optbuf, int in_critical) |
2006 | { | 2038 | { |
2007 | char *name, *arg; | 2039 | char *name, *arg, *hex; |
2008 | struct sshbuf *options, *option = NULL; | 2040 | struct sshbuf *options, *option = NULL; |
2009 | int r; | 2041 | int r; |
2010 | 2042 | ||
@@ -2033,11 +2065,14 @@ show_options(struct sshbuf *optbuf, int in_critical) | |||
2033 | __func__, ssh_err(r)); | 2065 | __func__, ssh_err(r)); |
2034 | printf(" %s\n", arg); | 2066 | printf(" %s\n", arg); |
2035 | free(arg); | 2067 | free(arg); |
2036 | } else { | 2068 | } else if (sshbuf_len(option) > 0) { |
2037 | printf(" UNKNOWN OPTION (len %zu)\n", | 2069 | hex = sshbuf_dtob16(option); |
2038 | sshbuf_len(option)); | 2070 | printf(" UNKNOWN OPTION: %s (len %zu)\n", |
2071 | hex, sshbuf_len(option)); | ||
2039 | sshbuf_reset(option); | 2072 | sshbuf_reset(option); |
2040 | } | 2073 | free(hex); |
2074 | } else | ||
2075 | printf(" UNKNOWN FLAG OPTION\n"); | ||
2041 | free(name); | 2076 | free(name); |
2042 | if (sshbuf_len(option) != 0) | 2077 | if (sshbuf_len(option) != 0) |
2043 | fatal("Option corrupt: extra data at end"); | 2078 | fatal("Option corrupt: extra data at end"); |
@@ -2504,6 +2539,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2504 | struct sshbuf *sigbuf = NULL, *abuf = NULL; | 2539 | struct sshbuf *sigbuf = NULL, *abuf = NULL; |
2505 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; | 2540 | int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; |
2506 | char *wfile = NULL, *asig = NULL, *fp = NULL; | 2541 | char *wfile = NULL, *asig = NULL, *fp = NULL; |
2542 | char *pin = NULL, *prompt = NULL; | ||
2507 | 2543 | ||
2508 | if (!quiet) { | 2544 | if (!quiet) { |
2509 | if (fd == STDIN_FILENO) | 2545 | if (fd == STDIN_FILENO) |
@@ -2511,17 +2547,25 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2511 | else | 2547 | else |
2512 | fprintf(stderr, "Signing file %s\n", filename); | 2548 | fprintf(stderr, "Signing file %s\n", filename); |
2513 | } | 2549 | } |
2514 | if (signer == NULL && sshkey_is_sk(signkey) && | 2550 | if (signer == NULL && sshkey_is_sk(signkey)) { |
2515 | (signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | 2551 | if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { |
2516 | if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, | 2552 | xasprintf(&prompt, "Enter PIN for %s key: ", |
2517 | SSH_FP_DEFAULT)) == NULL) | 2553 | sshkey_type(signkey)); |
2518 | fatal("%s: sshkey_fingerprint failed", __func__); | 2554 | if ((pin = read_passphrase(prompt, |
2519 | fprintf(stderr, "Confirm user presence for key %s %s\n", | 2555 | RP_ALLOW_STDIN)) == NULL) |
2520 | sshkey_type(signkey), fp); | 2556 | fatal("%s: couldn't read PIN", __func__); |
2521 | free(fp); | 2557 | } |
2558 | if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | ||
2559 | if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, | ||
2560 | SSH_FP_DEFAULT)) == NULL) | ||
2561 | fatal("%s: fingerprint failed", __func__); | ||
2562 | fprintf(stderr, "Confirm user presence for key %s %s\n", | ||
2563 | sshkey_type(signkey), fp); | ||
2564 | free(fp); | ||
2565 | } | ||
2522 | } | 2566 | } |
2523 | if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, fd, sig_namespace, | 2567 | if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, pin, |
2524 | &sigbuf, signer, signer_ctx)) != 0) { | 2568 | fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) { |
2525 | error("Signing %s failed: %s", filename, ssh_err(r)); | 2569 | error("Signing %s failed: %s", filename, ssh_err(r)); |
2526 | goto out; | 2570 | goto out; |
2527 | } | 2571 | } |
@@ -2569,7 +2613,10 @@ sign_one(struct sshkey *signkey, const char *filename, int fd, | |||
2569 | r = 0; | 2613 | r = 0; |
2570 | out: | 2614 | out: |
2571 | free(wfile); | 2615 | free(wfile); |
2616 | free(prompt); | ||
2572 | free(asig); | 2617 | free(asig); |
2618 | if (pin != NULL) | ||
2619 | freezero(pin, strlen(pin)); | ||
2573 | sshbuf_free(abuf); | 2620 | sshbuf_free(abuf); |
2574 | sshbuf_free(sigbuf); | 2621 | sshbuf_free(sigbuf); |
2575 | if (wfd != -1) | 2622 | if (wfd != -1) |
@@ -2934,27 +2981,24 @@ do_download_sk(const char *skprovider, const char *device) | |||
2934 | { | 2981 | { |
2935 | struct sshkey **keys; | 2982 | struct sshkey **keys; |
2936 | size_t nkeys, i; | 2983 | size_t nkeys, i; |
2937 | int r, ok = -1; | 2984 | int r, ret = -1; |
2938 | char *fp, *pin = NULL, *pass = NULL, *path, *pubpath; | 2985 | char *fp, *pin = NULL, *pass = NULL, *path, *pubpath; |
2939 | const char *ext; | 2986 | const char *ext; |
2940 | 2987 | ||
2941 | if (skprovider == NULL) | 2988 | if (skprovider == NULL) |
2942 | fatal("Cannot download keys without provider"); | 2989 | fatal("Cannot download keys without provider"); |
2943 | 2990 | ||
2944 | for (i = 0; i < 2; i++) { | 2991 | pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); |
2945 | if (i == 1) { | 2992 | if (!quiet) { |
2946 | pin = read_passphrase("Enter PIN for authenticator: ", | 2993 | printf("You may need to touch your authenticator " |
2947 | RP_ALLOW_STDIN); | 2994 | "to authorize key download.\n"); |
2948 | } | 2995 | } |
2949 | if ((r = sshsk_load_resident(skprovider, device, pin, | 2996 | if ((r = sshsk_load_resident(skprovider, device, pin, |
2950 | &keys, &nkeys)) != 0) { | 2997 | &keys, &nkeys)) != 0) { |
2951 | if (i == 0 && r == SSH_ERR_KEY_WRONG_PASSPHRASE) | 2998 | if (pin != NULL) |
2952 | continue; | 2999 | freezero(pin, strlen(pin)); |
2953 | if (pin != NULL) | 3000 | error("Unable to load resident keys: %s", ssh_err(r)); |
2954 | freezero(pin, strlen(pin)); | 3001 | return -1; |
2955 | error("Unable to load resident keys: %s", ssh_err(r)); | ||
2956 | return -1; | ||
2957 | } | ||
2958 | } | 3002 | } |
2959 | if (nkeys == 0) | 3003 | if (nkeys == 0) |
2960 | logit("No keys to download"); | 3004 | logit("No keys to download"); |
@@ -3018,28 +3062,50 @@ do_download_sk(const char *skprovider, const char *device) | |||
3018 | } | 3062 | } |
3019 | 3063 | ||
3020 | if (i >= nkeys) | 3064 | if (i >= nkeys) |
3021 | ok = 0; /* success */ | 3065 | ret = 0; /* success */ |
3022 | if (pass != NULL) | 3066 | if (pass != NULL) |
3023 | freezero(pass, strlen(pass)); | 3067 | freezero(pass, strlen(pass)); |
3024 | for (i = 0; i < nkeys; i++) | 3068 | for (i = 0; i < nkeys; i++) |
3025 | sshkey_free(keys[i]); | 3069 | sshkey_free(keys[i]); |
3026 | free(keys); | 3070 | free(keys); |
3027 | return ok ? 0 : -1; | 3071 | return ret; |
3072 | } | ||
3073 | |||
3074 | static void | ||
3075 | save_attestation(struct sshbuf *attest, const char *path) | ||
3076 | { | ||
3077 | mode_t omask; | ||
3078 | int r; | ||
3079 | |||
3080 | if (path == NULL) | ||
3081 | return; /* nothing to do */ | ||
3082 | if (attest == NULL || sshbuf_len(attest) == 0) | ||
3083 | fatal("Enrollment did not return attestation data"); | ||
3084 | omask = umask(077); | ||
3085 | r = sshbuf_write_file(path, attest); | ||
3086 | umask(omask); | ||
3087 | if (r != 0) | ||
3088 | fatal("Unable to write attestation data \"%s\": %s", path, | ||
3089 | ssh_err(r)); | ||
3090 | if (!quiet) | ||
3091 | printf("Your FIDO attestation certificate has been saved in " | ||
3092 | "%s\n", path); | ||
3028 | } | 3093 | } |
3029 | 3094 | ||
3030 | static void | 3095 | static void |
3031 | usage(void) | 3096 | usage(void) |
3032 | { | 3097 | { |
3033 | fprintf(stderr, | 3098 | fprintf(stderr, |
3034 | "usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]\n" | 3099 | "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n" |
3100 | " [-m format] [-N new_passphrase] [-O option]\n" | ||
3035 | " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n" | 3101 | " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n" |
3036 | " [-N new_passphrase] [-O option] [-w provider]\n" | 3102 | " [-w provider]\n" |
3037 | " ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]\n" | 3103 | " ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n" |
3038 | " [-P old_passphrase]\n" | 3104 | " [-P old_passphrase]\n" |
3039 | " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" | 3105 | " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" |
3040 | " ssh-keygen -e [-f input_keyfile] [-m key_format]\n" | 3106 | " ssh-keygen -e [-f input_keyfile] [-m key_format]\n" |
3041 | " ssh-keygen -y [-f input_keyfile]\n" | 3107 | " ssh-keygen -y [-f input_keyfile]\n" |
3042 | " ssh-keygen -c [-C comment] [-f keyfile] [-P passphrase]\n" | 3108 | " ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase]\n" |
3043 | " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" | 3109 | " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" |
3044 | " ssh-keygen -B [-f input_keyfile]\n"); | 3110 | " ssh-keygen -B [-f input_keyfile]\n"); |
3045 | #ifdef ENABLE_PKCS11 | 3111 | #ifdef ENABLE_PKCS11 |
@@ -3049,7 +3115,7 @@ usage(void) | |||
3049 | fprintf(stderr, | 3115 | fprintf(stderr, |
3050 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" | 3116 | " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" |
3051 | " ssh-keygen -H [-f known_hosts_file]\n" | 3117 | " ssh-keygen -H [-f known_hosts_file]\n" |
3052 | " ssh-keygen -K [-w provider]\n" | 3118 | " ssh-keygen -K [-a rounds] [-w provider]\n" |
3053 | " ssh-keygen -R hostname [-f known_hosts_file]\n" | 3119 | " ssh-keygen -R hostname [-f known_hosts_file]\n" |
3054 | " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" | 3120 | " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" |
3055 | #ifdef WITH_OPENSSL | 3121 | #ifdef WITH_OPENSSL |
@@ -3060,7 +3126,7 @@ usage(void) | |||
3060 | " [-n principals] [-O option] [-V validity_interval]\n" | 3126 | " [-n principals] [-O option] [-V validity_interval]\n" |
3061 | " [-z serial_number] file ...\n" | 3127 | " [-z serial_number] file ...\n" |
3062 | " ssh-keygen -L [-f input_keyfile]\n" | 3128 | " ssh-keygen -L [-f input_keyfile]\n" |
3063 | " ssh-keygen -A [-f prefix_path]\n" | 3129 | " ssh-keygen -A [-a rounds] [-f prefix_path]\n" |
3064 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" | 3130 | " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" |
3065 | " file ...\n" | 3131 | " file ...\n" |
3066 | " ssh-keygen -Q [-l] -f krl_file [file ...]\n" | 3132 | " ssh-keygen -Q [-l] -f krl_file [file ...]\n" |
@@ -3078,11 +3144,10 @@ usage(void) | |||
3078 | int | 3144 | int |
3079 | main(int argc, char **argv) | 3145 | main(int argc, char **argv) |
3080 | { | 3146 | { |
3081 | char dotsshdir[PATH_MAX], comment[1024], *passphrase; | 3147 | char comment[1024], *passphrase; |
3082 | char *rr_hostname = NULL, *ep, *fp, *ra; | 3148 | char *rr_hostname = NULL, *ep, *fp, *ra; |
3083 | struct sshkey *private, *public; | 3149 | struct sshkey *private, *public; |
3084 | struct passwd *pw; | 3150 | struct passwd *pw; |
3085 | struct stat st; | ||
3086 | int r, opt, type; | 3151 | int r, opt, type; |
3087 | int change_passphrase = 0, change_comment = 0, show_cert = 0; | 3152 | int change_passphrase = 0, change_comment = 0, show_cert = 0; |
3088 | int find_host = 0, delete_host = 0, hash_hosts = 0; | 3153 | int find_host = 0, delete_host = 0, hash_hosts = 0; |
@@ -3093,7 +3158,7 @@ main(int argc, char **argv) | |||
3093 | unsigned long long cert_serial = 0; | 3158 | unsigned long long cert_serial = 0; |
3094 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; | 3159 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; |
3095 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; | 3160 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; |
3096 | char *sk_attestaion_path = NULL; | 3161 | char *sk_attestation_path = NULL; |
3097 | struct sshbuf *challenge = NULL, *attest = NULL; | 3162 | struct sshbuf *challenge = NULL, *attest = NULL; |
3098 | size_t i, nopts = 0; | 3163 | size_t i, nopts = 0; |
3099 | u_int32_t bits = 0; | 3164 | u_int32_t bits = 0; |
@@ -3532,6 +3597,8 @@ main(int argc, char **argv) | |||
3532 | for (i = 0; i < nopts; i++) { | 3597 | for (i = 0; i < nopts; i++) { |
3533 | if (strcasecmp(opts[i], "no-touch-required") == 0) { | 3598 | if (strcasecmp(opts[i], "no-touch-required") == 0) { |
3534 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; | 3599 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; |
3600 | } else if (strcasecmp(opts[i], "verify-required") == 0) { | ||
3601 | sk_flags |= SSH_SK_USER_VERIFICATION_REQD; | ||
3535 | } else if (strcasecmp(opts[i], "resident") == 0) { | 3602 | } else if (strcasecmp(opts[i], "resident") == 0) { |
3536 | sk_flags |= SSH_SK_RESIDENT_KEY; | 3603 | sk_flags |= SSH_SK_RESIDENT_KEY; |
3537 | } else if (strncasecmp(opts[i], "device=", 7) == 0) { | 3604 | } else if (strncasecmp(opts[i], "device=", 7) == 0) { |
@@ -3547,7 +3614,7 @@ main(int argc, char **argv) | |||
3547 | } | 3614 | } |
3548 | } else if (strncasecmp(opts[i], | 3615 | } else if (strncasecmp(opts[i], |
3549 | "write-attestation=", 18) == 0) { | 3616 | "write-attestation=", 18) == 0) { |
3550 | sk_attestaion_path = opts[i] + 18; | 3617 | sk_attestation_path = opts[i] + 18; |
3551 | } else if (strncasecmp(opts[i], | 3618 | } else if (strncasecmp(opts[i], |
3552 | "application=", 12) == 0) { | 3619 | "application=", 12) == 0) { |
3553 | sk_application = xstrdup(opts[i] + 12); | 3620 | sk_application = xstrdup(opts[i] + 12); |
@@ -3564,9 +3631,15 @@ main(int argc, char **argv) | |||
3564 | printf("You may need to touch your authenticator " | 3631 | printf("You may need to touch your authenticator " |
3565 | "to authorize key generation.\n"); | 3632 | "to authorize key generation.\n"); |
3566 | } | 3633 | } |
3567 | passphrase = NULL; | ||
3568 | if ((attest = sshbuf_new()) == NULL) | 3634 | if ((attest = sshbuf_new()) == NULL) |
3569 | fatal("sshbuf_new failed"); | 3635 | fatal("sshbuf_new failed"); |
3636 | if ((sk_flags & | ||
3637 | (SSH_SK_USER_VERIFICATION_REQD|SSH_SK_RESIDENT_KEY))) { | ||
3638 | passphrase = read_passphrase("Enter PIN for " | ||
3639 | "authenticator: ", RP_ALLOW_STDIN); | ||
3640 | } else { | ||
3641 | passphrase = NULL; | ||
3642 | } | ||
3570 | for (i = 0 ; ; i++) { | 3643 | for (i = 0 ; ; i++) { |
3571 | fflush(stdout); | 3644 | fflush(stdout); |
3572 | r = sshsk_enroll(type, sk_provider, sk_device, | 3645 | r = sshsk_enroll(type, sk_provider, sk_device, |
@@ -3577,9 +3650,8 @@ main(int argc, char **argv) | |||
3577 | break; | 3650 | break; |
3578 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) | 3651 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) |
3579 | fatal("Key enrollment failed: %s", ssh_err(r)); | 3652 | fatal("Key enrollment failed: %s", ssh_err(r)); |
3580 | else if (i > 0) | 3653 | else if (passphrase != NULL) { |
3581 | error("PIN incorrect"); | 3654 | error("PIN incorrect"); |
3582 | if (passphrase != NULL) { | ||
3583 | freezero(passphrase, strlen(passphrase)); | 3655 | freezero(passphrase, strlen(passphrase)); |
3584 | passphrase = NULL; | 3656 | passphrase = NULL; |
3585 | } | 3657 | } |
@@ -3587,6 +3659,11 @@ main(int argc, char **argv) | |||
3587 | fatal("Too many incorrect PINs"); | 3659 | fatal("Too many incorrect PINs"); |
3588 | passphrase = read_passphrase("Enter PIN for " | 3660 | passphrase = read_passphrase("Enter PIN for " |
3589 | "authenticator: ", RP_ALLOW_STDIN); | 3661 | "authenticator: ", RP_ALLOW_STDIN); |
3662 | if (!quiet) { | ||
3663 | printf("You may need to touch your " | ||
3664 | "authenticator (again) to authorize " | ||
3665 | "key generation.\n"); | ||
3666 | } | ||
3590 | } | 3667 | } |
3591 | if (passphrase != NULL) { | 3668 | if (passphrase != NULL) { |
3592 | freezero(passphrase, strlen(passphrase)); | 3669 | freezero(passphrase, strlen(passphrase)); |
@@ -3605,20 +3682,8 @@ main(int argc, char **argv) | |||
3605 | ask_filename(pw, "Enter file in which to save the key"); | 3682 | ask_filename(pw, "Enter file in which to save the key"); |
3606 | 3683 | ||
3607 | /* Create ~/.ssh directory if it doesn't already exist. */ | 3684 | /* Create ~/.ssh directory if it doesn't already exist. */ |
3608 | snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", | 3685 | hostfile_create_user_ssh_dir(identity_file, !quiet); |
3609 | pw->pw_dir, _PATH_SSH_USER_DIR); | 3686 | |
3610 | if (strstr(identity_file, dotsshdir) != NULL) { | ||
3611 | if (stat(dotsshdir, &st) == -1) { | ||
3612 | if (errno != ENOENT) { | ||
3613 | error("Could not stat %s: %s", dotsshdir, | ||
3614 | strerror(errno)); | ||
3615 | } else if (mkdir(dotsshdir, 0700) == -1) { | ||
3616 | error("Could not create directory '%s': %s", | ||
3617 | dotsshdir, strerror(errno)); | ||
3618 | } else if (!quiet) | ||
3619 | printf("Created directory '%s'.\n", dotsshdir); | ||
3620 | } | ||
3621 | } | ||
3622 | /* If the file already exists, ask the user to confirm. */ | 3687 | /* If the file already exists, ask the user to confirm. */ |
3623 | if (!confirm_overwrite(identity_file)) | 3688 | if (!confirm_overwrite(identity_file)) |
3624 | exit(1); | 3689 | exit(1); |
@@ -3671,20 +3736,9 @@ main(int argc, char **argv) | |||
3671 | free(fp); | 3736 | free(fp); |
3672 | } | 3737 | } |
3673 | 3738 | ||
3674 | if (sk_attestaion_path != NULL) { | 3739 | if (sk_attestation_path != NULL) |
3675 | if (attest == NULL || sshbuf_len(attest) == 0) { | 3740 | save_attestation(attest, sk_attestation_path); |
3676 | fatal("Enrollment did not return attestation " | 3741 | |
3677 | "certificate"); | ||
3678 | } | ||
3679 | if ((r = sshbuf_write_file(sk_attestaion_path, attest)) != 0) { | ||
3680 | fatal("Unable to write attestation certificate " | ||
3681 | "\"%s\": %s", sk_attestaion_path, ssh_err(r)); | ||
3682 | } | ||
3683 | if (!quiet) { | ||
3684 | printf("Your FIDO attestation certificate has been " | ||
3685 | "saved in %s\n", sk_attestaion_path); | ||
3686 | } | ||
3687 | } | ||
3688 | sshbuf_free(attest); | 3742 | sshbuf_free(attest); |
3689 | sshkey_free(public); | 3743 | sshkey_free(public); |
3690 | 3744 | ||