On Mon, Dec 07, 2020 at 09:55:06PM +0000, Job Snijders wrote:
> On Mon, Dec 07, 2020 at 10:03:57PM +0100, Claudio Jeker wrote:
> > So ghostbuster records (rfc6493) are showing up in RPKI datasets and
> > instead of silently ignoring them rpki-client should first of all validate
> > the file hash in the manifest and later on also validate the CMS data.
> >
> > This first diff does ensure that all files in an mft are present and have
> > a valid hash (no matter if rpki-client is able to handle them or not).
> > While enquing the work for the parser issue a warning for files that are
> > currently not known (anything else than .crl, .cer and .roa).
> >
> > Seems to work for me :)
>
> OK job@
Actually here is a diff that does the full verify of .gbr files (or at
least the one that is currently in rpki)
This diff does currently nothing with the provided VCARD.
--
:wq Claudio
Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/Makefile,v
retrieving revision 1.14
diff -u -p -r1.14 Makefile
--- Makefile 4 Dec 2019 12:40:17 -0000 1.14
+++ Makefile 7 Dec 2020 21:39:04 -0000
@@ -1,7 +1,7 @@
# $OpenBSD: Makefile,v 1.14 2019/12/04 12:40:17 deraadt Exp $
PROG= rpki-client
-SRCS= as.c cert.c cms.c crl.c io.c ip.c log.c main.c mft.c output.c \
+SRCS= as.c cert.c cms.c crl.c gbr.c io.c ip.c log.c main.c mft.c output.c \
output-bgpd.c output-bird.c output-csv.c output-json.c \
roa.c rsync.c tal.c validate.c x509.c
MAN= rpki-client.8
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/extern.h,v
retrieving revision 1.35
diff -u -p -r1.35 extern.h
--- extern.h 2 Dec 2020 15:31:15 -0000 1.35
+++ extern.h 7 Dec 2020 21:45:36 -0000
@@ -185,6 +185,15 @@ struct roa {
};
/*
+ * A single Ghostbuster record
+ */
+struct gbr {
+ char *vcard;
+ char *ski; /* SKI */
+ char *aki; /* AKI */
+};
+
+/*
* A single VRP element (including ASID)
*/
struct vrp {
@@ -245,7 +254,8 @@ enum rtype {
RTYPE_MFT,
RTYPE_ROA,
RTYPE_CER,
- RTYPE_CRL
+ RTYPE_CRL,
+ RTYPE_GBR,
};
/*
@@ -264,6 +274,7 @@ struct stats {
size_t roas_invalid; /* invalid resources */
size_t repos; /* repositories */
size_t crls; /* revocation lists */
+ size_t gbrs; /* ghostbuster records */
size_t vrps; /* total number of vrps */
size_t uniqs; /* number of unique vrps */
size_t del_files; /* number of files removed in cleanup */
@@ -302,6 +313,9 @@ struct roa *roa_parse(X509 **, const cha
struct roa *roa_read(int);
void roa_insert_vrps(struct vrp_tree *, struct roa *, size_t *,
size_t *);
+
+void gbr_free(struct gbr *);
+struct gbr *gbr_parse(X509 **, const char *);
/* crl.c */
X509_CRL *crl_parse(const char *, const unsigned char *);
Index: gbr.c
===================================================================
RCS file: gbr.c
diff -N gbr.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gbr.c 7 Dec 2020 22:11:26 -0000
@@ -0,0 +1,88 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2020 Claudio Jeker <[email protected]>
+ *
+ * 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 <stdarg.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+/*
+ * Parse results and data of the manifest file.
+ */
+struct parse {
+ const char *fn; /* manifest file name */
+ struct gbr *res; /* results */
+};
+
+/*
+ * Parse a full RFC 6493 file and signed by the certificate "cacert"
+ * (the latter is optional and may be passed as NULL to disable).
+ * Returns the payload or NULL if the document was malformed.
+ */
+struct gbr *
+gbr_parse(X509 **x509, const char *fn)
+{
+ struct parse p;
+ size_t cmsz;
+ unsigned char *cms;
+
+ memset(&p, 0, sizeof(struct parse));
+ p.fn = fn;
+
+ /* OID from section 9.1, RFC 6493. */
+
+ cms = cms_parse_validate(x509, fn,
+ "1.2.840.113549.1.9.16.1.35", NULL, &cmsz);
+ if (cms == NULL)
+ return NULL;
+
+ if ((p.res = calloc(1, sizeof(struct roa))) == NULL)
+ err(1, NULL);
+ if ((p.res->vcard = strndup(cms, cmsz)) == NULL)
+ err(1, NULL);
+ if (!x509_get_ski_aki(*x509, fn, &p.res->ski, &p.res->aki)) {
+ gbr_free(p.res);
+ X509_free(*x509);
+ *x509 = NULL;
+ return NULL;
+ }
+
+ free(cms);
+ return p.res;
+}
+
+/*
+ * Free an GBR pointer.
+ * Safe to call with NULL.
+ */
+void
+gbr_free(struct gbr *p)
+{
+
+ if (p == NULL)
+ return;
+ free(p->aki);
+ free(p->ski);
+ free(p->vcard);
+ free(p);
+}
Index: main.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/main.c,v
retrieving revision 1.85
diff -u -p -r1.85 main.c
--- main.c 2 Dec 2020 15:31:15 -0000 1.85
+++ main.c 7 Dec 2020 21:57:17 -0000
@@ -518,6 +518,27 @@ queue_add_from_mft_set(int fd, struct en
continue;
queue_add_from_mft(fd, q, mft->file, f, RTYPE_ROA, eid);
}
+
+ for (i = 0; i < mft->filesz; i++) {
+ f = &mft->files[i];
+ sz = strlen(f->file);
+ assert(sz > 4);
+ if (strcasecmp(f->file + sz - 4, ".gbr"))
+ continue;
+ queue_add_from_mft(fd, q, mft->file, f, RTYPE_GBR, eid);
+ }
+
+ for (i = 0; i < mft->filesz; i++) {
+ f = &mft->files[i];
+ sz = strlen(f->file);
+ assert(sz > 4);
+ if (strcasecmp(f->file + sz - 4, ".crl") == 0 ||
+ strcasecmp(f->file + sz - 4, ".cer") == 0 ||
+ strcasecmp(f->file + sz - 4, ".roa") == 0 ||
+ strcasecmp(f->file + sz - 4, ".gbr") == 0)
+ continue;
+ logx("%s: unsupported file type: %s", mft->file, f->file);
+ }
}
/*
@@ -937,6 +958,49 @@ proc_parser_crl(struct entity *entp, X50
}
}
+/*
+ * Parse a ghostbuster record
+ */
+static void
+proc_parser_gbr(struct entity *entp, X509_STORE *store,
+ X509_STORE_CTX *ctx, struct auth_tree *auths, struct crl_tree *crlt)
+{
+ struct gbr *gbr;
+ X509 *x509;
+ int c;
+ struct auth *a;
+ STACK_OF(X509) *chain;
+ STACK_OF(X509_CRL) *crls;
+
+ if ((gbr = gbr_parse(&x509, entp->uri)) == NULL)
+ return;
+
+ a = valid_ski_aki(entp->uri, auths, gbr->ski, gbr->aki);
+
+ build_chain(a, &chain);
+ build_crls(a, crlt, &crls);
+
+ assert(x509 != NULL);
+ if (!X509_STORE_CTX_init(ctx, store, x509, chain))
+ cryptoerrx("X509_STORE_CTX_init");
+ X509_STORE_CTX_set_flags(ctx,
+ X509_V_FLAG_IGNORE_CRITICAL | X509_V_FLAG_CRL_CHECK);
+ X509_STORE_CTX_set0_crls(ctx, crls);
+
+ if (X509_verify_cert(ctx) <= 0) {
+ c = X509_STORE_CTX_get_error(ctx);
+ if (verbose > 0 || c != X509_V_ERR_UNABLE_TO_GET_CRL)
+ warnx("%s: %s", entp->uri,
+ X509_verify_cert_error_string(c));
+ }
+
+ X509_STORE_CTX_cleanup(ctx);
+ sk_X509_free(chain);
+ sk_X509_CRL_free(crls);
+ X509_free(x509);
+ gbr_free(gbr);
+}
+
/* use the parent (id) to walk the tree to the root and
build a certificate chain from cert->x509 */
static void
@@ -1129,6 +1193,9 @@ proc_parser(int fd)
roa_buffer(&b, &bsz, &bmax, roa);
roa_free(roa);
break;
+ case RTYPE_GBR:
+ proc_parser_gbr(entp, store, ctx, &auths, &crlt);
+ break;
default:
abort();
}
@@ -1236,6 +1303,9 @@ entity_process(int proc, int rsync, stru
st->roas_invalid++;
roa_free(roa);
break;
+ case RTYPE_GBR:
+ st->gbrs++;
+ break;
default:
abort();
}
@@ -1696,6 +1766,7 @@ main(int argc, char *argv[])
logx("Manifests: %zu (%zu failed parse, %zu stale)",
stats.mfts, stats.mfts_fail, stats.mfts_stale);
logx("Certificate revocation lists: %zu", stats.crls);
+ logx("Ghostbuster records: %zu", stats.gbrs);
logx("Repositories: %zu", stats.repos);
logx("Files removed: %zu", stats.del_files);
logx("VRP Entries: %zu (%zu unique)", stats.vrps, stats.uniqs);
Index: mft.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/mft.c,v
retrieving revision 1.19
diff -u -p -r1.19 mft.c
--- mft.c 6 Nov 2020 04:22:18 -0000 1.19
+++ mft.c 7 Dec 2020 20:47:53 -0000
@@ -171,16 +171,6 @@ mft_parse_filehash(struct parse *p, cons
goto out;
}
- if (strcasecmp(fn + sz - 4, ".roa") &&
- strcasecmp(fn + sz - 4, ".crl") &&
- strcasecmp(fn + sz - 4, ".cer")) {
- /* ignore unknown files */
- free(fn);
- fn = NULL;
- rc = 1;
- goto out;
- }
-
/* Now hash value. */
hash = sk_ASN1_TYPE_value(seq, 1);
Index: output-json.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/output-json.c,v
retrieving revision 1.13
diff -u -p -r1.13 output-json.c
--- output-json.c 12 Sep 2020 15:46:48 -0000 1.13
+++ output-json.c 7 Dec 2020 21:57:46 -0000
@@ -55,6 +55,7 @@ outputheader_json(FILE *out, struct stat
"\t\t\"failedmanifests\": %zu,\n"
"\t\t\"stalemanifests\": %zu,\n"
"\t\t\"crls\": %zu,\n"
+ "\t\t\"gbrs\": %zu,\n"
"\t\t\"repositories\": %zu,\n"
"\t\t\"vrps\": %zu,\n"
"\t\t\"uniquevrps\": %zu\n"
@@ -66,6 +67,7 @@ outputheader_json(FILE *out, struct stat
st->tals, st->talnames,
st->mfts, st->mfts_fail, st->mfts_stale,
st->crls,
+ st->gbrs,
st->repos,
st->vrps, st->uniqs) < 0)
return -1;
Index: output.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/output.c,v
retrieving revision 1.18
diff -u -p -r1.18 output.c
--- output.c 6 Nov 2020 05:42:43 -0000 1.18
+++ output.c 7 Dec 2020 21:46:16 -0000
@@ -191,6 +191,7 @@ outputheader(FILE *out, struct stats *st
"# Trust Anchor Locators: %zu (%s)\n"
"# Manifests: %zu (%zu failed parse, %zu stale)\n"
"# Certificate revocation lists: %zu\n"
+ "# Ghostbuster records: %zu\n"
"# Repositories: %zu\n"
"# VRP Entries: %zu (%zu unique)\n",
hn, tbuf, (long long)st->elapsed_time.tv_sec,
@@ -200,6 +201,7 @@ outputheader(FILE *out, struct stats *st
st->tals, st->talnames,
st->mfts, st->mfts_fail, st->mfts_stale,
st->crls,
+ st->gbrs,
st->repos,
st->vrps, st->uniqs) < 0)
return -1;