diff options
-rw-r--r-- | usr.sbin/rpki-client/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/rpki-client/extern.h | 82 | ||||
-rw-r--r-- | usr.sbin/rpki-client/filemode.c | 17 | ||||
-rw-r--r-- | usr.sbin/rpki-client/main.c | 35 | ||||
-rw-r--r-- | usr.sbin/rpki-client/mft.c | 5 | ||||
-rw-r--r-- | usr.sbin/rpki-client/output-bgpd.c | 4 | ||||
-rw-r--r-- | usr.sbin/rpki-client/output-bird.c | 8 | ||||
-rw-r--r-- | usr.sbin/rpki-client/output-csv.c | 4 | ||||
-rw-r--r-- | usr.sbin/rpki-client/output-json.c | 28 | ||||
-rw-r--r-- | usr.sbin/rpki-client/output-ometric.c | 16 | ||||
-rw-r--r-- | usr.sbin/rpki-client/output.c | 8 | ||||
-rw-r--r-- | usr.sbin/rpki-client/parser.c | 51 | ||||
-rw-r--r-- | usr.sbin/rpki-client/print.c | 56 | ||||
-rw-r--r-- | usr.sbin/rpki-client/repo.c | 26 | ||||
-rw-r--r-- | usr.sbin/rpki-client/rpki-client.8 | 10 | ||||
-rw-r--r-- | usr.sbin/rpki-client/spl.c | 487 | ||||
-rw-r--r-- | usr.sbin/rpki-client/validate.c | 18 | ||||
-rw-r--r-- | usr.sbin/rpki-client/x509.c | 7 |
18 files changed, 826 insertions, 40 deletions
diff --git a/usr.sbin/rpki-client/Makefile b/usr.sbin/rpki-client/Makefile index edb66b69757..25db8469197 100644 --- a/usr.sbin/rpki-client/Makefile +++ b/usr.sbin/rpki-client/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.33 2023/10/13 12:06:49 job Exp $ +# $OpenBSD: Makefile,v 1.34 2024/02/22 12:49:42 job Exp $ PROG= rpki-client SRCS= as.c aspa.c cert.c cms.c constraints.c crl.c encoding.c filemode.c \ @@ -6,7 +6,7 @@ SRCS= as.c aspa.c cert.c cms.c constraints.c crl.c encoding.c filemode.c \ output.c output-bgpd.c output-bird.c output-csv.c output-json.c \ output-ometric.c parser.c print.c repo.c rfc3779.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 + rsc.c rsync.c spl.c tak.c tal.c validate.c x509.c MAN= rpki-client.8 LDADD+= -lexpat -ltls -lssl -lcrypto -lutil -lz diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index 10235441c0b..0aedd75aba1 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.207 2024/02/21 12:48:25 tb Exp $ */ +/* $OpenBSD: extern.h,v 1.208 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -178,6 +178,7 @@ enum rtype { RTYPE_ASPA, RTYPE_TAK, RTYPE_GEOFEED, + RTYPE_SPL, }; enum location { @@ -281,6 +282,34 @@ struct rsc { }; /* + * An IP address prefix in a given SignedPrefixList. + */ +struct spl_pfx { + enum afi afi; + struct ip_addr prefix; +}; + +/* + * An SPL, draft-ietf-sidrops-rpki-prefixlist + * This consists of an ASID and its IP prefixes. + */ +struct spl { + uint32_t asid; + struct spl_pfx *pfxs; + size_t pfxsz; + int talid; + char *aia; + char *aki; + char *sia; + char *ski; + time_t signtime; /* CMS signing-time attribute */ + time_t notbefore; /* EE cert's Not Before */ + time_t notafter; /* EE cert's Not After */ + time_t expires; /* when the certification path expires */ + int valid; +}; + +/* * Datastructure representing the TAKey sequence inside TAKs. */ struct takey { @@ -410,6 +439,26 @@ RB_HEAD(vrp_tree, vrp); RB_PROTOTYPE(vrp_tree, vrp, entry, vrpcmp); /* + * Validated SignedPrefixList Payload + * A single VSP element (including ASID) + * draft-ietf-sidrops-rpki-prefixlist + */ +struct vsp { + RB_ENTRY(vsp) entry; + uint32_t asid; + struct spl_pfx *prefixes; + size_t prefixesz; + time_t expires; + int talid; + unsigned int repoid; +}; +/* + * Tree of VSP sorted by asid + */ +RB_HEAD(vsp_tree, vsp); +RB_PROTOTYPE(vsp_tree, vsp, entry, vspcmp); + +/* * A single BGPsec Router Key (including ASID) */ struct brk { @@ -561,6 +610,11 @@ struct repotalstats { uint32_t vaps_pas; /* total number of providers */ uint32_t vrps; /* total number of Validated ROA Payloads */ uint32_t vrps_uniqs; /* number of unique vrps */ + uint32_t spls; /* signed prefix list */ + uint32_t spls_fail; /* failing syntactic parse */ + uint32_t spls_invalid; /* invalid asid */ + uint32_t vsps; /* total number of Validated SPL Payloads */ + uint32_t vsps_uniqs; /* number of unique vsps */ }; struct repostats { @@ -637,6 +691,14 @@ struct roa *roa_read(struct ibuf *); void roa_insert_vrps(struct vrp_tree *, struct roa *, struct repo *); +void spl_buffer(struct ibuf *, const struct spl *); +void spl_free(struct spl *); +struct spl *spl_parse(X509 **, const char *, int, const unsigned char *, + size_t); +struct spl *spl_read(struct ibuf *); +void spl_insert_vsps(struct vsp_tree *, struct spl *, + struct repo *); + void gbr_free(struct gbr *); struct gbr *gbr_parse(X509 **, const char *, int, const unsigned char *, size_t); @@ -690,6 +752,7 @@ int valid_aspa(const char *, struct cert *, struct aspa *); int valid_geofeed(const char *, struct cert *, struct geofeed *); int valid_uuid(const char *); int valid_ca_pkey(const char *, EVP_PKEY *); +int valid_spl(const char *, struct cert *, struct spl *); /* Working with CMS. */ unsigned char *cms_parse_validate(X509 **, const char *, @@ -870,6 +933,7 @@ 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 *); +void spl_print(const X509 *, const struct spl *); /* Missing RFC 3779 API */ IPAddrBlocks *IPAddrBlocks_new(void); @@ -885,22 +949,22 @@ extern int outformats; #define FORMAT_OMETRIC 0x10 int outputfiles(struct vrp_tree *v, struct brk_tree *b, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int outputheader(FILE *, struct stats *); int output_bgpd(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int output_bird1v4(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int output_bird1v6(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int output_bird2(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int output_csv(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int output_json(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); int output_ometric(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); void logx(const char *fmt, ...) __attribute__((format(printf, 1, 2))); diff --git a/usr.sbin/rpki-client/filemode.c b/usr.sbin/rpki-client/filemode.c index 7532b68c363..5daf03b72f3 100644 --- a/usr.sbin/rpki-client/filemode.c +++ b/usr.sbin/rpki-client/filemode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: filemode.c,v 1.37 2024/01/23 09:32:57 job Exp $ */ +/* $OpenBSD: filemode.c,v 1.38 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -296,6 +296,7 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) struct mft *mft = NULL; struct roa *roa = NULL; struct rsc *rsc = NULL; + struct spl *spl = NULL; struct tak *tak = NULL; struct tal *tal = NULL; char *aia = NULL, *aki = NULL; @@ -422,6 +423,15 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) expires = &rsc->expires; notafter = &rsc->notafter; break; + case RTYPE_SPL: + spl = spl_parse(&x509, file, -1, buf, len); + if (spl == NULL) + break; + aia = spl->aia; + aki = spl->aki; + expires = &spl->expires; + notafter = &spl->notafter; + break; case RTYPE_TAK: tak = tak_parse(&x509, file, -1, buf, len); if (tak == NULL) @@ -464,6 +474,8 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) case RTYPE_RSC: status = rsc->valid; break; + case RTYPE_SPL: + status = spl->valid; default: break; } @@ -523,6 +535,9 @@ proc_parser_file(char *file, unsigned char *buf, size_t len) case RTYPE_RSC: rsc_print(x509, rsc); break; + case RTYPE_SPL: + spl_print(x509, spl); + break; case RTYPE_TAK: tak_print(x509, tak); break; diff --git a/usr.sbin/rpki-client/main.c b/usr.sbin/rpki-client/main.c index 6f51e0fac75..933494a2540 100644 --- a/usr.sbin/rpki-client/main.c +++ b/usr.sbin/rpki-client/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.250 2024/02/21 12:48:25 tb Exp $ */ +/* $OpenBSD: main.c,v 1.251 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -557,7 +557,8 @@ queue_add_from_cert(const struct cert *cert) */ static void entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, - struct brk_tree *brktree, struct vap_tree *vaptree) + struct brk_tree *brktree, struct vap_tree *vaptree, + struct vsp_tree *vsptree) { enum rtype type; struct tal *tal; @@ -565,6 +566,7 @@ entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, struct mft *mft; struct roa *roa; struct aspa *aspa; + struct spl *spl; struct repo *rp; char *file; time_t mtime; @@ -665,6 +667,19 @@ entity_process(struct ibuf *b, struct stats *st, struct vrp_tree *tree, repo_stat_inc(rp, talid, type, STYPE_INVALID); aspa_free(aspa); break; + case RTYPE_SPL: + io_read_buf(b, &c, sizeof(c)); + if (c == 0) { + repo_stat_inc(rp, talid, type, STYPE_FAIL); + break; + } + spl = spl_read(b); + if (spl->valid) + spl_insert_vsps(vsptree, spl, rp); + else + repo_stat_inc(rp, talid, type, STYPE_INVALID); + spl_free(spl); + break; case RTYPE_TAK: break; case RTYPE_FILE: @@ -755,6 +770,11 @@ sum_stats(const struct repo *rp, const struct repotalstats *in, void *arg) out->vaps += in->vaps; out->vaps_uniqs += in->vaps_uniqs; out->vaps_pas += in->vaps_pas; + out->spls += in->spls; + out->spls_fail += in->spls_fail; + out->spls_invalid += in->spls_invalid; + out->vsps += in->vsps; + out->vsps_uniqs += in->vsps_uniqs; } static void @@ -947,6 +967,7 @@ main(int argc, char *argv[]) const char *errs, *name; const char *skiplistfile = NULL; struct vrp_tree vrps = RB_INITIALIZER(&vrps); + struct vsp_tree vsps = RB_INITIALIZER(&vsps); struct brk_tree brks = RB_INITIALIZER(&brks); struct vap_tree vaps = RB_INITIALIZER(&vaps); struct rusage ru; @@ -1341,7 +1362,8 @@ main(int argc, char *argv[]) if ((pfd[0].revents & POLLIN)) { b = io_buf_read(proc, &procbuf); if (b != NULL) { - entity_process(b, &stats, &vrps, &brks, &vaps); + entity_process(b, &stats, &vrps, &brks, &vaps, + &vsps); ibuf_free(b); } } @@ -1434,7 +1456,7 @@ main(int argc, char *argv[]) } repo_stats_collect(sum_repostats, &stats.repo_stats); - if (outputfiles(&vrps, &brks, &vaps, &stats)) + if (outputfiles(&vrps, &brks, &vaps, &vsps, &stats)) rc = 1; printf("Processing time %lld seconds " @@ -1451,6 +1473,9 @@ main(int argc, char *argv[]) "invalid)\n", stats.repo_tal_stats.aspas, stats.repo_tal_stats.aspas_fail, stats.repo_tal_stats.aspas_invalid); + printf("Signed Prefix Lists: %u (%u failed parse, %u invalid)\n", + stats.repo_tal_stats.spls, stats.repo_tal_stats.spls_fail, + stats.repo_tal_stats.spls_invalid); printf("BGPsec Router Certificates: %u\n", stats.repo_tal_stats.brks); printf("Certificates: %u (%u invalid)\n", stats.repo_tal_stats.certs, stats.repo_tal_stats.certs_fail); @@ -1470,6 +1495,8 @@ main(int argc, char *argv[]) stats.repo_tal_stats.vrps_uniqs); printf("VAP Entries: %u (%u unique)\n", stats.repo_tal_stats.vaps, stats.repo_tal_stats.vaps_uniqs); + printf("VSP Entries: %u (%u unique)\n", stats.repo_tal_stats.vsps, + stats.repo_tal_stats.vsps_uniqs); /* Memory cleanup. */ repo_free(); diff --git a/usr.sbin/rpki-client/mft.c b/usr.sbin/rpki-client/mft.c index bc3fb930b91..8f48a881d71 100644 --- a/usr.sbin/rpki-client/mft.c +++ b/usr.sbin/rpki-client/mft.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mft.c,v 1.111 2024/02/21 09:17:06 tb Exp $ */ +/* $OpenBSD: mft.c,v 1.112 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -121,6 +121,8 @@ rtype_from_file_extension(const char *fn) return RTYPE_TAK; if (strcasecmp(fn + sz - 4, ".csv") == 0) return RTYPE_GEOFEED; + if (strcasecmp(fn + sz - 4, ".spl") == 0) + return RTYPE_SPL; return RTYPE_INVALID; } @@ -162,6 +164,7 @@ rtype_from_mftfile(const char *fn) case RTYPE_GBR: case RTYPE_ROA: case RTYPE_ASPA: + case RTYPE_SPL: case RTYPE_TAK: return type; default: diff --git a/usr.sbin/rpki-client/output-bgpd.c b/usr.sbin/rpki-client/output-bgpd.c index 1207f46d51b..7bf47d30450 100644 --- a/usr.sbin/rpki-client/output-bgpd.c +++ b/usr.sbin/rpki-client/output-bgpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output-bgpd.c,v 1.28 2023/06/26 18:39:53 job Exp $ */ +/* $OpenBSD: output-bgpd.c,v 1.29 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -21,7 +21,7 @@ int output_bgpd(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { struct vrp *vrp; struct vap *vap; diff --git a/usr.sbin/rpki-client/output-bird.c b/usr.sbin/rpki-client/output-bird.c index 344f28d2724..fca66050797 100644 --- a/usr.sbin/rpki-client/output-bird.c +++ b/usr.sbin/rpki-client/output-bird.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output-bird.c,v 1.18 2023/05/30 12:14:48 claudio Exp $ */ +/* $OpenBSD: output-bird.c,v 1.19 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2020 Robert Scheck <robert@fedoraproject.org> @@ -22,7 +22,7 @@ int output_bird1v4(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { extern const char *bird_tablename; struct vrp *v; @@ -51,7 +51,7 @@ output_bird1v4(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, int output_bird1v6(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { extern const char *bird_tablename; struct vrp *v; @@ -80,7 +80,7 @@ output_bird1v6(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, int output_bird2(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { extern const char *bird_tablename; struct vrp *v; diff --git a/usr.sbin/rpki-client/output-csv.c b/usr.sbin/rpki-client/output-csv.c index 6338b7cad7c..7e46c44d4cb 100644 --- a/usr.sbin/rpki-client/output-csv.c +++ b/usr.sbin/rpki-client/output-csv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output-csv.c,v 1.13 2022/08/30 18:56:49 job Exp $ */ +/* $OpenBSD: output-csv.c,v 1.14 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> * @@ -21,7 +21,7 @@ int output_csv(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { struct vrp *v; diff --git a/usr.sbin/rpki-client/output-json.c b/usr.sbin/rpki-client/output-json.c index 9c8f3f2ba6c..8ff1e7f6243 100644 --- a/usr.sbin/rpki-client/output-json.c +++ b/usr.sbin/rpki-client/output-json.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output-json.c,v 1.42 2024/02/13 20:41:22 job Exp $ */ +/* $OpenBSD: output-json.c,v 1.43 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> * @@ -47,6 +47,9 @@ outputheader_json(struct stats *st) json_do_int("roas", st->repo_tal_stats.roas); json_do_int("failedroas", st->repo_tal_stats.roas_fail); json_do_int("invalidroas", st->repo_tal_stats.roas_invalid); + json_do_int("spls", st->repo_tal_stats.spls); + json_do_int("failedspls", st->repo_tal_stats.spls_fail); + json_do_int("invalidspls", st->repo_tal_stats.spls_invalid); json_do_int("aspas", st->repo_tal_stats.aspas); json_do_int("failedaspas", st->repo_tal_stats.aspas_fail); json_do_int("invalidaspas", st->repo_tal_stats.aspas_invalid); @@ -69,6 +72,8 @@ outputheader_json(struct stats *st) json_do_int("repositories", st->repos); json_do_int("vrps", st->repo_tal_stats.vrps); json_do_int("uniquevrps", st->repo_tal_stats.vrps_uniqs); + json_do_int("vsps", st->repo_tal_stats.vsps); + json_do_int("uniquevsps", st->repo_tal_stats.vsps_uniqs); json_do_int("vaps", st->repo_tal_stats.vaps); json_do_int("uniquevaps", st->repo_tal_stats.vaps_uniqs); json_do_int("cachedir_del_files", st->repo_stats.del_files); @@ -109,11 +114,13 @@ output_aspa(struct vap_tree *vaps) int output_json(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { char buf[64]; struct vrp *v; struct brk *b; + struct vsp *vsp; + size_t i; json_do_start(out); outputheader_json(st); @@ -147,5 +154,22 @@ output_json(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, if (!excludeaspa) output_aspa(vaps); + json_do_array("signedprefixlists"); + RB_FOREACH(vsp, vsp_tree, vsps) { + json_do_object("vsp", 1); + json_do_int("origin_as", vsp->asid); + json_do_array("prefixes"); + for (i = 0; i < vsp->prefixesz; i++) { + ip_addr_print(&vsp->prefixes[i].prefix, + vsp->prefixes[i].afi, buf, sizeof(buf)); + json_do_string("prefix", buf); + } + json_do_end(); + json_do_int("expires", vsp->expires); + json_do_string("ta", taldescs[vsp->talid]); + json_do_end(); + } + json_do_end(); + return json_do_finish(); } diff --git a/usr.sbin/rpki-client/output-ometric.c b/usr.sbin/rpki-client/output-ometric.c index 1addf16728c..b2775e159d5 100644 --- a/usr.sbin/rpki-client/output-ometric.c +++ b/usr.sbin/rpki-client/output-ometric.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output-ometric.c,v 1.7 2024/02/13 20:41:22 job Exp $ */ +/* $OpenBSD: output-ometric.c,v 1.8 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2022 Claudio Jeker <claudio@openbsd.org> * @@ -82,6 +82,18 @@ set_common_stats(const struct repotalstats *in, struct ometric *metric, OKV("type", "state"), OKV("vap", "unique"), ol); ometric_set_int_with_labels(metric, in->vaps_pas, OKV("type", "state"), OKV("vap providers", "total"), ol); + + ometric_set_int_with_labels(metric, in->spls, + OKV("type", "state"), OKV("spl", "valid"), ol); + ometric_set_int_with_labels(metric, in->spls_fail, + OKV("type", "state"), OKV("spl", "failed parse"), ol); + ometric_set_int_with_labels(metric, in->spls_invalid, + OKV("type", "state"), OKV("spl", "invalid"), ol); + + ometric_set_int_with_labels(metric, in->vsps, + OKV("type", "state"), OKV("vsp", "total"), ol); + ometric_set_int_with_labels(metric, in->vsps_uniqs, + OKV("type", "state"), OKV("vsp", "unique"), ol); } static void @@ -146,7 +158,7 @@ repo_stats(const struct repo *rp, const struct repostats *in, void *arg) int output_ometric(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, - struct vap_tree *vaps, struct stats *st) + struct vap_tree *vaps, struct vsp_tree *vsps, struct stats *st) { struct olabels *ol; const char *keys[4] = { "nodename", "domainname", "release", NULL }; diff --git a/usr.sbin/rpki-client/output.c b/usr.sbin/rpki-client/output.c index e875698ebdf..e4725d66a65 100644 --- a/usr.sbin/rpki-client/output.c +++ b/usr.sbin/rpki-client/output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output.c,v 1.32 2024/02/03 14:30:47 job Exp $ */ +/* $OpenBSD: output.c,v 1.33 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Theo de Raadt <deraadt@openbsd.org> * @@ -64,7 +64,7 @@ static const struct outputs { int format; char *name; int (*fn)(FILE *, struct vrp_tree *, struct brk_tree *, - struct vap_tree *, struct stats *); + struct vap_tree *, struct vsp_tree *, struct stats *); } outputs[] = { { FORMAT_OPENBGPD, "openbgpd", output_bgpd }, { FORMAT_BIRD, "bird1v4", output_bird1v4 }, @@ -84,7 +84,7 @@ static void set_signal_handler(void); int outputfiles(struct vrp_tree *v, struct brk_tree *b, struct vap_tree *a, - struct stats *st) + struct vsp_tree *p, struct stats *st) { int i, rc = 0; @@ -103,7 +103,7 @@ outputfiles(struct vrp_tree *v, struct brk_tree *b, struct vap_tree *a, rc = 1; continue; } - if ((*outputs[i].fn)(fout, v, b, a, st) != 0) { + if ((*outputs[i].fn)(fout, v, b, a, p, st) != 0) { warn("output for %s format failed", outputs[i].name); fclose(fout); output_cleantmp(); diff --git a/usr.sbin/rpki-client/parser.c b/usr.sbin/rpki-client/parser.c index e1ef9b973fd..62ebcade65e 100644 --- a/usr.sbin/rpki-client/parser.c +++ b/usr.sbin/rpki-client/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.128 2024/02/03 14:30:47 job Exp $ */ +/* $OpenBSD: parser.c,v 1.129 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -158,6 +158,41 @@ proc_parser_roa(char *file, const unsigned char *der, size_t len, } /* + * Parse and validate a draft-ietf-sidrops-rpki-prefixlist SPL. + * Returns the spl on success, NULL on failure. + */ +static struct spl * +proc_parser_spl(char *file, const unsigned char *der, size_t len, + const struct entity *entp) +{ + struct spl *spl; + struct auth *a; + struct crl *crl; + X509 *x509; + const char *errstr; + + if ((spl = spl_parse(&x509, file, entp->talid, der, len)) == NULL) + return NULL; + + a = valid_ski_aki(file, &auths, spl->ski, spl->aki, entp->mftaki); + crl = crl_get(&crlt, a); + + if (!valid_x509(file, ctx, x509, a, crl, &errstr)) { + warnx("%s: %s", file, errstr); + X509_free(x509); + spl_free(spl); + return NULL; + } + X509_free(x509); + + spl->talid = a->cert->talid; + + spl->expires = x509_find_expires(spl->notafter, a, &crlt); + + return spl; +} + +/* * Check all files and their hashes in a MFT structure. * Return zero on failure, non-zero on success. */ @@ -681,6 +716,7 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) struct aspa *aspa; struct gbr *gbr; struct tak *tak; + struct spl *spl; struct ibuf *b; unsigned char *f; time_t mtime, crlmtime; @@ -822,6 +858,19 @@ parse_entity(struct entityq *q, struct msgbuf *msgq) io_simple_buffer(b, &mtime, sizeof(mtime)); tak_free(tak); break; + case RTYPE_SPL: + file = parse_load_file(entp, &f, &flen); + io_str_buffer(b, file); + spl = proc_parser_spl(file, f, flen, entp); + if (spl != NULL) + mtime = spl->signtime; + io_simple_buffer(b, &mtime, sizeof(mtime)); + c = (spl != NULL); + io_simple_buffer(b, &c, sizeof(int)); + if (spl != NULL) + spl_buffer(b, spl); + spl_free(spl); + break; case RTYPE_CRL: default: file = parse_filepath(entp->repoid, entp->path, diff --git a/usr.sbin/rpki-client/print.c b/usr.sbin/rpki-client/print.c index 1e5503b2a1c..ac3852a482b 100644 --- a/usr.sbin/rpki-client/print.c +++ b/usr.sbin/rpki-client/print.c @@ -1,4 +1,4 @@ -/* $OpenBSD: print.c,v 1.49 2024/02/16 05:18:29 tb Exp $ */ +/* $OpenBSD: print.c,v 1.50 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -508,6 +508,60 @@ roa_print(const X509 *x, const struct roa *p) } void +spl_print(const X509 *x, const struct spl *s) +{ + char buf[128]; + size_t i; + + if (outformats & FORMAT_JSON) { + json_do_string("type", "spl"); + json_do_string("ski", pretty_key_id(s->ski)); + x509_print(x); + json_do_string("aki", pretty_key_id(s->aki)); + json_do_string("aia", s->aia); + json_do_string("sia", s->sia); + if (s->signtime != 0) + json_do_int("signing_time", s->signtime); + json_do_int("valid_since", s->notbefore); + json_do_int("valid_until", s->notafter); + if (s->expires) + json_do_int("expires", s->expires); + json_do_int("asid", s->asid); + } else { + printf("Subject key identifier: %s\n", pretty_key_id(s->ski)); + x509_print(x); + printf("Authority key identifier: %s\n", pretty_key_id(s->aki)); + printf("Authority info access: %s\n", s->aia); + printf("Subject info access: %s\n", s->sia); + if (s->signtime != 0) + printf("Signing time: %s\n", + time2str(s->signtime)); + printf("SPL not before: %s\n", + time2str(s->notbefore)); + printf("SPL not after: %s\n", time2str(s->notafter)); + printf("asID: %u\n", s->asid); + printf("Originated IP Prefixes: "); + } + + if (outformats & FORMAT_JSON) + json_do_array("prefixes"); + for (i = 0; i < s->pfxsz; i++) { + ip_addr_print(&s->pfxs[i].prefix, s->pfxs[i].afi, buf, + sizeof(buf)); + + if (outformats & FORMAT_JSON) { + json_do_string("prefix", buf); + } else { + if (i > 0) + printf("%26s", ""); + printf("%s\n", buf); + } + } + if (outformats & FORMAT_JSON) + json_do_end(); +} + +void gbr_print(const X509 *x, const struct gbr *p) { if (outformats & FORMAT_JSON) { diff --git a/usr.sbin/rpki-client/repo.c b/usr.sbin/rpki-client/repo.c index f1bf22ba5c5..60354de7f0e 100644 --- a/usr.sbin/rpki-client/repo.c +++ b/usr.sbin/rpki-client/repo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: repo.c,v 1.52 2024/02/03 14:30:47 job Exp $ */ +/* $OpenBSD: repo.c,v 1.53 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -1480,6 +1480,30 @@ repo_stat_inc(struct repo *rp, int talid, enum rtype type, enum stype subtype) break; } break; + case RTYPE_SPL: + switch (subtype) { + case STYPE_OK: + rp->stats[talid].spls++; + break; + case STYPE_FAIL: + rp->stats[talid].spls_fail++; + break; + case STYPE_INVALID: + rp->stats[talid].spls_invalid++; + break; + case STYPE_TOTAL: + rp->stats[talid].vsps++; + break; + case STYPE_UNIQUE: + rp->stats[talid].vsps_uniqs++; + break; + case STYPE_DEC_UNIQUE: + rp->stats[talid].vsps_uniqs--; + break; + default: + break; + } + break; case RTYPE_CRL: rp->stats[talid].crls++; break; diff --git a/usr.sbin/rpki-client/rpki-client.8 b/usr.sbin/rpki-client/rpki-client.8 index a7d2d0e27a8..d3b1aa8b264 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.100 2024/01/31 17:19:02 job Exp $ +.\" $OpenBSD: rpki-client.8,v 1.101 2024/02/22 12:49:42 job Exp $ .\" .\" Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> .\" @@ -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: January 31 2024 $ +.Dd $Mdocdate: February 22 2024 $ .Dt RPKI-CLIENT 8 .Os .Sh NAME @@ -451,6 +451,12 @@ agreement regarding ARIN service restrictions. .%U https://datatracker.ietf.org/doc/html/draft-spaghetti-sidrops-rrdp-desynchronization-00 .%D Jan, 2024 .Re +.Pp +.Rs +.%T A profile for Signed Prefix Lists for Use in the Resource Public Key Infrastructure (RPKI) +.%U https://datatracker.ietf.org/doc/html/draft-ietf-sidrops-rpki-prefixlist-02 +.%D Jan, 2024 +.Re .Sh HISTORY .Nm first appeared in diff --git a/usr.sbin/rpki-client/spl.c b/usr.sbin/rpki-client/spl.c new file mode 100644 index 00000000000..fdb5cdf172e --- /dev/null +++ b/usr.sbin/rpki-client/spl.c @@ -0,0 +1,487 @@ +/* $OpenBSD: spl.c,v 1.1 2024/02/22 12:49:42 job Exp $ */ +/* + * Copyright (c) 2024 Job Snijders <job@fastly.com> + * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> + * Copyright (c) 2019 Kristaps Dzonsons <kristaps@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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/stack.h> +#include <openssl/safestack.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> + +#include "extern.h" + +extern ASN1_OBJECT *spl_oid; + +/* + * Types and templates for the SPL eContent. + */ + +ASN1_ITEM_EXP AddressFamilyPrefixes_it; +ASN1_ITEM_EXP SignedPrefixList_it; + +DECLARE_STACK_OF(ASN1_BIT_STRING); + +typedef struct { + ASN1_OCTET_STRING *addressFamily; + STACK_OF(ASN1_BIT_STRING) *addressPrefixes; +} AddressFamilyPrefixes; + +DECLARE_STACK_OF(AddressFamilyPrefixes); + +ASN1_SEQUENCE(AddressFamilyPrefixes) = { + ASN1_SIMPLE(AddressFamilyPrefixes, addressFamily, ASN1_OCTET_STRING), + ASN1_SEQUENCE_OF(AddressFamilyPrefixes, addressPrefixes, + ASN1_BIT_STRING), +} ASN1_SEQUENCE_END(AddressFamilyPrefixes); + +#ifndef DEFINE_STACK_OF +#define sk_ASN1_BIT_STRING_num(st) SKM_sk_num(ASN1_BIT_STRING, (st)) +#define sk_ASN1_BIT_STRING_value(st, i) SKM_sk_value(ASN1_BIT_STRING, (st), (i)) + +#define sk_AddressFamilyPrefixes_num(st) \ + SKM_sk_num(AddressFamilyPrefixes, (st)) +#define sk_AddressFamilyPrefixes_value(st, i) \ + SKM_sk_value(AddressFamilyPrefixes, (st), (i)) +#endif + +typedef struct { + ASN1_INTEGER *version; + ASN1_INTEGER *asid; + STACK_OF(AddressFamilyPrefixes) *prefixBlocks; +} SignedPrefixList; + +ASN1_SEQUENCE(SignedPrefixList) = { + ASN1_EXP_OPT(SignedPrefixList, version, ASN1_INTEGER, 0), + ASN1_SIMPLE(SignedPrefixList, asid, ASN1_INTEGER), + ASN1_SEQUENCE_OF(SignedPrefixList, prefixBlocks, AddressFamilyPrefixes) +} ASN1_SEQUENCE_END(SignedPrefixList); + +DECLARE_ASN1_FUNCTIONS(SignedPrefixList); +IMPLEMENT_ASN1_FUNCTIONS(SignedPrefixList); + +/* + * Comparator to help sorting elements in SPL prefixBlocks and VSPs. + * Returns -1 if 'a' should precede 'b', 1 if 'b' should precede 'a', + * or '0' if a and b are equal. + */ +static int +prefix_cmp(enum afi afi, const struct ip_addr *a, const struct ip_addr *b) +{ + int cmp; + + switch (afi) { + case AFI_IPV4: + cmp = memcmp(&a->addr, &b->addr, 4); + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + break; + case AFI_IPV6: + cmp = memcmp(&a->addr, &b->addr, 16); + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + break; + default: + break; + } + + if (a->prefixlen < b->prefixlen) + return -1; + if (a->prefixlen > b->prefixlen) + return 1; + + return 0; +} + +/* + * Parses the eContent section of a SPL file, + * draft-ietf-sidrops-rpki-prefixlist-02 section 3. + * Returns zero on failure, non-zero on success. + */ +static int +spl_parse_econtent(const char *fn, struct spl *spl, const unsigned char *d, + size_t dsz) +{ + const unsigned char *oder; + SignedPrefixList *spl_asn1; + const AddressFamilyPrefixes *afp; + const STACK_OF(ASN1_BIT_STRING) *prefixes; + const ASN1_BIT_STRING *prefix_asn1; + int afpsz, prefixesz; + enum afi afi; + struct ip_addr ip_addr; + struct spl_pfx *prefix; + int ipv4_seen = 0, ipv6_seen = 0; + int i, j, rc = 0; + + oder = d; + if ((spl_asn1 = d2i_SignedPrefixList(NULL, &d, dsz)) == NULL) { + warnx("%s: RFC 6482 section 3: failed to parse " + "SignedPrefixList", fn); + goto out; + } + if (d != oder + dsz) { + warnx("%s: %td bytes trailing garbage in eContent", fn, + oder + dsz - d); + goto out; + } + + if (!valid_econtent_version(fn, spl_asn1->version, 0)) + goto out; + + if (!as_id_parse(spl_asn1->asid, &spl->asid)) { + warnx("%s: asid: malformed AS identifier", fn); + goto out; + } + + afpsz = sk_AddressFamilyPrefixes_num(spl_asn1->prefixBlocks); + if (afpsz < 0 || afpsz > 2) { + warnx("%s: unexpected number of AddressFamilyAddressPrefixes" + "(got %d, expected 0, 1, or 2)", fn, afpsz); + goto out; + } + + for (i = 0; i < afpsz; i++) { + struct ip_addr *prev_ip_addr = NULL; + + afp = sk_AddressFamilyPrefixes_value(spl_asn1->prefixBlocks, i); + prefixes = afp->addressPrefixes; + prefixesz = sk_ASN1_BIT_STRING_num(afp->addressPrefixes); + + if (prefixesz == 0) { + warnx("%s: empty AddressFamilyAddressPrefixes", fn); + goto out; + } + if (spl->pfxsz + prefixesz >= MAX_IP_SIZE) { + warnx("%s: too many addressPrefixes entries", fn); + goto out; + } + + if (!ip_addr_afi_parse(fn, afp->addressFamily, &afi)) + goto out; + + switch (afi) { + case AFI_IPV4: + if (ipv4_seen++ > 0) { + warnx("%s: addressFamilyIPv4 appeared twice", + fn); + goto out; + } + if (ipv6_seen > 0) { + warnx("%s: invalid sorting, IPv6 before IPv4", + fn); + goto out; + } + break; + case AFI_IPV6: + if (ipv6_seen++ > 0) { + warnx("%s: addressFamilyIPv6 appeared twice", + fn); + goto out; + } + } + + spl->pfxs = recallocarray(spl->pfxs, spl->pfxsz, + spl->pfxsz + prefixesz, sizeof(struct spl_pfx)); + if (spl->pfxs == NULL) + err(1, NULL); + + for (j = 0; j < prefixesz; j++) { + prefix_asn1 = sk_ASN1_BIT_STRING_value(prefixes, j); + + if (!ip_addr_parse(prefix_asn1, afi, fn, &ip_addr)) + goto out; + + if (j > 0 && + prefix_cmp(afi, prev_ip_addr, &ip_addr) != -1) { + warnx("%s: invalid addressPrefixes sorting", fn); + goto out; + } + + prefix = &spl->pfxs[spl->pfxsz++]; + prefix->prefix = ip_addr; + prefix->afi = afi; + prev_ip_addr = &prefix->prefix; + } + } + + rc = 1; + out: + SignedPrefixList_free(spl_asn1); + return rc; +} + +/* + * Parse a full Signed Prefix List file. + * Returns the SPL, or NULL if the object was malformed. + */ +struct spl * +spl_parse(X509 **x509, const char *fn, int talid, const unsigned char *der, + size_t len) +{ + struct spl *spl; + size_t cmsz; + unsigned char *cms; + struct cert *cert = NULL; + time_t signtime = 0; + int rc = 0; + + cms = cms_parse_validate(x509, fn, der, len, spl_oid, &cmsz, &signtime); + if (cms == NULL) + return NULL; + + if ((spl = calloc(1, sizeof(*spl))) == NULL) + err(1, NULL); + spl->signtime = signtime; + + if (!x509_get_aia(*x509, fn, &spl->aia)) + goto out; + if (!x509_get_aki(*x509, fn, &spl->aki)) + goto out; + if (!x509_get_sia(*x509, fn, &spl->sia)) + goto out; + if (!x509_get_ski(*x509, fn, &spl->ski)) + goto out; + if (spl->aia == NULL || spl->aki == NULL || spl->sia == NULL || + spl->ski == NULL) { + warnx("%s: RFC 6487 section 4.8: " + "missing AIA, AKI, SIA, or SKI X509 extension", fn); + goto out; + } + + if (!x509_get_notbefore(*x509, fn, &spl->notbefore)) + goto out; + if (!x509_get_notafter(*x509, fn, &spl->notafter)) + goto out; + + if (!spl_parse_econtent(fn, spl, cms, cmsz)) + goto out; + + if (x509_any_inherits(*x509)) { + warnx("%s: inherit elements not allowed in EE cert", fn); + goto out; + } + + if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL) + goto out; + + if (cert->asz == 0) { + warnx("%s: AS Resources extension missing", fn); + goto out; + } + + if (cert->ipsz > 0) { + warnx("%s: superfluous IP Resources extension present", fn); + goto out; + } + + /* + * If the SPL isn't valid, we accept it anyway and depend upon + * the code around spl_read() to check the "valid" field itself. + */ + spl->valid = valid_spl(fn, cert, spl); + + rc = 1; + out: + if (rc == 0) { + spl_free(spl); + spl = NULL; + X509_free(*x509); + *x509 = NULL; + } + cert_free(cert); + free(cms); + return spl; +} + +void +spl_free(struct spl *s) +{ + if (s == NULL) + return; + + free(s->aia); + free(s->aki); + free(s->sia); + free(s->ski); + free(s->pfxs); + free(s); +} + +/* + * Serialize parsed SPL content. + * See spl_read() for reader. + */ +void +spl_buffer(struct ibuf *b, const struct spl *s) +{ + io_simple_buffer(b, &s->valid, sizeof(s->valid)); + io_simple_buffer(b, &s->asid, sizeof(s->asid)); + io_simple_buffer(b, &s->talid, sizeof(s->talid)); + io_simple_buffer(b, &s->pfxsz, sizeof(s->pfxsz)); + io_simple_buffer(b, &s->expires, sizeof(s->expires)); + + io_simple_buffer(b, s->pfxs, s->pfxsz * sizeof(s->pfxs[0])); + + io_str_buffer(b, s->aia); + io_str_buffer(b, s->aki); + io_str_buffer(b, s->ski); +} + +/* + * Read parsed SPL content from descriptor. + * See spl_buffer() for writer. + * Result must be passed to spl_free(). + */ +struct spl * +spl_read(struct ibuf *b) +{ + struct spl *s; + + if ((s = calloc(1, sizeof(struct spl))) == NULL) + err(1, NULL); + + io_read_buf(b, &s->valid, sizeof(s->valid)); + io_read_buf(b, &s->asid, sizeof(s->asid)); + io_read_buf(b, &s->talid, sizeof(s->talid)); + io_read_buf(b, &s->pfxsz, sizeof(s->pfxsz)); + io_read_buf(b, &s->expires, sizeof(s->expires)); + + if ((s->pfxs = calloc(s->pfxsz, sizeof(struct spl_pfx))) == NULL) + err(1, NULL); + io_read_buf(b, s->pfxs, s->pfxsz * sizeof(s->pfxs[0])); + + io_read_str(b, &s->aia); + io_read_str(b, &s->aki); + io_read_str(b, &s->ski); + assert(s->aia && s->aki && s->ski); + + return s; +} + +static int +spl_pfx_cmp(const struct spl_pfx *a, const struct spl_pfx *b) +{ + if (a->afi > b->afi) + return 1; + if (a->afi < b->afi) + return -1; + + return prefix_cmp(a->afi, &a->prefix, &b->prefix); +} + +static void +insert_vsp(struct vsp *vsp, size_t idx, struct spl_pfx *pfx) +{ + if (idx < vsp->prefixesz) + memmove(vsp->prefixes + idx + 1, vsp->prefixes + idx, + (vsp->prefixesz - idx) * sizeof(*vsp->prefixes)); + vsp->prefixes[idx] = *pfx; + vsp->prefixesz++; +} + +/* + * Add each prefix in the SPL into the VSP tree. + * Updates "vsps" to be the number of VSPs and "uniqs" to be the unique + * number of prefixes. + */ +void +spl_insert_vsps(struct vsp_tree *tree, struct spl *spl, struct repo *rp) +{ + struct vsp *vsp, *found; + size_t i, j; + int cmp; + + if ((vsp = calloc(1, sizeof(*vsp))) == NULL) + err(1, NULL); + + vsp->asid = spl->asid; + vsp->talid = spl->talid; + vsp->expires = spl->expires; + if (rp != NULL) + vsp->repoid = repo_id(rp); + + if ((found = RB_INSERT(vsp_tree, tree, vsp)) != NULL) { + /* already exists */ + if (found->expires < vsp->expires) { + /* adjust unique count */ + repo_stat_inc(repo_byid(found->repoid), + found->talid, RTYPE_SPL, STYPE_DEC_UNIQUE); + found->expires = vsp->expires; + found->talid = vsp->talid; + found->repoid = vsp->repoid; + repo_stat_inc(rp, vsp->talid, RTYPE_SPL, + STYPE_UNIQUE); + } + free(vsp); + vsp = found; + } else + repo_stat_inc(rp, vsp->talid, RTYPE_SPL, STYPE_UNIQUE); + repo_stat_inc(rp, spl->talid, RTYPE_SPL, STYPE_TOTAL); + + /* merge content of multiple SPLs */ + vsp->prefixes = reallocarray(vsp->prefixes, + vsp->prefixesz + spl->pfxsz, sizeof(struct spl_pfx)); + if (vsp->prefixes == NULL) + err(1, NULL); + + /* + * Merge all data from the new SPL at hand into 'vsp': loop over + * all SPL->pfxs, and insert them in the right place in + * vsp->prefixes while keeping the order of the array. + */ + for (i = 0, j = 0; i < spl->pfxsz; ) { + cmp = -1; + if (j == vsp->prefixesz || + (cmp = spl_pfx_cmp(&spl->pfxs[i], &vsp->prefixes[j])) < 0) { + insert_vsp(vsp, j, &spl->pfxs[i]); + i++; + } else if (cmp == 0) + i++; + + if (j < vsp->prefixesz) + j++; + } +} + +/* + * Comparison function for the RB tree + */ +static inline int +vspcmp(const struct vsp *a, const struct vsp *b) +{ + if (a->asid > b->asid) + return 1; + if (a->asid < b->asid) + return -1; + + return 0; +} + +RB_GENERATE(vsp_tree, vsp, entry, vspcmp); diff --git a/usr.sbin/rpki-client/validate.c b/usr.sbin/rpki-client/validate.c index c0c4ee5e96e..5d4656cb556 100644 --- a/usr.sbin/rpki-client/validate.c +++ b/usr.sbin/rpki-client/validate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: validate.c,v 1.71 2024/02/01 15:11:38 tb Exp $ */ +/* $OpenBSD: validate.c,v 1.72 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -195,6 +195,22 @@ valid_roa(const char *fn, struct cert *cert, struct roa *roa) } /* + * Validate our SPL: check that the asID is contained in the end-entity + * certificate's resources. + * Returns 1 if valid, 0 otherwise. + */ +int +valid_spl(const char *fn, struct cert *cert, struct spl *spl) +{ + if (as_check_covered(spl->asid, spl->asid, cert->as, cert->asz) > 0) + return 1; + + warnx("%s: SPL: uncovered ASID: %u", fn, spl->asid); + + return 0; +} + +/* * Validate a file by verifying the SHA256 hash of that file. * The file to check is passed as a file descriptor. * Returns 1 if hash matched, 0 otherwise. Closes fd when done. diff --git a/usr.sbin/rpki-client/x509.c b/usr.sbin/rpki-client/x509.c index c9e340b6c19..5646eb7154d 100644 --- a/usr.sbin/rpki-client/x509.c +++ b/usr.sbin/rpki-client/x509.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509.c,v 1.80 2024/02/16 05:18:29 tb Exp $ */ +/* $OpenBSD: x509.c,v 1.81 2024/02/22 12:49:42 job Exp $ */ /* * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> @@ -44,6 +44,7 @@ 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 */ +ASN1_OBJECT *spl_oid; /* id-ct-signedPrefixList */ static const struct { const char *oid; @@ -117,6 +118,10 @@ static const struct { .oid = "1.2.840.113549.1.9.16.1.50", .ptr = &tak_oid, }, + { + .oid = "1.2.840.113549.1.9.16.1.51", + .ptr = &spl_oid, + }, }; void |