1 /* $OpenBSD: verify.c,v 1.11 2023/01/28 19:12:20 tb 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/crypto.h> 24 #include <openssl/err.h> 25 #include <openssl/pem.h> 26 #include <openssl/x509.h> 27 #include <openssl/x509v3.h> 28 29 #include "x509_verify.h" 30 31 #define MODE_MODERN_VFY 0 32 #define MODE_MODERN_VFY_DIR 1 33 #define MODE_LEGACY_VFY 2 34 #define MODE_VERIFY 3 35 36 static int verbose = 1; 37 38 static int 39 passwd_cb(char *buf, int size, int rwflag, void *u) 40 { 41 memset(buf, 0, size); 42 return (0); 43 } 44 45 static int 46 certs_from_file(const char *filename, STACK_OF(X509) **certs) 47 { 48 STACK_OF(X509_INFO) *xis = NULL; 49 STACK_OF(X509) *xs = NULL; 50 BIO *bio = NULL; 51 X509 *x; 52 int i; 53 54 if ((xs = sk_X509_new_null()) == NULL) 55 errx(1, "failed to create X509 stack"); 56 if ((bio = BIO_new_file(filename, "r")) == NULL) { 57 ERR_print_errors_fp(stderr); 58 errx(1, "failed to create bio"); 59 } 60 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) 61 errx(1, "failed to read PEM"); 62 63 for (i = 0; i < sk_X509_INFO_num(xis); i++) { 64 if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) 65 continue; 66 if (!sk_X509_push(xs, x)) 67 errx(1, "failed to push X509"); 68 X509_up_ref(x); 69 } 70 71 *certs = xs; 72 xs = NULL; 73 74 sk_X509_INFO_pop_free(xis, X509_INFO_free); 75 sk_X509_pop_free(xs, X509_free); 76 BIO_free(bio); 77 78 return 1; 79 } 80 81 static int 82 verify_cert_cb(int ok, X509_STORE_CTX *xsc) 83 { 84 X509 *current_cert; 85 int verify_err; 86 87 current_cert = X509_STORE_CTX_get_current_cert(xsc); 88 if (current_cert != NULL) { 89 X509_NAME_print_ex_fp(stderr, 90 X509_get_subject_name(current_cert), 0, 91 XN_FLAG_ONELINE); 92 fprintf(stderr, "\n"); 93 } 94 95 verify_err = X509_STORE_CTX_get_error(xsc); 96 if (verify_err != X509_V_OK) { 97 fprintf(stderr, "verify error at depth %d: %s\n", 98 X509_STORE_CTX_get_error_depth(xsc), 99 X509_verify_cert_error_string(verify_err)); 100 } 101 102 return ok; 103 } 104 105 static void 106 verify_cert(const char *roots_dir, const char *roots_file, 107 const char *bundle_file, int *chains, int *error, int *error_depth, 108 int mode) 109 { 110 STACK_OF(X509) *roots = NULL, *bundle = NULL; 111 X509_STORE_CTX *xsc = NULL; 112 X509_STORE *store = NULL; 113 X509 *leaf = NULL; 114 int use_dir; 115 int ret; 116 117 *chains = 0; 118 *error = 0; 119 *error_depth = 0; 120 121 use_dir = (mode == MODE_MODERN_VFY_DIR); 122 123 if (!use_dir && !certs_from_file(roots_file, &roots)) 124 errx(1, "failed to load roots from '%s'", roots_file); 125 if (!certs_from_file(bundle_file, &bundle)) 126 errx(1, "failed to load bundle from '%s'", bundle_file); 127 if (sk_X509_num(bundle) < 1) 128 errx(1, "not enough certs in bundle"); 129 leaf = sk_X509_shift(bundle); 130 131 if ((xsc = X509_STORE_CTX_new()) == NULL) 132 errx(1, "X509_STORE_CTX"); 133 if (use_dir && (store = X509_STORE_new()) == NULL) 134 errx(1, "X509_STORE"); 135 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 136 ERR_print_errors_fp(stderr); 137 errx(1, "failed to init store context"); 138 } 139 if (use_dir) { 140 if (!X509_STORE_load_locations(store, NULL, roots_dir)) 141 errx(1, "failed to set by_dir directory of %s", roots_dir); 142 } 143 if (mode == MODE_LEGACY_VFY) 144 X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY); 145 else 146 X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc), 147 X509_V_FLAG_LEGACY_VERIFY); 148 149 if (verbose) 150 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 151 if (!use_dir) 152 X509_STORE_CTX_set0_trusted_stack(xsc, roots); 153 154 ret = X509_verify_cert(xsc); 155 156 *error = X509_STORE_CTX_get_error(xsc); 157 *error_depth = X509_STORE_CTX_get_error_depth(xsc); 158 159 if (ret == 1) { 160 *chains = 1; /* XXX */ 161 goto done; 162 } 163 164 if (*error == 0) 165 errx(1, "Error unset on failure!\n"); 166 167 fprintf(stderr, "failed to verify at %d: %s\n", 168 *error_depth, X509_verify_cert_error_string(*error)); 169 170 done: 171 sk_X509_pop_free(roots, X509_free); 172 sk_X509_pop_free(bundle, X509_free); 173 X509_STORE_free(store); 174 X509_STORE_CTX_free(xsc); 175 X509_free(leaf); 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 { 240 const char *id; 241 int want_chains; 242 int want_error; 243 int want_error_depth; 244 int want_legacy_error; 245 int want_legacy_error_depth; 246 int failing; 247 }; 248 249 struct verify_cert_test verify_cert_tests[] = { 250 { 251 .id = "1a", 252 .want_chains = 1, 253 }, 254 { 255 .id = "2a", 256 .want_chains = 1, 257 }, 258 { 259 .id = "2b", 260 .want_chains = 0, 261 .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 262 .want_error_depth = 0, 263 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 264 .want_legacy_error_depth = 0, 265 }, 266 { 267 .id = "2c", 268 .want_chains = 1, 269 }, 270 { 271 .id = "3a", 272 .want_chains = 1, 273 }, 274 { 275 .id = "3b", 276 .want_chains = 0, 277 .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 278 .want_error_depth = 2, 279 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 280 .want_legacy_error_depth = 2, 281 }, 282 { 283 .id = "3c", 284 .want_chains = 0, 285 .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 286 .want_error_depth = 1, 287 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 288 .want_legacy_error_depth = 1, 289 }, 290 { 291 .id = "3d", 292 .want_chains = 0, 293 .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 294 .want_error_depth = 0, 295 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 296 .want_legacy_error_depth = 0, 297 }, 298 { 299 .id = "3e", 300 .want_chains = 1, 301 }, 302 { 303 .id = "4a", 304 .want_chains = 2, 305 }, 306 { 307 .id = "4b", 308 .want_chains = 1, 309 }, 310 { 311 .id = "4c", 312 .want_chains = 1, 313 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 314 .want_legacy_error_depth = 1, 315 .failing = 1, 316 }, 317 { 318 .id = "4d", 319 .want_chains = 1, 320 }, 321 { 322 .id = "4e", 323 .want_chains = 1, 324 }, 325 { 326 .id = "4f", 327 .want_chains = 2, 328 }, 329 { 330 .id = "4g", 331 .want_chains = 1, 332 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 333 .want_legacy_error_depth = 1, 334 .failing = 1, 335 }, 336 { 337 .id = "4h", 338 .want_chains = 1, 339 }, 340 { 341 .id = "5a", 342 .want_chains = 2, 343 }, 344 { 345 .id = "5b", 346 .want_chains = 1, 347 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 348 .want_legacy_error_depth = 2, 349 .failing = 1, 350 }, 351 { 352 .id = "5c", 353 .want_chains = 1, 354 }, 355 { 356 .id = "5d", 357 .want_chains = 1, 358 }, 359 { 360 .id = "5e", 361 .want_chains = 1, 362 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 363 .want_legacy_error_depth = 1, 364 .failing = 1, 365 }, 366 { 367 .id = "5f", 368 .want_chains = 1, 369 }, 370 { 371 .id = "5g", 372 .want_chains = 2, 373 }, 374 { 375 .id = "5h", 376 .want_chains = 1, 377 }, 378 { 379 .id = "5i", 380 .want_chains = 1, 381 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 382 .want_legacy_error_depth = 1, 383 .failing = 1, 384 }, 385 { 386 .id = "6a", 387 .want_chains = 1, 388 }, 389 { 390 .id = "6b", 391 .want_chains = 1, 392 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 393 .want_error_depth = 0, 394 .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 395 .want_legacy_error_depth = 2, 396 .failing = 1, 397 }, 398 { 399 .id = "7a", 400 .want_chains = 1, 401 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 402 .want_error_depth = 0, 403 .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 404 .want_legacy_error_depth = 3, 405 .failing = 1, 406 }, 407 { 408 .id = "7b", 409 .want_chains = 1, 410 }, 411 { 412 .id = "8a", 413 .want_chains = 0, 414 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 415 .want_error_depth = 0, 416 .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 417 .want_legacy_error_depth = 0, 418 }, 419 { 420 .id = "9a", 421 .want_chains = 0, 422 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 423 .want_error_depth = 1, 424 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 425 .want_legacy_error_depth = 0, 426 }, 427 { 428 .id = "10a", 429 .want_chains = 1, 430 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 431 .want_error_depth = 0, 432 }, 433 { 434 .id = "10b", 435 .want_chains = 1, 436 }, 437 { 438 .id = "11a", 439 .want_chains = 1, 440 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 441 .want_error_depth = 0, 442 .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 443 .want_legacy_error_depth = 1, 444 .failing = 1, 445 }, 446 { 447 .id = "11b", 448 .want_chains = 1, 449 }, 450 { 451 .id = "12a", 452 .want_chains = 1, 453 }, 454 { 455 .id = "13a", 456 .want_chains = 1, 457 .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 458 .want_error_depth = 0, 459 .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 460 .want_legacy_error_depth = 2, 461 .failing = 1, 462 }, 463 }; 464 465 #define N_VERIFY_CERT_TESTS \ 466 (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests)) 467 468 static int 469 verify_cert_test(const char *certs_path, int mode) 470 { 471 char *roots_file, *bundle_file, *roots_dir; 472 struct verify_cert_test *vct; 473 int chains, error, error_depth; 474 int failed = 0; 475 size_t i; 476 477 for (i = 0; i < N_VERIFY_CERT_TESTS; i++) { 478 vct = &verify_cert_tests[i]; 479 480 if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path, 481 vct->id) == -1) 482 errx(1, "asprintf"); 483 if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path, 484 vct->id) == -1) 485 errx(1, "asprintf"); 486 if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1) 487 errx(1, "asprintf"); 488 489 error = 0; 490 error_depth = 0; 491 492 fprintf(stderr, "== Test %zu (%s)\n", i, vct->id); 493 if (mode == MODE_VERIFY) 494 verify_cert_new(roots_file, bundle_file, &chains); 495 else 496 verify_cert(roots_dir, roots_file, bundle_file, &chains, 497 &error, &error_depth, mode); 498 499 if ((mode == MODE_VERIFY && chains == vct->want_chains) || 500 (chains == 0 && vct->want_chains == 0) || 501 (chains == 1 && vct->want_chains > 0)) { 502 fprintf(stderr, "INFO: Succeeded with %d chains%s\n", 503 chains, vct->failing ? " (legacy failure)" : ""); 504 if (mode == MODE_LEGACY_VFY && vct->failing) 505 failed |= 1; 506 } else { 507 fprintf(stderr, "FAIL: Failed with %d chains%s\n", 508 chains, vct->failing ? " (legacy failure)" : ""); 509 if (!vct->failing) 510 failed |= 1; 511 } 512 513 if (mode == MODE_LEGACY_VFY) { 514 if (error != vct->want_legacy_error) { 515 fprintf(stderr, "FAIL: Got legacy error %d, " 516 "want %d\n", error, vct->want_legacy_error); 517 failed |= 1; 518 } 519 if (error_depth != vct->want_legacy_error_depth) { 520 fprintf(stderr, "FAIL: Got legacy error depth " 521 "%d, want %d\n", error_depth, 522 vct->want_legacy_error_depth); 523 failed |= 1; 524 } 525 } else if (mode == MODE_MODERN_VFY || mode == MODE_MODERN_VFY_DIR) { 526 if (error != vct->want_error) { 527 fprintf(stderr, "FAIL: Got error %d, want %d\n", 528 error, vct->want_error); 529 failed |= 1; 530 } 531 if (error_depth != vct->want_error_depth) { 532 fprintf(stderr, "FAIL: Got error depth %d, want" 533 " %d\n", error_depth, vct->want_error_depth); 534 failed |= 1; 535 } 536 } 537 538 fprintf(stderr, "\n"); 539 540 free(roots_file); 541 free(bundle_file); 542 free(roots_dir); 543 } 544 545 return failed; 546 } 547 548 int 549 main(int argc, char **argv) 550 { 551 int failed = 0; 552 553 if (argc != 2) { 554 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 555 exit(1); 556 } 557 558 fprintf(stderr, "\n\nTesting legacy x509_vfy\n"); 559 failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY); 560 fprintf(stderr, "\n\nTesting modern x509_vfy\n"); 561 failed |= verify_cert_test(argv[1], MODE_MODERN_VFY); 562 fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n"); 563 failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR); 564 fprintf(stderr, "\n\nTesting x509_verify\n"); 565 failed |= verify_cert_test(argv[1], MODE_VERIFY); 566 567 return (failed); 568 } 569