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