From: job Date: Mon, 11 Oct 2021 16:50:03 +0000 (+0000) Subject: Add support for BGPsec Router Certificates (RFC 8209) X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=6b83d8e3659e413ca9395d2b12e330a96f00d095;p=openbsd Add support for BGPsec Router Certificates (RFC 8209) BGPsec router keys are extracted from RPKI certificates and emitted via the JSON output in base64 encoded form. OK tb@ claudio@ --- diff --git a/usr.sbin/rpki-client/cert.c b/usr.sbin/rpki-client/cert.c index 5331d9f1932..8c17ffa749f 100644 --- a/usr.sbin/rpki-client/cert.c +++ b/usr.sbin/rpki-client/cert.c @@ -1,5 +1,6 @@ -/* $OpenBSD: cert.c,v 1.36 2021/10/07 12:59:29 job Exp $ */ +/* $OpenBSD: cert.c,v 1.37 2021/10/11 16:50:03 job Exp $ */ /* + * Copyright (c) 2021 Job Snijders * Copyright (c) 2019 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any @@ -1068,6 +1069,37 @@ cert_parse_inner(X509 **xp, const char *fn, int ta) /* Validation on required fields. */ + switch (p.res->purpose) { + case CERT_PURPOSE_CA: + if (p.res->mft == NULL) { + warnx("%s: RFC 6487 section 4.8.8: missing SIA", p.fn); + goto out; + } + if (p.res->asz == 0 && p.res->ipsz == 0) { + warnx("%s: missing IP or AS resources", p.fn); + goto out; + } + break; + case CERT_PURPOSE_BGPSEC_ROUTER: + p.res->bgpsec_pubkey = x509_get_bgpsec_pubkey(x, p.fn); + if (p.res->bgpsec_pubkey == NULL) { + warnx("%s: x509_get_bgpsec_pubkey failed", p.fn); + goto out; + } + if (p.res->ipsz > 0) { + warnx("%s: unexpected IP resources in BGPsec cert", p.fn); + goto out; + } + if (sia_present) { + warnx("%s: unexpected SIA extension in BGPsec cert", p.fn); + goto out; + } + break; + default: + warnx("%s: x509_get_purpose failed in %s", p.fn, __func__); + goto out; + } + if (p.res->ski == NULL) { warnx("%s: RFC 6487 section 8.4.2: missing SKI", p.fn); goto out; @@ -1105,29 +1137,6 @@ cert_parse_inner(X509 **xp, const char *fn, int ta) goto out; } - if (p.res->asz == 0 && p.res->ipsz == 0) { - warnx("%s: RFC 6487 section 4.8.10 and 4.8.11: " - "missing IP or AS resources", p.fn); - goto out; - } - - if (p.res->ipsz > 0 && - p.res->purpose == CERT_PURPOSE_BGPSEC_ROUTER) { - warnx("%s: BGPsec Router Certificate must not have RFC 3779 IP " - "Addresses", p.fn); - goto out; - } - - if (p.res->purpose == CERT_PURPOSE_BGPSEC_ROUTER && sia_present) { - warnx("%s: BGPsec Router Certificate must not have SIA", p.fn); - goto out; - } - - if (p.res->purpose == CERT_PURPOSE_CA && p.res->mft == NULL) { - warnx("%s: RFC 6487 section 4.8.8: missing SIA", p.fn); - goto out; - } - if (X509_up_ref(x) == 0) errx(1, "%s: X509_up_ref failed", __func__); @@ -1207,6 +1216,8 @@ cert_free(struct cert *p) free(p->aia); free(p->aki); free(p->ski); + free(p->tal); + free(p->bgpsec_pubkey); X509_free(p->x509); free(p); } @@ -1249,6 +1260,7 @@ cert_buffer(struct ibuf *b, const struct cert *p) size_t i; io_simple_buffer(b, &p->valid, sizeof(int)); + io_simple_buffer(b, &p->expires, sizeof(time_t)); io_simple_buffer(b, &p->purpose, sizeof(enum cert_purpose)); io_simple_buffer(b, &p->ipsz, sizeof(size_t)); for (i = 0; i < p->ipsz; i++) @@ -1264,6 +1276,8 @@ cert_buffer(struct ibuf *b, const struct cert *p) io_str_buffer(b, p->aia); io_str_buffer(b, p->aki); io_str_buffer(b, p->ski); + io_str_buffer(b, p->tal); + io_str_buffer(b, p->bgpsec_pubkey); } static void @@ -1311,6 +1325,7 @@ cert_read(int fd) err(1, NULL); io_simple_read(fd, &p->valid, sizeof(int)); + io_simple_read(fd, &p->expires, sizeof(time_t)); io_simple_read(fd, &p->purpose, sizeof(enum cert_purpose)); io_simple_read(fd, &p->ipsz, sizeof(size_t)); p->ips = calloc(p->ipsz, sizeof(struct cert_ip)); @@ -1335,6 +1350,8 @@ cert_read(int fd) io_str_read(fd, &p->aki); io_str_read(fd, &p->ski); assert(p->ski); + io_str_read(fd, &p->tal); + io_str_read(fd, &p->bgpsec_pubkey); return p; } @@ -1359,3 +1376,78 @@ authcmp(struct auth *a, struct auth *b) } RB_GENERATE(auth_tree, auth, entry, authcmp); + +/* + * Extract + */ +static void +insert_brk(struct brk_tree *tree, struct cert *cert, int asid) +{ + struct brk *b, *found; + + if ((b = calloc(1, sizeof(*b))) == NULL) + err(1, NULL); + + b->asid = asid; + b->expires = cert->expires; + if ((b->key = strdup(cert->bgpsec_pubkey)) == NULL) + err(1, NULL); + if ((b->tal = strdup(cert->tal)) == NULL) + err(1, NULL); + + /* + * Check if a similar BRK already exists in the tree. If the found BRK + * expires sooner, update it to this BRK's later expiry moment. + */ + if ((found = RB_INSERT(brk_tree, tree, b)) != NULL) { + /* already exists */ + if (found->expires < b->expires) { + /* update found with preferred data */ + found->expires = b->expires; + free(found->tal); + found->tal = b->tal; + b->tal = NULL; + } + free(b->key); + free(b->tal); + free(b); + } +} + +/* + * Add each BGPsec Router Key into the BRK tree. + */ +void +cert_insert_brks(struct brk_tree *tree, struct cert *cert) +{ + size_t i, asid; + + for (i = 0; i < cert->asz; i++) { + switch (cert->as[i].type) { + case CERT_AS_ID: + insert_brk(tree, cert, cert->as[i].id); + break; + case CERT_AS_RANGE: + for (asid = cert->as[i].range.min; + asid <= cert->as[i].range.max; asid++) + insert_brk(tree, cert, asid); + break; + default: + warnx("invalid AS identifier type"); + continue; + } + } +} + +static inline int +brkcmp(struct brk *a, struct brk *b) +{ + if (a->asid > b->asid) + return 1; + if (a->asid < b->asid) + return -1; + + return strcmp(a->key, b->key); +} + +RB_GENERATE(brk_tree, brk, entry, brkcmp); diff --git a/usr.sbin/rpki-client/extern.h b/usr.sbin/rpki-client/extern.h index f1ea862904b..b8cc212950b 100644 --- a/usr.sbin/rpki-client/extern.h +++ b/usr.sbin/rpki-client/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.70 2021/10/10 21:57:43 job Exp $ */ +/* $OpenBSD: extern.h,v 1.71 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -102,7 +102,8 @@ struct cert_ip { }; enum cert_purpose { - CERT_PURPOSE_CA = 1, + CERT_PURPOSE_INVALID, + CERT_PURPOSE_CA, CERT_PURPOSE_BGPSEC_ROUTER }; @@ -124,7 +125,9 @@ struct cert { char *aia; /* AIA (or NULL, for trust anchor) */ char *aki; /* AKI (or NULL, for trust anchor) */ char *ski; /* SKI */ + char *tal; /* basename of TAL for this cert */ enum cert_purpose purpose; /* Certificate Purpose (BGPSec or CA) */ + char *bgpsec_pubkey; /* BGPsec Router Key */ int valid; /* validated resources */ X509 *x509; /* the cert */ time_t expires; /* do not use after */ @@ -226,6 +229,22 @@ struct vrp { RB_HEAD(vrp_tree, vrp); RB_PROTOTYPE(vrp_tree, vrp, entry, vrpcmp); +/* + * A single BGPsec Router Key (including ASID) + */ +struct brk { + RB_ENTRY(brk) entry; + uint32_t asid; + char *tal; /* basename of TAL for this key */ + uint8_t *key; /* raw P-256 ECDSA public key */ + time_t expires; /* transitive expiry moment */ +}; +/* + * Tree of BRK sorted by asid + */ +RB_HEAD(brk_tree, brk); +RB_PROTOTYPE(brk_tree, brk, entry, brkcmp); + /* * A single CRL */ @@ -359,8 +378,8 @@ struct stats { size_t uniqs; /* number of unique vrps */ size_t del_files; /* number of files removed in cleanup */ size_t del_dirs; /* number of directories removed in cleanup */ - size_t bgpsec_routers; /* number of BGPsec Router certs */ - size_t bgpsec_invalids; /* invalid bgpsec router certs */ + size_t brks; /* number of BGPsec Router Key (BRK) certificates */ + size_t brks_invalids; /* invalid BGPsec certs */ char *talnames; struct timeval elapsed_time; struct timeval user_time; @@ -385,6 +404,7 @@ void cert_free(struct cert *); struct cert *cert_parse(X509 **, const char *); struct cert *ta_parse(X509 **, const char *, const unsigned char *, size_t); struct cert *cert_read(int); +void cert_insert_brks(struct brk_tree *, struct cert *); void mft_buffer(struct ibuf *, const struct mft *); void mft_free(struct mft *); @@ -525,13 +545,13 @@ int io_recvfd(int, void *, size_t); /* X509 helpers. */ -char *hex_encode(const unsigned char *, size_t); char *x509_get_aia(X509 *, const char *); char *x509_get_aki(X509 *, int, const char *); char *x509_get_ski(X509 *, const char *); time_t x509_get_expire(X509 *, const char *); char *x509_get_crl(X509 *, const char *); char *x509_crl_get_aki(X509_CRL *, const char *); +char *x509_get_bgpsec_pubkey(X509 *, const char *); enum cert_purpose x509_get_purpose(X509 *, const char *); /* Output! */ @@ -542,14 +562,21 @@ extern int outformats; #define FORMAT_CSV 0x04 #define FORMAT_JSON 0x08 -int outputfiles(struct vrp_tree *v, struct stats *); +int outputfiles(struct vrp_tree *v, struct brk_tree *b, + struct stats *); int outputheader(FILE *, struct stats *); -int output_bgpd(FILE *, struct vrp_tree *, struct stats *); -int output_bird1v4(FILE *, struct vrp_tree *, struct stats *); -int output_bird1v6(FILE *, struct vrp_tree *, struct stats *); -int output_bird2(FILE *, struct vrp_tree *, struct stats *); -int output_csv(FILE *, struct vrp_tree *, struct stats *); -int output_json(FILE *, struct vrp_tree *, struct stats *); +int output_bgpd(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); +int output_bird1v4(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); +int output_bird1v6(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); +int output_bird2(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); +int output_csv(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); +int output_json(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); void logx(const char *fmt, ...) __attribute__((format(printf, 1, 2))); diff --git a/usr.sbin/rpki-client/main.c b/usr.sbin/rpki-client/main.c index 303adfd9254..bac50257ae9 100644 --- a/usr.sbin/rpki-client/main.c +++ b/usr.sbin/rpki-client/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.148 2021/10/10 22:04:33 job Exp $ */ +/* $OpenBSD: main.c,v 1.149 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -465,9 +465,10 @@ queue_add_from_cert(const struct cert *cert) * In all cases, we gather statistics. */ static void -entity_process(int proc, struct stats *st, struct vrp_tree *tree) +entity_process(int proc, struct stats *st, struct vrp_tree *tree, + struct brk_tree *brktree) { - enum rtype type; + enum rtype type; struct tal *tal; struct cert *cert; struct mft *mft; @@ -509,10 +510,11 @@ entity_process(int proc, struct stats *st, struct vrp_tree *tree) } else st->certs_invalid++; } else if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) { - if (cert->valid) - st->bgpsec_routers++; - else - st->bgpsec_invalids++; + if (cert->valid) { + cert_insert_brks(brktree, cert); + st->brks++; + } else + st->brks_invalids++; } else st->certs_invalid++; cert_free(cert); @@ -637,6 +639,7 @@ main(int argc, char *argv[]) const char *cachedir = NULL, *outputdir = NULL; const char *tals[TALSZ_MAX], *errs, *name; struct vrp_tree v = RB_INITIALIZER(&v); + struct brk_tree b = RB_INITIALIZER(&b); struct rusage ru; struct timeval start_time, now_time; @@ -1076,7 +1079,7 @@ main(int argc, char *argv[]) */ if ((pfd[0].revents & POLLIN)) { - entity_process(proc, &stats, &v); + entity_process(proc, &stats, &v, &b); } } @@ -1152,7 +1155,7 @@ main(int argc, char *argv[]) if (fchdir(outdirfd) == -1) err(1, "fchdir output dir"); - if (outputfiles(&v, &stats)) + if (outputfiles(&v, &b, &stats)) rc = 1; @@ -1166,7 +1169,7 @@ main(int argc, char *argv[]) logx("Certificate revocation lists: %zu", stats.crls); logx("Ghostbuster records: %zu", stats.gbrs); logx("BGPsec Router Certificates: %zu (%zu invalid)", - stats.bgpsec_routers, stats.bgpsec_invalids); + stats.brks, stats.brks_invalids); logx("Repositories: %zu", stats.repos); logx("Cleanup: removed %zu files, %zu directories", stats.del_files, stats.del_dirs); diff --git a/usr.sbin/rpki-client/output-bgpd.c b/usr.sbin/rpki-client/output-bgpd.c index 86ee1027559..ff033142398 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.22 2021/09/01 15:21:10 job Exp $ */ +/* $OpenBSD: output-bgpd.c,v 1.23 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -20,7 +20,8 @@ #include "extern.h" int -output_bgpd(FILE *out, struct vrp_tree *vrps, struct stats *st) +output_bgpd(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, + struct stats *st) { struct vrp *v; diff --git a/usr.sbin/rpki-client/output-bird.c b/usr.sbin/rpki-client/output-bird.c index 217f73e5bd2..a82c49fa801 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.11 2021/04/19 17:04:35 deraadt Exp $ */ +/* $OpenBSD: output-bird.c,v 1.12 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * Copyright (c) 2020 Robert Scheck @@ -21,7 +21,8 @@ #include "extern.h" int -output_bird1v4(FILE *out, struct vrp_tree *vrps, struct stats *st) +output_bird1v4(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, + struct stats *st) { extern const char *bird_tablename; struct vrp *v; @@ -49,7 +50,8 @@ output_bird1v4(FILE *out, struct vrp_tree *vrps, struct stats *st) } int -output_bird1v6(FILE *out, struct vrp_tree *vrps, struct stats *st) +output_bird1v6(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, + struct stats *st) { extern const char *bird_tablename; struct vrp *v; @@ -77,7 +79,8 @@ output_bird1v6(FILE *out, struct vrp_tree *vrps, struct stats *st) } int -output_bird2(FILE *out, struct vrp_tree *vrps, struct stats *st) +output_bird2(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, + 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 971672bd217..e8c23217f97 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.10 2021/05/06 17:03:57 job Exp $ */ +/* $OpenBSD: output-csv.c,v 1.11 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * @@ -20,7 +20,8 @@ #include "extern.h" int -output_csv(FILE *out, struct vrp_tree *vrps, struct stats *st) +output_csv(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, + 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 d1cc32ba712..e226231c0de 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.17 2021/05/06 17:03:57 job Exp $ */ +/* $OpenBSD: output-json.c,v 1.18 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * @@ -46,6 +46,8 @@ outputheader_json(FILE *out, struct stats *st) "\t\t\"roas\": %zu,\n" "\t\t\"failedroas\": %zu,\n" "\t\t\"invalidroas\": %zu,\n" + "\t\t\"bgpsec_router_keys\": %zu,\n" + "\t\t\"invalidbgpsec_router_keys\": %zu,\n" "\t\t\"certificates\": %zu,\n" "\t\t\"failcertificates\": %zu,\n" "\t\t\"invalidcertificates\": %zu,\n" @@ -65,6 +67,7 @@ outputheader_json(FILE *out, struct stats *st) hn, tbuf, (long long)st->elapsed_time.tv_sec, (long long)st->user_time.tv_sec, (long long)st->system_time.tv_sec, st->roas, st->roas_fail, st->roas_invalid, + st->brks, st->brks_invalids, st->certs, st->certs_fail, st->certs_invalid, st->tals, st->talnames, st->mfts, st->mfts_fail, st->mfts_stale, @@ -78,10 +81,12 @@ outputheader_json(FILE *out, struct stats *st) } int -output_json(FILE *out, struct vrp_tree *vrps, struct stats *st) +output_json(FILE *out, struct vrp_tree *vrps, struct brk_tree *brks, + struct stats *st) { char buf[64]; struct vrp *v; + struct brk *b; int first = 1; if (outputheader_json(out, st) < 0) @@ -91,12 +96,11 @@ output_json(FILE *out, struct vrp_tree *vrps, struct stats *st) return -1; RB_FOREACH(v, vrp_tree, vrps) { - if (first) - first = 0; - else { + if (!first) { if (fprintf(out, ",\n") < 0) return -1; } + first = 0; ip_addr_print(&v->addr, v->afi, buf, sizeof(buf)); @@ -107,6 +111,23 @@ output_json(FILE *out, struct vrp_tree *vrps, struct stats *st) return -1; } + if (fprintf(out, "\n\t],\n\n\t\"bgpsec_keys\": [\n") < 0) + return -1; + + first = 1; + RB_FOREACH(b, brk_tree, brks) { + if (!first) { + if (fprintf(out, ",\n") < 0) + return -1; + } + first = 0; + + if (fprintf(out, "\t\t{ \"asn\": %u, \"key\": \"%s\", \"ta\": " + "\"%s\", \"expires\": %lld }", b->asid, b->key, b->tal, + (long long)b->expires) < 0) + return -1; + } + if (fprintf(out, "\n\t]\n}\n") < 0) return -1; return 0; diff --git a/usr.sbin/rpki-client/output.c b/usr.sbin/rpki-client/output.c index 40a75812d61..5e4c64d04ab 100644 --- a/usr.sbin/rpki-client/output.c +++ b/usr.sbin/rpki-client/output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: output.c,v 1.21 2021/03/02 09:08:59 claudio Exp $ */ +/* $OpenBSD: output.c,v 1.22 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Theo de Raadt * @@ -64,7 +64,8 @@ static char output_name[PATH_MAX]; static const struct outputs { int format; char *name; - int (*fn)(FILE *, struct vrp_tree *, struct stats *); + int (*fn)(FILE *, struct vrp_tree *, struct brk_tree *, + struct stats *); } outputs[] = { { FORMAT_OPENBGPD, "openbgpd", output_bgpd }, { FORMAT_BIRD, "bird1v4", output_bird1v4 }, @@ -82,7 +83,7 @@ static void sig_handler(int); static void set_signal_handler(void); int -outputfiles(struct vrp_tree *v, struct stats *st) +outputfiles(struct vrp_tree *v, struct brk_tree *b, struct stats *st) { int i, rc = 0; @@ -101,7 +102,7 @@ outputfiles(struct vrp_tree *v, struct stats *st) rc = 1; continue; } - if ((*outputs[i].fn)(fout, v, st) != 0) { + if ((*outputs[i].fn)(fout, v, b, st) != 0) { warn("output for %s format failed", outputs[i].name); fclose(fout); output_cleantmp(); @@ -212,6 +213,7 @@ outputheader(FILE *out, struct stats *st) "# Generated on host %s at %s\n" "# Processing time %lld seconds (%lld seconds user, %lld seconds system)\n" "# Route Origin Authorizations: %zu (%zu failed parse, %zu invalid)\n" + "# BGPsec Router Certificates: %zu (%zu invalid)\n" "# Certificates: %zu (%zu failed parse, %zu invalid)\n" "# Trust Anchor Locators: %zu (%s)\n" "# Manifests: %zu (%zu failed parse, %zu stale)\n" @@ -222,6 +224,7 @@ outputheader(FILE *out, struct stats *st) hn, tbuf, (long long)st->elapsed_time.tv_sec, (long long)st->user_time.tv_sec, (long long)st->system_time.tv_sec, st->roas, st->roas_fail, st->roas_invalid, + st->brks, st->brks_invalids, st->certs, st->certs_fail, st->certs_invalid, st->tals, st->talnames, st->mfts, st->mfts_fail, st->mfts_stale, diff --git a/usr.sbin/rpki-client/parser.c b/usr.sbin/rpki-client/parser.c index cbb7a717473..cecec5f6c7c 100644 --- a/usr.sbin/rpki-client/parser.c +++ b/usr.sbin/rpki-client/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.12 2021/10/07 08:36:17 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.13 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Claudio Jeker * Copyright (c) 2019 Kristaps Dzonsons @@ -194,7 +194,6 @@ proc_parser_cert(const struct entity *entp, X509_STORE_CTX *ctx, X509 *x509; int c; struct auth *a = NULL, *na; - char *tal; STACK_OF(X509) *chain; STACK_OF(X509_CRL) *crls; @@ -252,11 +251,13 @@ proc_parser_cert(const struct entity *entp, X509_STORE_CTX *ctx, if (na == NULL) err(1, NULL); - tal = a->tal; + cert->tal = strdup(a->tal); + if (cert->tal == NULL) + err(1, NULL); na->parent = a; na->cert = cert; - na->tal = tal; + na->tal = a->tal; na->fn = strdup(entp->file); if (na->fn == NULL) err(1, NULL); diff --git a/usr.sbin/rpki-client/rpki-client.8 b/usr.sbin/rpki-client/rpki-client.8 index 4d25fd72fe2..2c3291043c2 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.47 2021/09/01 08:17:37 claudio Exp $ +.\" $OpenBSD: rpki-client.8,v 1.48 2021/10/11 16:50:03 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: September 1 2021 $ +.Dd $Mdocdate: October 11 2021 $ .Dt RPKI-CLIENT 8 .Os .Sh NAME @@ -237,6 +237,9 @@ The Resource Public Key Infrastructure (RPKI) Ghostbusters Record. Resource Public Key Infrastructure (RPKI) Trust Anchor Locator. .It RFC 8182 The RPKI Repository Delta Protocol (RRDP). +.It RFC 8209 +A Profile for BGPsec Router Certificates, Certificate Revocation Lists, and +Certification Requests. .El .\" .Sh HISTORY .Sh AUTHORS diff --git a/usr.sbin/rpki-client/validate.c b/usr.sbin/rpki-client/validate.c index d0a4e7c1efa..d249ba9125e 100644 --- a/usr.sbin/rpki-client/validate.c +++ b/usr.sbin/rpki-client/validate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: validate.c,v 1.15 2021/08/16 10:38:57 jsg Exp $ */ +/* $OpenBSD: validate.c,v 1.16 2021/10/11 16:50:03 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -163,8 +163,11 @@ valid_cert(const char *fn, struct auth_tree *auths, const struct cert *cert) return 0; for (i = 0; i < cert->asz; i++) { - if (cert->as[i].type == CERT_AS_INHERIT) + if (cert->as[i].type == CERT_AS_INHERIT) { + if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) + return 0; /* BGPsec doesn't permit inheriting */ continue; + } min = cert->as[i].type == CERT_AS_ID ? cert->as[i].id : cert->as[i].range.min; max = cert->as[i].type == CERT_AS_ID ? diff --git a/usr.sbin/rpki-client/x509.c b/usr.sbin/rpki-client/x509.c index 4e27686ac4c..5ba36fce4c5 100644 --- a/usr.sbin/rpki-client/x509.c +++ b/usr.sbin/rpki-client/x509.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509.c,v 1.23 2021/10/07 08:30:39 claudio Exp $ */ +/* $OpenBSD: x509.c,v 1.24 2021/10/11 16:50:04 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -24,6 +24,7 @@ #include #include +#include #include #include "extern.h" @@ -88,6 +89,7 @@ x509_get_aki(X509 *x, int ta, const char *fn) } res = hex_encode(d, dsz); + out: AUTHORITY_KEYID_free(akid); return res; @@ -177,6 +179,64 @@ x509_get_purpose(X509 *x, const char *fn) return purpose; } +/* + * Extract ECDSA key from a BGPsec Router Certificate. + * Returns NULL on failure, on success return public key, + */ +char * +x509_get_bgpsec_pubkey(X509 *x, const char *fn) +{ + EVP_PKEY *pubkey; + EC_KEY *ec; + int nid; + const char *cname; + int keylen; + uint8_t *key = NULL; + char *res = NULL; + + pubkey = X509_get0_pubkey(x); + if (pubkey == NULL) { + warnx("%s: X509_get_pubkey failed in %s", fn, __func__); + goto out; + } + if (EVP_PKEY_base_id(pubkey) != EVP_PKEY_EC) { + warnx("%s: Expected EVP_PKEY_EC, got %d", fn, + EVP_PKEY_base_id(pubkey)); + goto out; + } + + ec = EVP_PKEY_get0_EC_KEY(pubkey); + if (ec == NULL) { + warnx("%s: Incorrect key type", fn); + goto out; + } + + nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + if (nid != NID_X9_62_prime256v1) { + if ((cname = EC_curve_nid2nist(nid)) == NULL) + cname = OBJ_nid2sn(nid); + warnx("%s: Expected P-256, got %s", fn, cname); + goto out; + } + + if (!EC_KEY_check_key(ec)) { + warnx("%s: EC_KEY_check_key failed in %s", fn, __func__); + goto out; + } + + keylen = i2o_ECPublicKey(ec, &key); + if (keylen <= 0) { + warnx("%s: i2o_ECPublicKey failed in %s", fn, __func__); + goto out; + } + + if (base64_encode(key, keylen, &res) == -1) + errx(1, "base64_encode failed in %s", __func__); + + out: + free(key); + return res; +} /* * Parse the Authority Information Access (AIA) extension