On Sat, Aug 13, 2022 at 04:51:05PM +0200, Theo Buehler wrote:
> On Fri, Aug 12, 2022 at 09:59:11PM +0200, Theo Buehler wrote:
> > On Wed, Aug 10, 2022 at 06:16:30PM +0200, Theo Buehler wrote:
> > > 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?
> >
> > Here's a less intrusive version of this diff that parses the RFC 3779
> > extensions of the EE cert "by hand". That's all we need for proper
> > verification of the resources in ROAs and RSCs. Maybe that's preferable.
> >
> > If we decide to go with this diff, the "no AS numbers for ROA EE certs"
> > and the "no inheritance" check should be moved or added to the XXX.
>
> job mentioned that it might be preferable to do the validation in
> parse_{roa,rsc,aspa}(). So here's a diff that does this. It reworks
> valid_{roa,rsc}() to compare only against the EE cert's resources since
> it doesn't really make sense to walk the auth chain for this anyway.
> That the EE cert's resources are covered by the auth chain is checked
> later as part of valid_x509().
>
> Inheritance in the EE cert will now result in a warning and the roa/rsc
> won't be considered valid.
For me this diff is OK. At least it is the best we can do without totally
refactoring a lot more code. Having to work with struct cert in this case
is a bit annoying but the functions only work this way.
> 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 13 Aug 2022 14:34:38 -0000
> @@ -571,6 +571,49 @@ certificate_policies(struct parse *p, X5
> }
>
> /*
> + * Lightweight version of cert_parse_pre() for ASPA, ROA, and RSC 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.
> * Returns the parse results or NULL on failure.
> Index: extern.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
> retrieving revision 1.147
> diff -u -p -r1.147 extern.h
> --- extern.h 10 Aug 2022 10:27:03 -0000 1.147
> +++ extern.h 13 Aug 2022 14:34:38 -0000
> @@ -465,6 +465,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 *,
> @@ -509,7 +510,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 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);
> @@ -517,7 +518,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 cert *, 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 13 Aug 2022 14:34:38 -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 = roa->valid;
> else if (type == RTYPE_RSC)
> - status = valid_rsc(file, a, rsc);
> + status = rsc->valid;
> }
> 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 13 Aug 2022 14:34:38 -0000
> @@ -149,14 +149,6 @@ proc_parser_roa(char *file, const unsign
> 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
> */
> if (crl != NULL && roa->expires > crl->expires)
> Index: roa.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/rpki-client/roa.c,v
> retrieving revision 1.49
> diff -u -p -r1.49 roa.c
> --- roa.c 10 Aug 2022 14:54:03 -0000 1.49
> +++ roa.c 13 Aug 2022 14:34:38 -0000
> @@ -202,6 +202,7 @@ struct roa *
> roa_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len)
> {
> struct parse p;
> + struct cert *cert = NULL;
> size_t cmsz;
> unsigned char *cms;
> int rc = 0;
> @@ -229,11 +230,6 @@ roa_parse(X509 **x509, const char *fn, c
> 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, c
> 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: rsc.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/rpki-client/rsc.c,v
> retrieving revision 1.12
> diff -u -p -r1.12 rsc.c
> --- rsc.c 10 Jun 2022 10:41:09 -0000 1.12
> +++ rsc.c 13 Aug 2022 14:34:38 -0000
> @@ -378,6 +378,7 @@ rsc_parse(X509 **x509, const char *fn, c
> 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, c
> 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, c
> X509_free(*x509);
> *x509 = NULL;
> }
> + cert_free(cert);
> free(cms);
> return p.res;
> }
> 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 13 Aug 2022 14:34:38 -0000
> @@ -201,19 +201,19 @@ 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 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 *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 cert *cert, struct rsc *rsc)
> {
> size_t i;
> uint32_t min, max;
> @@ -459,7 +459,7 @@ valid_rsc(const char *fn, struct auth *a
> 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
> 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) {
>
--
:wq Claudio