1 /* $OpenBSD: verify.c,v 1.8 2020/10/10 10:19:45 tb Exp $ */ 2 /* 3 * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 4 * Copyright (c) 2020 Bob Beck <beck@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <err.h> 20 #include <string.h> 21 22 #include <sys/stat.h> 23 24 #include <openssl/bio.h> 25 #include <openssl/err.h> 26 #include <openssl/pem.h> 27 #include <openssl/x509.h> 28 #include <openssl/x509v3.h> 29 #include <openssl/x509_vfy.h> 30 31 static int verbose = 0; 32 static int json = 0; /* print out json like bettertls expects resuls in */ 33 34 static int 35 passwd_cb(char *buf, int size, int rwflag, void *u) 36 { 37 memset(buf, 0, size); 38 return (0); 39 } 40 41 static int 42 certs_from_file(const char *filename, STACK_OF(X509) **certs) 43 { 44 STACK_OF(X509_INFO) *xis = NULL; 45 STACK_OF(X509) *xs = NULL; 46 BIO *bio = NULL; 47 X509 *x; 48 int i; 49 50 if ((xs = sk_X509_new_null()) == NULL) 51 errx(1, "failed to create X509 stack"); 52 if ((bio = BIO_new_file(filename, "r")) == NULL) { 53 ERR_print_errors_fp(stderr); 54 errx(1, "failed to create bio"); 55 } 56 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) 57 errx(1, "failed to read PEM"); 58 59 for (i = 0; i < sk_X509_INFO_num(xis); i++) { 60 if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) 61 continue; 62 if (!sk_X509_push(xs, x)) 63 errx(1, "failed to push X509"); 64 X509_up_ref(x); 65 } 66 67 *certs = xs; 68 xs = NULL; 69 70 sk_X509_INFO_pop_free(xis, X509_INFO_free); 71 sk_X509_pop_free(xs, X509_free); 72 BIO_free(bio); 73 74 return 1; 75 } 76 77 static int 78 verify_cert_cb(int ok, X509_STORE_CTX *xsc) 79 { 80 X509 *current_cert; 81 int verify_err; 82 83 current_cert = X509_STORE_CTX_get_current_cert(xsc); 84 if (current_cert != NULL) { 85 X509_NAME_print_ex_fp(stderr, 86 X509_get_subject_name(current_cert), 0, XN_FLAG_ONELINE); 87 fprintf(stderr, "\n"); 88 } 89 90 verify_err = X509_STORE_CTX_get_error(xsc); 91 if (verify_err != X509_V_OK) { 92 fprintf(stderr, "verify error at depth %d: %s\n", 93 X509_STORE_CTX_get_error_depth(xsc), 94 X509_verify_cert_error_string(verify_err)); 95 } 96 97 return ok; 98 } 99 100 static void 101 verify_cert(X509_STORE *store, const char *roots_file, const char *bundle_file, 102 const char *cert_file, int *ip, int *dns) 103 { 104 STACK_OF(X509) *roots = NULL, *bundle = NULL, *cert = NULL; 105 X509_STORE_CTX *xsc = NULL; 106 X509_STORE_CTX *xscip = NULL; 107 X509_VERIFY_PARAM *param, *paramip; 108 X509 *leaf = NULL; 109 unsigned long flags, flagsip; 110 111 *ip = *dns = 0; 112 113 if (!certs_from_file(roots_file, &roots)) 114 errx(1, "failed to load roots from '%s'", roots_file); 115 if (!certs_from_file(bundle_file, &bundle)) 116 errx(1, "failed to load bundle from '%s'", bundle_file); 117 if (!certs_from_file(cert_file, &cert)) 118 errx(1, "failed to load cert from '%s'", cert_file); 119 if (sk_X509_num(cert) < 1) 120 errx(1, "no certs in cert bundle %s", cert_file); 121 leaf = sk_X509_shift(cert); 122 123 if ((xsc = X509_STORE_CTX_new()) == NULL) 124 errx(1, "X509_STORE_CTX"); 125 126 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 127 ERR_print_errors_fp(stderr); 128 errx(1, "failed to init store context"); 129 } 130 131 if (verbose) 132 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 133 134 if ((param = X509_STORE_CTX_get0_param(xsc)) == NULL) { 135 ERR_print_errors_fp(stderr); 136 errx(1, "failed to get verify parameters"); 137 } 138 flags = X509_VERIFY_PARAM_get_flags(param); 139 X509_VERIFY_PARAM_set_flags(param, flags); 140 X509_VERIFY_PARAM_set_time(param, 1600000000); 141 X509_VERIFY_PARAM_set1_host(param, "localhost.local", 142 strlen("localhost.local")); 143 144 X509_STORE_CTX_set0_trusted_stack(xsc, roots); 145 146 if (X509_verify_cert(xsc) == 1) 147 *dns = 1; 148 149 if ((xscip = X509_STORE_CTX_new()) == NULL) 150 errx(1, "X509_STORE_CTX"); 151 152 if (!X509_STORE_CTX_init(xscip, store, leaf, bundle)) { 153 ERR_print_errors_fp(stderr); 154 errx(1, "failed to init store context"); 155 } 156 157 if (verbose) 158 X509_STORE_CTX_set_verify_cb(xscip, verify_cert_cb); 159 160 if ((paramip = X509_STORE_CTX_get0_param(xscip)) == NULL) { 161 ERR_print_errors_fp(stderr); 162 errx(1, "failed to get verify parameters"); 163 } 164 flagsip = X509_VERIFY_PARAM_get_flags(paramip); 165 X509_VERIFY_PARAM_set_flags(paramip, flagsip); 166 X509_VERIFY_PARAM_set_time(paramip, 1600000000); 167 X509_VERIFY_PARAM_set1_ip_asc(paramip, "127.0.0.1"); 168 169 X509_STORE_CTX_set0_trusted_stack(xscip, roots); 170 171 if (X509_verify_cert(xscip) == 1) 172 *ip = 1; 173 174 sk_X509_pop_free(roots, X509_free); 175 sk_X509_pop_free(bundle, X509_free); 176 sk_X509_pop_free(cert, X509_free); 177 X509_STORE_CTX_free(xsc); 178 X509_STORE_CTX_free(xscip); 179 X509_free(leaf); 180 } 181 182 static void 183 bettertls_cert_test(const char *certs_path) 184 { 185 X509_STORE *store; 186 char *roots_file, *bundle_file, *cert_file; 187 int i; 188 189 if ((store = X509_STORE_new()) == NULL) 190 errx(1, "X509_STORE_new"); 191 192 X509_STORE_set_default_paths(store); 193 194 if (asprintf(&roots_file, "%s/root.crt", certs_path) == -1) 195 errx(1, "asprintf"); 196 197 for(i = 1;; i++) { 198 int ip, dns; 199 struct stat sb; 200 if (asprintf(&cert_file, "%s/%d.crt", certs_path, i) == -1) 201 errx(1, "asprintf"); 202 if (asprintf(&bundle_file, "%s/%d.chain", certs_path, i) == -1) 203 errx(1, "asprintf"); 204 if (stat(cert_file, &sb) == -1) 205 break; 206 if (stat(bundle_file, &sb) == -1) 207 break; 208 verify_cert(store, roots_file, bundle_file, cert_file, &ip, &dns); 209 /* Mmm. json. with my avocado toast */ 210 if (i > 1 && json) 211 fprintf(stdout, ","); 212 if (json) 213 fprintf(stdout, "{\"id\":%d,\"dnsResult\":%s,\"" 214 "ipResult\":%s}", i, dns ? "true" : "false", 215 ip ? "true" : "false"); 216 else 217 fprintf(stdout, "%d,%s,%s\n", i, dns ? "OK" : "ERROR", 218 ip ? "OK" : "ERROR"); 219 free(bundle_file); 220 free(cert_file); 221 } 222 free(bundle_file); 223 free(cert_file); 224 free(roots_file); 225 X509_STORE_free(store); 226 } 227 228 int 229 main(int argc, char **argv) 230 { 231 if (argc != 2) { 232 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 233 exit(1); 234 } 235 if (json) 236 fprintf(stdout, "{\"testVersion\":1,\"date\":%lld,\"userAgent\"" 237 ":\"LibreSSL OpenBSD 6.8\\n\",\"results\":[", time(NULL)); 238 239 bettertls_cert_test(argv[1]); 240 241 if (json) 242 fprintf(stdout, "],\"osVersion\":\"OpenBSD 6.7\\n\"}\n"); 243 244 return 0; 245 } 246