1 /* $OpenBSD: verify.c,v 1.8 2021/09/30 18:28:38 jsing Exp $ */ 2 /* 3 * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 4 * Copyright (c) 2020-2021 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 <openssl/bio.h> 23 #include <openssl/err.h> 24 #include <openssl/pem.h> 25 #include <openssl/x509.h> 26 #include <openssl/x509v3.h> 27 #include <openssl/x509_verify.h> 28 29 #define MODE_MODERN_VFY 0 30 #define MODE_MODERN_VFY_DIR 1 31 #define MODE_LEGACY_VFY 2 32 #define MODE_VERIFY 3 33 34 static int verbose = 1; 35 36 static int 37 passwd_cb(char *buf, int size, int rwflag, void *u) 38 { 39 memset(buf, 0, size); 40 return (0); 41 } 42 43 static int 44 certs_from_file(const char *filename, STACK_OF(X509) **certs) 45 { 46 STACK_OF(X509_INFO) *xis = NULL; 47 STACK_OF(X509) *xs = NULL; 48 BIO *bio = NULL; 49 X509 *x; 50 int i; 51 52 if ((xs = sk_X509_new_null()) == NULL) 53 errx(1, "failed to create X509 stack"); 54 if ((bio = BIO_new_file(filename, "r")) == NULL) { 55 ERR_print_errors_fp(stderr); 56 errx(1, "failed to create bio"); 57 } 58 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) 59 errx(1, "failed to read PEM"); 60 61 for (i = 0; i < sk_X509_INFO_num(xis); i++) { 62 if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) 63 continue; 64 if (!sk_X509_push(xs, x)) 65 errx(1, "failed to push X509"); 66 X509_up_ref(x); 67 } 68 69 *certs = xs; 70 xs = NULL; 71 72 sk_X509_INFO_pop_free(xis, X509_INFO_free); 73 sk_X509_pop_free(xs, X509_free); 74 BIO_free(bio); 75 76 return 1; 77 } 78 79 static int 80 verify_cert_cb(int ok, X509_STORE_CTX *xsc) 81 { 82 X509 *current_cert; 83 int verify_err; 84 85 current_cert = X509_STORE_CTX_get_current_cert(xsc); 86 if (current_cert != NULL) { 87 X509_NAME_print_ex_fp(stderr, 88 X509_get_subject_name(current_cert), 0, 89 XN_FLAG_ONELINE); 90 fprintf(stderr, "\n"); 91 } 92 93 verify_err = X509_STORE_CTX_get_error(xsc); 94 if (verify_err != X509_V_OK) { 95 fprintf(stderr, "verify error at depth %d: %s\n", 96 X509_STORE_CTX_get_error_depth(xsc), 97 X509_verify_cert_error_string(verify_err)); 98 } 99 100 return ok; 101 } 102 103 static void 104 verify_cert(const char *roots_dir, const char *roots_file, 105 const char *bundle_file, int *chains, int mode) 106 { 107 STACK_OF(X509) *roots = NULL, *bundle = NULL; 108 X509_STORE_CTX *xsc = NULL; 109 X509_STORE *store = NULL; 110 int verify_err, use_dir; 111 unsigned long flags; 112 X509 *leaf = NULL; 113 114 *chains = 0; 115 use_dir = (mode == MODE_MODERN_VFY_DIR); 116 117 if (!use_dir && !certs_from_file(roots_file, &roots)) 118 errx(1, "failed to load roots from '%s'", roots_file); 119 if (!certs_from_file(bundle_file, &bundle)) 120 errx(1, "failed to load bundle from '%s'", bundle_file); 121 if (sk_X509_num(bundle) < 1) 122 errx(1, "not enough certs in bundle"); 123 leaf = sk_X509_shift(bundle); 124 125 if ((xsc = X509_STORE_CTX_new()) == NULL) 126 errx(1, "X509_STORE_CTX"); 127 if (use_dir && (store = X509_STORE_new()) == NULL) 128 errx(1, "X509_STORE"); 129 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 130 ERR_print_errors_fp(stderr); 131 errx(1, "failed to init store context"); 132 } 133 if (use_dir) { 134 if (!X509_STORE_load_locations(store, NULL, roots_dir)) 135 errx(1, "failed to set by_dir directory of %s", roots_dir); 136 } 137 if (mode == MODE_LEGACY_VFY) { 138 flags = X509_VERIFY_PARAM_get_flags(xsc->param); 139 flags |= X509_V_FLAG_LEGACY_VERIFY; 140 X509_VERIFY_PARAM_set_flags(xsc->param, flags); 141 } else { 142 flags = X509_VERIFY_PARAM_get_flags(xsc->param); 143 flags &= ~X509_V_FLAG_LEGACY_VERIFY; 144 X509_VERIFY_PARAM_set_flags(xsc->param, flags); 145 } 146 147 if (verbose) 148 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 149 if (!use_dir) 150 X509_STORE_CTX_set0_trusted_stack(xsc, roots); 151 if (X509_verify_cert(xsc) == 1) { 152 *chains = 1; /* XXX */ 153 goto done; 154 } 155 156 verify_err = X509_STORE_CTX_get_error(xsc); 157 if (verify_err == 0) 158 errx(1, "Error unset on failure!\n"); 159 160 fprintf(stderr, "failed to verify at %d: %s\n", 161 X509_STORE_CTX_get_error_depth(xsc), 162 X509_verify_cert_error_string(verify_err)); 163 164 done: 165 sk_X509_pop_free(roots, X509_free); 166 sk_X509_pop_free(bundle, X509_free); 167 X509_STORE_free(store); 168 X509_STORE_CTX_free(xsc); 169 X509_free(leaf); 170 } 171 172 struct verify_cert_test { 173 const char *id; 174 int want_chains; 175 int failing; 176 }; 177 178 static void 179 verify_cert_new(const char *roots_file, const char *bundle_file, int *chains) 180 { 181 STACK_OF(X509) *roots = NULL, *bundle = NULL; 182 X509_STORE_CTX *xsc = NULL; 183 X509 *leaf = NULL; 184 struct x509_verify_ctx *ctx; 185 186 *chains = 0; 187 188 if (!certs_from_file(roots_file, &roots)) 189 errx(1, "failed to load roots from '%s'", roots_file); 190 if (!certs_from_file(bundle_file, &bundle)) 191 errx(1, "failed to load bundle from '%s'", bundle_file); 192 if (sk_X509_num(bundle) < 1) 193 errx(1, "not enough certs in bundle"); 194 leaf = sk_X509_shift(bundle); 195 196 if ((xsc = X509_STORE_CTX_new()) == NULL) 197 errx(1, "X509_STORE_CTX"); 198 if (!X509_STORE_CTX_init(xsc, NULL, leaf, bundle)) { 199 ERR_print_errors_fp(stderr); 200 errx(1, "failed to init store context"); 201 } 202 if (verbose) 203 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 204 205 if ((ctx = x509_verify_ctx_new(roots)) == NULL) 206 errx(1, "failed to create ctx"); 207 if (!x509_verify_ctx_set_intermediates(ctx, bundle)) 208 errx(1, "failed to set intermediates"); 209 210 if ((*chains = x509_verify(ctx, leaf, NULL)) == 0) { 211 fprintf(stderr, "failed to verify at %lu: %s\n", 212 x509_verify_ctx_error_depth(ctx), 213 x509_verify_ctx_error_string(ctx)); 214 } else { 215 int c; 216 217 for (c = 0; verbose && c < *chains; c++) { 218 STACK_OF(X509) *chain; 219 int i; 220 221 fprintf(stderr, "Chain %d\n--------\n", c); 222 chain = x509_verify_ctx_chain(ctx, c); 223 for (i = 0; i < sk_X509_num(chain); i++) { 224 X509 *cert = sk_X509_value(chain, i); 225 X509_NAME_print_ex_fp(stderr, 226 X509_get_subject_name(cert), 0, 227 XN_FLAG_ONELINE); 228 fprintf(stderr, "\n"); 229 } 230 } 231 } 232 sk_X509_pop_free(roots, X509_free); 233 sk_X509_pop_free(bundle, X509_free); 234 X509_free(leaf); 235 X509_STORE_CTX_free(xsc); 236 x509_verify_ctx_free(ctx); 237 } 238 239 struct verify_cert_test verify_cert_tests[] = { 240 { 241 .id = "1a", 242 .want_chains = 1, 243 }, 244 { 245 .id = "2a", 246 .want_chains = 1, 247 }, 248 { 249 .id = "2b", 250 .want_chains = 0, 251 }, 252 { 253 .id = "2c", 254 .want_chains = 1, 255 }, 256 { 257 .id = "3a", 258 .want_chains = 1, 259 }, 260 { 261 .id = "3b", 262 .want_chains = 0, 263 }, 264 { 265 .id = "3c", 266 .want_chains = 0, 267 }, 268 { 269 .id = "3d", 270 .want_chains = 0, 271 }, 272 { 273 .id = "3e", 274 .want_chains = 1, 275 }, 276 { 277 .id = "4a", 278 .want_chains = 2, 279 }, 280 { 281 .id = "4b", 282 .want_chains = 1, 283 }, 284 { 285 .id = "4c", 286 .want_chains = 1, 287 .failing = 1, 288 }, 289 { 290 .id = "4d", 291 .want_chains = 1, 292 }, 293 { 294 .id = "4e", 295 .want_chains = 1, 296 }, 297 { 298 .id = "4f", 299 .want_chains = 2, 300 }, 301 { 302 .id = "4g", 303 .want_chains = 1, 304 .failing = 1, 305 }, 306 { 307 .id = "4h", 308 .want_chains = 1, 309 }, 310 { 311 .id = "5a", 312 .want_chains = 2, 313 }, 314 { 315 .id = "5b", 316 .want_chains = 1, 317 .failing = 1, 318 }, 319 { 320 .id = "5c", 321 .want_chains = 1, 322 }, 323 { 324 .id = "5d", 325 .want_chains = 1, 326 }, 327 { 328 .id = "5e", 329 .want_chains = 1, 330 .failing = 1, 331 }, 332 { 333 .id = "5f", 334 .want_chains = 1, 335 }, 336 { 337 .id = "5g", 338 .want_chains = 2, 339 }, 340 { 341 .id = "5h", 342 .want_chains = 1, 343 }, 344 { 345 .id = "5i", 346 .want_chains = 1, 347 .failing = 1, 348 }, 349 { 350 .id = "6a", 351 .want_chains = 1, 352 }, 353 { 354 .id = "6b", 355 .want_chains = 1, 356 .failing = 1, 357 }, 358 { 359 .id = "7a", 360 .want_chains = 1, 361 .failing = 1, 362 }, 363 { 364 .id = "7b", 365 .want_chains = 1, 366 }, 367 { 368 .id = "8a", 369 .want_chains = 0, 370 }, 371 { 372 .id = "9a", 373 .want_chains = 0, 374 }, 375 { 376 .id = "10a", 377 .want_chains = 1, 378 }, 379 { 380 .id = "10b", 381 .want_chains = 1, 382 }, 383 { 384 .id = "11a", 385 .want_chains = 1, 386 .failing = 1, 387 }, 388 { 389 .id = "11b", 390 .want_chains = 1, 391 }, 392 { 393 .id = "12a", 394 .want_chains = 1, 395 }, 396 { 397 .id = "13a", 398 .want_chains = 1, 399 .failing = 1, 400 }, 401 }; 402 403 #define N_VERIFY_CERT_TESTS \ 404 (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests)) 405 406 static int 407 verify_cert_test(const char *certs_path, int mode) 408 { 409 char *roots_file, *bundle_file, *roots_dir; 410 struct verify_cert_test *vct; 411 int failed = 0; 412 int chains; 413 size_t i; 414 415 for (i = 0; i < N_VERIFY_CERT_TESTS; i++) { 416 vct = &verify_cert_tests[i]; 417 418 if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path, 419 vct->id) == -1) 420 errx(1, "asprintf"); 421 if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path, 422 vct->id) == -1) 423 errx(1, "asprintf"); 424 if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1) 425 errx(1, "asprintf"); 426 427 fprintf(stderr, "== Test %zu (%s)\n", i, vct->id); 428 if (mode == MODE_VERIFY) 429 verify_cert_new(roots_file, bundle_file, &chains); 430 else 431 verify_cert(roots_dir, roots_file, bundle_file, &chains, mode); 432 if ((mode == MODE_VERIFY && chains == vct->want_chains) || 433 (chains == 0 && vct->want_chains == 0) || 434 (chains == 1 && vct->want_chains > 0)) { 435 fprintf(stderr, "INFO: Succeeded with %d chains%s\n", 436 chains, vct->failing ? " (legacy failure)" : ""); 437 if (mode == MODE_LEGACY_VFY && vct->failing) 438 failed |= 1; 439 } else { 440 fprintf(stderr, "FAIL: Failed with %d chains%s\n", 441 chains, vct->failing ? " (legacy failure)" : ""); 442 if (!vct->failing) 443 failed |= 1; 444 } 445 fprintf(stderr, "\n"); 446 447 free(roots_file); 448 free(bundle_file); 449 free(roots_dir); 450 } 451 452 return failed; 453 } 454 455 int 456 main(int argc, char **argv) 457 { 458 int failed = 0; 459 460 if (argc != 2) { 461 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 462 exit(1); 463 } 464 465 fprintf(stderr, "\n\nTesting legacy x509_vfy\n"); 466 failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY); 467 fprintf(stderr, "\n\nTesting modern x509_vfy\n"); 468 failed |= verify_cert_test(argv[1], MODE_MODERN_VFY); 469 fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n"); 470 failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR); 471 fprintf(stderr, "\n\nTesting x509_verify\n"); 472 failed |= verify_cert_test(argv[1], MODE_VERIFY); 473 474 return (failed); 475 } 476