1*28dcb2c6Sbeck /* $OpenBSD: verify.c,v 1.9 2020/10/26 12:11:47 beck Exp $ */
23d006088Sbeck /*
33d006088Sbeck * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
43d006088Sbeck * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
53d006088Sbeck *
63d006088Sbeck * Permission to use, copy, modify, and distribute this software for any
73d006088Sbeck * purpose with or without fee is hereby granted, provided that the above
83d006088Sbeck * copyright notice and this permission notice appear in all copies.
93d006088Sbeck *
103d006088Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113d006088Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123d006088Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133d006088Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143d006088Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153d006088Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163d006088Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173d006088Sbeck */
183d006088Sbeck
193d006088Sbeck #include <err.h>
203d006088Sbeck #include <string.h>
213d006088Sbeck
223d006088Sbeck #include <sys/stat.h>
233d006088Sbeck
243d006088Sbeck #include <openssl/bio.h>
253d006088Sbeck #include <openssl/err.h>
263d006088Sbeck #include <openssl/pem.h>
273d006088Sbeck #include <openssl/x509.h>
283d006088Sbeck #include <openssl/x509v3.h>
293d006088Sbeck #include <openssl/x509_vfy.h>
303d006088Sbeck
313d006088Sbeck static int verbose = 0;
323d006088Sbeck static int json = 0; /* print out json like bettertls expects resuls in */
333d006088Sbeck
343d006088Sbeck static int
passwd_cb(char * buf,int size,int rwflag,void * u)353d006088Sbeck passwd_cb(char *buf, int size, int rwflag, void *u)
363d006088Sbeck {
373d006088Sbeck memset(buf, 0, size);
383d006088Sbeck return (0);
393d006088Sbeck }
403d006088Sbeck
413d006088Sbeck static int
certs_from_file(const char * filename,STACK_OF (X509)** certs)423d006088Sbeck certs_from_file(const char *filename, STACK_OF(X509) **certs)
433d006088Sbeck {
443d006088Sbeck STACK_OF(X509_INFO) *xis = NULL;
453d006088Sbeck STACK_OF(X509) *xs = NULL;
463d006088Sbeck BIO *bio = NULL;
473d006088Sbeck X509 *x;
483d006088Sbeck int i;
493d006088Sbeck
503d006088Sbeck if ((xs = sk_X509_new_null()) == NULL)
513d006088Sbeck errx(1, "failed to create X509 stack");
523d006088Sbeck if ((bio = BIO_new_file(filename, "r")) == NULL) {
533d006088Sbeck ERR_print_errors_fp(stderr);
543d006088Sbeck errx(1, "failed to create bio");
553d006088Sbeck }
563d006088Sbeck if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
573d006088Sbeck errx(1, "failed to read PEM");
583d006088Sbeck
593d006088Sbeck for (i = 0; i < sk_X509_INFO_num(xis); i++) {
603d006088Sbeck if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
613d006088Sbeck continue;
623d006088Sbeck if (!sk_X509_push(xs, x))
633d006088Sbeck errx(1, "failed to push X509");
643d006088Sbeck X509_up_ref(x);
653d006088Sbeck }
663d006088Sbeck
673d006088Sbeck *certs = xs;
683d006088Sbeck xs = NULL;
693d006088Sbeck
703d006088Sbeck sk_X509_INFO_pop_free(xis, X509_INFO_free);
713d006088Sbeck sk_X509_pop_free(xs, X509_free);
723d006088Sbeck BIO_free(bio);
733d006088Sbeck
743d006088Sbeck return 1;
753d006088Sbeck }
763d006088Sbeck
773d006088Sbeck static int
verify_cert_cb(int ok,X509_STORE_CTX * xsc)783d006088Sbeck verify_cert_cb(int ok, X509_STORE_CTX *xsc)
793d006088Sbeck {
803d006088Sbeck X509 *current_cert;
813d006088Sbeck int verify_err;
823d006088Sbeck
833d006088Sbeck current_cert = X509_STORE_CTX_get_current_cert(xsc);
843d006088Sbeck if (current_cert != NULL) {
853d006088Sbeck X509_NAME_print_ex_fp(stderr,
86555adfc2Stb X509_get_subject_name(current_cert), 0, XN_FLAG_ONELINE);
873d006088Sbeck fprintf(stderr, "\n");
883d006088Sbeck }
893d006088Sbeck
903d006088Sbeck verify_err = X509_STORE_CTX_get_error(xsc);
913d006088Sbeck if (verify_err != X509_V_OK) {
923d006088Sbeck fprintf(stderr, "verify error at depth %d: %s\n",
933d006088Sbeck X509_STORE_CTX_get_error_depth(xsc),
943d006088Sbeck X509_verify_cert_error_string(verify_err));
953d006088Sbeck }
963d006088Sbeck
973d006088Sbeck return ok;
983d006088Sbeck }
993d006088Sbeck
1003d006088Sbeck static void
verify_cert(X509_STORE * store,const char * roots_file,const char * bundle_file,const char * cert_file,int * ip,int * dns)10117102ee3Stb verify_cert(X509_STORE *store, const char *roots_file, const char *bundle_file,
1023d006088Sbeck const char *cert_file, int *ip, int *dns)
1033d006088Sbeck {
1043d006088Sbeck STACK_OF(X509) *roots = NULL, *bundle = NULL, *cert = NULL;
1053d006088Sbeck X509_STORE_CTX *xsc = NULL;
1063d006088Sbeck X509_STORE_CTX *xscip = NULL;
1077bee1859Stb X509_VERIFY_PARAM *param, *paramip;
1083d006088Sbeck X509 *leaf = NULL;
1092a4c313fStb unsigned long flags, flagsip;
110*28dcb2c6Sbeck int verify_err;
1113d006088Sbeck
1123d006088Sbeck *ip = *dns = 0;
1133d006088Sbeck
1143d006088Sbeck if (!certs_from_file(roots_file, &roots))
1153d006088Sbeck errx(1, "failed to load roots from '%s'", roots_file);
1163d006088Sbeck if (!certs_from_file(bundle_file, &bundle))
1173d006088Sbeck errx(1, "failed to load bundle from '%s'", bundle_file);
1183d006088Sbeck if (!certs_from_file(cert_file, &cert))
1193d006088Sbeck errx(1, "failed to load cert from '%s'", cert_file);
1203d006088Sbeck if (sk_X509_num(cert) < 1)
1213d006088Sbeck errx(1, "no certs in cert bundle %s", cert_file);
1223d006088Sbeck leaf = sk_X509_shift(cert);
1233d006088Sbeck
1243d006088Sbeck if ((xsc = X509_STORE_CTX_new()) == NULL)
1253d006088Sbeck errx(1, "X509_STORE_CTX");
1263d006088Sbeck
1273d006088Sbeck if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
1283d006088Sbeck ERR_print_errors_fp(stderr);
1293d006088Sbeck errx(1, "failed to init store context");
1303d006088Sbeck }
1313d006088Sbeck
1323d006088Sbeck if (verbose)
1333d006088Sbeck X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
1343d006088Sbeck
1357bee1859Stb if ((param = X509_STORE_CTX_get0_param(xsc)) == NULL) {
1367bee1859Stb ERR_print_errors_fp(stderr);
1377bee1859Stb errx(1, "failed to get verify parameters");
1387bee1859Stb }
1397bee1859Stb flags = X509_VERIFY_PARAM_get_flags(param);
1407bee1859Stb X509_VERIFY_PARAM_set_flags(param, flags);
1417bee1859Stb X509_VERIFY_PARAM_set_time(param, 1600000000);
1427bee1859Stb X509_VERIFY_PARAM_set1_host(param, "localhost.local",
1437bee1859Stb strlen("localhost.local"));
1443d006088Sbeck
1453d006088Sbeck X509_STORE_CTX_set0_trusted_stack(xsc, roots);
1463d006088Sbeck
1473d006088Sbeck if (X509_verify_cert(xsc) == 1)
1483d006088Sbeck *dns = 1;
149*28dcb2c6Sbeck verify_err = X509_STORE_CTX_get_error(xsc);
150*28dcb2c6Sbeck if (verify_err == X509_V_OK && *dns == 0) {
151*28dcb2c6Sbeck fprintf(stderr, "X509_V_OK on failure!\n");
152*28dcb2c6Sbeck *dns = 1;
153*28dcb2c6Sbeck }
1543d006088Sbeck
1553d006088Sbeck if ((xscip = X509_STORE_CTX_new()) == NULL)
1563d006088Sbeck errx(1, "X509_STORE_CTX");
1573d006088Sbeck
15817102ee3Stb if (!X509_STORE_CTX_init(xscip, store, leaf, bundle)) {
1593d006088Sbeck ERR_print_errors_fp(stderr);
1603d006088Sbeck errx(1, "failed to init store context");
1613d006088Sbeck }
1623d006088Sbeck
1633d006088Sbeck if (verbose)
1643d006088Sbeck X509_STORE_CTX_set_verify_cb(xscip, verify_cert_cb);
1653d006088Sbeck
1667bee1859Stb if ((paramip = X509_STORE_CTX_get0_param(xscip)) == NULL) {
1677bee1859Stb ERR_print_errors_fp(stderr);
1687bee1859Stb errx(1, "failed to get verify parameters");
1697bee1859Stb }
1707bee1859Stb flagsip = X509_VERIFY_PARAM_get_flags(paramip);
1717bee1859Stb X509_VERIFY_PARAM_set_flags(paramip, flagsip);
1727bee1859Stb X509_VERIFY_PARAM_set_time(paramip, 1600000000);
1737bee1859Stb X509_VERIFY_PARAM_set1_ip_asc(paramip, "127.0.0.1");
1743d006088Sbeck
1753d006088Sbeck X509_STORE_CTX_set0_trusted_stack(xscip, roots);
1763d006088Sbeck
1773d006088Sbeck if (X509_verify_cert(xscip) == 1)
1783d006088Sbeck *ip = 1;
179*28dcb2c6Sbeck verify_err = X509_STORE_CTX_get_error(xscip);
180*28dcb2c6Sbeck if (verify_err == X509_V_OK && *ip == 0) {
181*28dcb2c6Sbeck fprintf(stderr, "X509_V_OK on failure!\n");
182*28dcb2c6Sbeck *ip = 1;
183*28dcb2c6Sbeck }
1843d006088Sbeck
1853d006088Sbeck sk_X509_pop_free(roots, X509_free);
1863d006088Sbeck sk_X509_pop_free(bundle, X509_free);
1873d006088Sbeck sk_X509_pop_free(cert, X509_free);
1883d006088Sbeck X509_STORE_CTX_free(xsc);
1893d006088Sbeck X509_STORE_CTX_free(xscip);
1903d006088Sbeck X509_free(leaf);
1913d006088Sbeck }
1923d006088Sbeck
1933d006088Sbeck static void
bettertls_cert_test(const char * certs_path)1943d006088Sbeck bettertls_cert_test(const char *certs_path)
1953d006088Sbeck {
19617102ee3Stb X509_STORE *store;
1973d006088Sbeck char *roots_file, *bundle_file, *cert_file;
1983d006088Sbeck int i;
1993d006088Sbeck
20017102ee3Stb if ((store = X509_STORE_new()) == NULL)
20117102ee3Stb errx(1, "X509_STORE_new");
20217102ee3Stb
20317102ee3Stb X509_STORE_set_default_paths(store);
2043d006088Sbeck
2053d006088Sbeck if (asprintf(&roots_file, "%s/root.crt", certs_path) == -1)
2063d006088Sbeck errx(1, "asprintf");
2073d006088Sbeck
2083d006088Sbeck for(i = 1;; i++) {
2093d006088Sbeck int ip, dns;
2103d006088Sbeck struct stat sb;
2113d006088Sbeck if (asprintf(&cert_file, "%s/%d.crt", certs_path, i) == -1)
2123d006088Sbeck errx(1, "asprintf");
2133d006088Sbeck if (asprintf(&bundle_file, "%s/%d.chain", certs_path, i) == -1)
2143d006088Sbeck errx(1, "asprintf");
2153d006088Sbeck if (stat(cert_file, &sb) == -1)
2163d006088Sbeck break;
2173d006088Sbeck if (stat(bundle_file, &sb) == -1)
2183d006088Sbeck break;
21917102ee3Stb verify_cert(store, roots_file, bundle_file, cert_file, &ip, &dns);
2203d006088Sbeck /* Mmm. json. with my avocado toast */
2213d006088Sbeck if (i > 1 && json)
2223d006088Sbeck fprintf(stdout, ",");
2233d006088Sbeck if (json)
2243d006088Sbeck fprintf(stdout, "{\"id\":%d,\"dnsResult\":%s,\""
2253d006088Sbeck "ipResult\":%s}", i, dns ? "true" : "false",
2263d006088Sbeck ip ? "true" : "false");
2273d006088Sbeck else
2283d006088Sbeck fprintf(stdout, "%d,%s,%s\n", i, dns ? "OK" : "ERROR",
2293d006088Sbeck ip ? "OK" : "ERROR");
2303d006088Sbeck free(bundle_file);
2313d006088Sbeck free(cert_file);
2323d006088Sbeck }
23323a50ae6Stb free(bundle_file);
23423a50ae6Stb free(cert_file);
2353d006088Sbeck free(roots_file);
23617102ee3Stb X509_STORE_free(store);
2373d006088Sbeck }
2383d006088Sbeck
2393d006088Sbeck int
main(int argc,char ** argv)2403d006088Sbeck main(int argc, char **argv)
2413d006088Sbeck {
2423d006088Sbeck if (argc != 2) {
2433d006088Sbeck fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
2443d006088Sbeck exit(1);
2453d006088Sbeck }
2463d006088Sbeck if (json)
2473d006088Sbeck fprintf(stdout, "{\"testVersion\":1,\"date\":%lld,\"userAgent\""
2487ec19d65Sbeck ":\"LibreSSL OpenBSD 6.8\\n\",\"results\":[", time(NULL));
2493d006088Sbeck
2503d006088Sbeck bettertls_cert_test(argv[1]);
2513d006088Sbeck
2523d006088Sbeck if (json)
2533d006088Sbeck fprintf(stdout, "],\"osVersion\":\"OpenBSD 6.7\\n\"}\n");
2543d006088Sbeck
2553d006088Sbeck return 0;
2563d006088Sbeck }
257