1*6822f9c8Santon /* $OpenBSD: verify.c,v 1.12 2024/08/23 12:56:26 anton Exp $ */ 2fbd64e3cSjsing /* 3fbd64e3cSjsing * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 4b68b63e3Sbeck * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org> 5fbd64e3cSjsing * 6fbd64e3cSjsing * Permission to use, copy, modify, and distribute this software for any 7fbd64e3cSjsing * purpose with or without fee is hereby granted, provided that the above 8fbd64e3cSjsing * copyright notice and this permission notice appear in all copies. 9fbd64e3cSjsing * 10fbd64e3cSjsing * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11fbd64e3cSjsing * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12fbd64e3cSjsing * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13fbd64e3cSjsing * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14fbd64e3cSjsing * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15fbd64e3cSjsing * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16fbd64e3cSjsing * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17fbd64e3cSjsing */ 18fbd64e3cSjsing 19fbd64e3cSjsing #include <err.h> 20fbd64e3cSjsing #include <string.h> 21fbd64e3cSjsing 22fbd64e3cSjsing #include <openssl/bio.h> 237cbb8da0Sjsing #include <openssl/crypto.h> 24fbd64e3cSjsing #include <openssl/err.h> 25fbd64e3cSjsing #include <openssl/pem.h> 26fbd64e3cSjsing #include <openssl/x509.h> 27fbd64e3cSjsing #include <openssl/x509v3.h> 28f45080b3Stb 29f45080b3Stb #include "x509_verify.h" 3023258cfeSbeck 3123258cfeSbeck #define MODE_MODERN_VFY 0 32b68b63e3Sbeck #define MODE_MODERN_VFY_DIR 1 33b68b63e3Sbeck #define MODE_LEGACY_VFY 2 34b68b63e3Sbeck #define MODE_VERIFY 3 35fbd64e3cSjsing 36fbd64e3cSjsing static int verbose = 1; 37fbd64e3cSjsing 38fbd64e3cSjsing static int 39fbd64e3cSjsing passwd_cb(char *buf, int size, int rwflag, void *u) 40fbd64e3cSjsing { 41fbd64e3cSjsing memset(buf, 0, size); 42fbd64e3cSjsing return (0); 43fbd64e3cSjsing } 44fbd64e3cSjsing 45fbd64e3cSjsing static int 46fbd64e3cSjsing certs_from_file(const char *filename, STACK_OF(X509) **certs) 47fbd64e3cSjsing { 48fbd64e3cSjsing STACK_OF(X509_INFO) *xis = NULL; 49fbd64e3cSjsing STACK_OF(X509) *xs = NULL; 50fbd64e3cSjsing BIO *bio = NULL; 51fbd64e3cSjsing X509 *x; 52fbd64e3cSjsing int i; 53fbd64e3cSjsing 54fbd64e3cSjsing if ((xs = sk_X509_new_null()) == NULL) 55fbd64e3cSjsing errx(1, "failed to create X509 stack"); 56fbd64e3cSjsing if ((bio = BIO_new_file(filename, "r")) == NULL) { 57fbd64e3cSjsing ERR_print_errors_fp(stderr); 58fbd64e3cSjsing errx(1, "failed to create bio"); 59fbd64e3cSjsing } 60fbd64e3cSjsing if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) 61fbd64e3cSjsing errx(1, "failed to read PEM"); 62fbd64e3cSjsing 63fbd64e3cSjsing for (i = 0; i < sk_X509_INFO_num(xis); i++) { 64fbd64e3cSjsing if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) 65fbd64e3cSjsing continue; 66fbd64e3cSjsing if (!sk_X509_push(xs, x)) 67fbd64e3cSjsing errx(1, "failed to push X509"); 68fbd64e3cSjsing X509_up_ref(x); 69fbd64e3cSjsing } 70fbd64e3cSjsing 71fbd64e3cSjsing *certs = xs; 72fbd64e3cSjsing xs = NULL; 73fbd64e3cSjsing 74fbd64e3cSjsing sk_X509_INFO_pop_free(xis, X509_INFO_free); 75fbd64e3cSjsing sk_X509_pop_free(xs, X509_free); 76fbd64e3cSjsing BIO_free(bio); 77fbd64e3cSjsing 78fbd64e3cSjsing return 1; 79fbd64e3cSjsing } 80fbd64e3cSjsing 81fbd64e3cSjsing static int 82fbd64e3cSjsing verify_cert_cb(int ok, X509_STORE_CTX *xsc) 83fbd64e3cSjsing { 84fbd64e3cSjsing X509 *current_cert; 85fbd64e3cSjsing int verify_err; 86fbd64e3cSjsing 87fbd64e3cSjsing current_cert = X509_STORE_CTX_get_current_cert(xsc); 88fbd64e3cSjsing if (current_cert != NULL) { 89fbd64e3cSjsing X509_NAME_print_ex_fp(stderr, 90fbd64e3cSjsing X509_get_subject_name(current_cert), 0, 91fbd64e3cSjsing XN_FLAG_ONELINE); 92fbd64e3cSjsing fprintf(stderr, "\n"); 93fbd64e3cSjsing } 94fbd64e3cSjsing 95fbd64e3cSjsing verify_err = X509_STORE_CTX_get_error(xsc); 96fbd64e3cSjsing if (verify_err != X509_V_OK) { 97fbd64e3cSjsing fprintf(stderr, "verify error at depth %d: %s\n", 98fbd64e3cSjsing X509_STORE_CTX_get_error_depth(xsc), 99fbd64e3cSjsing X509_verify_cert_error_string(verify_err)); 100fbd64e3cSjsing } 101fbd64e3cSjsing 102fbd64e3cSjsing return ok; 103fbd64e3cSjsing } 104fbd64e3cSjsing 105fbd64e3cSjsing static void 106b68b63e3Sbeck verify_cert(const char *roots_dir, const char *roots_file, 1077cbb8da0Sjsing const char *bundle_file, int *chains, int *error, int *error_depth, 1087cbb8da0Sjsing int mode) 109fbd64e3cSjsing { 110fbd64e3cSjsing STACK_OF(X509) *roots = NULL, *bundle = NULL; 111fbd64e3cSjsing X509_STORE_CTX *xsc = NULL; 112b68b63e3Sbeck X509_STORE *store = NULL; 113fbd64e3cSjsing X509 *leaf = NULL; 1147cbb8da0Sjsing int use_dir; 1157cbb8da0Sjsing int ret; 116fbd64e3cSjsing 117fbd64e3cSjsing *chains = 0; 1187cbb8da0Sjsing *error = 0; 1197cbb8da0Sjsing *error_depth = 0; 1207cbb8da0Sjsing 121b68b63e3Sbeck use_dir = (mode == MODE_MODERN_VFY_DIR); 122fbd64e3cSjsing 123b68b63e3Sbeck if (!use_dir && !certs_from_file(roots_file, &roots)) 124fbd64e3cSjsing errx(1, "failed to load roots from '%s'", roots_file); 125fbd64e3cSjsing if (!certs_from_file(bundle_file, &bundle)) 126fbd64e3cSjsing errx(1, "failed to load bundle from '%s'", bundle_file); 127fbd64e3cSjsing if (sk_X509_num(bundle) < 1) 128fbd64e3cSjsing errx(1, "not enough certs in bundle"); 129fbd64e3cSjsing leaf = sk_X509_shift(bundle); 130fbd64e3cSjsing 131fbd64e3cSjsing if ((xsc = X509_STORE_CTX_new()) == NULL) 132fbd64e3cSjsing errx(1, "X509_STORE_CTX"); 133b68b63e3Sbeck if (use_dir && (store = X509_STORE_new()) == NULL) 134b68b63e3Sbeck errx(1, "X509_STORE"); 135b68b63e3Sbeck if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 136fbd64e3cSjsing ERR_print_errors_fp(stderr); 137fbd64e3cSjsing errx(1, "failed to init store context"); 138fbd64e3cSjsing } 139b68b63e3Sbeck if (use_dir) { 140b68b63e3Sbeck if (!X509_STORE_load_locations(store, NULL, roots_dir)) 141b68b63e3Sbeck errx(1, "failed to set by_dir directory of %s", roots_dir); 142b68b63e3Sbeck } 143920585faStb if (mode == MODE_LEGACY_VFY) 144920585faStb X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY); 145920585faStb else 146920585faStb X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc), 147920585faStb X509_V_FLAG_LEGACY_VERIFY); 14823258cfeSbeck 149fbd64e3cSjsing if (verbose) 150fbd64e3cSjsing X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 151b68b63e3Sbeck if (!use_dir) 152fbd64e3cSjsing X509_STORE_CTX_set0_trusted_stack(xsc, roots); 1537cbb8da0Sjsing 1547cbb8da0Sjsing ret = X509_verify_cert(xsc); 1557cbb8da0Sjsing 1567cbb8da0Sjsing *error = X509_STORE_CTX_get_error(xsc); 1577cbb8da0Sjsing *error_depth = X509_STORE_CTX_get_error_depth(xsc); 1587cbb8da0Sjsing 1597cbb8da0Sjsing if (ret == 1) { 160fbd64e3cSjsing *chains = 1; /* XXX */ 161fbd64e3cSjsing goto done; 162fbd64e3cSjsing } 163fbd64e3cSjsing 1647cbb8da0Sjsing if (*error == 0) 165*6822f9c8Santon errx(1, "Error unset on failure!"); 16628dcb2c6Sbeck 167fbd64e3cSjsing fprintf(stderr, "failed to verify at %d: %s\n", 1687cbb8da0Sjsing *error_depth, X509_verify_cert_error_string(*error)); 169fbd64e3cSjsing 170fbd64e3cSjsing done: 171fbd64e3cSjsing sk_X509_pop_free(roots, X509_free); 172fbd64e3cSjsing sk_X509_pop_free(bundle, X509_free); 173b68b63e3Sbeck X509_STORE_free(store); 174fbd64e3cSjsing X509_STORE_CTX_free(xsc); 175fbd64e3cSjsing X509_free(leaf); 176fbd64e3cSjsing } 177fbd64e3cSjsing 17823258cfeSbeck static void 17923258cfeSbeck verify_cert_new(const char *roots_file, const char *bundle_file, int *chains) 18023258cfeSbeck { 18123258cfeSbeck STACK_OF(X509) *roots = NULL, *bundle = NULL; 18223258cfeSbeck X509_STORE_CTX *xsc = NULL; 18323258cfeSbeck X509 *leaf = NULL; 18423258cfeSbeck struct x509_verify_ctx *ctx; 18523258cfeSbeck 18623258cfeSbeck *chains = 0; 18723258cfeSbeck 18823258cfeSbeck if (!certs_from_file(roots_file, &roots)) 18923258cfeSbeck errx(1, "failed to load roots from '%s'", roots_file); 19023258cfeSbeck if (!certs_from_file(bundle_file, &bundle)) 19123258cfeSbeck errx(1, "failed to load bundle from '%s'", bundle_file); 19223258cfeSbeck if (sk_X509_num(bundle) < 1) 19323258cfeSbeck errx(1, "not enough certs in bundle"); 19423258cfeSbeck leaf = sk_X509_shift(bundle); 19523258cfeSbeck 19623258cfeSbeck if ((xsc = X509_STORE_CTX_new()) == NULL) 19723258cfeSbeck errx(1, "X509_STORE_CTX"); 19823258cfeSbeck if (!X509_STORE_CTX_init(xsc, NULL, leaf, bundle)) { 19923258cfeSbeck ERR_print_errors_fp(stderr); 20023258cfeSbeck errx(1, "failed to init store context"); 20123258cfeSbeck } 20223258cfeSbeck if (verbose) 20323258cfeSbeck X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 20423258cfeSbeck 20523258cfeSbeck if ((ctx = x509_verify_ctx_new(roots)) == NULL) 20623258cfeSbeck errx(1, "failed to create ctx"); 20723258cfeSbeck if (!x509_verify_ctx_set_intermediates(ctx, bundle)) 20823258cfeSbeck errx(1, "failed to set intermediates"); 20923258cfeSbeck 21023258cfeSbeck if ((*chains = x509_verify(ctx, leaf, NULL)) == 0) { 21123258cfeSbeck fprintf(stderr, "failed to verify at %lu: %s\n", 21223258cfeSbeck x509_verify_ctx_error_depth(ctx), 21323258cfeSbeck x509_verify_ctx_error_string(ctx)); 21423258cfeSbeck } else { 2154820661fStb int c; 2164820661fStb 2174820661fStb for (c = 0; verbose && c < *chains; c++) { 2184820661fStb STACK_OF(X509) *chain; 2194820661fStb int i; 2204820661fStb 22123258cfeSbeck fprintf(stderr, "Chain %d\n--------\n", c); 2224820661fStb chain = x509_verify_ctx_chain(ctx, c); 2234820661fStb for (i = 0; i < sk_X509_num(chain); i++) { 22423258cfeSbeck X509 *cert = sk_X509_value(chain, i); 22523258cfeSbeck X509_NAME_print_ex_fp(stderr, 22623258cfeSbeck X509_get_subject_name(cert), 0, 22723258cfeSbeck XN_FLAG_ONELINE); 22823258cfeSbeck fprintf(stderr, "\n"); 22923258cfeSbeck } 23023258cfeSbeck } 23123258cfeSbeck } 23223258cfeSbeck sk_X509_pop_free(roots, X509_free); 23323258cfeSbeck sk_X509_pop_free(bundle, X509_free); 23423258cfeSbeck X509_free(leaf); 235047399e3Stb X509_STORE_CTX_free(xsc); 236047399e3Stb x509_verify_ctx_free(ctx); 23723258cfeSbeck } 23823258cfeSbeck 2397cbb8da0Sjsing struct verify_cert_test { 2407cbb8da0Sjsing const char *id; 2417cbb8da0Sjsing int want_chains; 2427cbb8da0Sjsing int want_error; 2437cbb8da0Sjsing int want_error_depth; 2447cbb8da0Sjsing int want_legacy_error; 2457cbb8da0Sjsing int want_legacy_error_depth; 2467cbb8da0Sjsing int failing; 2477cbb8da0Sjsing }; 2487cbb8da0Sjsing 249fbd64e3cSjsing struct verify_cert_test verify_cert_tests[] = { 250fbd64e3cSjsing { 251fbd64e3cSjsing .id = "1a", 252fbd64e3cSjsing .want_chains = 1, 253fbd64e3cSjsing }, 254fbd64e3cSjsing { 255fbd64e3cSjsing .id = "2a", 256fbd64e3cSjsing .want_chains = 1, 257fbd64e3cSjsing }, 258fbd64e3cSjsing { 259fbd64e3cSjsing .id = "2b", 260fbd64e3cSjsing .want_chains = 0, 2617cbb8da0Sjsing .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2627cbb8da0Sjsing .want_error_depth = 0, 2637cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2647cbb8da0Sjsing .want_legacy_error_depth = 0, 265fbd64e3cSjsing }, 266fbd64e3cSjsing { 2676f4c0c98Sbeck .id = "2c", 2686f4c0c98Sbeck .want_chains = 1, 2696f4c0c98Sbeck }, 2706f4c0c98Sbeck { 271fbd64e3cSjsing .id = "3a", 272fbd64e3cSjsing .want_chains = 1, 273fbd64e3cSjsing }, 274fbd64e3cSjsing { 275fbd64e3cSjsing .id = "3b", 276fbd64e3cSjsing .want_chains = 0, 2777cbb8da0Sjsing .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2787cbb8da0Sjsing .want_error_depth = 2, 2797cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2807cbb8da0Sjsing .want_legacy_error_depth = 2, 281fbd64e3cSjsing }, 282fbd64e3cSjsing { 283fbd64e3cSjsing .id = "3c", 284fbd64e3cSjsing .want_chains = 0, 2857cbb8da0Sjsing .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2867cbb8da0Sjsing .want_error_depth = 1, 2877cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2887cbb8da0Sjsing .want_legacy_error_depth = 1, 289fbd64e3cSjsing }, 290fbd64e3cSjsing { 291fbd64e3cSjsing .id = "3d", 292fbd64e3cSjsing .want_chains = 0, 2937cbb8da0Sjsing .want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2947cbb8da0Sjsing .want_error_depth = 0, 2957cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 2967cbb8da0Sjsing .want_legacy_error_depth = 0, 297fbd64e3cSjsing }, 298fbd64e3cSjsing { 299fbd64e3cSjsing .id = "3e", 300fbd64e3cSjsing .want_chains = 1, 301fbd64e3cSjsing }, 302fbd64e3cSjsing { 303fbd64e3cSjsing .id = "4a", 304fbd64e3cSjsing .want_chains = 2, 305fbd64e3cSjsing }, 306fbd64e3cSjsing { 307fbd64e3cSjsing .id = "4b", 308fbd64e3cSjsing .want_chains = 1, 309fbd64e3cSjsing }, 310fbd64e3cSjsing { 311fbd64e3cSjsing .id = "4c", 312fbd64e3cSjsing .want_chains = 1, 3137cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 3147cbb8da0Sjsing .want_legacy_error_depth = 1, 315fbd64e3cSjsing .failing = 1, 316fbd64e3cSjsing }, 317fbd64e3cSjsing { 318fbd64e3cSjsing .id = "4d", 319fbd64e3cSjsing .want_chains = 1, 320fbd64e3cSjsing }, 321fbd64e3cSjsing { 322fbd64e3cSjsing .id = "4e", 323fbd64e3cSjsing .want_chains = 1, 324fbd64e3cSjsing }, 325fbd64e3cSjsing { 326fbd64e3cSjsing .id = "4f", 327fbd64e3cSjsing .want_chains = 2, 328fbd64e3cSjsing }, 329fbd64e3cSjsing { 330fbd64e3cSjsing .id = "4g", 331fbd64e3cSjsing .want_chains = 1, 3327cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 3337cbb8da0Sjsing .want_legacy_error_depth = 1, 334fbd64e3cSjsing .failing = 1, 335fbd64e3cSjsing }, 336fbd64e3cSjsing { 337fbd64e3cSjsing .id = "4h", 338fbd64e3cSjsing .want_chains = 1, 339fbd64e3cSjsing }, 340fbd64e3cSjsing { 341fbd64e3cSjsing .id = "5a", 342fbd64e3cSjsing .want_chains = 2, 343fbd64e3cSjsing }, 344fbd64e3cSjsing { 345fbd64e3cSjsing .id = "5b", 346fbd64e3cSjsing .want_chains = 1, 3477cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 3487cbb8da0Sjsing .want_legacy_error_depth = 2, 349fbd64e3cSjsing .failing = 1, 350fbd64e3cSjsing }, 351fbd64e3cSjsing { 352fbd64e3cSjsing .id = "5c", 353fbd64e3cSjsing .want_chains = 1, 354fbd64e3cSjsing }, 355fbd64e3cSjsing { 356fbd64e3cSjsing .id = "5d", 357fbd64e3cSjsing .want_chains = 1, 358fbd64e3cSjsing }, 359fbd64e3cSjsing { 360fbd64e3cSjsing .id = "5e", 361fbd64e3cSjsing .want_chains = 1, 3627cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 3637cbb8da0Sjsing .want_legacy_error_depth = 1, 364fbd64e3cSjsing .failing = 1, 365fbd64e3cSjsing }, 366fbd64e3cSjsing { 367fbd64e3cSjsing .id = "5f", 368fbd64e3cSjsing .want_chains = 1, 369fbd64e3cSjsing }, 370fbd64e3cSjsing { 371fbd64e3cSjsing .id = "5g", 372fbd64e3cSjsing .want_chains = 2, 373fbd64e3cSjsing }, 374fbd64e3cSjsing { 375fbd64e3cSjsing .id = "5h", 376fbd64e3cSjsing .want_chains = 1, 377fbd64e3cSjsing }, 378fbd64e3cSjsing { 379fbd64e3cSjsing .id = "5i", 380fbd64e3cSjsing .want_chains = 1, 3817cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 3827cbb8da0Sjsing .want_legacy_error_depth = 1, 383fbd64e3cSjsing .failing = 1, 384fbd64e3cSjsing }, 385fbd64e3cSjsing { 386fbd64e3cSjsing .id = "6a", 387fbd64e3cSjsing .want_chains = 1, 388fbd64e3cSjsing }, 389fbd64e3cSjsing { 390fbd64e3cSjsing .id = "6b", 391fbd64e3cSjsing .want_chains = 1, 3927cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 3937cbb8da0Sjsing .want_error_depth = 0, 3947cbb8da0Sjsing .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 3957cbb8da0Sjsing .want_legacy_error_depth = 2, 396fbd64e3cSjsing .failing = 1, 397fbd64e3cSjsing }, 398fbd64e3cSjsing { 399fbd64e3cSjsing .id = "7a", 400fbd64e3cSjsing .want_chains = 1, 4017cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 4027cbb8da0Sjsing .want_error_depth = 0, 4037cbb8da0Sjsing .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 4047cbb8da0Sjsing .want_legacy_error_depth = 3, 405fbd64e3cSjsing .failing = 1, 406fbd64e3cSjsing }, 407fbd64e3cSjsing { 408fbd64e3cSjsing .id = "7b", 409fbd64e3cSjsing .want_chains = 1, 410fbd64e3cSjsing }, 411fbd64e3cSjsing { 412fbd64e3cSjsing .id = "8a", 413fbd64e3cSjsing .want_chains = 0, 4147cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 4157cbb8da0Sjsing .want_error_depth = 0, 4167cbb8da0Sjsing .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 4177cbb8da0Sjsing .want_legacy_error_depth = 0, 418fbd64e3cSjsing }, 419fbd64e3cSjsing { 420fbd64e3cSjsing .id = "9a", 421fbd64e3cSjsing .want_chains = 0, 4227cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 4237cbb8da0Sjsing .want_error_depth = 1, 4247cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 4257cbb8da0Sjsing .want_legacy_error_depth = 0, 426fbd64e3cSjsing }, 427fbd64e3cSjsing { 428fbd64e3cSjsing .id = "10a", 429fbd64e3cSjsing .want_chains = 1, 4307cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 4317cbb8da0Sjsing .want_error_depth = 0, 432fbd64e3cSjsing }, 433fbd64e3cSjsing { 434fbd64e3cSjsing .id = "10b", 435fbd64e3cSjsing .want_chains = 1, 436fbd64e3cSjsing }, 437fbd64e3cSjsing { 438fbd64e3cSjsing .id = "11a", 439fbd64e3cSjsing .want_chains = 1, 4407cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 4417cbb8da0Sjsing .want_error_depth = 0, 4427cbb8da0Sjsing .want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 4437cbb8da0Sjsing .want_legacy_error_depth = 1, 444fbd64e3cSjsing .failing = 1, 445fbd64e3cSjsing }, 446fbd64e3cSjsing { 447fbd64e3cSjsing .id = "11b", 448fbd64e3cSjsing .want_chains = 1, 449fbd64e3cSjsing }, 450fbd64e3cSjsing { 451fbd64e3cSjsing .id = "12a", 452fbd64e3cSjsing .want_chains = 1, 453fbd64e3cSjsing }, 454fbd64e3cSjsing { 455fbd64e3cSjsing .id = "13a", 456fbd64e3cSjsing .want_chains = 1, 4577cbb8da0Sjsing .want_error = X509_V_ERR_CERT_HAS_EXPIRED, 4587cbb8da0Sjsing .want_error_depth = 0, 4597cbb8da0Sjsing .want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED, 4607cbb8da0Sjsing .want_legacy_error_depth = 2, 46107499e19Sjsing .failing = 1, 462fbd64e3cSjsing }, 463fbd64e3cSjsing }; 464fbd64e3cSjsing 465fbd64e3cSjsing #define N_VERIFY_CERT_TESTS \ 466fbd64e3cSjsing (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests)) 467fbd64e3cSjsing 468fbd64e3cSjsing static int 46923258cfeSbeck verify_cert_test(const char *certs_path, int mode) 470fbd64e3cSjsing { 471b68b63e3Sbeck char *roots_file, *bundle_file, *roots_dir; 472fbd64e3cSjsing struct verify_cert_test *vct; 4737cbb8da0Sjsing int chains, error, error_depth; 474fbd64e3cSjsing int failed = 0; 475fbd64e3cSjsing size_t i; 476fbd64e3cSjsing 477fbd64e3cSjsing for (i = 0; i < N_VERIFY_CERT_TESTS; i++) { 478fbd64e3cSjsing vct = &verify_cert_tests[i]; 479fbd64e3cSjsing 480fbd64e3cSjsing if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path, 481fbd64e3cSjsing vct->id) == -1) 482fbd64e3cSjsing errx(1, "asprintf"); 483fbd64e3cSjsing if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path, 484fbd64e3cSjsing vct->id) == -1) 485fbd64e3cSjsing errx(1, "asprintf"); 486b68b63e3Sbeck if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1) 487b68b63e3Sbeck errx(1, "asprintf"); 488fbd64e3cSjsing 4897cbb8da0Sjsing error = 0; 4907cbb8da0Sjsing error_depth = 0; 4917cbb8da0Sjsing 492fbd64e3cSjsing fprintf(stderr, "== Test %zu (%s)\n", i, vct->id); 49323258cfeSbeck if (mode == MODE_VERIFY) 49423258cfeSbeck verify_cert_new(roots_file, bundle_file, &chains); 49523258cfeSbeck else 4967cbb8da0Sjsing verify_cert(roots_dir, roots_file, bundle_file, &chains, 4977cbb8da0Sjsing &error, &error_depth, mode); 4987cbb8da0Sjsing 499b68b63e3Sbeck if ((mode == MODE_VERIFY && chains == vct->want_chains) || 50023258cfeSbeck (chains == 0 && vct->want_chains == 0) || 501fbd64e3cSjsing (chains == 1 && vct->want_chains > 0)) { 502fbd64e3cSjsing fprintf(stderr, "INFO: Succeeded with %d chains%s\n", 50323258cfeSbeck chains, vct->failing ? " (legacy failure)" : ""); 50423258cfeSbeck if (mode == MODE_LEGACY_VFY && vct->failing) 505fbd64e3cSjsing failed |= 1; 506fbd64e3cSjsing } else { 507fbd64e3cSjsing fprintf(stderr, "FAIL: Failed with %d chains%s\n", 50823258cfeSbeck chains, vct->failing ? " (legacy failure)" : ""); 509fbd64e3cSjsing if (!vct->failing) 510fbd64e3cSjsing failed |= 1; 511fbd64e3cSjsing } 5127cbb8da0Sjsing 5137cbb8da0Sjsing if (mode == MODE_LEGACY_VFY) { 5147cbb8da0Sjsing if (error != vct->want_legacy_error) { 5157cbb8da0Sjsing fprintf(stderr, "FAIL: Got legacy error %d, " 5167cbb8da0Sjsing "want %d\n", error, vct->want_legacy_error); 5177cbb8da0Sjsing failed |= 1; 5187cbb8da0Sjsing } 5197cbb8da0Sjsing if (error_depth != vct->want_legacy_error_depth) { 5207cbb8da0Sjsing fprintf(stderr, "FAIL: Got legacy error depth " 5217cbb8da0Sjsing "%d, want %d\n", error_depth, 5227cbb8da0Sjsing vct->want_legacy_error_depth); 5237cbb8da0Sjsing failed |= 1; 5247cbb8da0Sjsing } 5257cbb8da0Sjsing } else if (mode == MODE_MODERN_VFY || mode == MODE_MODERN_VFY_DIR) { 5267cbb8da0Sjsing if (error != vct->want_error) { 5277cbb8da0Sjsing fprintf(stderr, "FAIL: Got error %d, want %d\n", 5287cbb8da0Sjsing error, vct->want_error); 5297cbb8da0Sjsing failed |= 1; 5307cbb8da0Sjsing } 5317cbb8da0Sjsing if (error_depth != vct->want_error_depth) { 5327cbb8da0Sjsing fprintf(stderr, "FAIL: Got error depth %d, want" 5337cbb8da0Sjsing " %d\n", error_depth, vct->want_error_depth); 5347cbb8da0Sjsing failed |= 1; 5357cbb8da0Sjsing } 5367cbb8da0Sjsing } 5377cbb8da0Sjsing 538fbd64e3cSjsing fprintf(stderr, "\n"); 539fbd64e3cSjsing 540fbd64e3cSjsing free(roots_file); 541fbd64e3cSjsing free(bundle_file); 542b68b63e3Sbeck free(roots_dir); 543fbd64e3cSjsing } 544fbd64e3cSjsing 545fbd64e3cSjsing return failed; 546fbd64e3cSjsing } 547fbd64e3cSjsing 548fbd64e3cSjsing int 549fbd64e3cSjsing main(int argc, char **argv) 550fbd64e3cSjsing { 551fbd64e3cSjsing int failed = 0; 552fbd64e3cSjsing 553fbd64e3cSjsing if (argc != 2) { 554fbd64e3cSjsing fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 555fbd64e3cSjsing exit(1); 556fbd64e3cSjsing } 557fbd64e3cSjsing 55823258cfeSbeck fprintf(stderr, "\n\nTesting legacy x509_vfy\n"); 55923258cfeSbeck failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY); 56023258cfeSbeck fprintf(stderr, "\n\nTesting modern x509_vfy\n"); 56123258cfeSbeck failed |= verify_cert_test(argv[1], MODE_MODERN_VFY); 562b68b63e3Sbeck fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n"); 563b68b63e3Sbeck failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR); 56423258cfeSbeck fprintf(stderr, "\n\nTesting x509_verify\n"); 56523258cfeSbeck failed |= verify_cert_test(argv[1], MODE_VERIFY); 566fbd64e3cSjsing 567fbd64e3cSjsing return (failed); 568fbd64e3cSjsing } 569