1 /* $OpenBSD: verify.c,v 1.9 2021/10/31 08:27:15 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/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 X509 *leaf = NULL; 112 113 *chains = 0; 114 use_dir = (mode == MODE_MODERN_VFY_DIR); 115 116 if (!use_dir && !certs_from_file(roots_file, &roots)) 117 errx(1, "failed to load roots from '%s'", roots_file); 118 if (!certs_from_file(bundle_file, &bundle)) 119 errx(1, "failed to load bundle from '%s'", bundle_file); 120 if (sk_X509_num(bundle) < 1) 121 errx(1, "not enough certs in bundle"); 122 leaf = sk_X509_shift(bundle); 123 124 if ((xsc = X509_STORE_CTX_new()) == NULL) 125 errx(1, "X509_STORE_CTX"); 126 if (use_dir && (store = X509_STORE_new()) == NULL) 127 errx(1, "X509_STORE"); 128 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 129 ERR_print_errors_fp(stderr); 130 errx(1, "failed to init store context"); 131 } 132 if (use_dir) { 133 if (!X509_STORE_load_locations(store, NULL, roots_dir)) 134 errx(1, "failed to set by_dir directory of %s", roots_dir); 135 } 136 if (mode == MODE_LEGACY_VFY) 137 X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY); 138 else 139 X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc), 140 X509_V_FLAG_LEGACY_VERIFY); 141 142 if (verbose) 143 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 144 if (!use_dir) 145 X509_STORE_CTX_set0_trusted_stack(xsc, roots); 146 if (X509_verify_cert(xsc) == 1) { 147 *chains = 1; /* XXX */ 148 goto done; 149 } 150 151 verify_err = X509_STORE_CTX_get_error(xsc); 152 if (verify_err == 0) 153 errx(1, "Error unset on failure!\n"); 154 155 fprintf(stderr, "failed to verify at %d: %s\n", 156 X509_STORE_CTX_get_error_depth(xsc), 157 X509_verify_cert_error_string(verify_err)); 158 159 done: 160 sk_X509_pop_free(roots, X509_free); 161 sk_X509_pop_free(bundle, X509_free); 162 X509_STORE_free(store); 163 X509_STORE_CTX_free(xsc); 164 X509_free(leaf); 165 } 166 167 struct verify_cert_test { 168 const char *id; 169 int want_chains; 170 int failing; 171 }; 172 173 static void 174 verify_cert_new(const char *roots_file, const char *bundle_file, int *chains) 175 { 176 STACK_OF(X509) *roots = NULL, *bundle = NULL; 177 X509_STORE_CTX *xsc = NULL; 178 X509 *leaf = NULL; 179 struct x509_verify_ctx *ctx; 180 181 *chains = 0; 182 183 if (!certs_from_file(roots_file, &roots)) 184 errx(1, "failed to load roots from '%s'", roots_file); 185 if (!certs_from_file(bundle_file, &bundle)) 186 errx(1, "failed to load bundle from '%s'", bundle_file); 187 if (sk_X509_num(bundle) < 1) 188 errx(1, "not enough certs in bundle"); 189 leaf = sk_X509_shift(bundle); 190 191 if ((xsc = X509_STORE_CTX_new()) == NULL) 192 errx(1, "X509_STORE_CTX"); 193 if (!X509_STORE_CTX_init(xsc, NULL, leaf, bundle)) { 194 ERR_print_errors_fp(stderr); 195 errx(1, "failed to init store context"); 196 } 197 if (verbose) 198 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 199 200 if ((ctx = x509_verify_ctx_new(roots)) == NULL) 201 errx(1, "failed to create ctx"); 202 if (!x509_verify_ctx_set_intermediates(ctx, bundle)) 203 errx(1, "failed to set intermediates"); 204 205 if ((*chains = x509_verify(ctx, leaf, NULL)) == 0) { 206 fprintf(stderr, "failed to verify at %lu: %s\n", 207 x509_verify_ctx_error_depth(ctx), 208 x509_verify_ctx_error_string(ctx)); 209 } else { 210 int c; 211 212 for (c = 0; verbose && c < *chains; c++) { 213 STACK_OF(X509) *chain; 214 int i; 215 216 fprintf(stderr, "Chain %d\n--------\n", c); 217 chain = x509_verify_ctx_chain(ctx, c); 218 for (i = 0; i < sk_X509_num(chain); i++) { 219 X509 *cert = sk_X509_value(chain, i); 220 X509_NAME_print_ex_fp(stderr, 221 X509_get_subject_name(cert), 0, 222 XN_FLAG_ONELINE); 223 fprintf(stderr, "\n"); 224 } 225 } 226 } 227 sk_X509_pop_free(roots, X509_free); 228 sk_X509_pop_free(bundle, X509_free); 229 X509_free(leaf); 230 X509_STORE_CTX_free(xsc); 231 x509_verify_ctx_free(ctx); 232 } 233 234 struct verify_cert_test verify_cert_tests[] = { 235 { 236 .id = "1a", 237 .want_chains = 1, 238 }, 239 { 240 .id = "2a", 241 .want_chains = 1, 242 }, 243 { 244 .id = "2b", 245 .want_chains = 0, 246 }, 247 { 248 .id = "2c", 249 .want_chains = 1, 250 }, 251 { 252 .id = "3a", 253 .want_chains = 1, 254 }, 255 { 256 .id = "3b", 257 .want_chains = 0, 258 }, 259 { 260 .id = "3c", 261 .want_chains = 0, 262 }, 263 { 264 .id = "3d", 265 .want_chains = 0, 266 }, 267 { 268 .id = "3e", 269 .want_chains = 1, 270 }, 271 { 272 .id = "4a", 273 .want_chains = 2, 274 }, 275 { 276 .id = "4b", 277 .want_chains = 1, 278 }, 279 { 280 .id = "4c", 281 .want_chains = 1, 282 .failing = 1, 283 }, 284 { 285 .id = "4d", 286 .want_chains = 1, 287 }, 288 { 289 .id = "4e", 290 .want_chains = 1, 291 }, 292 { 293 .id = "4f", 294 .want_chains = 2, 295 }, 296 { 297 .id = "4g", 298 .want_chains = 1, 299 .failing = 1, 300 }, 301 { 302 .id = "4h", 303 .want_chains = 1, 304 }, 305 { 306 .id = "5a", 307 .want_chains = 2, 308 }, 309 { 310 .id = "5b", 311 .want_chains = 1, 312 .failing = 1, 313 }, 314 { 315 .id = "5c", 316 .want_chains = 1, 317 }, 318 { 319 .id = "5d", 320 .want_chains = 1, 321 }, 322 { 323 .id = "5e", 324 .want_chains = 1, 325 .failing = 1, 326 }, 327 { 328 .id = "5f", 329 .want_chains = 1, 330 }, 331 { 332 .id = "5g", 333 .want_chains = 2, 334 }, 335 { 336 .id = "5h", 337 .want_chains = 1, 338 }, 339 { 340 .id = "5i", 341 .want_chains = 1, 342 .failing = 1, 343 }, 344 { 345 .id = "6a", 346 .want_chains = 1, 347 }, 348 { 349 .id = "6b", 350 .want_chains = 1, 351 .failing = 1, 352 }, 353 { 354 .id = "7a", 355 .want_chains = 1, 356 .failing = 1, 357 }, 358 { 359 .id = "7b", 360 .want_chains = 1, 361 }, 362 { 363 .id = "8a", 364 .want_chains = 0, 365 }, 366 { 367 .id = "9a", 368 .want_chains = 0, 369 }, 370 { 371 .id = "10a", 372 .want_chains = 1, 373 }, 374 { 375 .id = "10b", 376 .want_chains = 1, 377 }, 378 { 379 .id = "11a", 380 .want_chains = 1, 381 .failing = 1, 382 }, 383 { 384 .id = "11b", 385 .want_chains = 1, 386 }, 387 { 388 .id = "12a", 389 .want_chains = 1, 390 }, 391 { 392 .id = "13a", 393 .want_chains = 1, 394 .failing = 1, 395 }, 396 }; 397 398 #define N_VERIFY_CERT_TESTS \ 399 (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests)) 400 401 static int 402 verify_cert_test(const char *certs_path, int mode) 403 { 404 char *roots_file, *bundle_file, *roots_dir; 405 struct verify_cert_test *vct; 406 int failed = 0; 407 int chains; 408 size_t i; 409 410 for (i = 0; i < N_VERIFY_CERT_TESTS; i++) { 411 vct = &verify_cert_tests[i]; 412 413 if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path, 414 vct->id) == -1) 415 errx(1, "asprintf"); 416 if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path, 417 vct->id) == -1) 418 errx(1, "asprintf"); 419 if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1) 420 errx(1, "asprintf"); 421 422 fprintf(stderr, "== Test %zu (%s)\n", i, vct->id); 423 if (mode == MODE_VERIFY) 424 verify_cert_new(roots_file, bundle_file, &chains); 425 else 426 verify_cert(roots_dir, roots_file, bundle_file, &chains, mode); 427 if ((mode == MODE_VERIFY && chains == vct->want_chains) || 428 (chains == 0 && vct->want_chains == 0) || 429 (chains == 1 && vct->want_chains > 0)) { 430 fprintf(stderr, "INFO: Succeeded with %d chains%s\n", 431 chains, vct->failing ? " (legacy failure)" : ""); 432 if (mode == MODE_LEGACY_VFY && vct->failing) 433 failed |= 1; 434 } else { 435 fprintf(stderr, "FAIL: Failed with %d chains%s\n", 436 chains, vct->failing ? " (legacy failure)" : ""); 437 if (!vct->failing) 438 failed |= 1; 439 } 440 fprintf(stderr, "\n"); 441 442 free(roots_file); 443 free(bundle_file); 444 free(roots_dir); 445 } 446 447 return failed; 448 } 449 450 int 451 main(int argc, char **argv) 452 { 453 int failed = 0; 454 455 if (argc != 2) { 456 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]); 457 exit(1); 458 } 459 460 fprintf(stderr, "\n\nTesting legacy x509_vfy\n"); 461 failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY); 462 fprintf(stderr, "\n\nTesting modern x509_vfy\n"); 463 failed |= verify_cert_test(argv[1], MODE_MODERN_VFY); 464 fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n"); 465 failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR); 466 fprintf(stderr, "\n\nTesting x509_verify\n"); 467 failed |= verify_cert_test(argv[1], MODE_VERIFY); 468 469 return (failed); 470 } 471