On Wed, Aug 10, 2022 at 03:10:19PM +0000, Job Snijders wrote:
> Hi all,
> 
> An errata exists for RFC 6482, which informs us: """The EE certificate
> MUST NOT use "inherit" elements as described in [RFC3779].""" Read the
> full report here: https://www.rfc-editor.org/errata/eid3166
> 
> Although it might seem a bit 'wasteful' to d2i the IP Resources
> extension in multiple places, noodling through parameters when to check
> for inheritance and when not to check didn't improve code readability.
> I'm open to suggestions how to perform this check differently.

As I understand it, what really is missing isn't a check for inheritance
per se, but rather a check whether the prefixes in the ROA are covered
by the EE cert's IP address delegation extension (the bullet point in
RFC 6482, section 4). If we had such a check, that would be the natural
place for adding an inheritance check for the EE cert.

Below is my "overclaim" diff from a few weeks back that prepended the EE
cert to the auth chain for ROAs and RSCs so that we check their
resources against the EE cert instead of our currently incorrect checks
that permitted overclaiming. The diff was ok job and claudio told me
that it looked ok - I will need to think it through in detail once more,
however.

I believe that with something like this diff, your desired inheritance
check should be added to valid_roa() above the for() loop.

Does that make sense?

Index: cert.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v
retrieving revision 1.84
diff -u -p -r1.84 cert.c
--- cert.c      31 May 2022 18:51:35 -0000      1.84
+++ cert.c      10 Aug 2022 15:40:25 -0000
@@ -467,23 +467,19 @@ sbgp_sia(struct parse *p, X509_EXTENSION
                }
        }
 
-       if (p->res->mft == NULL || p->res->repo == NULL) {
-               warnx("%s: RFC 6487 section 4.8.8: SIA: missing caRepository "
-                   "or rpkiManifest", p->fn);
-               goto out;
-       }
-
-       if (strstr(p->res->mft, p->res->repo) != p->res->mft) {
-               warnx("%s: RFC 6487 section 4.8.8: SIA: "
-                   "conflicting URIs for caRepository and rpkiManifest",
-                   p->fn);
-               goto out;
-       }
+       if (p->res->mft != NULL && p->res->repo != NULL) {
+               if (strstr(p->res->mft, p->res->repo) != p->res->mft) {
+                       warnx("%s: RFC 6487 section 4.8.8: SIA: conflicting "
+                           "URIs for caRepository and rpkiManifest",
+                           p->fn);
+                       goto out;
+               }
 
-       if (rtype_from_file_extension(p->res->mft) != RTYPE_MFT) {
-               warnx("%s: RFC 6487 section 4.8.8: SIA: "
-                   "not an MFT file", p->fn);
-               goto out;
+               if (rtype_from_file_extension(p->res->mft) != RTYPE_MFT) {
+                       warnx("%s: RFC 6487 section 4.8.8: SIA: "
+                           "not an MFT file", p->fn);
+                       goto out;
+               }
        }
 
        rc = 1;
@@ -570,42 +566,20 @@ certificate_policies(struct parse *p, X5
        return rc;
 }
 
-/*
- * Parse and partially validate an RPKI X509 certificate (either a trust
- * anchor or a certificate) as defined in RFC 6487.
- * Returns the parse results or NULL on failure.
- */
-struct cert *
-cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
+static struct cert *
+cert_extract_x509(const char *fn, X509 *x)
 {
        int              extsz;
-       int              sia_present = 0;
        size_t           i;
-       X509            *x = NULL;
        X509_EXTENSION  *ext = NULL;
        ASN1_OBJECT     *obj;
        struct parse     p;
 
-       /* just fail for empty buffers, the warning was printed elsewhere */
-       if (der == NULL)
-               return NULL;
-
        memset(&p, 0, sizeof(struct parse));
        p.fn = fn;
        if ((p.res = calloc(1, sizeof(struct cert))) == NULL)
                err(1, NULL);
 
-       if ((x = d2i_X509(NULL, &der, len)) == NULL) {
-               cryptowarnx("%s: d2i_X509", p.fn);
-               goto out;
-       }
-
-       /* Cache X509v3 extensions, see X509_check_ca(3). */
-       if (X509_check_purpose(x, -1, -1) <= 0) {
-               cryptowarnx("%s: could not cache X509v3 extensions", p.fn);
-               goto out;
-       }
-
        /* Look for X509v3 extensions. */
 
        if ((extsz = X509_get_ext_count(x)) < 0)
@@ -627,7 +601,6 @@ cert_parse_pre(const char *fn, const uns
                                goto out;
                        break;
                case NID_sinfo_access:
-                       sia_present = 1;
                        if (!sbgp_sia(&p, ext))
                                goto out;
                        break;
@@ -647,12 +620,6 @@ cert_parse_pre(const char *fn, const uns
                case NID_ext_key_usage:
                        break;
                default:
-                       /* {
-                               char objn[64];
-                               OBJ_obj2txt(objn, sizeof(objn), obj, 0);
-                               warnx("%s: ignoring %s (NID %d)",
-                                       p.fn, objn, OBJ_obj2nid(obj));
-                       } */
                        break;
                }
        }
@@ -667,54 +634,95 @@ cert_parse_pre(const char *fn, const uns
                goto out;
        if (!x509_get_expire(x, p.fn, &p.res->expires))
                goto out;
-       p.res->purpose = x509_get_purpose(x, p.fn);
 
-       /* Validation on required fields. */
+       p.res->x509 = x;
+       return p.res;
+
+ out:
+       cert_free(p.res);
+       X509_free(x);
+       return NULL;
+}
+
+/*
+ * Parse and partially validate an RPKI X509 certificate (either a trust
+ * anchor or a certificate) as defined in RFC 6487.
+ * Returns the parse results or NULL on failure.
+ */
+struct cert *
+cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
+{
+       X509                    *x = NULL;
+       struct cert             *cert = NULL;
+
+       /* just fail for empty buffers, the warning was printed elsewhere */
+       if (der == NULL)
+               return NULL;
+
+       if ((x = d2i_X509(NULL, &der, len)) == NULL) {
+               cryptowarnx("%s: d2i_X509", fn);
+               goto out;
+       }
+
+       /* Cache X509v3 extensions, see X509_check_ca(3). */
+       if (X509_check_purpose(x, -1, -1) <= 0) {
+               cryptowarnx("%s: could not cache X509v3 extensions", fn);
+               goto out;
+       }
+
+       if (!X509_up_ref(x)) {
+               cryptowarnx("%s: X509_up_ref failed", fn);
+               goto out;
+       }
+
+       if ((cert = cert_extract_x509(fn, x)) == NULL)
+               goto out;
 
-       switch (p.res->purpose) {
+       /* Validation on required fields. */
+       cert->purpose = x509_get_purpose(x, fn);
+       switch (cert->purpose) {
        case CERT_PURPOSE_CA:
-               if (p.res->mft == NULL) {
-                       warnx("%s: RFC 6487 section 4.8.8: missing SIA", p.fn);
+               if (cert->mft == NULL) {
+                       warnx("%s: RFC 6487 section 4.8.8: missing SIA", fn);
                        goto out;
                }
-               if (p.res->asz == 0 && p.res->ipsz == 0) {
-                       warnx("%s: missing IP or AS resources", p.fn);
+               if (cert->asz == 0 && cert->ipsz == 0) {
+                       warnx("%s: missing IP or AS resources", fn);
                        goto out;
                }
                break;
        case CERT_PURPOSE_BGPSEC_ROUTER:
-               p.res->pubkey = x509_get_pubkey(x, p.fn);
-               if (p.res->pubkey == NULL) {
-                       warnx("%s: x509_get_pubkey failed", p.fn);
+               cert->pubkey = x509_get_pubkey(x, fn);
+               if (cert->pubkey == NULL) {
+                       warnx("%s: x509_get_pubkey failed", fn);
                        goto out;
                }
-               if (p.res->ipsz > 0) {
-                       warnx("%s: unexpected IP resources in BGPsec cert",
-                           p.fn);
+               if (cert->ipsz > 0) {
+                       warnx("%s: unexpected IP resources in BGPsec cert", fn);
                        goto out;
                }
-               if (sia_present) {
+               if (X509_get_ext_by_NID(x, NID_sinfo_access, -1) != -1) {
                        warnx("%s: unexpected SIA extension in BGPsec cert",
-                           p.fn);
+                           fn);
                        goto out;
                }
                break;
        default:
-               warnx("%s: x509_get_purpose failed in %s", p.fn, __func__);
+               warnx("%s: x509_get_purpose failed in %s", fn, __func__);
                goto out;
        }
 
-       if (p.res->ski == NULL) {
-               warnx("%s: RFC 6487 section 8.4.2: missing SKI", p.fn);
+       if (cert->ski == NULL) {
+               warnx("%s: RFC 6487 section 8.4.2: missing SKI", fn);
                goto out;
        }
 
-       p.res->x509 = x;
-       return p.res;
+       X509_free(x);
+       return cert;
 
-out:
-       cert_free(p.res);
+ out:
        X509_free(x);
+       cert_free(cert);
        return NULL;
 }
 
@@ -951,6 +959,45 @@ auth_insert(struct auth_tree *auths, str
                err(1, "auth tree corrupted");
 
        return na;
+}
+
+/*
+ * Prepend leaf to auth tree for validation purposes.
+ * Returns extended auth tree on success and NULL on failure.
+ */
+struct auth *
+auth_extend(const char *fn, X509 *x, struct auth *parent)
+{
+       struct cert     *cert;
+       struct auth     *a;
+
+       if (!X509_up_ref(x)) {
+               cryptowarnx("%s: X509_up_ref failed", fn);
+               return NULL;
+       }
+
+       if ((cert = cert_extract_x509(fn, x)) == NULL)
+               return NULL;
+
+       if ((a = calloc(1, sizeof(*a))) == NULL)
+               err(1, NULL);
+       a->parent = parent;
+       a->cert = cert;
+
+       return a;
+}
+
+/*
+ * Free a single auth structure
+ */
+void
+auth_free(struct auth *a)
+{
+       if (a == NULL)
+               return;
+
+       cert_free(a->cert);
+       free(a);
 }
 
 static void
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
retrieving revision 1.146
diff -u -p -r1.146 extern.h
--- extern.h    9 Aug 2022 09:02:26 -0000       1.146
+++ extern.h    10 Aug 2022 15:41:09 -0000
@@ -350,6 +350,8 @@ RB_HEAD(auth_tree, auth);
 
 struct auth    *auth_find(struct auth_tree *, const char *);
 struct auth    *auth_insert(struct auth_tree *, struct cert *, struct auth *);
+struct auth    *auth_extend(const char *, X509 *, struct auth *);
+void            auth_free(struct auth *);
 
 enum http_result {
        HTTP_FAILED,    /* anything else */
@@ -509,7 +511,7 @@ struct auth *valid_ski_aki(const char *,
 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 auth *, X509 *, 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);
@@ -517,7 +519,7 @@ int          valid_uri(const char *, size_t, co
 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 auth *, X509 *, struct rsc *);
 int             valid_econtent_version(const char *, const ASN1_INTEGER *);
 
 /* Working with CMS. */
Index: filemode.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/filemode.c,v
retrieving revision 1.7
diff -u -p -r1.7 filemode.c
--- filemode.c  11 May 2022 14:42:01 -0000      1.7
+++ filemode.c  10 Aug 2022 15:40:25 -0000
@@ -392,9 +392,9 @@ proc_parser_file(char *file, unsigned ch
 
                if ((status = valid_x509(file, ctx, x509, a, c, 0))) {
                        if (type == RTYPE_ROA)
-                               status = valid_roa(file, a, roa);
+                               status = valid_roa(file, a, x509, roa);
                        else if (type == RTYPE_RSC)
-                               status = valid_rsc(file, a, rsc);
+                               status = valid_rsc(file, a, x509, rsc);
                }
                if (status)
                        printf("OK");
Index: parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/parser.c,v
retrieving revision 1.73
diff -u -p -r1.73 parser.c
--- parser.c    21 Apr 2022 12:59:03 -0000      1.73
+++ parser.c    10 Aug 2022 15:40:25 -0000
@@ -144,7 +144,6 @@ proc_parser_roa(char *file, const unsign
                roa_free(roa);
                return NULL;
        }
-       X509_free(x509);
 
        roa->talid = a->cert->talid;
 
@@ -153,8 +152,9 @@ proc_parser_roa(char *file, const unsign
         * the code around roa_read() to check the "valid" field itself.
         */
 
-       if (valid_roa(file, a, roa))
+       if (valid_roa(file, a, x509, roa))
                roa->valid = 1;
+       X509_free(x509);
 
        /*
         * Check CRL to figure out the soonest transitive expiry moment
Index: validate.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/validate.c,v
retrieving revision 1.40
diff -u -p -r1.40 validate.c
--- validate.c  10 Jun 2022 10:36:43 -0000      1.40
+++ validate.c  10 Aug 2022 15:40:25 -0000
@@ -201,11 +201,14 @@ valid_cert(const char *fn, struct auth *
  * Returns 1 if valid, 0 otherwise.
  */
 int
-valid_roa(const char *fn, struct auth *a, struct roa *roa)
+valid_roa(const char *fn, struct auth *a, X509 *x, struct roa *roa)
 {
        size_t   i;
        char     buf[64];
 
+       if ((a = auth_extend(fn, x, a)) == NULL)
+               return 0;
+
        for (i = 0; i < roa->ipsz; i++) {
                if (valid_ip(a, roa->ips[i].afi, roa->ips[i].min,
                    roa->ips[i].max))
@@ -214,9 +217,11 @@ valid_roa(const char *fn, struct auth *a
                    roa->ips[i].afi, buf, sizeof(buf));
                warnx("%s: RFC 6482: uncovered IP: "
                    "%s", fn, buf);
+               auth_free(a);
                return 0;
        }
 
+       auth_free(a);
        return 1;
 }
 
@@ -442,16 +447,20 @@ valid_x509(char *file, X509_STORE_CTX *s
  * Returns 1 if valid, 0 otherwise.
  */
 int
-valid_rsc(const char *fn, struct auth *a, struct rsc *rsc)
+valid_rsc(const char *fn, struct auth *a, X509 *x, struct rsc *rsc)
 {
        size_t          i;
        uint32_t        min, max;
        char            buf1[64], buf2[64];
+       int             rc = 0;
+
+       if ((a = auth_extend(fn, x, a)) == NULL)
+               goto out;
 
        for (i = 0; i < rsc->asz; i++) {
                if (rsc->as[i].type == CERT_AS_INHERIT) {
                        warnx("%s: RSC ResourceBlock: illegal inherit", fn);
-                       return 0;
+                       goto out;
                }
 
                min = rsc->as[i].type == CERT_AS_RANGE ? rsc->as[i].range.min
@@ -474,13 +483,13 @@ valid_rsc(const char *fn, struct auth *a
                default:
                        break;
                }
-               return 0;
+               goto out;
        }
 
        for (i = 0; i < rsc->ipsz; i++) {
                if (rsc->ips[i].type == CERT_IP_INHERIT) {
                        warnx("%s: RSC ResourceBlock: illegal inherit", fn);
-                       return 0;
+                       goto out;
                }
 
                if (valid_ip(a, rsc->ips[i].afi, rsc->ips[i].min,
@@ -505,10 +514,13 @@ valid_rsc(const char *fn, struct auth *a
                default:
                        break;
                }
-               return 0;
+               goto out;
        }
 
-       return 1;
+       rc = 1;
+ out:
+       auth_free(a);
+       return rc;
 }
 
 int

Reply via email to