Add support for BGPsec Router Certificates (RFC 8209)
authorjob <job@openbsd.org>
Mon, 11 Oct 2021 16:50:03 +0000 (16:50 +0000)
committerjob <job@openbsd.org>
Mon, 11 Oct 2021 16:50:03 +0000 (16:50 +0000)
BGPsec router keys are extracted from RPKI certificates and
emitted via the JSON output in base64 encoded form.

OK tb@ claudio@

12 files changed:
usr.sbin/rpki-client/cert.c
usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/main.c
usr.sbin/rpki-client/output-bgpd.c
usr.sbin/rpki-client/output-bird.c
usr.sbin/rpki-client/output-csv.c
usr.sbin/rpki-client/output-json.c
usr.sbin/rpki-client/output.c
usr.sbin/rpki-client/parser.c
usr.sbin/rpki-client/rpki-client.8
usr.sbin/rpki-client/validate.c
usr.sbin/rpki-client/x509.c

index 5331d9f..8c17ffa 100644 (file)
@@ -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 <job@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
  * 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);
index f1ea862..b8cc212 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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)));
index 303adfd..bac5025 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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);
index 86ee102..ff03314 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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;
 
index 217f73e..a82c49f 100644 (file)
@@ -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 <claudio@openbsd.org>
  * Copyright (c) 2020 Robert Scheck <robert@fedoraproject.org>
@@ -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;
index 971672b..e8c2321 100644 (file)
@@ -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 <claudio@openbsd.org>
  *
@@ -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;
 
index d1cc32b..e226231 100644 (file)
@@ -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 <claudio@openbsd.org>
  *
@@ -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;
index 40a7581..5e4c64d 100644 (file)
@@ -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 <deraadt@openbsd.org>
  *
@@ -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,
index cbb7a71..cecec5f 100644 (file)
@@ -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 <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -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);
index 4d25fd7..2c32910 100644 (file)
@@ -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 <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: 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
index d0a4e7c..d249ba9 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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 ?
index 4e27686..5ba36fc 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <openssl/evp.h>
 #include <openssl/x509v3.h>
 
 #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