summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2020-08-03 02:53:51 +0000
committerDamien Miller <djm@mindrot.org>2020-08-03 14:27:59 +1000
commit2d8a3b7e8b0408dfeb933ac5cfd3a58f5bac49af (patch)
tree28d4825abb375d31a7ac6e08e196280ff2f9da0c
parenta8732d74cb8e72f0c6366015687f1e649f60be87 (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
-rw-r--r--ssh-keygen.c152
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;
133static char *certflags_src_addr = NULL; 133static char *certflags_src_addr = NULL;
134 134
135/* Arbitrary extensions specified by user */ 135/* Arbitrary extensions specified by user */
136struct cert_userext { 136struct cert_ext {
137 char *key; 137 char *key;
138 char *val; 138 char *val;
139 int crit; 139 int crit;
140}; 140};
141static struct cert_userext *cert_userext; 141static struct cert_ext *cert_ext;
142static size_t ncert_userext; 142static size_t ncert_ext;
143 143
144/* Conversion to/from various formats */ 144/* Conversion to/from various formats */
145enum { 145enum {
@@ -1601,31 +1601,32 @@ do_change_comment(struct passwd *pw, const char *identity_comment)
1601} 1601}
1602 1602
1603static void 1603static void
1604add_flag_option(struct sshbuf *c, const char *name) 1604cert_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
1614static void 1613/* qsort(3) comparison function for certificate extensions */
1615add_string_option(struct sshbuf *c, const char *name, const char *value) 1614static int
1615cert_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)
1633static void 1634static void
1634prepare_options_buf(struct sshbuf *c, int which) 1635prepare_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
1669static void
1670finalise_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
1676static struct sshkey * 1695static 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)
2008static void 2023static void
2009show_options(struct sshbuf *optbuf, int in_critical) 2024show_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");