diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2022-04-21 09:53:08 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2022-04-21 09:53:08 +0000 |
commit | a75434b1ba7c81b372ae9f902436ec66b0ebcbd1 (patch) | |
tree | 4d01d130181a7ccb4e4157db104ba34bc186b23c /usr.sbin/rpki-client/parser.c | |
parent | 9c8ec03a5411ec8214e2c60aedcbf1b934e63f9c (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.c | 597 |
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) { |