1*6822f9c8Santon /* $OpenBSD: callbackfailures.c,v 1.3 2024/08/23 12:56:26 anton Exp $ */ 2d8913d6aSbeck /* 3d8913d6aSbeck * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 4d8913d6aSbeck * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org> 5d8913d6aSbeck * 6d8913d6aSbeck * Permission to use, copy, modify, and distribute this software for any 7d8913d6aSbeck * purpose with or without fee is hereby granted, provided that the above 8d8913d6aSbeck * copyright notice and this permission notice appear in all copies. 9d8913d6aSbeck * 10d8913d6aSbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11d8913d6aSbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12d8913d6aSbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13d8913d6aSbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14d8913d6aSbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15d8913d6aSbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16d8913d6aSbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17d8913d6aSbeck */ 18d8913d6aSbeck 19d8913d6aSbeck #include <err.h> 20d8913d6aSbeck #include <string.h> 21d8913d6aSbeck 22d8913d6aSbeck #include <openssl/bio.h> 23d8913d6aSbeck #include <openssl/err.h> 24d8913d6aSbeck #include <openssl/pem.h> 25d8913d6aSbeck #include <openssl/x509.h> 26d8913d6aSbeck #include <openssl/x509v3.h> 27f45080b3Stb 28f45080b3Stb #include "x509_verify.h" 29d8913d6aSbeck 30d8913d6aSbeck #define MODE_MODERN_VFY 0 31d8913d6aSbeck #define MODE_MODERN_VFY_DIR 1 32d8913d6aSbeck #define MODE_LEGACY_VFY 2 33d8913d6aSbeck #define MODE_VERIFY 3 34d8913d6aSbeck 35d8913d6aSbeck static int verbose = 1; 36d8913d6aSbeck 37d8913d6aSbeck static int expected_depth; 38d8913d6aSbeck static int expected_error; 39d8913d6aSbeck static int seen_depth; 40d8913d6aSbeck static int seen_error; 41d8913d6aSbeck 42d8913d6aSbeck static int 43d8913d6aSbeck passwd_cb(char *buf, int size, int rwflag, void *u) 44d8913d6aSbeck { 45d8913d6aSbeck memset(buf, 0, size); 46d8913d6aSbeck return (0); 47d8913d6aSbeck } 48d8913d6aSbeck 49d8913d6aSbeck static int 50d8913d6aSbeck certs_from_file(const char *filename, STACK_OF(X509) **certs, int clear) 51d8913d6aSbeck { 52d8913d6aSbeck STACK_OF(X509_INFO) *xis = NULL; 53d8913d6aSbeck STACK_OF(X509) *xs = NULL; 54d8913d6aSbeck BIO *bio = NULL; 55d8913d6aSbeck X509 *x; 56d8913d6aSbeck int i; 57d8913d6aSbeck 58d8913d6aSbeck if (clear) { 59d8913d6aSbeck if ((xs = sk_X509_new_null()) == NULL) 60d8913d6aSbeck errx(1, "failed to create X509 stack"); 61d8913d6aSbeck } else 62d8913d6aSbeck xs = *certs; 63d8913d6aSbeck if ((bio = BIO_new_file(filename, "r")) == NULL) { 64d8913d6aSbeck ERR_print_errors_fp(stderr); 65d8913d6aSbeck errx(1, "failed to create bio"); 66d8913d6aSbeck } 67d8913d6aSbeck if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) 68d8913d6aSbeck errx(1, "failed to read PEM"); 69d8913d6aSbeck 70d8913d6aSbeck for (i = 0; i < sk_X509_INFO_num(xis); i++) { 71d8913d6aSbeck if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) 72d8913d6aSbeck continue; 73d8913d6aSbeck if (!sk_X509_push(xs, x)) 74d8913d6aSbeck errx(1, "failed to push X509"); 75d8913d6aSbeck X509_up_ref(x); 76d8913d6aSbeck } 77d8913d6aSbeck 78d8913d6aSbeck *certs = xs; 79d8913d6aSbeck xs = NULL; 80d8913d6aSbeck 81d8913d6aSbeck sk_X509_INFO_pop_free(xis, X509_INFO_free); 82d8913d6aSbeck sk_X509_pop_free(xs, X509_free); 83d8913d6aSbeck BIO_free(bio); 84d8913d6aSbeck 85d8913d6aSbeck return 1; 86d8913d6aSbeck } 87d8913d6aSbeck 88d8913d6aSbeck static int 89d8913d6aSbeck verify_cert_cb(int ok, X509_STORE_CTX *xsc) 90d8913d6aSbeck { 91d8913d6aSbeck X509 *current_cert; 92d8913d6aSbeck int verify_err; 93d8913d6aSbeck 94d8913d6aSbeck current_cert = X509_STORE_CTX_get_current_cert(xsc); 95d8913d6aSbeck if (current_cert != NULL) { 96d8913d6aSbeck X509_NAME_print_ex_fp(stderr, 97d8913d6aSbeck X509_get_subject_name(current_cert), 0, 98d8913d6aSbeck XN_FLAG_ONELINE); 99d8913d6aSbeck fprintf(stderr, "\n"); 100d8913d6aSbeck } 101d8913d6aSbeck 102d8913d6aSbeck verify_err = X509_STORE_CTX_get_error(xsc); 103d8913d6aSbeck if (verify_err != X509_V_OK) { 104d8913d6aSbeck seen_depth = X509_STORE_CTX_get_error_depth(xsc); 105d8913d6aSbeck seen_error = verify_err; 106d8913d6aSbeck fprintf(stderr, "verify error at depth %d: %s\n", 107d8913d6aSbeck X509_STORE_CTX_get_error_depth(xsc), 108d8913d6aSbeck X509_verify_cert_error_string(verify_err)); 109d8913d6aSbeck } 110d8913d6aSbeck 111d8913d6aSbeck fprintf(stderr, "chain of length %d\n", sk_X509_num (X509_STORE_CTX_get0_chain (xsc))); 112d8913d6aSbeck 113d8913d6aSbeck return ok; 114d8913d6aSbeck } 115d8913d6aSbeck 116d8913d6aSbeck static void 117d8913d6aSbeck verify_cert(const char *roots_dir, const char *roots_file, 118d8913d6aSbeck const char *bundle_file, const char*bundle_file2, int *chains, int mode) 119d8913d6aSbeck { 120d8913d6aSbeck STACK_OF(X509) *roots = NULL, *bundle = NULL; 121d8913d6aSbeck X509_STORE_CTX *xsc = NULL; 122d8913d6aSbeck X509_STORE *store = NULL; 123d8913d6aSbeck int verify_err, use_dir; 124d8913d6aSbeck X509 *leaf = NULL; 125d8913d6aSbeck 126d8913d6aSbeck *chains = 0; 127d8913d6aSbeck use_dir = (mode == MODE_MODERN_VFY_DIR); 128d8913d6aSbeck 129d8913d6aSbeck if (!use_dir && !certs_from_file(roots_file, &roots, 1)) 130d8913d6aSbeck errx(1, "failed to load roots from '%s'", roots_file); 131d8913d6aSbeck if (!certs_from_file(bundle_file, &bundle, 1)) 132d8913d6aSbeck errx(1, "failed to load bundle from '%s'", bundle_file); 133d8913d6aSbeck if (!certs_from_file(bundle_file, &bundle, 0)) 134d8913d6aSbeck errx(1, "failed to load bundle from '%s'", bundle_file2); 135d8913d6aSbeck if (sk_X509_num(bundle) < 1) 136d8913d6aSbeck errx(1, "not enough certs in bundle"); 137d8913d6aSbeck leaf = sk_X509_shift(bundle); 138d8913d6aSbeck 139d8913d6aSbeck if ((xsc = X509_STORE_CTX_new()) == NULL) 140d8913d6aSbeck errx(1, "X509_STORE_CTX"); 141d8913d6aSbeck if (use_dir && (store = X509_STORE_new()) == NULL) 142d8913d6aSbeck errx(1, "X509_STORE"); 143d8913d6aSbeck if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 144d8913d6aSbeck ERR_print_errors_fp(stderr); 145d8913d6aSbeck errx(1, "failed to init store context"); 146d8913d6aSbeck } 147d8913d6aSbeck 148d8913d6aSbeck if (use_dir) { 149d8913d6aSbeck if (!X509_STORE_load_locations(store, NULL, roots_dir)) 150d8913d6aSbeck errx(1, "failed to set by_dir directory of %s", roots_dir); 151d8913d6aSbeck } 152d8913d6aSbeck if (mode == MODE_LEGACY_VFY) 153d8913d6aSbeck X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY); 154d8913d6aSbeck else 155d8913d6aSbeck X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc), 156d8913d6aSbeck X509_V_FLAG_LEGACY_VERIFY); 157d8913d6aSbeck 158d8913d6aSbeck if (verbose) 159d8913d6aSbeck X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 160d8913d6aSbeck if (!use_dir) 161d8913d6aSbeck X509_STORE_CTX_set0_trusted_stack(xsc, roots); 162d8913d6aSbeck 163d8913d6aSbeck if (X509_verify_cert(xsc) == 1) { 164d8913d6aSbeck *chains = 1; /* XXX */ 165d8913d6aSbeck goto done; 166d8913d6aSbeck } 167d8913d6aSbeck 168d8913d6aSbeck verify_err = X509_STORE_CTX_get_error(xsc); 169d8913d6aSbeck if (verify_err == 0) 170*6822f9c8Santon errx(1, "Error unset on failure!"); 171d8913d6aSbeck 172d8913d6aSbeck fprintf(stderr, "failed to verify at %d: %s\n", 173d8913d6aSbeck X509_STORE_CTX_get_error_depth(xsc), 174d8913d6aSbeck X509_verify_cert_error_string(verify_err)); 175d8913d6aSbeck 176d8913d6aSbeck done: 177d8913d6aSbeck sk_X509_pop_free(roots, X509_free); 178d8913d6aSbeck sk_X509_pop_free(bundle, X509_free); 179d8913d6aSbeck X509_STORE_free(store); 180d8913d6aSbeck X509_STORE_CTX_free(xsc); 181d8913d6aSbeck X509_free(leaf); 182d8913d6aSbeck } 183d8913d6aSbeck 184d8913d6aSbeck struct verify_cert_test { 185d8913d6aSbeck const char *id; 186d8913d6aSbeck int want_chains; 187d8913d6aSbeck int failing; 188d8913d6aSbeck int depth; 189d8913d6aSbeck int error; 190d8913d6aSbeck }; 191d8913d6aSbeck 192d8913d6aSbeck struct verify_cert_test verify_cert_tests[] = { 193d8913d6aSbeck { 194d8913d6aSbeck .id = "1a", 195d8913d6aSbeck .want_chains = 0, 196d8913d6aSbeck .depth = 0, 197d8913d6aSbeck .error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 198d8913d6aSbeck }, 199d8913d6aSbeck { 200d8913d6aSbeck .id = "2a", 201d8913d6aSbeck .want_chains = 0, 202d8913d6aSbeck .depth = 1, 203d8913d6aSbeck .error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 204d8913d6aSbeck }, 205d8913d6aSbeck { 206d8913d6aSbeck .id = "2c", 207d8913d6aSbeck .want_chains = 0, 208d8913d6aSbeck .depth = 2, 209d8913d6aSbeck .error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, 210d8913d6aSbeck }, 211d8913d6aSbeck }; 212d8913d6aSbeck 213d8913d6aSbeck #define N_VERIFY_CERT_TESTS \ 214d8913d6aSbeck (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests)) 215d8913d6aSbeck 216d8913d6aSbeck static int 217d8913d6aSbeck verify_cert_test(const char *certs_path, int mode) 218d8913d6aSbeck { 219d8913d6aSbeck char *roots_file, *bundle_file, *bundle_file2, *roots_dir; 220d8913d6aSbeck struct verify_cert_test *vct; 221d8913d6aSbeck int failed = 0; 222d8913d6aSbeck int chains; 223d8913d6aSbeck size_t i; 224d8913d6aSbeck 225d8913d6aSbeck for (i = 0; i < N_VERIFY_CERT_TESTS; i++) { 226d8913d6aSbeck vct = &verify_cert_tests[i]; 227d8913d6aSbeck 228d8913d6aSbeck if (asprintf(&roots_file, "/etc/ssl/cert.pem") == -1) 229d8913d6aSbeck errx(1, "asprintf"); 230d8913d6aSbeck if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path, 231d8913d6aSbeck vct->id) == -1) 232d8913d6aSbeck errx(1, "asprintf"); 233d8913d6aSbeck if (asprintf(&bundle_file2, "%s/%s/roots.pem", certs_path, 234d8913d6aSbeck vct->id) == -1) 235d8913d6aSbeck errx(1, "asprintf"); 236d8913d6aSbeck if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1) 237d8913d6aSbeck errx(1, "asprintf"); 238d8913d6aSbeck 239d8913d6aSbeck fprintf(stderr, "== Test %zu (%s)\n", i, vct->id); 240d8913d6aSbeck fprintf(stderr, "== depth %d\n", vct->depth); 241d8913d6aSbeck fprintf(stderr, "== error %d\n", vct->error); 242d8913d6aSbeck expected_depth = vct->depth; 243d8913d6aSbeck expected_error = vct->error; 244d8913d6aSbeck verify_cert(roots_dir, roots_file, bundle_file, bundle_file2, &chains, mode); 245d8913d6aSbeck if (chains == 0 && vct->want_chains == 0) { 246d8913d6aSbeck if (seen_error != expected_error) { 247d8913d6aSbeck fprintf(stderr, "FAIL: expected error %d, got %d\n", 248d8913d6aSbeck seen_error, expected_error); 249d8913d6aSbeck failed |= 1; 250d8913d6aSbeck } 251d8913d6aSbeck if (seen_depth != expected_depth) { 252d8913d6aSbeck fprintf(stderr, "FAIL: expected depth %d, got %d\n", 253d8913d6aSbeck seen_depth, expected_depth); 254d8913d6aSbeck failed |= 1; 255d8913d6aSbeck } 256d8913d6aSbeck } 257d8913d6aSbeck 258d8913d6aSbeck if ((mode == MODE_VERIFY && chains == vct->want_chains) || 259d8913d6aSbeck (chains == 0 && vct->want_chains == 0) || 260d8913d6aSbeck (chains == 1 && vct->want_chains > 0)) { 261d8913d6aSbeck fprintf(stderr, "INFO: Succeeded with %d chains%s\n", 262d8913d6aSbeck chains, vct->failing ? " (legacy failure)" : ""); 263d8913d6aSbeck if (mode == MODE_LEGACY_VFY && vct->failing) 264d8913d6aSbeck failed |= 1; 265d8913d6aSbeck } else { 266d8913d6aSbeck fprintf(stderr, "FAIL: Failed with %d chains%s\n", 267d8913d6aSbeck chains, vct->failing ? " (legacy failure)" : ""); 268d8913d6aSbeck if (!vct->failing) 269d8913d6aSbeck failed |= 1; 270d8913d6aSbeck } 271d8913d6aSbeck fprintf(stderr, "\n"); 272d8913d6aSbeck 273d8913d6aSbeck free(roots_file); 274d8913d6aSbeck free(bundle_file); 275d8913d6aSbeck free(bundle_file2); 276d8913d6aSbeck free(roots_dir); 277d8913d6aSbeck } 278d8913d6aSbeck 279d8913d6aSbeck return failed; 280d8913d6aSbeck } 281d8913d6aSbeck 282d8913d6aSbeck int 283d8913d6aSbeck main(int argc, char **argv) 284d8913d6aSbeck { 285d8913d6aSbeck int failed = 0; 286d8913d6aSbeck 287d8913d6aSbeck if (argc != 2) { 288d8913d6aSbeck fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 289d8913d6aSbeck exit(1); 290d8913d6aSbeck } 291d8913d6aSbeck 292d8913d6aSbeck fprintf(stderr, "\n\nTesting legacy x509_vfy\n"); 293d8913d6aSbeck failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY); 294d8913d6aSbeck fprintf(stderr, "\n\nTesting modern x509_vfy\n"); 295d8913d6aSbeck failed |= verify_cert_test(argv[1], MODE_MODERN_VFY); 296d8913d6aSbeck 297d8913d6aSbeck return (failed); 298d8913d6aSbeck } 299