diff options
author | beck <> | 2022-06-25 20:01:43 +0000 |
---|---|---|
committer | beck <> | 2022-06-25 20:01:43 +0000 |
commit | 8f49e18d8b58138d52d4c7d55385ef2f47508529 (patch) | |
tree | a057ee19ce4f7b27f7b8864ab578d7746ac590e9 /src/regress/lib/libcrypto/x509/expirecallback.c | |
parent | 45d168a6140632da2ca76c8437622ccf56118001 (diff) | |
download | openbsd-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.c | 279 |
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 | |||
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 | } | ||