1 /* $OpenBSD: verify.c,v 1.9 2020/10/26 12:11:47 beck 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 int verify_err; 111 112 *ip = *dns = 0; 113 114 if (!certs_from_file(roots_file, &roots)) 115 errx(1, "failed to load roots from '%s'", roots_file); 116 if (!certs_from_file(bundle_file, &bundle)) 117 errx(1, "failed to load bundle from '%s'", bundle_file); 118 if (!certs_from_file(cert_file, &cert)) 119 errx(1, "failed to load cert from '%s'", cert_file); 120 if (sk_X509_num(cert) < 1) 121 errx(1, "no certs in cert bundle %s", cert_file); 122 leaf = sk_X509_shift(cert); 123 124 if ((xsc = X509_STORE_CTX_new()) == NULL) 125 errx(1, "X509_STORE_CTX"); 126 127 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 128 ERR_print_errors_fp(stderr); 129 errx(1, "failed to init store context"); 130 } 131 132 if (verbose) 133 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 134 135 if ((param = X509_STORE_CTX_get0_param(xsc)) == NULL) { 136 ERR_print_errors_fp(stderr); 137 errx(1, "failed to get verify parameters"); 138 } 139 flags = X509_VERIFY_PARAM_get_flags(param); 140 X509_VERIFY_PARAM_set_flags(param, flags); 141 X509_VERIFY_PARAM_set_time(param, 1600000000); 142 X509_VERIFY_PARAM_set1_host(param, "localhost.local", 143 strlen("localhost.local")); 144 145 X509_STORE_CTX_set0_trusted_stack(xsc, roots); 146 147 if (X509_verify_cert(xsc) == 1) 148 *dns = 1; 149 verify_err = X509_STORE_CTX_get_error(xsc); 150 if (verify_err == X509_V_OK && *dns == 0) { 151 fprintf(stderr, "X509_V_OK on failure!\n"); 152 *dns = 1; 153 } 154 155 if ((xscip = X509_STORE_CTX_new()) == NULL) 156 errx(1, "X509_STORE_CTX"); 157 158 if (!X509_STORE_CTX_init(xscip, store, leaf, bundle)) { 159 ERR_print_errors_fp(stderr); 160 errx(1, "failed to init store context"); 161 } 162 163 if (verbose) 164 X509_STORE_CTX_set_verify_cb(xscip, verify_cert_cb); 165 166 if ((paramip = X509_STORE_CTX_get0_param(xscip)) == NULL) { 167 ERR_print_errors_fp(stderr); 168 errx(1, "failed to get verify parameters"); 169 } 170 flagsip = X509_VERIFY_PARAM_get_flags(paramip); 171 X509_VERIFY_PARAM_set_flags(paramip, flagsip); 172 X509_VERIFY_PARAM_set_time(paramip, 1600000000); 173 X509_VERIFY_PARAM_set1_ip_asc(paramip, "127.0.0.1"); 174 175 X509_STORE_CTX_set0_trusted_stack(xscip, roots); 176 177 if (X509_verify_cert(xscip) == 1) 178 *ip = 1; 179 verify_err = X509_STORE_CTX_get_error(xscip); 180 if (verify_err == X509_V_OK && *ip == 0) { 181 fprintf(stderr, "X509_V_OK on failure!\n"); 182 *ip = 1; 183 } 184 185 sk_X509_pop_free(roots, X509_free); 186 sk_X509_pop_free(bundle, X509_free); 187 sk_X509_pop_free(cert, X509_free); 188 X509_STORE_CTX_free(xsc); 189 X509_STORE_CTX_free(xscip); 190 X509_free(leaf); 191 } 192 193 static void 194 bettertls_cert_test(const char *certs_path) 195 { 196 X509_STORE *store; 197 char *roots_file, *bundle_file, *cert_file; 198 int i; 199 200 if ((store = X509_STORE_new()) == NULL) 201 errx(1, "X509_STORE_new"); 202 203 X509_STORE_set_default_paths(store); 204 205 if (asprintf(&roots_file, "%s/root.crt", certs_path) == -1) 206 errx(1, "asprintf"); 207 208 for(i = 1;; i++) { 209 int ip, dns; 210 struct stat sb; 211 if (asprintf(&cert_file, "%s/%d.crt", certs_path, i) == -1) 212 errx(1, "asprintf"); 213 if (asprintf(&bundle_file, "%s/%d.chain", certs_path, i) == -1) 214 errx(1, "asprintf"); 215 if (stat(cert_file, &sb) == -1) 216 break; 217 if (stat(bundle_file, &sb) == -1) 218 break; 219 verify_cert(store, roots_file, bundle_file, cert_file, &ip, &dns); 220 /* Mmm. json. with my avocado toast */ 221 if (i > 1 && json) 222 fprintf(stdout, ","); 223 if (json) 224 fprintf(stdout, "{\"id\":%d,\"dnsResult\":%s,\"" 225 "ipResult\":%s}", i, dns ? "true" : "false", 226 ip ? "true" : "false"); 227 else 228 fprintf(stdout, "%d,%s,%s\n", i, dns ? "OK" : "ERROR", 229 ip ? "OK" : "ERROR"); 230 free(bundle_file); 231 free(cert_file); 232 } 233 free(bundle_file); 234 free(cert_file); 235 free(roots_file); 236 X509_STORE_free(store); 237 } 238 239 int 240 main(int argc, char **argv) 241 { 242 if (argc != 2) { 243 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 244 exit(1); 245 } 246 if (json) 247 fprintf(stdout, "{\"testVersion\":1,\"date\":%lld,\"userAgent\"" 248 ":\"LibreSSL OpenBSD 6.8\\n\",\"results\":[", time(NULL)); 249 250 bettertls_cert_test(argv[1]); 251 252 if (json) 253 fprintf(stdout, "],\"osVersion\":\"OpenBSD 6.7\\n\"}\n"); 254 255 return 0; 256 } 257