summaryrefslogtreecommitdiff
path: root/usr.sbin/rpki-client/parser.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2022-04-21 09:53:08 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2022-04-21 09:53:08 +0000
commita75434b1ba7c81b372ae9f902436ec66b0ebcbd1 (patch)
tree4d01d130181a7ccb4e4157db104ba34bc186b23c /usr.sbin/rpki-client/parser.c
parent9c8ec03a5411ec8214e2c60aedcbf1b934e63f9c (diff)
The filemode code is enough different from the regular parser code that it
makes sense to totally split it out. Duplicate proc_parser_cert_validate() and proc_parser_root_cert() for now. The valid_x509() plus the required static functions are moved to validate.c. The crl_tree code moved into crl.c similar to the auth_tree handling in cert.c. All the proc functions are now tagged with __attribute(noreturn) which allows to remove the errx() after them. OK tb@
Diffstat (limited to 'usr.sbin/rpki-client/parser.c')
-rw-r--r--usr.sbin/rpki-client/parser.c597
1 files changed, 10 insertions, 587 deletions
diff --git a/usr.sbin/rpki-client/parser.c b/usr.sbin/rpki-client/parser.c
index d11d044c0fd..f45c5b341ed 100644
--- a/usr.sbin/rpki-client/parser.c
+++ b/usr.sbin/rpki-client/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.71 2022/04/20 15:13:08 job Exp $ */
+/* $OpenBSD: parser.c,v 1.72 2022/04/21 09:53:07 claudio Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -39,18 +39,10 @@
#include "extern.h"
-static void build_chain(const struct auth *, STACK_OF(X509) **);
-static struct crl *get_crl(const struct auth *);
-static void build_crls(const struct crl *, STACK_OF(X509_CRL) **);
-
static X509_STORE_CTX *ctx;
static struct auth_tree auths = RB_INITIALIZER(&auths);
static struct crl_tree crlt = RB_INITIALIZER(&crlt);
-struct tal *talobj[TALSZ_MAX];
-
-extern ASN1_OBJECT *certpol_oid;
-
struct parse_repo {
RB_ENTRY(parse_repo) entry;
char *path;
@@ -129,122 +121,6 @@ parse_filepath(unsigned int repoid, const char *path, const char *file,
}
/*
- * Callback for X509_verify_cert() to handle critical extensions in old
- * LibreSSL libraries or OpenSSL libs without RFC3779 support.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *store_ctx)
-{
- X509 *cert;
- const STACK_OF(X509_EXTENSION) *exts;
- X509_EXTENSION *ext;
- ASN1_OBJECT *obj;
- char *file;
- int depth, error, i, nid;
-
- error = X509_STORE_CTX_get_error(store_ctx);
- depth = X509_STORE_CTX_get_error_depth(store_ctx);
-
- if (error != X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
- return ok;
-
- if ((file = X509_STORE_CTX_get_app_data(store_ctx)) == NULL)
- cryptoerrx("X509_STORE_CTX_get_app_data");
-
- if ((cert = X509_STORE_CTX_get_current_cert(store_ctx)) == NULL) {
- warnx("%s: got no current cert", file);
- return 0;
- }
- if ((exts = X509_get0_extensions(cert)) == NULL) {
- warnx("%s: got no cert extensions", file);
- return 0;
- }
-
- for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
- ext = sk_X509_EXTENSION_value(exts, i);
-
- /* skip over non-critical and known extensions */
- if (!X509_EXTENSION_get_critical(ext))
- continue;
- if (X509_supported_extension(ext))
- continue;
-
- if ((obj = X509_EXTENSION_get_object(ext)) == NULL) {
- warnx("%s: got no extension object", file);
- return 0;
- }
-
- nid = OBJ_obj2nid(obj);
- switch (nid) {
- case NID_sbgp_ipAddrBlock:
- case NID_sbgp_autonomousSysNum:
- continue;
- default:
- warnx("%s: depth %d: unknown extension: nid %d",
- file, depth, nid);
- return 0;
- }
- }
-
- return 1;
-}
-
-/*
- * Validate the X509 certificate. If crl is NULL don't check CRL.
- * Returns 1 for valid certificates, returns 0 if there is a verify error
- */
-static int
-valid_x509(char *file, X509 *x509, struct auth *a, struct crl *crl, int nowarn)
-{
- X509_VERIFY_PARAM *params;
- ASN1_OBJECT *cp_oid;
- STACK_OF(X509) *chain;
- STACK_OF(X509_CRL) *crls = NULL;
- unsigned long flags;
- int c;
-
- build_chain(a, &chain);
- build_crls(crl, &crls);
-
- assert(x509 != NULL);
- if (!X509_STORE_CTX_init(ctx, NULL, x509, NULL))
- cryptoerrx("X509_STORE_CTX_init");
-
- if ((params = X509_STORE_CTX_get0_param(ctx)) == NULL)
- cryptoerrx("X509_STORE_CTX_get0_param");
- if ((cp_oid = OBJ_dup(certpol_oid)) == NULL)
- cryptoerrx("OBJ_dup");
- if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid))
- cryptoerrx("X509_VERIFY_PARAM_add0_policy");
-
- X509_STORE_CTX_set_verify_cb(ctx, verify_cb);
- if (!X509_STORE_CTX_set_app_data(ctx, file))
- cryptoerrx("X509_STORE_CTX_set_app_data");
- flags = X509_V_FLAG_CRL_CHECK;
- flags |= X509_V_FLAG_EXPLICIT_POLICY;
- flags |= X509_V_FLAG_INHIBIT_MAP;
- X509_STORE_CTX_set_flags(ctx, flags);
- X509_STORE_CTX_set_depth(ctx, MAX_CERT_DEPTH);
- X509_STORE_CTX_set0_trusted_stack(ctx, chain);
- X509_STORE_CTX_set0_crls(ctx, crls);
-
- if (X509_verify_cert(ctx) <= 0) {
- c = X509_STORE_CTX_get_error(ctx);
- if (!nowarn || verbose > 1)
- warnx("%s: %s", file, X509_verify_cert_error_string(c));
- X509_STORE_CTX_cleanup(ctx);
- sk_X509_free(chain);
- sk_X509_CRL_free(crls);
- return 0;
- }
-
- X509_STORE_CTX_cleanup(ctx);
- sk_X509_free(chain);
- sk_X509_CRL_free(crls);
- return 1;
-}
-
-/*
* Parse and validate a ROA.
* This is standard stuff.
* Returns the roa on success, NULL on failure.
@@ -261,9 +137,9 @@ proc_parser_roa(char *file, const unsigned char *der, size_t len)
return NULL;
a = valid_ski_aki(file, &auths, roa->ski, roa->aki);
- crl = get_crl(a);
+ crl = crl_get(&crlt, a);
- if (!valid_x509(file, x509, a, crl, 0)) {
+ if (!valid_x509(file, ctx, x509, a, crl, 0)) {
X509_free(x509);
roa_free(roa);
return NULL;
@@ -399,7 +275,7 @@ proc_parser_mft_pre(char *file, const unsigned char *der, size_t len,
*crl = parse_load_crl_from_mft(entp, mft, loc);
a = valid_ski_aki(file, &auths, mft->ski, mft->aki);
- if (!valid_x509(file, x509, a, *crl, 1)) {
+ if (!valid_x509(file, ctx, x509, a, *crl, 1)) {
X509_free(x509);
mft_free(mft);
crl_free(*crl);
@@ -502,7 +378,7 @@ proc_parser_mft(struct entity *entp, struct mft **mp)
}
if (*mp != NULL) {
- if (RB_INSERT(crl_tree, &crlt, crl) != NULL) {
+ if (!crl_insert(&crlt, crl)) {
warnx("%s: duplicate AKI %s", file, crl->aki);
crl_free(crl);
}
@@ -522,9 +398,9 @@ proc_parser_cert_validate(char *file, struct cert *cert)
struct crl *crl;
a = valid_ski_aki(file, &auths, cert->ski, cert->aki);
- crl = get_crl(a);
+ crl = crl_get(&crlt, a);
- if (!valid_x509(file, cert->x509, a, crl, 0)) {
+ if (!valid_x509(file, ctx, cert->x509, a, crl, 0)) {
cert_free(cert);
return NULL;
}
@@ -612,27 +488,6 @@ proc_parser_root_cert(char *file, const unsigned char *der, size_t len,
}
/*
- * Parse a certificate revocation list
- * This simply parses the CRL content itself, optionally validating it
- * within the digest if it comes from a manifest, then adds it to the
- * CRL tree.
- */
-static void
-proc_parser_crl(char *file, const unsigned char *der, size_t len)
-{
- struct crl *crl;
-
- if ((crl = crl_parse(file, der, len)) == NULL)
- return;
-
- if (RB_INSERT(crl_tree, &crlt, crl) != NULL) {
- if (!filemode)
- warnx("%s: duplicate AKI %s", file, crl->aki);
- crl_free(crl);
- }
-}
-
-/*
* Parse a ghostbuster record
*/
static void
@@ -647,69 +502,16 @@ proc_parser_gbr(char *file, const unsigned char *der, size_t len)
return;
a = valid_ski_aki(file, &auths, gbr->ski, gbr->aki);
- crl = get_crl(a);
+ crl = crl_get(&crlt, a);
/* return value can be ignored since nothing happens here */
- valid_x509(file, x509, a, crl, 0);
+ valid_x509(file, ctx, x509, a, crl, 0);
X509_free(x509);
gbr_free(gbr);
}
/*
- * Walk the certificate tree to the root and build a certificate
- * chain from cert->x509. All certs in the tree are validated and
- * can be loaded as trusted stack into the validator.
- */
-static void
-build_chain(const struct auth *a, STACK_OF(X509) **chain)
-{
- *chain = NULL;
-
- if (a == NULL)
- return;
-
- if ((*chain = sk_X509_new_null()) == NULL)
- err(1, "sk_X509_new_null");
- for (; a != NULL; a = a->parent) {
- assert(a->cert->x509 != NULL);
- if (!sk_X509_push(*chain, a->cert->x509))
- errx(1, "sk_X509_push");
- }
-}
-
-/*
- * Find a CRL based on the auth SKI value.
- */
-static struct crl *
-get_crl(const struct auth *a)
-{
- struct crl find;
-
- if (a == NULL)
- return NULL;
- find.aki = a->cert->ski;
- return RB_FIND(crl_tree, &crlt, &find);
-}
-
-/*
- * Add the CRL based on the certs SKI value.
- * No need to insert any other CRL since those were already checked.
- */
-static void
-build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls)
-{
- *crls = NULL;
-
- if (crl == NULL)
- return;
- if ((*crls = sk_X509_CRL_new_null()) == NULL)
- errx(1, "sk_X509_CRL_new_null");
- if (!sk_X509_CRL_push(*crls, crl->x509_crl))
- err(1, "sk_X509_CRL_push");
-}
-
-/*
* Load the file specified by the entity information.
*/
static char *
@@ -837,382 +639,6 @@ parse_entity(struct entityq *q, struct msgbuf *msgq)
}
/*
- * Use the X509 CRL Distribution Points to locate the CRL needed for
- * verification.
- */
-static void
-parse_load_crl(char *uri)
-{
- char *f;
- size_t flen;
-
- if (uri == NULL)
- return;
- if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) {
- warnx("bad CRL distribution point URI %s", uri);
- return;
- }
- uri += strlen("rsync://");
-
- f = load_file(uri, &flen);
- if (f == NULL) {
- warn("parse file %s", uri);
- return;
- }
-
- proc_parser_crl(uri, f, flen);
-
- free(f);
-}
-
-/*
- * Parse the cert pointed at by the AIA URI while doing that also load
- * the CRL of this cert. While the CRL is validated the returned cert
- * is not. The caller needs to make sure it is validated once all
- * necessary certs were loaded. Returns NULL on failure.
- */
-static struct cert *
-parse_load_cert(char *uri)
-{
- struct cert *cert = NULL;
- char *f;
- size_t flen;
-
- if (uri == NULL)
- return NULL;
-
- if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) {
- warnx("bad authority information access URI %s", uri);
- return NULL;
- }
- uri += strlen("rsync://");
-
- f = load_file(uri, &flen);
- if (f == NULL) {
- warn("parse file %s", uri);
- goto done;
- }
-
- cert = cert_parse_pre(uri, f, flen);
- free(f);
-
- if (cert == NULL)
- goto done;
- if (cert->purpose != CERT_PURPOSE_CA) {
- warnx("AIA reference to bgpsec cert %s", uri);
- goto done;
- }
- /* try to load the CRL of this cert */
- parse_load_crl(cert->crl);
-
- return cert;
-
- done:
- cert_free(cert);
- return NULL;
-}
-
-/*
- * Build the certificate chain by using the Authority Information Access.
- * This requires that the TA are already validated and added to the auths
- * tree. Once the TA is located in the chain the chain is validated in
- * reverse order.
- */
-static void
-parse_load_certchain(char *uri)
-{
- struct cert *stack[MAX_CERT_DEPTH];
- char *filestack[MAX_CERT_DEPTH];
- struct cert *cert;
- int i, failed;
-
- for (i = 0; i < MAX_CERT_DEPTH; i++) {
- cert = parse_load_cert(uri);
- if (cert == NULL) {
- warnx("failed to build authority chain");
- return;
- }
- if (auth_find(&auths, cert->ski) != NULL) {
- assert(i == 0);
- cert_free(cert);
- return; /* cert already added */
- }
- stack[i] = cert;
- filestack[i] = uri;
- if (auth_find(&auths, cert->aki) != NULL)
- break; /* found chain to TA */
- uri = cert->aia;
- }
-
- if (i >= MAX_CERT_DEPTH) {
- warnx("authority chain exceeds max depth of %d",
- MAX_CERT_DEPTH);
- for (i = 0; i < MAX_CERT_DEPTH; i++)
- cert_free(stack[i]);
- return;
- }
-
- /* TA found play back the stack and add all certs */
- for (failed = 0; i >= 0; i--) {
- cert = stack[i];
- uri = filestack[i];
-
- if (failed)
- cert_free(cert);
- else if (proc_parser_cert_validate(uri, cert) == NULL)
- failed = 1;
- }
-}
-
-static void
-parse_load_ta(struct tal *tal)
-{
- const char *file;
- char *nfile, *f;
- size_t flen;
-
- /* does not matter which URI, all end with same filename */
- file = strrchr(tal->uri[0], '/');
- assert(file);
-
- if (asprintf(&nfile, "ta/%s%s", tal->descr, file) == -1)
- err(1, NULL);
-
- f = load_file(nfile, &flen);
- if (f == NULL) {
- warn("parse file %s", nfile);
- free(nfile);
- return;
- }
-
- /* if TA is valid it was added as a root which is all we need */
- proc_parser_root_cert(nfile, f, flen, tal->pkey, tal->pkeysz, tal->id);
- free(nfile);
- free(f);
-}
-
-static struct tal *
-find_tal(struct cert *cert)
-{
- EVP_PKEY *pk, *opk;
- struct tal *tal;
- int i;
-
- if ((opk = X509_get0_pubkey(cert->x509)) == NULL)
- return NULL;
-
- for (i = 0; i < TALSZ_MAX; i++) {
- const unsigned char *pkey;
-
- if (talobj[i] == NULL)
- break;
- tal = talobj[i];
- pkey = tal->pkey;
- pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz);
- if (pk == NULL)
- continue;
- if (EVP_PKEY_cmp(pk, opk) == 1) {
- EVP_PKEY_free(pk);
- return tal;
- }
- EVP_PKEY_free(pk);
- }
- return NULL;
-}
-
-/*
- * Parse file passed with -f option.
- */
-static void
-proc_parser_file(char *file, unsigned char *buf, size_t len)
-{
- static int num;
- X509 *x509 = NULL;
- struct cert *cert = NULL;
- struct crl *crl = NULL;
- struct mft *mft = NULL;
- struct roa *roa = NULL;
- struct gbr *gbr = NULL;
- struct tal *tal = NULL;
- char *aia = NULL, *aki = NULL;
- enum rtype type;
- int is_ta = 0;
-
- if (num++ > 0) {
- if (outformats & FORMAT_JSON)
- printf("\n");
- else
- printf("--\n");
- }
-
- if (strncmp(file, "rsync://", strlen("rsync://")) == 0) {
- file += strlen("rsync://");
- buf = load_file(file, &len);
- if (buf == NULL) {
- warn("parse file %s", file);
- return;
- }
- }
-
- if (outformats & FORMAT_JSON)
- printf("{\n\t\"file\": \"%s\",\n", file);
- else
- printf("File: %s\n", file);
-
- type = rtype_from_file_extension(file);
-
- switch (type) {
- case RTYPE_CER:
- cert = cert_parse_pre(file, buf, len);
- if (cert == NULL)
- break;
- is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS;
- if (!is_ta)
- cert = cert_parse(file, cert);
- if (cert == NULL)
- break;
- cert_print(cert);
- aia = cert->aia;
- aki = cert->aki;
- x509 = cert->x509;
- if (X509_up_ref(x509) == 0)
- errx(1, "%s: X509_up_ref failed", __func__);
- break;
- case RTYPE_CRL:
- crl = crl_parse(file, buf, len);
- if (crl == NULL)
- break;
- crl_print(crl);
- break;
- case RTYPE_MFT:
- mft = mft_parse(&x509, file, buf, len);
- if (mft == NULL)
- break;
- mft_print(x509, mft);
- aia = mft->aia;
- aki = mft->aki;
- break;
- case RTYPE_ROA:
- roa = roa_parse(&x509, file, buf, len);
- if (roa == NULL)
- break;
- roa_print(x509, roa);
- aia = roa->aia;
- aki = roa->aki;
- break;
- case RTYPE_GBR:
- gbr = gbr_parse(&x509, file, buf, len);
- if (gbr == NULL)
- break;
- gbr_print(x509, gbr);
- aia = gbr->aia;
- aki = gbr->aki;
- break;
- case RTYPE_TAL:
- tal = tal_parse(file, buf, len);
- if (tal == NULL)
- break;
- tal_print(tal);
- break;
- default:
- printf("%s: unsupported file type\n", file);
- break;
- }
-
- if (outformats & FORMAT_JSON)
- printf("\t\"validation\": \"");
- else
- printf("Validation: ");
-
- if (aia != NULL) {
- struct auth *a;
- struct crl *c;
- char *crl_uri;
-
- x509_get_crl(x509, file, &crl_uri);
- parse_load_crl(crl_uri);
- free(crl_uri);
- if (auth_find(&auths, aki) == NULL)
- parse_load_certchain(aia);
- a = auth_find(&auths, aki);
- c = get_crl(a);
-
- if (valid_x509(file, x509, a, c, 0))
- printf("OK");
- else
- printf("Failed");
- } else if (is_ta) {
- if ((tal = find_tal(cert)) != NULL) {
- cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
- if (cert != NULL)
- printf("OK");
- else
- printf("Failed");
- if (outformats & FORMAT_JSON)
- printf("\",\n\t\"tal\": \"%s", tal->descr);
- else
- printf("\nTAL: %s", tal->descr);
- tal = NULL;
- } else {
- cert_free(cert);
- cert = NULL;
- printf("Failed");
- }
- }
-
- if (outformats & FORMAT_JSON)
- printf("\"\n}");
- else
- printf("\n");
-
- X509_free(x509);
- cert_free(cert);
- crl_free(crl);
- mft_free(mft);
- roa_free(roa);
- gbr_free(gbr);
- tal_free(tal);
-}
-
-/*
- * Process a file request, in general don't send anything back.
- */
-static void
-parse_file(struct entityq *q, struct msgbuf *msgq)
-{
- struct entity *entp;
- struct ibuf *b;
- struct tal *tal;
-
- while ((entp = TAILQ_FIRST(q)) != NULL) {
- TAILQ_REMOVE(q, entp, entries);
-
- switch (entp->type) {
- case RTYPE_FILE:
- proc_parser_file(entp->file, entp->data, entp->datasz);
- break;
- case RTYPE_TAL:
- if ((tal = tal_parse(entp->file, entp->data,
- entp->datasz)) == NULL)
- errx(1, "%s: could not parse tal file",
- entp->file);
- tal->id = entp->talid;
- talobj[tal->id] = tal;
- parse_load_ta(tal);
- break;
- default:
- errx(1, "unhandled entity type %d", entp->type);
- }
-
- b = io_new_buffer();
- io_simple_buffer(b, &entp->type, sizeof(entp->type));
- io_str_buffer(b, entp->file);
- io_close_buffer(msgq, b);
- entity_free(entp);
- }
-}
-
-/*
* Process responsible for parsing and validating content.
* All this process does is wait to be told about a file to parse, then
* it parses it and makes sure that the data being returned is fully
@@ -1288,10 +714,7 @@ proc_parser(int fd)
}
}
- if (!filemode)
- parse_entity(&q, &msgq);
- else
- parse_file(&q, &msgq);
+ parse_entity(&q, &msgq);
}
while ((entp = TAILQ_FIRST(&q)) != NULL) {