xref: /openbsd-src/regress/lib/libcrypto/x509/expirecallback.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /* $OpenBSD: expirecallback.c,v 1.1 2022/06/25 20:01:43 beck 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 		if (verify_err == X509_V_ERR_CERT_HAS_EXPIRED)
96 			fprintf(stderr, "IGNORING ");
97 		fprintf(stderr, "verify error at depth %d: %s\n",
98 		    X509_STORE_CTX_get_error_depth(xsc),
99 		    X509_verify_cert_error_string(verify_err));
100 	}
101 
102 	/*
103 	 * Ignore expired certs, in the way people are told to do it
104 	 * by OpenSSL
105 	 */
106 
107 	if (verify_err == X509_V_ERR_CERT_HAS_EXPIRED)
108 		return 1;
109 
110 	return ok;
111 }
112 
113 static void
114 verify_cert(const char *roots_dir, const char *roots_file,
115     const char *bundle_file, int *chains, int mode)
116 {
117 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
118 	time_t future = 2000000000; /* May 17 2033 */
119 	X509_STORE_CTX *xsc = NULL;
120 	X509_STORE *store = NULL;
121 	int verify_err, use_dir;
122 	X509 *leaf = NULL;
123 
124 	*chains = 0;
125 	use_dir = (mode == MODE_MODERN_VFY_DIR);
126 
127 	if (!use_dir && !certs_from_file(roots_file, &roots))
128 		errx(1, "failed to load roots from '%s'", roots_file);
129 	if (!certs_from_file(bundle_file, &bundle))
130 		errx(1, "failed to load bundle from '%s'", bundle_file);
131 	if (sk_X509_num(bundle) < 1)
132 		errx(1, "not enough certs in bundle");
133 	leaf = sk_X509_shift(bundle);
134 
135 	if ((xsc = X509_STORE_CTX_new()) == NULL)
136 		errx(1, "X509_STORE_CTX");
137 	if (use_dir && (store = X509_STORE_new()) == NULL)
138 		errx(1, "X509_STORE");
139 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
140 		ERR_print_errors_fp(stderr);
141 		errx(1, "failed to init store context");
142 	}
143 
144 	/*
145 	 * Set the time int the future to exercise the expired cert
146 	 * callback
147 	 */
148 	X509_STORE_CTX_set_time(xsc, 0, future);
149 
150 	if (use_dir) {
151 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
152 			errx(1, "failed to set by_dir directory of %s", roots_dir);
153 	}
154 	if (mode == MODE_LEGACY_VFY)
155 		X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY);
156 	else
157 		X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc),
158 		    X509_V_FLAG_LEGACY_VERIFY);
159 
160 	if (verbose)
161 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
162 	if (!use_dir)
163 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
164 	if (X509_verify_cert(xsc) == 1) {
165 		*chains = 1; /* XXX */
166 		goto done;
167 	}
168 
169 	verify_err = X509_STORE_CTX_get_error(xsc);
170 	if (verify_err == 0)
171 		errx(1, "Error unset on failure!\n");
172 
173 	fprintf(stderr, "failed to verify at %d: %s\n",
174 	    X509_STORE_CTX_get_error_depth(xsc),
175 	    X509_verify_cert_error_string(verify_err));
176 
177  done:
178 	sk_X509_pop_free(roots, X509_free);
179 	sk_X509_pop_free(bundle, X509_free);
180 	X509_STORE_free(store);
181 	X509_STORE_CTX_free(xsc);
182 	X509_free(leaf);
183 }
184 
185 struct verify_cert_test {
186 	const char *id;
187 	int want_chains;
188 	int failing;
189 };
190 
191 struct verify_cert_test verify_cert_tests[] = {
192 	{
193 		.id = "1a",
194 		.want_chains = 1,
195 	},
196 	{
197 		.id = "2a",
198 		.want_chains = 1,
199 		.failing = 1,
200 	},
201 	{
202 		.id = "2b",
203 		.want_chains = 0,
204 	},
205 	{
206 		.id = "2c",
207 		.want_chains = 1,
208 		.failing = 1,
209 	},
210 };
211 
212 #define N_VERIFY_CERT_TESTS \
213     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
214 
215 static int
216 verify_cert_test(const char *certs_path, int mode)
217 {
218 	char *roots_file, *bundle_file, *roots_dir;
219 	struct verify_cert_test *vct;
220 	int failed = 0;
221 	int chains;
222 	size_t i;
223 
224 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
225 		vct = &verify_cert_tests[i];
226 
227 		if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
228 		    vct->id) == -1)
229 			errx(1, "asprintf");
230 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
231 		    vct->id) == -1)
232 			errx(1, "asprintf");
233 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
234 			errx(1, "asprintf");
235 
236 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
237 		verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
238 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
239 		    (chains == 0 && vct->want_chains == 0) ||
240 		    (chains == 1 && vct->want_chains > 0)) {
241 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
242 			    chains, vct->failing ? " (legacy failure)" : "");
243 			if (mode == MODE_LEGACY_VFY && vct->failing)
244 				failed |= 1;
245 		} else {
246 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
247 			    chains, vct->failing ? " (legacy failure)" : "");
248 			if (!vct->failing)
249 				failed |= 1;
250 		}
251 		fprintf(stderr, "\n");
252 
253 		free(roots_file);
254 		free(bundle_file);
255 		free(roots_dir);
256 	}
257 
258 	return failed;
259 }
260 
261 int
262 main(int argc, char **argv)
263 {
264 	int failed = 0;
265 
266 	if (argc != 2) {
267 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
268 		exit(1);
269 	}
270 
271 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
272 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
273 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
274 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY);
275 	fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n");
276 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR);
277 
278 	return (failed);
279 }
280