/* $OpenBSD: verify.c,v 1.7 2020/10/08 14:38:09 tb Exp $ */ /* * Copyright (c) 2020 Joel Sing * Copyright (c) 2020 Bob Beck * * 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 #include #include #include #include #include #include #include #include static int verbose = 0; static int json = 0; /* print out json like bettertls expects resuls in */ static int passwd_cb(char *buf, int size, int rwflag, void *u) { memset(buf, 0, size); return (0); } static int certs_from_file(const char *filename, STACK_OF(X509) **certs) { STACK_OF(X509_INFO) *xis = NULL; STACK_OF(X509) *xs = NULL; BIO *bio = NULL; X509 *x; int i; if ((xs = sk_X509_new_null()) == NULL) errx(1, "failed to create X509 stack"); if ((bio = BIO_new_file(filename, "r")) == NULL) { ERR_print_errors_fp(stderr); errx(1, "failed to create bio"); } if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) errx(1, "failed to read PEM"); for (i = 0; i < sk_X509_INFO_num(xis); i++) { if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) continue; if (!sk_X509_push(xs, x)) errx(1, "failed to push X509"); X509_up_ref(x); } *certs = xs; xs = NULL; sk_X509_INFO_pop_free(xis, X509_INFO_free); sk_X509_pop_free(xs, X509_free); BIO_free(bio); return 1; } static int verify_cert_cb(int ok, X509_STORE_CTX *xsc) { X509 *current_cert; int verify_err; current_cert = X509_STORE_CTX_get_current_cert(xsc); if (current_cert != NULL) { X509_NAME_print_ex_fp(stderr, X509_get_subject_name(current_cert), 0, XN_FLAG_ONELINE); fprintf(stderr, "\n"); } verify_err = X509_STORE_CTX_get_error(xsc); if (verify_err != X509_V_OK) { fprintf(stderr, "verify error at depth %d: %s\n", X509_STORE_CTX_get_error_depth(xsc), X509_verify_cert_error_string(verify_err)); } return ok; } static void verify_cert(X509_STORE *store, const char *roots_file, const char *bundle_file, const char *cert_file, int *ip, int *dns) { STACK_OF(X509) *roots = NULL, *bundle = NULL, *cert = NULL; X509_STORE_CTX *xsc = NULL; X509_STORE_CTX *xscip = NULL; X509_VERIFY_PARAM *param, *paramip; X509 *leaf = NULL; unsigned long flags, flagsip; *ip = *dns = 0; if (!certs_from_file(roots_file, &roots)) errx(1, "failed to load roots from '%s'", roots_file); if (!certs_from_file(bundle_file, &bundle)) errx(1, "failed to load bundle from '%s'", bundle_file); if (!certs_from_file(cert_file, &cert)) errx(1, "failed to load cert from '%s'", cert_file); if (sk_X509_num(cert) < 1) errx(1, "no certs in cert bundle %s", cert_file); leaf = sk_X509_shift(cert); if ((xsc = X509_STORE_CTX_new()) == NULL) errx(1, "X509_STORE_CTX"); if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { ERR_print_errors_fp(stderr); errx(1, "failed to init store context"); } if (verbose) X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); if ((param = X509_STORE_CTX_get0_param(xsc)) == NULL) { ERR_print_errors_fp(stderr); errx(1, "failed to get verify parameters"); } flags = X509_VERIFY_PARAM_get_flags(param); X509_VERIFY_PARAM_set_flags(param, flags); X509_VERIFY_PARAM_set_time(param, 1600000000); X509_VERIFY_PARAM_set1_host(param, "localhost.local", strlen("localhost.local")); X509_STORE_CTX_set0_trusted_stack(xsc, roots); if (X509_verify_cert(xsc) == 1) *dns = 1; if ((xscip = X509_STORE_CTX_new()) == NULL) errx(1, "X509_STORE_CTX"); if (!X509_STORE_CTX_init(xscip, store, leaf, bundle)) { ERR_print_errors_fp(stderr); errx(1, "failed to init store context"); } if (verbose) X509_STORE_CTX_set_verify_cb(xscip, verify_cert_cb); if ((paramip = X509_STORE_CTX_get0_param(xscip)) == NULL) { ERR_print_errors_fp(stderr); errx(1, "failed to get verify parameters"); } flagsip = X509_VERIFY_PARAM_get_flags(paramip); X509_VERIFY_PARAM_set_flags(paramip, flagsip); X509_VERIFY_PARAM_set_time(paramip, 1600000000); X509_VERIFY_PARAM_set1_ip_asc(paramip, "127.0.0.1"); X509_STORE_CTX_set0_trusted_stack(xscip, roots); if (X509_verify_cert(xscip) == 1) *ip = 1; sk_X509_pop_free(roots, X509_free); sk_X509_pop_free(bundle, X509_free); sk_X509_pop_free(cert, X509_free); X509_STORE_CTX_free(xsc); X509_STORE_CTX_free(xscip); X509_free(leaf); } static void bettertls_cert_test(const char *certs_path) { X509_STORE *store; char *roots_file, *bundle_file, *cert_file; int i; if ((store = X509_STORE_new()) == NULL) errx(1, "X509_STORE_new"); X509_STORE_set_default_paths(store); if (asprintf(&roots_file, "%s/root.crt", certs_path) == -1) errx(1, "asprintf"); for(i = 1;; i++) { int ip, dns; struct stat sb; if (asprintf(&cert_file, "%s/%d.crt", certs_path, i) == -1) errx(1, "asprintf"); if (asprintf(&bundle_file, "%s/%d.chain", certs_path, i) == -1) errx(1, "asprintf"); if (stat(cert_file, &sb) == -1) break; if (stat(bundle_file, &sb) == -1) break; verify_cert(store, roots_file, bundle_file, cert_file, &ip, &dns); /* Mmm. json. with my avocado toast */ if (i > 1 && json) fprintf(stdout, ","); if (json) fprintf(stdout, "{\"id\":%d,\"dnsResult\":%s,\"" "ipResult\":%s}", i, dns ? "true" : "false", ip ? "true" : "false"); else fprintf(stdout, "%d,%s,%s\n", i, dns ? "OK" : "ERROR", ip ? "OK" : "ERROR"); free(bundle_file); free(cert_file); } free(roots_file); X509_STORE_free(store); } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } if (json) fprintf(stdout, "{\"testVersion\":1,\"date\":%lld,\"userAgent\"" ":\"LibreSSL OpenBSD 6.8\\n\",\"results\":[", time(NULL)); bettertls_cert_test(argv[1]); if (json) fprintf(stdout, "],\"osVersion\":\"OpenBSD 6.7\\n\"}\n"); return 0; }