From 99dbdb7f37c7d4919ca554be92389894fe017ade Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 19 Aug 2022 12:45:53 +0000 Subject: [PATCH] Check the resources in ROAs and RSCs against EE certs 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 | 45 ++++++++++++++++++++++++++++++++- usr.sbin/rpki-client/extern.h | 7 ++--- usr.sbin/rpki-client/filemode.c | 6 ++--- usr.sbin/rpki-client/parser.c | 10 +------- usr.sbin/rpki-client/roa.c | 25 +++++++++++++----- usr.sbin/rpki-client/rsc.c | 11 +++++++- usr.sbin/rpki-client/validate.c | 24 +++++++++--------- 7 files changed, 92 insertions(+), 36 deletions(-) diff --git a/usr.sbin/rpki-client/cert.c b/usr.sbin/rpki-client/cert.c index 9ff4bbcda70..06a6d199b07 100644 --- a/usr.sbin/rpki-client/cert.c +++ b/usr.sbin/rpki-client/cert.c @@ -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 * Copyright (c) 2021 Job Snijders @@ -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. diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index 93a714dddb2..803083815f6 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -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 * @@ -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. */ diff --git a/usr.sbin/rpki-client/filemode.c b/usr.sbin/rpki-client/filemode.c index f43a2fa5565..3d622e54be1 100644 --- a/usr.sbin/rpki-client/filemode.c +++ b/usr.sbin/rpki-client/filemode.c @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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"); diff --git a/usr.sbin/rpki-client/parser.c b/usr.sbin/rpki-client/parser.c index 38d653188e8..e6a9fc9faec 100644 --- a/usr.sbin/rpki-client/parser.c +++ b/usr.sbin/rpki-client/parser.c @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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 */ diff --git a/usr.sbin/rpki-client/roa.c b/usr.sbin/rpki-client/roa.c index db725a1a7df..6c3aaa3f6fe 100644 --- a/usr.sbin/rpki-client/roa.c +++ b/usr.sbin/rpki-client/roa.c @@ -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 * Copyright (c) 2019 Kristaps Dzonsons @@ -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; } diff --git a/usr.sbin/rpki-client/rsc.c b/usr.sbin/rpki-client/rsc.c index 586cd71f0ad..4db9fc68a24 100644 --- a/usr.sbin/rpki-client/rsc.c +++ b/usr.sbin/rpki-client/rsc.c @@ -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 * Copyright (c) 2022 Job Snijders @@ -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; } diff --git a/usr.sbin/rpki-client/validate.c b/usr.sbin/rpki-client/validate.c index 5c3fcd87acd..cc6d9511a13 100644 --- a/usr.sbin/rpki-client/validate.c +++ b/usr.sbin/rpki-client/validate.c @@ -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 * @@ -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) { -- 2.20.1