The filemode code is enough different from the regular parser code that it
authorclaudio <claudio@openbsd.org>
Thu, 21 Apr 2022 09:53:07 +0000 (09:53 +0000)
committerclaudio <claudio@openbsd.org>
Thu, 21 Apr 2022 09:53:07 +0000 (09:53 +0000)
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@

usr.sbin/rpki-client/Makefile
usr.sbin/rpki-client/cert.c
usr.sbin/rpki-client/crl.c
usr.sbin/rpki-client/extern.h
usr.sbin/rpki-client/filemode.c [new file with mode: 0644]
usr.sbin/rpki-client/main.c
usr.sbin/rpki-client/parser.c
usr.sbin/rpki-client/validate.c

index df64ea0..6f53821 100644 (file)
@@ -1,8 +1,8 @@
-#      $OpenBSD: Makefile,v 1.23 2021/11/24 15:24:16 claudio Exp $
+#      $OpenBSD: Makefile,v 1.24 2022/04/21 09:53:07 claudio Exp $
 
 PROG=  rpki-client
-SRCS=  as.c cert.c cms.c crl.c encoding.c gbr.c http.c io.c ip.c log.c \
-       main.c mft.c mkdir.c output.c output-bgpd.c output-bird.c \
+SRCS=  as.c cert.c cms.c crl.c encoding.c filemode.c gbr.c http.c io.c ip.c \
+       log.c main.c mft.c mkdir.c output.c output-bgpd.c output-bird.c \
        output-csv.c output-json.c parser.c print.c repo.c roa.c rrdp.c \
        rrdp_delta.c rrdp_notification.c rrdp_snapshot.c rrdp_util.c \
        rsync.c tal.c validate.c x509.c
index 79a1010..5da34a4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cert.c,v 1.69 2022/04/12 09:48:23 tb Exp $ */
+/*     $OpenBSD: cert.c,v 1.70 2022/04/21 09:53:07 claudio Exp $ */
 /*
  * Copyright (c) 2021 Job Snijders <job@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -1186,6 +1186,14 @@ cert_read(struct ibuf *b)
        return p;
 }
 
+static inline int
+authcmp(struct auth *a, struct auth *b)
+{
+       return strcmp(a->cert->ski, b->cert->ski);
+}
+
+RB_GENERATE_STATIC(auth_tree, auth, entry, authcmp);
+
 struct auth *
 auth_find(struct auth_tree *auths, const char *aki)
 {
@@ -1215,14 +1223,6 @@ auth_insert(struct auth_tree *auths, struct cert *cert, struct auth *parent)
                err(1, "auth tree corrupted");
 }
 
-static inline int
-authcmp(struct auth *a, struct auth *b)
-{
-       return strcmp(a->cert->ski, b->cert->ski);
-}
-
-RB_GENERATE(auth_tree, auth, entry, authcmp);
-
 static void
 insert_brk(struct brk_tree *tree, struct cert *cert, int asid)
 {
index 8d2235f..749f23c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: crl.c,v 1.14 2022/02/10 15:33:47 claudio Exp $ */
+/*     $OpenBSD: crl.c,v 1.15 2022/04/21 09:53:07 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -87,7 +87,27 @@ crlcmp(struct crl *a, struct crl *b)
        return strcmp(a->aki, b->aki);
 }
 
-RB_GENERATE(crl_tree, crl, entry, crlcmp);
+RB_GENERATE_STATIC(crl_tree, crl, entry, crlcmp);
+
+/*
+ * Find a CRL based on the auth SKI value.
+ */
+struct crl *
+crl_get(struct crl_tree *crlt, const struct auth *a)
+{
+       struct crl      find;
+
+       if (a == NULL)
+               return NULL;
+       find.aki = a->cert->ski;
+       return RB_FIND(crl_tree, crlt, &find);
+}
+
+int
+crl_insert(struct crl_tree *crlt, struct crl *crl)
+{
+       return RB_INSERT(crl_tree, crlt, crl) == NULL;
+}
 
 void
 crl_free(struct crl *crl)
index 37a6b2b..7d33f27 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: extern.h,v 1.130 2022/04/20 15:38:24 deraadt Exp $ */
+/*     $OpenBSD: extern.h,v 1.131 2022/04/21 09:53:07 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -291,7 +291,6 @@ struct crl {
  * Tree of CRLs sorted by uri
  */
 RB_HEAD(crl_tree, crl);
-RB_PROTOTYPE(crl_tree, crl, entry, crlcmp);
 
 /*
  * An authentication tuple.
@@ -307,7 +306,6 @@ struct auth {
  * Tree of auth sorted by ski
  */
 RB_HEAD(auth_tree, auth);
-RB_PROTOTYPE(auth_tree, auth, entry, authcmp);
 
 struct auth    *auth_find(struct auth_tree *, const char *);
 void            auth_insert(struct auth_tree *, struct cert *, struct auth *);
@@ -454,6 +452,8 @@ struct gbr  *gbr_parse(X509 **, const char *, const unsigned char *,
 
 /* crl.c */
 struct crl     *crl_parse(const char *, const unsigned char *, size_t);
+struct crl     *crl_get(struct crl_tree *, const struct auth *);
+int             crl_insert(struct crl_tree *, struct crl *);
 void            crl_free(struct crl *);
 
 /* Validation of our objects. */
@@ -468,6 +468,8 @@ int          valid_filehash(int, const char *, size_t);
 int             valid_hash(unsigned char *, size_t, const char *, size_t);
 int             valid_uri(const char *, size_t, const char *);
 int             valid_origin(const char *, const char *);
+int             valid_x509(char *, X509_STORE_CTX *, X509 *, struct auth *,
+                   struct crl *, int);
 
 /* Working with CMS. */
 unsigned char  *cms_parse_validate(X509 **, const char *,
@@ -508,6 +510,7 @@ void                 entity_free(struct entity *);
 void            entity_read_req(struct ibuf *, struct entity *);
 void            entityq_flush(struct entityq *, struct repo *);
 void            proc_parser(int) __attribute__((noreturn));
+void            proc_filemode(int) __attribute__((noreturn));
 
 /* Rsync-specific. */
 
@@ -516,8 +519,8 @@ void                 proc_rsync(char *, char *, int) __attribute__((noreturn));
 
 /* HTTP and RRDP processes. */
 
-void            proc_http(char *, int);
-void            proc_rrdp(int);
+void            proc_http(char *, int) __attribute__((noreturn));
+void            proc_rrdp(int) __attribute__((noreturn));
 
 /* Repository handling */
 int             filepath_add(struct filepath_tree *, char *);
diff --git a/usr.sbin/rpki-client/filemode.c b/usr.sbin/rpki-client/filemode.c
new file mode 100644 (file)
index 0000000..a90d965
--- /dev/null
@@ -0,0 +1,589 @@
+/*     $OpenBSD: filemode.c,v 1.1 2022/04/21 09:53:07 claudio Exp $ */
+/*
+ * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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 <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <imsg.h>
+
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "extern.h"
+
+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];
+
+/*
+ * Validate a certificate, if invalid free the resouces and return NULL.
+ */
+static struct cert *
+proc_parser_cert_validate(char *file, struct cert *cert)
+{
+       struct auth     *a;
+       struct crl      *crl;
+
+       a = valid_ski_aki(file, &auths, cert->ski, cert->aki);
+       crl = crl_get(&crlt, a);
+
+       if (!valid_x509(file, ctx, cert->x509, a, crl, 0)) {
+               cert_free(cert);
+               return NULL;
+       }
+
+       cert->talid = a->cert->talid;
+
+       /* Validate the cert */
+       if (!valid_cert(file, a, cert)) {
+               cert_free(cert);
+               return NULL;
+       }
+
+       /*
+        * Add validated CA certs to the RPKI auth tree.
+        */
+       if (cert->purpose == CERT_PURPOSE_CA)
+               auth_insert(&auths, cert, a);
+
+       return cert;
+}
+
+/*
+ * Root certificates come from TALs (has a pkey and is self-signed).
+ * Parse the certificate, ensure that its public key matches the
+ * known public key from the TAL, and then validate the RPKI
+ * content.
+ *
+ * This returns a certificate (which must not be freed) or NULL on
+ * parse failure.
+ */
+static struct cert *
+proc_parser_root_cert(char *file, const unsigned char *der, size_t len,
+    unsigned char *pkey, size_t pkeysz, int talid)
+{
+       struct cert             *cert;
+
+       /* Extract certificate data. */
+
+       cert = cert_parse_pre(file, der, len);
+       if (cert == NULL)
+               return NULL;
+       cert = ta_parse(file, cert, pkey, pkeysz);
+       if (cert == NULL)
+               return NULL;
+
+       if (!valid_ta(file, &auths, cert)) {
+               warnx("%s: certificate not a valid ta", file);
+               cert_free(cert);
+               return NULL;
+       }
+
+       cert->talid = talid;
+
+       /*
+        * Add valid roots to the RPKI auth tree.
+        */
+       auth_insert(&auths, cert, NULL);
+
+       return cert;
+}
+
+/*
+ * Use the X509 CRL Distribution Points to locate the CRL needed for
+ * verification.
+ */
+static void
+parse_load_crl(char *uri)
+{
+       struct crl *crl;
+       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;
+       }
+
+       crl = crl_parse(uri, f, flen);
+       if (crl != NULL && !crl_insert(&crlt, crl))
+               crl_free(crl);
+
+       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 = crl_get(&crlt, a);
+
+               if (valid_x509(file, ctx, 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
+ * validated and verified.
+ * The process will exit cleanly only when fd is closed.
+ */
+void
+proc_filemode(int fd)
+{
+       struct entityq   q;
+       struct msgbuf    msgq;
+       struct pollfd    pfd;
+       struct entity   *entp;
+       struct ibuf     *b, *inbuf = NULL;
+
+       /* Only allow access to the cache directory. */
+       if (unveil(".", "r") == -1)
+               err(1, "unveil cachedir");
+       if (pledge("stdio rpath", NULL) == -1)
+               err(1, "pledge");
+
+       ERR_load_crypto_strings();
+       OpenSSL_add_all_ciphers();
+       OpenSSL_add_all_digests();
+       x509_init_oid();
+
+       if ((ctx = X509_STORE_CTX_new()) == NULL)
+               cryptoerrx("X509_STORE_CTX_new");
+       TAILQ_INIT(&q);
+
+       msgbuf_init(&msgq);
+       msgq.fd = fd;
+
+       pfd.fd = fd;
+
+       for (;;) {
+               pfd.events = POLLIN;
+               if (msgq.queued)
+                       pfd.events |= POLLOUT;
+
+               if (poll(&pfd, 1, INFTIM) == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       err(1, "poll");
+               }
+               if ((pfd.revents & (POLLERR|POLLNVAL)))
+                       errx(1, "poll: bad descriptor");
+
+               /* If the parent closes, return immediately. */
+
+               if ((pfd.revents & POLLHUP))
+                       break;
+
+               if ((pfd.revents & POLLIN)) {
+                       b = io_buf_read(fd, &inbuf);
+                       if (b != NULL) {
+                               entp = calloc(1, sizeof(struct entity));
+                               if (entp == NULL)
+                                       err(1, NULL);
+                               entity_read_req(b, entp);
+                               TAILQ_INSERT_TAIL(&q, entp, entries);
+                               ibuf_free(b);
+                       }
+               }
+
+               if (pfd.revents & POLLOUT) {
+                       switch (msgbuf_write(&msgq)) {
+                       case 0:
+                               errx(1, "write: connection closed");
+                       case -1:
+                               err(1, "write");
+                       }
+               }
+
+               parse_file(&q, &msgq);
+       }
+
+       msgbuf_clear(&msgq);
+       while ((entp = TAILQ_FIRST(&q)) != NULL) {
+               TAILQ_REMOVE(&q, entp, entries);
+               entity_free(entp);
+       }
+
+       /* XXX free auths and crl tree */
+       X509_STORE_CTX_free(ctx);
+
+       exit(0);
+}
index c800e2c..cfc67d1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.198 2022/04/20 04:40:33 tb Exp $ */
+/*     $OpenBSD: main.c,v 1.199 2022/04/21 09:53:07 claudio Exp $ */
 /*
  * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -861,8 +861,10 @@ main(int argc, char *argv[])
 
        procpid = process_start("parser", &proc);
        if (procpid == 0) {
-               proc_parser(proc);
-               errx(1, "parser process returned");
+               if (!filemode)
+                       proc_parser(proc);
+               else
+                       proc_filemode(proc);
        }
 
        /*
@@ -877,7 +879,6 @@ main(int argc, char *argv[])
                if (rsyncpid == 0) {
                        close(proc);
                        proc_rsync(rsync_prog, bind_addr, rsync);
-                       errx(1, "rsync process returned");
                }
        } else {
                rsync = -1;
@@ -897,7 +898,6 @@ main(int argc, char *argv[])
                        close(proc);
                        close(rsync);
                        proc_http(bind_addr, http);
-                       errx(1, "http process returned");
                }
        } else {
                http = -1;
@@ -917,7 +917,6 @@ main(int argc, char *argv[])
                        close(rsync);
                        close(http);
                        proc_rrdp(rrdp);
-                       errx(1, "rrdp process returned");
                }
        } else {
                rrdp = -1;
index d11d044..f45c5b3 100644 (file)
@@ -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>
 
 #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;
@@ -128,122 +120,6 @@ parse_filepath(unsigned int repoid, const char *path, const char *file,
        return fn;
 }
 
-/*
- * 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.
@@ -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;
        }
@@ -611,27 +487,6 @@ proc_parser_root_cert(char *file, const unsigned char *der, size_t len,
        return cert;
 }
 
-/*
- * 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
  */
@@ -647,68 +502,15 @@ 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.
  */
@@ -836,382 +638,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
@@ -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) {
index 93eead1..4677093 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: validate.c,v 1.30 2022/04/19 09:52:29 claudio Exp $ */
+/*     $OpenBSD: validate.c,v 1.31 2022/04/21 09:53:07 claudio Exp $ */
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -30,6 +30,8 @@
 
 #include "extern.h"
 
+extern ASN1_OBJECT     *certpol_oid;
+
 /*
  * Walk up the chain of certificates trying to match our AS number to
  * one of the allocations in that chain.
@@ -327,3 +329,160 @@ valid_origin(const char *uri, const char *proto)
 
        return 1;
 }
+
+/*
+ * 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;
+}
+
+/*
+ * 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");
+       }
+}
+
+/*
+ * 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");
+}
+
+/*
+ * 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
+ */
+int
+valid_x509(char *file, X509_STORE_CTX *store_ctx, 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(store_ctx != NULL);
+       assert(x509 != NULL);
+       if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL))
+               cryptoerrx("X509_STORE_CTX_init");
+
+       if ((params = X509_STORE_CTX_get0_param(store_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(store_ctx, verify_cb);
+       if (!X509_STORE_CTX_set_app_data(store_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(store_ctx, flags);
+       X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH);
+       X509_STORE_CTX_set0_trusted_stack(store_ctx, chain);
+       X509_STORE_CTX_set0_crls(store_ctx, crls);
+
+       if (X509_verify_cert(store_ctx) <= 0) {
+               c = X509_STORE_CTX_get_error(store_ctx);
+               if (!nowarn || verbose > 1)
+                       warnx("%s: %s", file, X509_verify_cert_error_string(c));
+               X509_STORE_CTX_cleanup(store_ctx);
+               sk_X509_free(chain);
+               sk_X509_CRL_free(crls);
+               return 0;
+       }
+
+       X509_STORE_CTX_cleanup(store_ctx);
+       sk_X509_free(chain);
+       sk_X509_CRL_free(crls);
+       return 1;
+}