Check the resources in ROAs and RSCs against EE certs
authortb <tb@openbsd.org>
Fri, 19 Aug 2022 12:45:53 +0000 (12:45 +0000)
committertb <tb@openbsd.org>
Fri, 19 Aug 2022 12:45:53 +0000 (12:45 +0000)
The resources delegated in the RFC 3779 extensions of the EE cert for
ROAs or RSCs can be a subset of the resources in the auth chain. So far
we compared that the resources of ROAs and RSCs are covered by the auth
chain, which is not entirely correct. Extract the necessary data from
the EE cert into rpki-client's own data structures, then verify that
the EE cert's resources cover the ones claimed in the ROA or RSC.

Do this as part or ROA and RSC parsing, that the EE cert's resources are
covered by the auth chain is checked in valid_x509() later on.

All this is a bit more annoying and intrusive than it should be...

ok claudio job

usr.sbin/rpki-client/cert.c
usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/filemode.c
usr.sbin/rpki-client/parser.c
usr.sbin/rpki-client/roa.c
usr.sbin/rpki-client/rsc.c
usr.sbin/rpki-client/validate.c

index 9ff4bbc..06a6d19 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cert.c,v 1.84 2022/05/31 18:51:35 tb Exp $ */
+/*     $OpenBSD: cert.c,v 1.85 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
  * Copyright (c) 2021 Job Snijders <job@openbsd.org>
@@ -570,6 +570,49 @@ certificate_policies(struct parse *p, X509_EXTENSION *ext)
        return rc;
 }
 
+/*
+ * Lightweight version of cert_parse_pre() for ASPA, ROA, and RSC EE certs.
+ * This only parses the RFC 3779 extensions since these are necessary for
+ * validation.
+ * Returns cert on success and NULL on failure.
+ */
+struct cert *
+cert_parse_ee_cert(const char *fn, X509 *x)
+{
+       struct parse             p;
+       X509_EXTENSION          *ext;
+       int                      index;
+
+       memset(&p, 0, sizeof(struct parse));
+       p.fn = fn;
+       if ((p.res = calloc(1, sizeof(struct cert))) == NULL)
+               err(1, NULL);
+
+       index = X509_get_ext_by_NID(x, NID_sbgp_ipAddrBlock, -1);
+       if ((ext = X509_get_ext(x, index)) != NULL) {
+               if (!sbgp_ipaddrblk(&p, ext))
+                       goto out;
+       }
+
+       index = X509_get_ext_by_NID(x, NID_sbgp_autonomousSysNum, -1);
+       if ((ext = X509_get_ext(x, index)) != NULL) {
+               if (!sbgp_assysnum(&p, ext))
+                       goto out;
+       }
+
+       if (!X509_up_ref(x)) {
+               cryptowarnx("%s: X509_up_ref failed", fn);
+               goto out;
+       }
+
+       p.res->x509 = x;
+       return p.res;
+
+ out:
+       cert_free(p.res);
+       return NULL;
+}
+
 /*
  * Parse and partially validate an RPKI X509 certificate (either a trust
  * anchor or a certificate) as defined in RFC 6487.
index 93a714d..8030838 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: extern.h,v 1.149 2022/08/18 15:20:27 job Exp $ */
+/*     $OpenBSD: extern.h,v 1.150 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -464,6 +464,7 @@ struct tal  *tal_read(struct ibuf *);
 
 void            cert_buffer(struct ibuf *, const struct cert *);
 void            cert_free(struct cert *);
+struct cert    *cert_parse_ee_cert(const char *, X509 *);
 struct cert    *cert_parse_pre(const char *, const unsigned char *, size_t);
 struct cert    *cert_parse(const char *, struct cert *);
 struct cert    *ta_parse(const char *, struct cert *, const unsigned char *,
@@ -508,7 +509,7 @@ struct auth *valid_ski_aki(const char *, struct auth_tree *,
 int             valid_ta(const char *, struct auth_tree *,
                    const struct cert *);
 int             valid_cert(const char *, struct auth *, const struct cert *);
-int             valid_roa(const char *, struct auth *, struct roa *);
+int             valid_roa(const char *, struct cert *, struct roa *);
 int             valid_filehash(int, const char *, size_t);
 int             valid_hash(unsigned char *, size_t, const char *, size_t);
 int             valid_filename(const char *, size_t);
@@ -516,7 +517,7 @@ int          valid_uri(const char *, size_t, const char *);
 int             valid_origin(const char *, const char *);
 int             valid_x509(char *, X509_STORE_CTX *, X509 *, struct auth *,
                    struct crl *, int);
-int             valid_rsc(const char *, struct auth *, struct rsc *);
+int             valid_rsc(const char *, struct cert *, struct rsc *);
 int             valid_econtent_version(const char *, const ASN1_INTEGER *);
 
 /* Working with CMS. */
index f43a2fa..3d622e5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: filemode.c,v 1.7 2022/05/11 14:42:01 job Exp $ */
+/*     $OpenBSD: filemode.c,v 1.8 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -392,9 +392,9 @@ proc_parser_file(char *file, unsigned char *buf, size_t len)
 
                if ((status = valid_x509(file, ctx, x509, a, c, 0))) {
                        if (type == RTYPE_ROA)
-                               status = valid_roa(file, a, roa);
+                               status = roa->valid;
                        else if (type == RTYPE_RSC)
-                               status = valid_rsc(file, a, rsc);
+                               status = rsc->valid;
                }
                if (status)
                        printf("OK");
index 38d6531..e6a9fc9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parser.c,v 1.73 2022/04/21 12:59:03 claudio Exp $ */
+/*     $OpenBSD: parser.c,v 1.74 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -148,14 +148,6 @@ proc_parser_roa(char *file, const unsigned char *der, size_t len)
 
        roa->talid = a->cert->talid;
 
-       /*
-        * If the ROA isn't valid, we accept it anyway and depend upon
-        * the code around roa_read() to check the "valid" field itself.
-        */
-
-       if (valid_roa(file, a, roa))
-               roa->valid = 1;
-
        /*
         * Check CRL to figure out the soonest transitive expiry moment
         */
index db725a1..6c3aaa3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: roa.c,v 1.49 2022/08/10 14:54:03 job Exp $ */
+/*     $OpenBSD: roa.c,v 1.50 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -204,8 +204,9 @@ roa_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
        struct parse     p;
        size_t           cmsz;
        unsigned char   *cms;
-       int              rc = 0;
        const ASN1_TIME *at;
+       struct cert     *cert = NULL;
+       int              rc = 0;
 
        memset(&p, 0, sizeof(struct parse));
        p.fn = fn;
@@ -229,11 +230,6 @@ roa_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
                goto out;
        }
 
-       if (X509_get_ext_by_NID(*x509, NID_sbgp_autonomousSysNum, -1) != -1) {
-               warnx("%s: superfluous AS Resources extension present", fn);
-               goto out;
-       }
-
        at = X509_get0_notAfter(*x509);
        if (at == NULL) {
                warnx("%s: X509_get0_notAfter failed", fn);
@@ -247,6 +243,20 @@ roa_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
        if (!roa_parse_econtent(cms, cmsz, &p))
                goto out;
 
+       if ((cert = cert_parse_ee_cert(fn, *x509)) == NULL)
+               goto out;
+
+       if (cert->asz > 0) {
+               warnx("%s: superfluous AS Resources extension present", fn);
+               goto out;
+       }
+
+       /*
+        * If the ROA isn't valid, we accept it anyway and depend upon
+        * the code around roa_read() to check the "valid" field itself.
+        */
+       p.res->valid = valid_roa(fn, cert, p.res);
+
        rc = 1;
 out:
        if (rc == 0) {
@@ -255,6 +265,7 @@ out:
                X509_free(*x509);
                *x509 = NULL;
        }
+       cert_free(cert);
        free(cms);
        return p.res;
 }
index 586cd71..4db9fc6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rsc.c,v 1.12 2022/06/10 10:41:09 tb Exp $ */
+/*     $OpenBSD: rsc.c,v 1.13 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
  * Copyright (c) 2022 Job Snijders <job@fastly.com>
@@ -378,6 +378,7 @@ rsc_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
        unsigned char           *cms;
        size_t                   cmsz;
        const ASN1_TIME         *at;
+       struct cert             *cert = NULL;
        int                      rc = 0;
 
        memset(&p, 0, sizeof(struct parse));
@@ -412,9 +413,16 @@ rsc_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
                goto out;
        }
 
+       /* XXX - check that SIA is absent. */
+
        if (!rsc_parse_econtent(cms, cmsz, &p))
                goto out;
 
+       if ((cert = cert_parse_ee_cert(fn, *x509)) == NULL)
+               goto out;
+
+       p.res->valid = valid_rsc(fn, cert, p.res);
+
        rc = 1;
  out:
        if (rc == 0) {
@@ -423,6 +431,7 @@ rsc_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
                X509_free(*x509);
                *x509 = NULL;
        }
+       cert_free(cert);
        free(cms);
        return p.res;
 }
index 5c3fcd8..cc6d951 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: validate.c,v 1.40 2022/06/10 10:36:43 tb Exp $ */
+/*     $OpenBSD: validate.c,v 1.41 2022/08/19 12:45:53 tb Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -201,19 +201,19 @@ valid_cert(const char *fn, struct auth *a, const struct cert *cert)
  * Returns 1 if valid, 0 otherwise.
  */
 int
-valid_roa(const char *fn, struct auth *a, struct roa *roa)
+valid_roa(const char *fn, struct cert *cert, struct roa *roa)
 {
        size_t   i;
        char     buf[64];
 
        for (i = 0; i < roa->ipsz; i++) {
-               if (valid_ip(a, roa->ips[i].afi, roa->ips[i].min,
-                   roa->ips[i].max))
+               if (ip_addr_check_covered(roa->ips[i].afi, roa->ips[i].min,
+                   roa->ips[i].max, cert->ips, cert->ipsz) > 0)
                        continue;
-               ip_addr_print(&roa->ips[i].addr,
-                   roa->ips[i].afi, buf, sizeof(buf));
-               warnx("%s: RFC 6482: uncovered IP: "
-                   "%s", fn, buf);
+
+               ip_addr_print(&roa->ips[i].addr, roa->ips[i].afi, buf,
+                   sizeof(buf));
+               warnx("%s: RFC 6482: uncovered IP: %s", fn, buf);
                return 0;
        }
 
@@ -442,7 +442,7 @@ valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a,
  * Returns 1 if valid, 0 otherwise.
  */
 int
-valid_rsc(const char *fn, struct auth *a, struct rsc *rsc)
+valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc)
 {
        size_t          i;
        uint32_t        min, max;
@@ -459,7 +459,7 @@ valid_rsc(const char *fn, struct auth *a, struct rsc *rsc)
                max = rsc->as[i].type == CERT_AS_RANGE ? rsc->as[i].range.max
                    : rsc->as[i].id;
 
-               if (valid_as(a, min, max))
+               if (as_check_covered(min, max, cert->as, cert->asz) > 0)
                        continue;
 
                switch (rsc->as[i].type) {
@@ -483,8 +483,8 @@ valid_rsc(const char *fn, struct auth *a, struct rsc *rsc)
                        return 0;
                }
 
-               if (valid_ip(a, rsc->ips[i].afi, rsc->ips[i].min,
-                   rsc->ips[i].max))
+               if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min,
+                   rsc->ips[i].max, cert->ips, cert->ipsz) > 0)
                        continue;
 
                switch (rsc->ips[i].type) {