From: job Date: Sat, 26 Nov 2022 12:02:36 +0000 (+0000) Subject: Add support for authenticating geofeed data CSV files in filemode X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=ef3f6f56d640954e77440eccd5b05c90fd370d7f;p=openbsd Add support for authenticating geofeed data CSV files in filemode RFC 9092 describes a scheme in which an authenticator is appended to a geofeed (RFC 8805) file. It is a digest of the main body of the file signed by the private key of the relevant RPKI certificate for a covering address range. The authenticator is a detached CMS signature. with and OK tb@ --- diff --git a/usr.sbin/rpki-client/Makefile b/usr.sbin/rpki-client/Makefile index 37393fbb3f4..052ee49d035 100644 --- a/usr.sbin/rpki-client/Makefile +++ b/usr.sbin/rpki-client/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.27 2022/11/02 12:43:02 job Exp $ +# $OpenBSD: Makefile,v 1.28 2022/11/26 12:02:36 job Exp $ PROG= rpki-client -SRCS= as.c aspa.c cert.c cms.c crl.c encoding.c filemode.c gbr.c http.c io.c \ - ip.c log.c main.c mft.c mkdir.c output.c output-bgpd.c output-bird.c \ - output-csv.c output-json.c parser.c print.c repo.c roa.c rrdp.c \ - rrdp_delta.c rrdp_notification.c rrdp_snapshot.c rrdp_util.c \ - rsc.c rsync.c tak.c tal.c validate.c x509.c +SRCS= as.c aspa.c cert.c cms.c crl.c encoding.c filemode.c gbr.c geofeed.c \ + http.c io.c ip.c log.c main.c mft.c mkdir.c output.c output-bgpd.c \ + output-bird.c output-csv.c output-json.c parser.c print.c repo.c \ + roa.c rrdp.c rrdp_delta.c rrdp_notification.c rrdp_snapshot.c \ + rrdp_util.c rsc.c rsync.c tak.c tal.c validate.c x509.c MAN= rpki-client.8 LDADD+= -lexpat -ltls -lssl -lcrypto -lutil diff --git a/usr.sbin/rpki-client/cms.c b/usr.sbin/rpki-client/cms.c index ed5e2bf8b72..6e0b334e326 100644 --- a/usr.sbin/rpki-client/cms.c +++ b/usr.sbin/rpki-client/cms.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cms.c,v 1.21 2022/08/12 13:19:02 tb Exp $ */ +/* $OpenBSD: cms.c,v 1.22 2022/11/26 12:02:36 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -23,6 +23,7 @@ #include #include +#include #include #include "extern.h" @@ -32,38 +33,32 @@ extern ASN1_OBJECT *msg_dgst_oid; extern ASN1_OBJECT *sign_time_oid; extern ASN1_OBJECT *bin_sign_time_oid; -/* - * Parse and validate a self-signed CMS message, where the signing X509 - * certificate has been hashed to dgst (optional). - * Conforms to RFC 6488. - * The eContentType of the message must be an oid object. - * Return the eContent as a string and set "rsz" to be its length. - */ -unsigned char * -cms_parse_validate(X509 **xp, const char *fn, const unsigned char *der, - size_t derlen, const ASN1_OBJECT *oid, size_t *rsz) +static int +cms_parse_validate_internal(X509 **xp, const char *fn, const unsigned char *der, + size_t derlen, const ASN1_OBJECT *oid, BIO *bio, unsigned char **res, + size_t *rsz) { char buf[128], obuf[128]; const ASN1_OBJECT *obj, *octype; ASN1_OCTET_STRING **os = NULL, *kid = NULL; CMS_ContentInfo *cms; - int rc = 0; STACK_OF(X509) *certs = NULL; STACK_OF(X509_CRL) *crls; STACK_OF(CMS_SignerInfo) *sinfos; CMS_SignerInfo *si; X509_ALGOR *pdig, *psig; - unsigned char *res = NULL; int i, nattrs, nid; int has_ct = 0, has_md = 0, has_st = 0, has_bst = 0; + int rc = 0; - *rsz = 0; *xp = NULL; + if (rsz != NULL) + *rsz = 0; /* just fail for empty buffers, the warning was printed elsewhere */ if (der == NULL) - return NULL; + return 0; if ((cms = d2i_CMS_ContentInfo(NULL, &der, derlen)) == NULL) { cryptowarnx("%s: RFC 6488: failed CMS parse", fn); @@ -74,10 +69,9 @@ cms_parse_validate(X509 **xp, const char *fn, const unsigned char *der, * The CMS is self-signed with a signing certifiate. * Verify that the self-signage is correct. */ - - if (!CMS_verify(cms, NULL, NULL, NULL, NULL, + if (!CMS_verify(cms, NULL, NULL, bio, NULL, CMS_NO_SIGNER_CERT_VERIFY)) { - cryptowarnx("%s: RFC 6488: CMS not self-signed", fn); + cryptowarnx("%s: CMS verification error", fn); goto out; } @@ -244,7 +238,14 @@ cms_parse_validate(X509 **xp, const char *fn, const unsigned char *der, goto out; } - /* Verify that we have eContent to disseminate. */ + /* + * In the detached sig case: there won't be eContent to extract, so + * jump to out. + */ + if (res == NULL) { + rc = 1; + goto out; + } if ((os = CMS_get0_content(cms)) == NULL || *os == NULL) { warnx("%s: RFC 6488 section 2.1.4: " @@ -258,21 +259,50 @@ cms_parse_validate(X509 **xp, const char *fn, const unsigned char *der, * this information; and since we're going to d2i it anyway, * simply pass it as the desired underlying types. */ - - if ((res = malloc((*os)->length)) == NULL) + if ((*res = malloc((*os)->length)) == NULL) err(1, NULL); - memcpy(res, (*os)->data, (*os)->length); + memcpy(*res, (*os)->data, (*os)->length); *rsz = (*os)->length; rc = 1; -out: - sk_X509_free(certs); - CMS_ContentInfo_free(cms); - + out: if (rc == 0) { X509_free(*xp); *xp = NULL; } + sk_X509_free(certs); + CMS_ContentInfo_free(cms); + return rc; +} + +/* + * Parse and validate a self-signed CMS message. + * Conforms to RFC 6488. + * The eContentType of the message must be an oid object. + * Return the eContent as a string and set "rsz" to be its length. + */ +unsigned char * +cms_parse_validate(X509 **xp, const char *fn, const unsigned char *der, + size_t derlen, const ASN1_OBJECT *oid, size_t *rsz) +{ + unsigned char *res = NULL; + + if (!cms_parse_validate_internal(xp, fn, der, derlen, oid, NULL, &res, + rsz)) + return NULL; return res; } + +/* + * Parse and validate a detached CMS signature. + * bio must contain the original message, der must contain the CMS. + * Return the 1 on success, 0 on failure. + */ +int +cms_parse_validate_detached(X509 **xp, const char *fn, const unsigned char *der, + size_t derlen, const ASN1_OBJECT *oid, BIO *bio) +{ + return cms_parse_validate_internal(xp, fn, der, derlen, oid, bio, NULL, + NULL); +} diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index df65e490a19..d303bdd9092 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.160 2022/11/18 14:38:34 tb Exp $ */ +/* $OpenBSD: extern.h,v 1.161 2022/11/26 12:02:36 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -175,6 +175,7 @@ enum rtype { RTYPE_RSC, RTYPE_ASPA, RTYPE_TAK, + RTYPE_GEOFEED, }; enum location { @@ -296,6 +297,27 @@ struct tak { time_t expires; /* Not After of the TAK EE */ }; +/* + * A single geofeed record + */ +struct geoip { + struct cert_ip *ip; + char *loc; +}; + +/* + * A geofeed file + */ +struct geofeed { + struct geoip *geoips; /* Prefix + location entry in the CSV */ + size_t geoipsz; /* number of IPs */ + char *aia; /* AIA */ + char *aki; /* AKI */ + char *ski; /* SKI */ + time_t expires; /* Not After of the Geofeed EE */ + int valid; /* all resources covered */ +}; + /* * A single Ghostbuster record */ @@ -565,6 +587,9 @@ void gbr_free(struct gbr *); struct gbr *gbr_parse(X509 **, const char *, const unsigned char *, size_t); +void geofeed_free(struct geofeed *); +struct geofeed *geofeed_parse(X509 **, const char *, char *, size_t); + void rsc_free(struct rsc *); struct rsc *rsc_parse(X509 **, const char *, const unsigned char *, size_t); @@ -608,11 +633,15 @@ int valid_x509(char *, X509_STORE_CTX *, X509 *, struct auth *, int valid_rsc(const char *, struct cert *, struct rsc *); int valid_econtent_version(const char *, const ASN1_INTEGER *); int valid_aspa(const char *, struct cert *, struct aspa *); +int valid_geofeed(const char *, struct cert *, struct geofeed *); /* Working with CMS. */ unsigned char *cms_parse_validate(X509 **, const char *, const unsigned char *, size_t, const ASN1_OBJECT *, size_t *); +int cms_parse_validate_detached(X509 **, const char *, + const unsigned char *, size_t, + const ASN1_OBJECT *, BIO *); /* Work with RFC 3779 IP addresses, prefixes, ranges. */ @@ -759,6 +788,7 @@ void gbr_print(const X509 *, const struct gbr *); void rsc_print(const X509 *, const struct rsc *); void aspa_print(const X509 *, const struct aspa *); void tak_print(const X509 *, const struct tak *); +void geofeed_print(const X509 *, const struct geofeed *); /* Output! */ diff --git a/usr.sbin/rpki-client/filemode.c b/usr.sbin/rpki-client/filemode.c index 99ac65d7d3b..2b3042163ff 100644 --- a/usr.sbin/rpki-client/filemode.c +++ b/usr.sbin/rpki-client/filemode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: filemode.c,v 1.16 2022/11/04 17:39:36 job Exp $ */ +/* $OpenBSD: filemode.c,v 1.17 2022/11/26 12:02:37 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -270,6 +270,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) struct rsc *rsc = NULL; struct aspa *aspa = NULL; struct tak *tak = NULL; + struct geofeed *geofeed = NULL; char *aia = NULL, *aki = NULL; char filehash[SHA256_DIGEST_LENGTH]; char *hash; @@ -385,6 +386,14 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) aia = tak->aia; aki = tak->aki; break; + case RTYPE_GEOFEED: + geofeed = geofeed_parse(&x509, file, buf, len); + if (geofeed == NULL) + break; + geofeed_print(x509, geofeed); + aia = geofeed->aia; + aki = geofeed->aki; + break; default: printf("%s: unsupported file type\n", file); break; @@ -420,6 +429,9 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) case RTYPE_ASPA: status = aspa->valid; break; + case RTYPE_GEOFEED: + status = geofeed->valid; + break; default: break; } @@ -479,6 +491,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) rsc_free(rsc); aspa_free(aspa); tak_free(tak); + geofeed_free(geofeed); } /* diff --git a/usr.sbin/rpki-client/geofeed.c b/usr.sbin/rpki-client/geofeed.c new file mode 100644 index 00000000000..b5838919b98 --- /dev/null +++ b/usr.sbin/rpki-client/geofeed.c @@ -0,0 +1,297 @@ +/* $OpenBSD: geofeed.c,v 1.1 2022/11/26 12:02:37 job Exp $ */ +/* + * Copyright (c) 2022 Job Snijders + * Copyright (c) 2019 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "extern.h" + +struct parse { + const char *fn; + struct geofeed *res; +}; + +extern ASN1_OBJECT *geofeed_oid; + +/* + * Take a CIDR prefix (in presentation format) and add it to parse results. + * Returns 1 on success, 0 on failure. + */ +static int +geofeed_parse_geoip(struct geofeed *res, char *cidr, char *loc) +{ + struct geoip *geoip; + struct ip_addr *ipaddr; + enum afi afi; + int plen; + + if ((ipaddr = calloc(1, sizeof(struct ip_addr))) == NULL) + err(1, NULL); + + if ((plen = inet_net_pton(AF_INET, cidr, ipaddr->addr, + sizeof(ipaddr->addr))) != -1) + afi = AFI_IPV4; + else if ((plen = inet_net_pton(AF_INET6, cidr, ipaddr->addr, + sizeof(ipaddr->addr))) != -1) + afi = AFI_IPV6; + else { + static char buf[80]; + + if (strnvis(buf, cidr, sizeof(buf), VIS_SAFE) + >= (int)sizeof(buf)) { + memcpy(buf + sizeof(buf) - 4, "...", 4); + } + warnx("invalid address: %s", buf); + free(ipaddr); + return 0; + } + + ipaddr->prefixlen = plen; + + res->geoips = recallocarray(res->geoips, res->geoipsz, + res->geoipsz + 1, sizeof(struct geoip)); + if (res->geoips == NULL) + err(1, NULL); + geoip = &res->geoips[res->geoipsz++]; + + if ((geoip->ip = calloc(1, sizeof(struct cert_ip))) == NULL) + err(1, NULL); + + geoip->ip->type = CERT_IP_ADDR; + geoip->ip->ip = *ipaddr; + geoip->ip->afi = afi; + + if ((geoip->loc = strdup(loc)) == NULL) + err(1, NULL); + + if (!ip_cert_compose_ranges(geoip->ip)) + return 0; + + return 1; +} + +/* + * Parse a full RFC 9092 file. + * Returns the Geofeed, or NULL if the object was malformed. + */ +struct geofeed * +geofeed_parse(X509 **x509, const char *fn, char *buf, size_t len) +{ + struct parse p; + char *delim, *line, *loc, *nl; + size_t linelen; + BIO *bio; + char *b64 = NULL; + size_t b64sz; + unsigned char *der = NULL; + size_t dersz; + const ASN1_TIME *at; + struct cert *cert = NULL; + int rpki_signature_seen = 0, end_signature_seen = 0; + int rc = 0; + + bio = BIO_new(BIO_s_mem()); + + memset(&p, 0, sizeof(struct parse)); + p.fn = fn; + + if ((p.res = calloc(1, sizeof(struct geofeed))) == NULL) + err(1, NULL); + + if ((b64 = calloc(1, len)) == NULL) + err(1, NULL); + b64sz = len; + + while ((nl = memchr(buf, '\n', len)) != NULL) { + line = buf; + + /* advance buffer to next line */ + len -= nl + 1 - buf; + buf = nl + 1; + + /* replace LF and CR with NUL, point nl at first NUL */ + *nl = '\0'; + if (nl > line && nl[-1] == '\r') { + nl[-1] = '\0'; + nl--; + linelen = nl - line; + } else { + warnx("%s: malformed file, expected CRLF line" + " endings", fn); + goto out; + } + + if (end_signature_seen) { + warnx("%s: trailing data after signature section", fn); + goto out; + } + + if (strncmp(line, "# End Signature:", + strlen("# End Signature:")) == 0) { + end_signature_seen = 1; + continue; + } + + if (rpki_signature_seen) { + if (linelen > 74) { + warnx("%s: line in signature section too long", + fn); + goto out; + } + if (strncmp(line, "# ", strlen("# ")) != 0) { + warnx("%s: line in signature section too " + "short", fn); + goto out; + } + + /* skip over "# " */ + line += 2; + strlcat(b64, line, b64sz); + continue; + } + + if (strncmp(line, "# RPKI Signature:", + strlen("# RPKI Signature:")) == 0) { + rpki_signature_seen = 1; + continue; + } + + /* + * Read the Geofeed CSV records into a BIO to later on + * calculate the message digest and compare with the one + * in the detached CMS signature. + */ + if (BIO_puts(bio, line) <= 0 || BIO_puts(bio, "\r\n") <= 0) { + warnx("%s: BIO_puts failed", fn); + goto out; + } + + /* Skip empty lines or commented lines. */ + if (linelen == 0 || line[0] == '#') + continue; + + /* zap comments */ + delim = memchr(line, '#', linelen); + if (delim != NULL) + *delim = '\0'; + + /* Split prefix and location info */ + delim = memchr(line, ',', linelen); + if (delim != NULL) { + *delim = '\0'; + loc = delim + 1; + } else + loc = ""; + + /* read each prefix */ + if (!geofeed_parse_geoip(p.res, line, loc)) + goto out; + } + + if (!rpki_signature_seen || !end_signature_seen) { + warnx("%s: absent or invalid signature", fn); + goto out; + } + + if ((base64_decode(b64, strlen(b64), &der, &dersz)) == -1) { + warnx("%s: base64_decode failed", fn); + goto out; + } + + if (!cms_parse_validate_detached(x509, fn, der, dersz, geofeed_oid, + bio)) + goto out; + + if (!x509_get_aia(*x509, fn, &p.res->aia)) + goto out; + if (!x509_get_aki(*x509, fn, &p.res->aki)) + goto out; + if (!x509_get_ski(*x509, fn, &p.res->ski)) + goto out; + + if (p.res->aia == NULL || p.res->aki == NULL || p.res->ski == NULL) { + warnx("%s: missing AIA, AKI, SIA, or SKI X509 extension", fn); + goto out; + } + + at = X509_get0_notAfter(*x509); + if (at == NULL) { + warnx("%s: X509_get0_notAfter failed", fn); + goto out; + } + if (!x509_get_time(at, &p.res->expires)) { + warnx("%s: ASN1_time_parse failed", fn); + 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; + } + + p.res->valid = valid_geofeed(fn, cert, p.res); + + rc = 1; + out: + if (rc == 0) { + geofeed_free(p.res); + p.res = NULL; + X509_free(*x509); + *x509 = NULL; + } + cert_free(cert); + BIO_free(bio); + free(b64); + free(der); + + return p.res; +} + +/* + * Free what follows a pointer to a geofeed structure. + * Safe to call with NULL. + */ +void +geofeed_free(struct geofeed *p) +{ + size_t i; + + if (p == NULL) + return; + + for (i = 0; i < p->geoipsz; i++) { + free(p->geoips[i].ip); + free(p->geoips[i].loc); + } + + free(p->geoips); + free(p->aia); + free(p->aki); + free(p->ski); + free(p); +} diff --git a/usr.sbin/rpki-client/mft.c b/usr.sbin/rpki-client/mft.c index 097ec7a6691..ed9cd187120 100644 --- a/usr.sbin/rpki-client/mft.c +++ b/usr.sbin/rpki-client/mft.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mft.c,v 1.78 2022/11/07 16:23:32 job Exp $ */ +/* $OpenBSD: mft.c,v 1.79 2022/11/26 12:02:37 job Exp $ */ /* * Copyright (c) 2022 Theo Buehler * Copyright (c) 2019 Kristaps Dzonsons @@ -170,6 +170,8 @@ rtype_from_file_extension(const char *fn) return RTYPE_ASPA; if (strcasecmp(fn + sz - 4, ".tak") == 0) return RTYPE_TAK; + if (strcasecmp(fn + sz - 4, ".csv") == 0) + return RTYPE_GEOFEED; return RTYPE_INVALID; } diff --git a/usr.sbin/rpki-client/print.c b/usr.sbin/rpki-client/print.c index 8848b819b13..64fbd302179 100644 --- a/usr.sbin/rpki-client/print.c +++ b/usr.sbin/rpki-client/print.c @@ -1,4 +1,4 @@ -/* $OpenBSD: print.c,v 1.20 2022/11/16 08:57:38 job Exp $ */ +/* $OpenBSD: print.c,v 1.21 2022/11/26 12:02:37 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -723,3 +723,49 @@ tak_print(const X509 *x, const struct tak *p) if (outformats & FORMAT_JSON) printf("\n\t],\n"); } + +void +geofeed_print(const X509 *x, const struct geofeed *p) +{ + char buf[128]; + size_t i; + + if (outformats & FORMAT_JSON) { + printf("\t\"type\": \"geofeed\",\n"); + printf("\t\"ski\": \"%s\",\n", pretty_key_id(p->ski)); + x509_print(x); + printf("\t\"aki\": \"%s\",\n", pretty_key_id(p->aki)); + printf("\t\"aia\": \"%s\",\n", p->aia); + printf("\t\"valid_until\": %lld,\n", (long long)p->expires); + printf("\t\"records\": [\n"); + } else { + printf("Subject key identifier: %s\n", pretty_key_id(p->ski)); + x509_print(x); + printf("Authority key identifier: %s\n", pretty_key_id(p->aki)); + printf("Authority info access: %s\n", p->aia); + printf("Geofeed valid until: %s\n", time2str(p->expires)); + printf("Geofeed CSV records:\n"); + } + + for (i = 0; i < p->geoipsz; i++) { + if (p->geoips[i].ip->type != CERT_IP_ADDR) + continue; + + ip_addr_print(&p->geoips[i].ip->ip, p->geoips[i].ip->afi, buf, + sizeof(buf)); + if (outformats & FORMAT_JSON) + printf("\t\t{ \"prefix\": \"%s\", \"location\": \"%s\"" + "}", buf, p->geoips[i].loc); + else + printf("%5zu: IP: %s (%s)", i + 1, buf, + p->geoips[i].loc); + + if (outformats & FORMAT_JSON && i + 1 < p->geoipsz) + printf(",\n"); + else + printf("\n"); + } + + if (outformats & FORMAT_JSON) + printf("\t],\n"); +} diff --git a/usr.sbin/rpki-client/rpki-client.8 b/usr.sbin/rpki-client/rpki-client.8 index 39a970df370..ccdbb114ee1 100644 --- a/usr.sbin/rpki-client/rpki-client.8 +++ b/usr.sbin/rpki-client/rpki-client.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: rpki-client.8,v 1.80 2022/11/17 20:49:38 job Exp $ +.\" $OpenBSD: rpki-client.8,v 1.81 2022/11/26 12:02:37 job Exp $ .\" .\" Copyright (c) 2019 Kristaps Dzonsons .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 17 2022 $ +.Dd $Mdocdate: November 26 2022 $ .Dt RPKI-CLIENT 8 .Os .Sh NAME @@ -313,6 +313,8 @@ A Profile for BGPsec Router Certificates, Certificate Revocation Lists, and Certification Requests. .It RFC 8630 Resource Public Key Infrastructure (RPKI) Trust Anchor Locator. +.It RFC 9092 +Finding and Using Geofeed Data. .It RFC 9323 A Profile for RPKI Signed Checklists (RSCs). .It draft-ietf-sidrops-aspa-profile-10 diff --git a/usr.sbin/rpki-client/validate.c b/usr.sbin/rpki-client/validate.c index 71d08236084..d7623808704 100644 --- a/usr.sbin/rpki-client/validate.c +++ b/usr.sbin/rpki-client/validate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: validate.c,v 1.46 2022/11/02 11:28:36 tb Exp $ */ +/* $OpenBSD: validate.c,v 1.47 2022/11/26 12:02:37 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -523,3 +523,28 @@ valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa) return 0; } + +/* + * Validate Geofeed prefixes: check that the prefixes are contained. + * Returns 1 if valid, 0 otherwise. + */ +int +valid_geofeed(const char *fn, struct cert *cert, struct geofeed *g) +{ + size_t i; + char buf[64]; + + for (i = 0; i < g->geoipsz; i++) { + if (ip_addr_check_covered(g->geoips[i].ip->afi, + g->geoips[i].ip->min, g->geoips[i].ip->max, cert->ips, + cert->ipsz) > 0) + continue; + + ip_addr_print(&g->geoips[i].ip->ip, g->geoips[i].ip->afi, buf, + sizeof(buf)); + warnx("%s: Geofeed: uncovered IP: %s", fn, buf); + return 0; + } + + return 1; +} diff --git a/usr.sbin/rpki-client/x509.c b/usr.sbin/rpki-client/x509.c index 8cb01f116e0..c23b69dfb17 100644 --- a/usr.sbin/rpki-client/x509.c +++ b/usr.sbin/rpki-client/x509.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509.c,v 1.58 2022/11/07 09:18:14 job Exp $ */ +/* $OpenBSD: x509.c,v 1.59 2022/11/26 12:02:37 job Exp $ */ /* * Copyright (c) 2022 Theo Buehler * Copyright (c) 2021 Claudio Jeker @@ -47,6 +47,7 @@ ASN1_OBJECT *bin_sign_time_oid; /* pkcs-9 id-aa-binarySigningTime */ ASN1_OBJECT *rsc_oid; /* id-ct-signedChecklist */ ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */ ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */ +ASN1_OBJECT *geofeed_oid; /* id-ct-geofeedCSVwithCRLF */ static const struct { const char *oid; @@ -104,6 +105,10 @@ static const struct { .oid = "1.2.840.113549.1.9.16.2.46", .ptr = &bin_sign_time_oid, }, + { + .oid = "1.2.840.113549.1.9.16.1.47", + .ptr = &geofeed_oid, + }, { .oid = "1.2.840.113549.1.9.16.1.48", .ptr = &rsc_oid,