xref: /openbsd-src/regress/lib/libcrypto/x509/callback.c (revision 6822f9c8033774f6aab00bcb43e8718e7034e67d)
1*6822f9c8Santon /* $OpenBSD: callback.c,v 1.5 2024/08/23 12:56:26 anton Exp $ */
28657d445Sbeck /*
38657d445Sbeck  * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
48657d445Sbeck  * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org>
58657d445Sbeck  *
68657d445Sbeck  * Permission to use, copy, modify, and distribute this software for any
78657d445Sbeck  * purpose with or without fee is hereby granted, provided that the above
88657d445Sbeck  * copyright notice and this permission notice appear in all copies.
98657d445Sbeck  *
108657d445Sbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118657d445Sbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128657d445Sbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138657d445Sbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148657d445Sbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158657d445Sbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168657d445Sbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178657d445Sbeck  */
188657d445Sbeck 
198657d445Sbeck #include <err.h>
208657d445Sbeck #include <string.h>
218657d445Sbeck 
228657d445Sbeck #include <openssl/bio.h>
238657d445Sbeck #include <openssl/err.h>
248657d445Sbeck #include <openssl/pem.h>
258657d445Sbeck #include <openssl/x509.h>
268657d445Sbeck #include <openssl/x509v3.h>
27f45080b3Stb 
28f45080b3Stb #include "x509_verify.h"
298657d445Sbeck 
308657d445Sbeck #define MODE_MODERN_VFY		0
318657d445Sbeck #define MODE_MODERN_VFY_DIR	1
328657d445Sbeck #define MODE_LEGACY_VFY		2
338657d445Sbeck #define MODE_VERIFY		3
348657d445Sbeck 
358657d445Sbeck static int verbose = 1;
368657d445Sbeck FILE *output;
378657d445Sbeck 
388657d445Sbeck static int
398657d445Sbeck passwd_cb(char *buf, int size, int rwflag, void *u)
408657d445Sbeck {
418657d445Sbeck 	memset(buf, 0, size);
428657d445Sbeck 	return (0);
438657d445Sbeck }
448657d445Sbeck 
458657d445Sbeck static int
468657d445Sbeck certs_from_file(const char *filename, STACK_OF(X509) **certs)
478657d445Sbeck {
488657d445Sbeck 	STACK_OF(X509_INFO) *xis = NULL;
498657d445Sbeck 	STACK_OF(X509) *xs = NULL;
508657d445Sbeck 	BIO *bio = NULL;
518657d445Sbeck 	X509 *x;
528657d445Sbeck 	int i;
538657d445Sbeck 
548657d445Sbeck 	if ((xs = sk_X509_new_null()) == NULL)
558657d445Sbeck 		errx(1, "failed to create X509 stack");
568657d445Sbeck 	if ((bio = BIO_new_file(filename, "r")) == NULL) {
578657d445Sbeck 		ERR_print_errors_fp(stderr);
588657d445Sbeck 		errx(1, "failed to create bio");
598657d445Sbeck 	}
608657d445Sbeck 	if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
618657d445Sbeck 		errx(1, "failed to read PEM");
628657d445Sbeck 
638657d445Sbeck 	for (i = 0; i < sk_X509_INFO_num(xis); i++) {
648657d445Sbeck 		if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
658657d445Sbeck 			continue;
668657d445Sbeck 		if (!sk_X509_push(xs, x))
678657d445Sbeck 			errx(1, "failed to push X509");
688657d445Sbeck 		X509_up_ref(x);
698657d445Sbeck 	}
708657d445Sbeck 
718657d445Sbeck 	*certs = xs;
728657d445Sbeck 	xs = NULL;
738657d445Sbeck 
748657d445Sbeck 	sk_X509_INFO_pop_free(xis, X509_INFO_free);
758657d445Sbeck 	sk_X509_pop_free(xs, X509_free);
768657d445Sbeck 	BIO_free(bio);
778657d445Sbeck 
788657d445Sbeck 	return 1;
798657d445Sbeck }
808657d445Sbeck 
818657d445Sbeck static int
828657d445Sbeck verify_cert_cb(int ok, X509_STORE_CTX *xsc)
838657d445Sbeck {
848657d445Sbeck 	X509 *current_cert, *issuer_cert;
858657d445Sbeck 	int verify_err, verify_depth;
868657d445Sbeck 
878657d445Sbeck 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
888657d445Sbeck 	issuer_cert = X509_STORE_CTX_get0_current_issuer(xsc);
898657d445Sbeck 	verify_depth =  X509_STORE_CTX_get_error_depth(xsc);
908657d445Sbeck 	verify_err = X509_STORE_CTX_get_error(xsc);
918657d445Sbeck 	fprintf(output, "depth %d error %d", verify_depth, verify_err);
928657d445Sbeck 	fprintf(output, " cert: ");
938657d445Sbeck 	if (current_cert != NULL) {
948657d445Sbeck 		X509_NAME_print_ex_fp(output,
958657d445Sbeck 		    X509_get_subject_name(current_cert), 0,
968657d445Sbeck 		    XN_FLAG_ONELINE);
978657d445Sbeck 	} else
988657d445Sbeck 		fprintf(output, "NULL");
998657d445Sbeck 	fprintf(output, " issuer: ");
1008657d445Sbeck 	if (issuer_cert != NULL) {
1018657d445Sbeck 		X509_NAME_print_ex_fp(output,
1028657d445Sbeck 		    X509_get_subject_name(issuer_cert), 0,
1038657d445Sbeck 		    XN_FLAG_ONELINE);
1048657d445Sbeck 	} else
1058657d445Sbeck 		fprintf(output, "NULL");
1068657d445Sbeck 	fprintf(output, "\n");
1078657d445Sbeck 
1088657d445Sbeck 	return ok;
1098657d445Sbeck }
1108657d445Sbeck 
1118657d445Sbeck static void
1128657d445Sbeck verify_cert(const char *roots_dir, const char *roots_file,
1138657d445Sbeck     const char *bundle_file, int *chains, int mode)
1148657d445Sbeck {
1158657d445Sbeck 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
1168657d445Sbeck 	X509_STORE_CTX *xsc = NULL;
1178657d445Sbeck 	X509_STORE *store = NULL;
1188657d445Sbeck 	int verify_err, use_dir;
1198657d445Sbeck 	X509 *leaf = NULL;
1208657d445Sbeck 
1218657d445Sbeck 	*chains = 0;
1228657d445Sbeck 	use_dir = (mode == MODE_MODERN_VFY_DIR);
1238657d445Sbeck 
1248657d445Sbeck 	if (!use_dir && !certs_from_file(roots_file, &roots))
1258657d445Sbeck 		errx(1, "failed to load roots from '%s'", roots_file);
1268657d445Sbeck 	if (!certs_from_file(bundle_file, &bundle))
1278657d445Sbeck 		errx(1, "failed to load bundle from '%s'", bundle_file);
1288657d445Sbeck 	if (sk_X509_num(bundle) < 1)
1298657d445Sbeck 		errx(1, "not enough certs in bundle");
1308657d445Sbeck 	leaf = sk_X509_shift(bundle);
1318657d445Sbeck 
1328657d445Sbeck 	if ((xsc = X509_STORE_CTX_new()) == NULL)
1338657d445Sbeck 		errx(1, "X509_STORE_CTX");
1348657d445Sbeck 	if (use_dir && (store = X509_STORE_new()) == NULL)
1358657d445Sbeck 		errx(1, "X509_STORE");
1368657d445Sbeck 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
1378657d445Sbeck 		ERR_print_errors_fp(stderr);
1388657d445Sbeck 		errx(1, "failed to init store context");
1398657d445Sbeck 	}
1408657d445Sbeck 	if (use_dir) {
1418657d445Sbeck 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
1428657d445Sbeck 			errx(1, "failed to set by_dir directory of %s", roots_dir);
1438657d445Sbeck 	}
144920585faStb 	if (mode == MODE_LEGACY_VFY)
145920585faStb 		X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY);
146920585faStb 	else
147920585faStb 		X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc),
148920585faStb 		    X509_V_FLAG_LEGACY_VERIFY);
1498657d445Sbeck 
1508657d445Sbeck 	if (verbose)
1518657d445Sbeck 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
1528657d445Sbeck 	if (!use_dir)
1538657d445Sbeck 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
1548657d445Sbeck 	if (X509_verify_cert(xsc) == 1) {
1558657d445Sbeck 		*chains = 1; /* XXX */
1568657d445Sbeck 		goto done;
1578657d445Sbeck 	}
1588657d445Sbeck 
1598657d445Sbeck 	verify_err = X509_STORE_CTX_get_error(xsc);
1608657d445Sbeck 	if (verify_err == 0)
161*6822f9c8Santon 		errx(1, "Error unset on failure!");
1628657d445Sbeck 
1638657d445Sbeck 	fprintf(stderr, "failed to verify at %d: %s\n",
1648657d445Sbeck 	    X509_STORE_CTX_get_error_depth(xsc),
1658657d445Sbeck 	    X509_verify_cert_error_string(verify_err));
1668657d445Sbeck 
1678657d445Sbeck  done:
1688657d445Sbeck 	sk_X509_pop_free(roots, X509_free);
1698657d445Sbeck 	sk_X509_pop_free(bundle, X509_free);
1708657d445Sbeck 	X509_STORE_free(store);
1718657d445Sbeck 	X509_STORE_CTX_free(xsc);
1728657d445Sbeck 	X509_free(leaf);
1738657d445Sbeck }
1748657d445Sbeck 
1758657d445Sbeck struct verify_cert_test {
1768657d445Sbeck 	const char *id;
1778657d445Sbeck 	int want_chains;
1788657d445Sbeck 	int failing;
1798657d445Sbeck };
1808657d445Sbeck 
1818657d445Sbeck struct verify_cert_test verify_cert_tests[] = {
1828657d445Sbeck 	{
1838657d445Sbeck 		.id = "1a",
1848657d445Sbeck 		.want_chains = 1,
1858657d445Sbeck 	},
1868657d445Sbeck 	{
1878657d445Sbeck 		.id = "2a",
1888657d445Sbeck 		.want_chains = 1,
1898657d445Sbeck 	},
1908657d445Sbeck 	{
1918657d445Sbeck 		.id = "2b",
1928657d445Sbeck 		.want_chains = 0,
1938657d445Sbeck 	},
1948657d445Sbeck 	{
1958657d445Sbeck 		.id = "2c",
1968657d445Sbeck 		.want_chains = 1,
1978657d445Sbeck 	},
1988657d445Sbeck 	{
1998657d445Sbeck 		.id = "3a",
2008657d445Sbeck 		.want_chains = 1,
2018657d445Sbeck 	},
2028657d445Sbeck 	{
2038657d445Sbeck 		.id = "3b",
2048657d445Sbeck 		.want_chains = 0,
2058657d445Sbeck 	},
2068657d445Sbeck 	{
2078657d445Sbeck 		.id = "3c",
2088657d445Sbeck 		.want_chains = 0,
2098657d445Sbeck 	},
2108657d445Sbeck 	{
2118657d445Sbeck 		.id = "3d",
2128657d445Sbeck 		.want_chains = 0,
2138657d445Sbeck 	},
2148657d445Sbeck 	{
2158657d445Sbeck 		.id = "3e",
2168657d445Sbeck 		.want_chains = 1,
2178657d445Sbeck 	},
2188657d445Sbeck 	{
2198657d445Sbeck 		.id = "4a",
2208657d445Sbeck 		.want_chains = 2,
2218657d445Sbeck 	},
2228657d445Sbeck 	{
2238657d445Sbeck 		.id = "4b",
2248657d445Sbeck 		.want_chains = 1,
2258657d445Sbeck 	},
2268657d445Sbeck 	{
2278657d445Sbeck 		.id = "4c",
2288657d445Sbeck 		.want_chains = 1,
2298657d445Sbeck 		.failing = 1,
2308657d445Sbeck 	},
2318657d445Sbeck 	{
2328657d445Sbeck 		.id = "4d",
2338657d445Sbeck 		.want_chains = 1,
2348657d445Sbeck 	},
2358657d445Sbeck 	{
2368657d445Sbeck 		.id = "4e",
2378657d445Sbeck 		.want_chains = 1,
2388657d445Sbeck 	},
2398657d445Sbeck 	{
2408657d445Sbeck 		.id = "4f",
2418657d445Sbeck 		.want_chains = 2,
2428657d445Sbeck 	},
2438657d445Sbeck 	{
2448657d445Sbeck 		.id = "4g",
2458657d445Sbeck 		.want_chains = 1,
2468657d445Sbeck 		.failing = 1,
2478657d445Sbeck 	},
2488657d445Sbeck 	{
2498657d445Sbeck 		.id = "4h",
2508657d445Sbeck 		.want_chains = 1,
2518657d445Sbeck 	},
2528657d445Sbeck 	{
2538657d445Sbeck 		.id = "5a",
2548657d445Sbeck 		.want_chains = 2,
2558657d445Sbeck 	},
2568657d445Sbeck 	{
2578657d445Sbeck 		.id = "5b",
2588657d445Sbeck 		.want_chains = 1,
2598657d445Sbeck 		.failing = 1,
2608657d445Sbeck 	},
2618657d445Sbeck 	{
2628657d445Sbeck 		.id = "5c",
2638657d445Sbeck 		.want_chains = 1,
2648657d445Sbeck 	},
2658657d445Sbeck 	{
2668657d445Sbeck 		.id = "5d",
2678657d445Sbeck 		.want_chains = 1,
2688657d445Sbeck 	},
2698657d445Sbeck 	{
2708657d445Sbeck 		.id = "5e",
2718657d445Sbeck 		.want_chains = 1,
2728657d445Sbeck 		.failing = 1,
2738657d445Sbeck 	},
2748657d445Sbeck 	{
2758657d445Sbeck 		.id = "5f",
2768657d445Sbeck 		.want_chains = 1,
2778657d445Sbeck 	},
2788657d445Sbeck 	{
2798657d445Sbeck 		.id = "5g",
2808657d445Sbeck 		.want_chains = 2,
2818657d445Sbeck 	},
2828657d445Sbeck 	{
2838657d445Sbeck 		.id = "5h",
2848657d445Sbeck 		.want_chains = 1,
2858657d445Sbeck 	},
2868657d445Sbeck 	{
2878657d445Sbeck 		.id = "5i",
2888657d445Sbeck 		.want_chains = 1,
2898657d445Sbeck 		.failing = 1,
2908657d445Sbeck 	},
2918657d445Sbeck 	{
2928657d445Sbeck 		.id = "6a",
2938657d445Sbeck 		.want_chains = 1,
2948657d445Sbeck 	},
2958657d445Sbeck 	{
2968657d445Sbeck 		.id = "6b",
2978657d445Sbeck 		.want_chains = 1,
2988657d445Sbeck 		.failing = 1,
2998657d445Sbeck 	},
3008657d445Sbeck 	{
3018657d445Sbeck 		.id = "7a",
3028657d445Sbeck 		.want_chains = 1,
3038657d445Sbeck 		.failing = 1,
3048657d445Sbeck 	},
3058657d445Sbeck 	{
3068657d445Sbeck 		.id = "7b",
3078657d445Sbeck 		.want_chains = 1,
3088657d445Sbeck 	},
3098657d445Sbeck 	{
3108657d445Sbeck 		.id = "8a",
3118657d445Sbeck 		.want_chains = 0,
3128657d445Sbeck 	},
3138657d445Sbeck 	{
3148657d445Sbeck 		.id = "9a",
3158657d445Sbeck 		.want_chains = 0,
3168657d445Sbeck 	},
3178657d445Sbeck 	{
3188657d445Sbeck 		.id = "10a",
3198657d445Sbeck 		.want_chains = 1,
3208657d445Sbeck 	},
3218657d445Sbeck 	{
3228657d445Sbeck 		.id = "10b",
3238657d445Sbeck 		.want_chains = 1,
3248657d445Sbeck 	},
3258657d445Sbeck 	{
3268657d445Sbeck 		.id = "11a",
3278657d445Sbeck 		.want_chains = 1,
3288657d445Sbeck 		.failing = 1,
3298657d445Sbeck 	},
3308657d445Sbeck 	{
3318657d445Sbeck 		.id = "11b",
3328657d445Sbeck 		.want_chains = 1,
3338657d445Sbeck 	},
3348657d445Sbeck 	{
3358657d445Sbeck 		.id = "12a",
3368657d445Sbeck 		.want_chains = 1,
3378657d445Sbeck 	},
3388657d445Sbeck 	{
3398657d445Sbeck 		.id = "13a",
3408657d445Sbeck 		.want_chains = 1,
34107499e19Sjsing 		.failing = 1,
3428657d445Sbeck 	},
3438657d445Sbeck };
3448657d445Sbeck 
3458657d445Sbeck #define N_VERIFY_CERT_TESTS \
3468657d445Sbeck     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
3478657d445Sbeck 
3488657d445Sbeck static int
3498657d445Sbeck verify_cert_test(const char *certs_path, int mode)
3508657d445Sbeck {
3518657d445Sbeck 	char *roots_file, *bundle_file, *roots_dir;
3528657d445Sbeck 	struct verify_cert_test *vct;
3538657d445Sbeck 	int failed = 0;
3548657d445Sbeck 	int chains;
3558657d445Sbeck 	size_t i;
3568657d445Sbeck 
3578657d445Sbeck 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
3588657d445Sbeck 		vct = &verify_cert_tests[i];
3598657d445Sbeck 
3608657d445Sbeck 		if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
3618657d445Sbeck 		    vct->id) == -1)
3628657d445Sbeck 			errx(1, "asprintf");
3638657d445Sbeck 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
3648657d445Sbeck 		    vct->id) == -1)
3658657d445Sbeck 			errx(1, "asprintf");
3668657d445Sbeck 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
3678657d445Sbeck 			errx(1, "asprintf");
3688657d445Sbeck 
3698657d445Sbeck 		fprintf(output, "== Test %zu (%s)\n", i, vct->id);
3708657d445Sbeck 		fprintf(output, "== Legacy:\n");
3718657d445Sbeck 		mode = MODE_LEGACY_VFY;
3728657d445Sbeck 		verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
3738657d445Sbeck 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
3748657d445Sbeck 		    (chains == 0 && vct->want_chains == 0) ||
3758657d445Sbeck 		    (chains == 1 && vct->want_chains > 0)) {
3768657d445Sbeck 			fprintf(output, "INFO: Succeeded with %d chains%s\n",
3778657d445Sbeck 			    chains, vct->failing ? " (legacy failure)" : "");
3788657d445Sbeck 			if (mode == MODE_LEGACY_VFY && vct->failing)
3798657d445Sbeck 				failed |= 1;
3808657d445Sbeck 		} else {
3818657d445Sbeck 			fprintf(output, "FAIL: Failed with %d chains%s\n",
3828657d445Sbeck 			    chains, vct->failing ? " (legacy failure)" : "");
3838657d445Sbeck 			if (!vct->failing)
3848657d445Sbeck 				failed |= 1;
3858657d445Sbeck 		}
3868657d445Sbeck 		fprintf(output, "\n");
3878657d445Sbeck 		fprintf(output, "== Modern:\n");
3888657d445Sbeck 		mode = MODE_MODERN_VFY;
3898657d445Sbeck 		verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
3908657d445Sbeck 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
3918657d445Sbeck 		    (chains == 0 && vct->want_chains == 0) ||
3928657d445Sbeck 		    (chains == 1 && vct->want_chains > 0)) {
3938657d445Sbeck 			fprintf(output, "INFO: Succeeded with %d chains%s\n",
3948657d445Sbeck 			    chains, vct->failing ? " (legacy failure)" : "");
3958657d445Sbeck 			if (mode == MODE_LEGACY_VFY && vct->failing)
3968657d445Sbeck 				failed |= 1;
3978657d445Sbeck 		} else {
3988657d445Sbeck 			fprintf(output, "FAIL: Failed with %d chains%s\n",
3998657d445Sbeck 			    chains, vct->failing ? " (legacy failure)" : "");
4008657d445Sbeck 			if (!vct->failing)
4018657d445Sbeck 				failed |= 1;
4028657d445Sbeck 		}
4038657d445Sbeck 		fprintf(output, "\n");
4048657d445Sbeck 
4058657d445Sbeck 		free(roots_file);
4068657d445Sbeck 		free(bundle_file);
4078657d445Sbeck 		free(roots_dir);
4088657d445Sbeck 	}
4098657d445Sbeck 
4108657d445Sbeck 	return failed;
4118657d445Sbeck }
4128657d445Sbeck 
4138657d445Sbeck int
4148657d445Sbeck main(int argc, char **argv)
4158657d445Sbeck {
4168657d445Sbeck 	int failed = 0;
4178657d445Sbeck 
4188657d445Sbeck 	if (argc != 2) {
4198657d445Sbeck 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
4208657d445Sbeck 		exit(1);
4218657d445Sbeck 	}
4228657d445Sbeck 
4238657d445Sbeck 	output = fopen("callback.out", "w+");
4248657d445Sbeck 
4258657d445Sbeck 	fprintf(stderr, "\n\nTesting legacy and modern X509_vfy\n");
4268657d445Sbeck 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
4278657d445Sbeck 	return (failed);
4288657d445Sbeck }
429