summaryrefslogtreecommitdiff
path: root/src/regress/lib/libcrypto/x509/expirecallback.c
diff options
context:
space:
mode:
authorbeck <>2022-06-25 20:01:43 +0000
committerbeck <>2022-06-25 20:01:43 +0000
commit8f49e18d8b58138d52d4c7d55385ef2f47508529 (patch)
treea057ee19ce4f7b27f7b8864ab578d7746ac590e9 /src/regress/lib/libcrypto/x509/expirecallback.c
parent45d168a6140632da2ca76c8437622ccf56118001 (diff)
downloadopenbsd-8f49e18d8b58138d52d4c7d55385ef2f47508529.tar.gz
openbsd-8f49e18d8b58138d52d4c7d55385ef2f47508529.tar.bz2
openbsd-8f49e18d8b58138d52d4c7d55385ef2f47508529.zip
Move leaf certificate checks to the last thing after chain validation.
While seemingly illogical and not what is done in Go's validator, this mimics OpenSSL's behavior so that callback overrides for the expiry of a certificate will not "sticky" override a failure to build a chain. ok jsing@
Diffstat (limited to 'src/regress/lib/libcrypto/x509/expirecallback.c')
-rw-r--r--src/regress/lib/libcrypto/x509/expirecallback.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/src/regress/lib/libcrypto/x509/expirecallback.c b/src/regress/lib/libcrypto/x509/expirecallback.c
new file mode 100644
index 0000000000..3da49708ff
--- /dev/null
+++ b/src/regress/lib/libcrypto/x509/expirecallback.c
@@ -0,0 +1,279 @@
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
34static int verbose = 1;
35
36static int
37passwd_cb(char *buf, int size, int rwflag, void *u)
38{
39 memset(buf, 0, size);
40 return (0);
41}
42
43static int
44certs_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
79static int
80verify_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
113static void
114verify_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
185struct verify_cert_test {
186 const char *id;
187 int want_chains;
188 int failing;
189};
190
191struct 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
215static int
216verify_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
261int
262main(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}