diff options
author | djm@openbsd.org <djm@openbsd.org> | 2020-08-03 02:53:51 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2020-08-03 14:27:59 +1000 |
commit | 2d8a3b7e8b0408dfeb933ac5cfd3a58f5bac49af (patch) | |
tree | 28d4825abb375d31a7ac6e08e196280ff2f9da0c /ssh-keygen.c | |
parent | a8732d74cb8e72f0c6366015687f1e649f60be87 (diff) |
upstream: ensure that certificate extensions are lexically sorted.
Previously if the user specified a custom extension then the everything would
be in order except the custom ones. bz3198 ok dtucker markus
OpenBSD-Commit-ID: d97deb90587b06cb227c66ffebb2d9667bf886f0
Diffstat (limited to 'ssh-keygen.c')
-rw-r--r-- | ssh-keygen.c | 152 |
1 files changed, 85 insertions, 67 deletions
diff --git a/ssh-keygen.c b/ssh-keygen.c index 4a27d3bfd..cc092368e 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.414 2020/07/15 07:50:46 solene Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.415 2020/08/03 02:53:51 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 { |
@@ -1601,31 +1601,32 @@ do_change_comment(struct passwd *pw, const char *identity_comment) | |||
1601 | } | 1601 | } |
1602 | 1602 | ||
1603 | static void | 1603 | static void |
1604 | add_flag_option(struct sshbuf *c, const char *name) | 1604 | cert_ext_add(const char *key, const char *value, int iscrit) |
1605 | { | 1605 | { |
1606 | int r; | 1606 | cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext)); |
1607 | 1607 | cert_ext[ncert_ext].key = xstrdup(key); | |
1608 | debug3("%s: %s", __func__, name); | 1608 | cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value); |
1609 | if ((r = sshbuf_put_cstring(c, name)) != 0 || | 1609 | cert_ext[ncert_ext].crit = iscrit; |
1610 | (r = sshbuf_put_string(c, NULL, 0)) != 0) | 1610 | ncert_ext++; |
1611 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | ||
1612 | } | 1611 | } |
1613 | 1612 | ||
1614 | static void | 1613 | /* qsort(3) comparison function for certificate extensions */ |
1615 | add_string_option(struct sshbuf *c, const char *name, const char *value) | 1614 | static int |
1615 | cert_ext_cmp(const void *_a, const void *_b) | ||
1616 | { | 1616 | { |
1617 | struct sshbuf *b; | 1617 | const struct cert_ext *a = (const struct cert_ext *)_a; |
1618 | const struct cert_ext *b = (const struct cert_ext *)_b; | ||
1618 | int r; | 1619 | int r; |
1619 | 1620 | ||
1620 | debug3("%s: %s=%s", __func__, name, value); | 1621 | if (a->crit != b->crit) |
1621 | if ((b = sshbuf_new()) == NULL) | 1622 | return (a->crit < b->crit) ? -1 : 1; |
1622 | fatal("%s: sshbuf_new failed", __func__); | 1623 | if ((r = strcmp(a->key, b->key)) != 0) |
1623 | if ((r = sshbuf_put_cstring(b, value)) != 0 || | 1624 | return r; |
1624 | (r = sshbuf_put_cstring(c, name)) != 0 || | 1625 | if ((a->val == NULL) != (b->val == NULL)) |
1625 | (r = sshbuf_put_stringb(c, b)) != 0) | 1626 | return (a->val == NULL) ? -1 : 1; |
1626 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 1627 | if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0) |
1627 | 1628 | return r; | |
1628 | sshbuf_free(b); | 1629 | return 0; |
1629 | } | 1630 | } |
1630 | 1631 | ||
1631 | #define OPTIONS_CRITICAL 1 | 1632 | #define OPTIONS_CRITICAL 1 |
@@ -1633,44 +1634,62 @@ add_string_option(struct sshbuf *c, const char *name, const char *value) | |||
1633 | static void | 1634 | static void |
1634 | prepare_options_buf(struct sshbuf *c, int which) | 1635 | prepare_options_buf(struct sshbuf *c, int which) |
1635 | { | 1636 | { |
1637 | struct sshbuf *b; | ||
1636 | size_t i; | 1638 | size_t i; |
1639 | int r; | ||
1640 | const struct cert_ext *ext; | ||
1637 | 1641 | ||
1642 | if ((b = sshbuf_new()) == NULL) | ||
1643 | fatal("%s: sshbuf_new failed", __func__); | ||
1638 | sshbuf_reset(c); | 1644 | sshbuf_reset(c); |
1639 | if ((which & OPTIONS_CRITICAL) != 0 && | 1645 | for (i = 0; i < ncert_ext; i++) { |
1640 | certflags_command != NULL) | 1646 | ext = &cert_ext[i]; |
1641 | add_string_option(c, "force-command", certflags_command); | 1647 | if ((ext->crit && (which & OPTIONS_EXTENSIONS)) || |
1642 | if ((which & OPTIONS_EXTENSIONS) != 0 && | 1648 | (!ext->crit && (which & OPTIONS_CRITICAL))) |
1643 | (certflags_flags & CERTOPT_X_FWD) != 0) | ||
1644 | add_flag_option(c, "permit-X11-forwarding"); | ||
1645 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1646 | (certflags_flags & CERTOPT_AGENT_FWD) != 0) | ||
1647 | add_flag_option(c, "permit-agent-forwarding"); | ||
1648 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1649 | (certflags_flags & CERTOPT_PORT_FWD) != 0) | ||
1650 | add_flag_option(c, "permit-port-forwarding"); | ||
1651 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1652 | (certflags_flags & CERTOPT_PTY) != 0) | ||
1653 | add_flag_option(c, "permit-pty"); | ||
1654 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1655 | (certflags_flags & CERTOPT_USER_RC) != 0) | ||
1656 | add_flag_option(c, "permit-user-rc"); | ||
1657 | if ((which & OPTIONS_EXTENSIONS) != 0 && | ||
1658 | (certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) | ||
1659 | add_flag_option(c, "no-touch-required"); | ||
1660 | if ((which & OPTIONS_CRITICAL) != 0 && | ||
1661 | certflags_src_addr != NULL) | ||
1662 | add_string_option(c, "source-address", certflags_src_addr); | ||
1663 | for (i = 0; i < ncert_userext; i++) { | ||
1664 | if ((cert_userext[i].crit && (which & OPTIONS_EXTENSIONS)) || | ||
1665 | (!cert_userext[i].crit && (which & OPTIONS_CRITICAL))) | ||
1666 | continue; | 1649 | continue; |
1667 | if (cert_userext[i].val == NULL) | 1650 | if (ext->val == NULL) { |
1668 | add_flag_option(c, cert_userext[i].key); | 1651 | /* flag option */ |
1669 | else { | 1652 | debug3("%s: %s", __func__, ext->key); |
1670 | add_string_option(c, cert_userext[i].key, | 1653 | if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || |
1671 | cert_userext[i].val); | 1654 | (r = sshbuf_put_string(c, NULL, 0)) != 0) |
1655 | fatal("%s: buffer: %s", __func__, ssh_err(r)); | ||
1656 | } else { | ||
1657 | /* key/value option */ | ||
1658 | debug3("%s: %s=%s", __func__, ext->key, ext->val); | ||
1659 | sshbuf_reset(b); | ||
1660 | if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || | ||
1661 | (r = sshbuf_put_cstring(b, ext->val)) != 0 || | ||
1662 | (r = sshbuf_put_stringb(c, b)) != 0) | ||
1663 | fatal("%s: buffer: %s", __func__, ssh_err(r)); | ||
1672 | } | 1664 | } |
1673 | } | 1665 | } |
1666 | sshbuf_free(b); | ||
1667 | } | ||
1668 | |||
1669 | static void | ||
1670 | finalise_cert_exts(void) | ||
1671 | { | ||
1672 | /* critical options */ | ||
1673 | if (certflags_command != NULL) | ||
1674 | cert_ext_add("force-command", certflags_command, 1); | ||
1675 | if (certflags_src_addr != NULL) | ||
1676 | cert_ext_add("source-address", certflags_src_addr, 1); | ||
1677 | /* extensions */ | ||
1678 | if ((certflags_flags & CERTOPT_X_FWD) != 0) | ||
1679 | cert_ext_add("permit-X11-forwarding", NULL, 0); | ||
1680 | if ((certflags_flags & CERTOPT_AGENT_FWD) != 0) | ||
1681 | cert_ext_add("permit-agent-forwarding", NULL, 0); | ||
1682 | if ((certflags_flags & CERTOPT_PORT_FWD) != 0) | ||
1683 | cert_ext_add("permit-port-forwarding", NULL, 0); | ||
1684 | if ((certflags_flags & CERTOPT_PTY) != 0) | ||
1685 | cert_ext_add("permit-pty", NULL, 0); | ||
1686 | if ((certflags_flags & CERTOPT_USER_RC) != 0) | ||
1687 | cert_ext_add("permit-user-rc", NULL, 0); | ||
1688 | if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) | ||
1689 | cert_ext_add("no-touch-required", NULL, 0); | ||
1690 | /* order lexically by key */ | ||
1691 | if (ncert_ext > 0) | ||
1692 | qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp); | ||
1674 | } | 1693 | } |
1675 | 1694 | ||
1676 | static struct sshkey * | 1695 | static struct sshkey * |
@@ -1780,6 +1799,7 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, | |||
1780 | } | 1799 | } |
1781 | ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); | 1800 | ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); |
1782 | 1801 | ||
1802 | finalise_cert_exts(); | ||
1783 | for (i = 0; i < argc; i++) { | 1803 | for (i = 0; i < argc; i++) { |
1784 | /* Split list of principals */ | 1804 | /* Split list of principals */ |
1785 | n = 0; | 1805 | n = 0; |
@@ -1994,13 +2014,8 @@ add_cert_option(char *opt) | |||
1994 | val = xstrdup(strchr(opt, ':') + 1); | 2014 | val = xstrdup(strchr(opt, ':') + 1); |
1995 | if ((cp = strchr(val, '=')) != NULL) | 2015 | if ((cp = strchr(val, '=')) != NULL) |
1996 | *cp++ = '\0'; | 2016 | *cp++ = '\0'; |
1997 | cert_userext = xreallocarray(cert_userext, ncert_userext + 1, | 2017 | cert_ext_add(val, cp, iscrit); |
1998 | sizeof(*cert_userext)); | 2018 | free(val); |
1999 | cert_userext[ncert_userext].key = val; | ||
2000 | cert_userext[ncert_userext].val = cp == NULL ? | ||
2001 | NULL : xstrdup(cp); | ||
2002 | cert_userext[ncert_userext].crit = iscrit; | ||
2003 | ncert_userext++; | ||
2004 | } else | 2019 | } else |
2005 | fatal("Unsupported certificate option \"%s\"", opt); | 2020 | fatal("Unsupported certificate option \"%s\"", opt); |
2006 | } | 2021 | } |
@@ -2008,7 +2023,7 @@ add_cert_option(char *opt) | |||
2008 | static void | 2023 | static void |
2009 | show_options(struct sshbuf *optbuf, int in_critical) | 2024 | show_options(struct sshbuf *optbuf, int in_critical) |
2010 | { | 2025 | { |
2011 | char *name, *arg; | 2026 | char *name, *arg, *hex; |
2012 | struct sshbuf *options, *option = NULL; | 2027 | struct sshbuf *options, *option = NULL; |
2013 | int r; | 2028 | int r; |
2014 | 2029 | ||
@@ -2037,11 +2052,14 @@ show_options(struct sshbuf *optbuf, int in_critical) | |||
2037 | __func__, ssh_err(r)); | 2052 | __func__, ssh_err(r)); |
2038 | printf(" %s\n", arg); | 2053 | printf(" %s\n", arg); |
2039 | free(arg); | 2054 | free(arg); |
2040 | } else { | 2055 | } else if (sshbuf_len(option) > 0) { |
2041 | printf(" UNKNOWN OPTION (len %zu)\n", | 2056 | hex = sshbuf_dtob16(option); |
2042 | sshbuf_len(option)); | 2057 | printf(" UNKNOWN OPTION: %s (len %zu)\n", |
2058 | hex, sshbuf_len(option)); | ||
2043 | sshbuf_reset(option); | 2059 | sshbuf_reset(option); |
2044 | } | 2060 | free(hex); |
2061 | } else | ||
2062 | printf(" UNKNOWN FLAG OPTION\n"); | ||
2045 | free(name); | 2063 | free(name); |
2046 | if (sshbuf_len(option) != 0) | 2064 | if (sshbuf_len(option) != 0) |
2047 | fatal("Option corrupt: extra data at end"); | 2065 | fatal("Option corrupt: extra data at end"); |