Dear all, RFC 9092 describes a scheme that uses the Routing Public Key Infrastructure to authenticate geofeed data CSV files. Good fit for rpki-client's filemode. Example:
$ whois -h whois.ripe.net 2001:67c:208c::/48 | fgrep Geofeed remarks: Geofeed https://sobornost.net/geofeed.csv $ ftp -MV https://sobornost.net/geofeed.csv $ rpki-client -f geofeed.csv File: geofeed.csv Hash identifier: fhp1yOsOrw6Y838H4cHyqJJ1uIzaSvIWeI2AfWVliZ8= Subject key identifier: 87:3F:7B:78:DC:37:CB:79:68:24:74:9C:5B:7B:8F:63:D1:3C:C5:02 Certificate serial: 0 Authority key identifier: CA:A8:05:DB:AC:36:47:49:B9:B1:15:59:0A:B6:EF:0F:97:0C:DB:D8 Authority info access: rsync://rpki.ripe.net/repository/DEFAULT/yqgF26w2R0m5sRVZCrbvD5cM29g.cer Geofeed valid until: Nov 05 14:40:31 2023 GMT Geofeed CSV records: 1: IP: 2001:67c:208c::/48 (NL,NL-NH,Amsterdam) Validation: OK Many thanks to Theo Buehler for suggestions and feedback. OK? Kind regards, Job Index: Makefile =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/Makefile,v retrieving revision 1.27 diff -u -p -r1.27 Makefile --- Makefile 2 Nov 2022 12:43:02 -0000 1.27 +++ Makefile 25 Nov 2022 09:26:11 -0000 @@ -1,11 +1,11 @@ # $OpenBSD: Makefile,v 1.27 2022/11/02 12:43:02 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 Index: cms.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/cms.c,v retrieving revision 1.21 diff -u -p -r1.21 cms.c --- cms.c 12 Aug 2022 13:19:02 -0000 1.21 +++ cms.c 25 Nov 2022 09:26:11 -0000 @@ -23,6 +23,7 @@ #include <string.h> #include <unistd.h> +#include <openssl/bio.h> #include <openssl/cms.h> #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,16 +69,14 @@ cms_parse_validate(X509 **xp, const char * 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: invalid CMS", fn); goto out; } /* RFC 6488 section 3 verify the CMS */ /* the version of SignedData and SignerInfos can't be verified */ - sinfos = CMS_get0_SignerInfos(cms); assert(sinfos != NULL); if (sk_CMS_SignerInfo_num(sinfos) != 1) { @@ -173,7 +166,6 @@ cms_parse_validate(X509 **xp, const char } /* RFC 6488 section 2.1.3.1: check the object's eContentType. */ - obj = CMS_get0_eContentType(cms); if (obj == NULL) { warnx("%s: RFC 6488 section 2.1.3.1: eContentType: " @@ -189,8 +181,8 @@ cms_parse_validate(X509 **xp, const char } /* Compare content-type with eContentType */ - octype = CMS_signed_get0_data_by_OBJ(si, cnt_type_oid, - -3, V_ASN1_OBJECT); + octype = CMS_signed_get0_data_by_OBJ(si, cnt_type_oid, -3, + V_ASN1_OBJECT); assert(octype != NULL); if (OBJ_cmp(obj, octype) != 0) { OBJ_obj2txt(buf, sizeof(buf), obj, 1); @@ -215,7 +207,6 @@ cms_parse_validate(X509 **xp, const char * signing authority according to RFC 6488, 2.1.4. * We extract that certificate now for later verification. */ - certs = CMS_get0_signers(cms); if (certs == NULL || sk_X509_num(certs) != 1) { warnx("%s: RFC 6488 section 2.1.4: eContent: " @@ -244,35 +235,71 @@ cms_parse_validate(X509 **xp, const char goto out; } - /* Verify that we have eContent to disseminate. */ - - if ((os = CMS_get0_content(cms)) == NULL || *os == NULL) { - warnx("%s: RFC 6488 section 2.1.4: " - "eContent: zero-length content", fn); - goto out; - } - /* - * Extract and duplicate the eContent. - * The CMS framework offers us no other way of easily managing - * this information; and since we're going to d2i it anyway, - * simply pass it as the desired underlying types. + * In the detached sig case: no eContent to extract, so jump to out. + * else, verify that we have eContent to disseminate. */ + if (res == NULL) { + rc = 1; + goto out; + } else { + if ((os = CMS_get0_content(cms)) == NULL || *os == NULL) { + warnx("%s: RFC 6488 section 2.1.4: " + "eContent: zero-length content", fn); + goto out; + } - if ((res = malloc((*os)->length)) == NULL) - err(1, NULL); - memcpy(res, (*os)->data, (*os)->length); - *rsz = (*os)->length; - - rc = 1; -out: - sk_X509_free(certs); - CMS_ContentInfo_free(cms); + /* + * Extract and duplicate the eContent. + * The CMS framework offers us no other way of easily managing + * 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) + err(1, NULL); + memcpy(*res, (*os)->data, (*os)->length); + *rsz = (*os)->length; + rc = 1; + } + 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); } Index: extern.h =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v retrieving revision 1.160 diff -u -p -r1.160 extern.h --- extern.h 18 Nov 2022 14:38:34 -0000 1.160 +++ extern.h 25 Nov 2022 09:26:11 -0000 @@ -175,6 +175,7 @@ enum rtype { RTYPE_RSC, RTYPE_ASPA, RTYPE_TAK, + RTYPE_GEOFEED, }; enum location { @@ -297,6 +298,21 @@ struct tak { }; /* + * A geofeed file + */ +struct geofeed { + struct cert_ip *ips; /* IP prefixes in the CSV */ + size_t ipsz; /* 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 */ + size_t locsz; + char **locs; /* location info */ +}; + +/* * A single Ghostbuster record */ struct gbr { @@ -565,6 +581,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,12 +627,18 @@ int valid_x509(char *, X509_STORE_CTX 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 *); +/* Working with CMS detached signatures */ +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. */ int ip_addr_afi_parse(const char *, const ASN1_OCTET_STRING *, @@ -759,6 +784,7 @@ void gbr_print(const X509 *, const str 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! */ Index: filemode.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/filemode.c,v retrieving revision 1.16 diff -u -p -r1.16 filemode.c --- filemode.c 4 Nov 2022 17:39:36 -0000 1.16 +++ filemode.c 25 Nov 2022 09:26:11 -0000 @@ -270,6 +270,7 @@ proc_parser_file(char *file, unsigned ch 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 ch 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 ch 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 ch rsc_free(rsc); aspa_free(aspa); tak_free(tak); + geofeed_free(geofeed); } /* Index: geofeed.c =================================================================== RCS file: geofeed.c diff -N geofeed.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ geofeed.c 25 Nov 2022 09:26:11 -0000 @@ -0,0 +1,233 @@ +/* $OpenBSD: geofeed.c,v 1.18 2022/11/02 12:46:49 job Exp $ */ +/* + * Copyright (c) 2022 Job Snijders <j...@fastly.com> + * Copyright (c) 2019 Kristaps Dzonsons <krist...@bsd.lv> + * + * 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 <assert.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <openssl/bio.h> +#include <openssl/x509.h> + +#include "extern.h" + +/* + * Parse results and data of the Signed Checklist file. + */ +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. + */ +static int +geofeed_parse_cidr(struct parse *p, char *cidr) +{ + struct cert_ip *res; + 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 { + warnx("invalid address: %s", cidr); + return 0; + } + + ipaddr->prefixlen = plen; + + p->res->ips = recallocarray(p->res->ips, p->res->ipsz, + p->res->ipsz + 1, sizeof(struct cert_ip)); + if (p->res->ips == NULL) + err(1, NULL); + + res = &p->res->ips[p->res->ipsz++]; + res->type = CERT_IP_ADDR; + res->ip = *ipaddr; + res->afi = afi; + + if (!ip_cert_compose_ranges(res)) + 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; + BIO *bio; + char *b64; + unsigned char *der; + size_t dersz; + const ASN1_TIME *at; + struct cert *cert = NULL; + int rc = 0; + + bio = BIO_new(BIO_s_mem()); + assert(bio != NULL); + + 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); + + while ((line = strsep(&buf, "\n"))) { + + /* zap optional CR, canonicalization happens later */ + delim = memchr(line, '\r', strlen(line)); + if (delim != NULL) + *delim = '\0'; + + /* read the Geofeed CSV records */ + if (*line != '#') { + BIO_puts(bio, line); + BIO_puts(bio, "\r\n"); /* canonicalization */ + + delim = memchr(line, ',', strlen(line)); + *delim = '\0'; + + if (!geofeed_parse_cidr(&p, line)) + goto out; + + /* store the geo location */ + p.res->locs = recallocarray(p.res->locs, p.res->locsz, + p.res->locsz + 1, sizeof(char *)); + if (p.res->locs == NULL) + err(1, NULL); + + p.res->locs[p.res->locsz] = strdup(delim + 1); + if (p.res->locs[p.res->locsz] == NULL) + goto out; + + p.res->locsz++; + + } else { /* read the base64 encoded CMS signature */ + if (strncmp(line, "# RPKI Signature:", + strlen("# RPKI Signature:")) == 0) + continue; + + if (strncmp(line, "# End Signature:", + strlen("# End Signature:")) == 0) + break; + + /* skip over '# ' prefix */ + strlcat(b64, line + 2, len); + } + } + + if ((base64_decode(b64, strlen(b64), &der, &dersz)) == -1) { + warnx("base64_decode failed"); + 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); + + 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->locsz; i++) + free(p->locs[i]); + free(p->locs); + free(p->aia); + free(p->aki); + free(p->ski); + free(p->ips); + free(p); +} Index: mft.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/mft.c,v retrieving revision 1.78 diff -u -p -r1.78 mft.c --- mft.c 7 Nov 2022 16:23:32 -0000 1.78 +++ mft.c 25 Nov 2022 09:26:11 -0000 @@ -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; } Index: print.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/print.c,v retrieving revision 1.20 diff -u -p -r1.20 print.c --- print.c 16 Nov 2022 08:57:38 -0000 1.20 +++ print.c 25 Nov 2022 09:26:11 -0000 @@ -723,3 +723,47 @@ tak_print(const X509 *x, const struct ta 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->ipsz; i++) { + if (p->ips[i].type != CERT_IP_ADDR) + continue; + + ip_addr_print(&p->ips[i].ip, p->ips[i].afi, buf, sizeof(buf)); + if (outformats & FORMAT_JSON) + printf("\t\t{ \"prefix\": \"%s\", \"location\": \"%s\"" + "}", buf, p->locs[i]); + else + printf("%5zu: IP: %s (%s)", i + 1, buf, p->locs[i]); + + if (outformats & FORMAT_JSON && i + 1 < p->ipsz) + printf(",\n"); + else + printf("\n"); + } + + if (outformats & FORMAT_JSON) + printf("\t],\n"); +} Index: rpki-client.8 =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/rpki-client.8,v retrieving revision 1.80 diff -u -p -r1.80 rpki-client.8 --- rpki-client.8 17 Nov 2022 20:49:38 -0000 1.80 +++ rpki-client.8 25 Nov 2022 09:26:11 -0000 @@ -313,6 +313,8 @@ A Profile for BGPsec Router Certificates 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 Index: validate.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/validate.c,v retrieving revision 1.46 diff -u -p -r1.46 validate.c --- validate.c 2 Nov 2022 11:28:36 -0000 1.46 +++ validate.c 25 Nov 2022 09:26:11 -0000 @@ -523,3 +523,28 @@ valid_aspa(const char *fn, struct cert * 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 *geofeed) +{ + size_t i; + char buf[64]; + + for (i = 0; i < geofeed->ipsz; i++) { + if (ip_addr_check_covered(geofeed->ips[i].afi, + geofeed->ips[i].min, geofeed->ips[i].max, cert->ips, + cert->ipsz) > 0) + continue; + + ip_addr_print(&geofeed->ips[i].ip, geofeed->ips[i].afi, buf, + sizeof(buf)); + warnx("%s: Geofeed: uncovered IP: %s", fn, buf); + return 0; + } + + return 1; +} Index: x509.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/x509.c,v retrieving revision 1.58 diff -u -p -r1.58 x509.c --- x509.c 7 Nov 2022 09:18:14 -0000 1.58 +++ x509.c 25 Nov 2022 09:26:11 -0000 @@ -47,6 +47,7 @@ ASN1_OBJECT *bin_sign_time_oid; /* pkcs- 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; @@ -103,6 +104,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",