summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/lib/libcrypto/x509/x509_verify.c51
-rw-r--r--src/regress/lib/libcrypto/x509/Makefile7
-rw-r--r--src/regress/lib/libcrypto/x509/expirecallback.c279
3 files changed, 317 insertions, 20 deletions
diff --git a/src/lib/libcrypto/x509/x509_verify.c b/src/lib/libcrypto/x509/x509_verify.c
index 630c9f9b5a..f6959d1f3a 100644
--- a/src/lib/libcrypto/x509/x509_verify.c
+++ b/src/lib/libcrypto/x509/x509_verify.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: x509_verify.c,v 1.55 2022/04/12 10:42:35 tb Exp $ */ 1/* $OpenBSD: x509_verify.c,v 1.56 2022/06/25 20:01:43 beck Exp $ */
2/* 2/*
3 * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org> 3 * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org>
4 * 4 *
@@ -32,8 +32,10 @@
32 32
33static int x509_verify_cert_valid(struct x509_verify_ctx *ctx, X509 *cert, 33static int x509_verify_cert_valid(struct x509_verify_ctx *ctx, X509 *cert,
34 struct x509_verify_chain *current_chain); 34 struct x509_verify_chain *current_chain);
35static int x509_verify_cert_hostname(struct x509_verify_ctx *ctx, X509 *cert,
36 char *name);
35static void x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, 37static void x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
36 struct x509_verify_chain *current_chain, int full_chain); 38 struct x509_verify_chain *current_chain, int full_chain, char *name);
37static int x509_verify_cert_error(struct x509_verify_ctx *ctx, X509 *cert, 39static int x509_verify_cert_error(struct x509_verify_ctx *ctx, X509 *cert,
38 size_t depth, int error, int ok); 40 size_t depth, int error, int ok);
39static void x509_verify_chain_free(struct x509_verify_chain *chain); 41static void x509_verify_chain_free(struct x509_verify_chain *chain);
@@ -233,7 +235,7 @@ x509_verify_ctx_clear(struct x509_verify_ctx *ctx)
233 x509_verify_ctx_reset(ctx); 235 x509_verify_ctx_reset(ctx);
234 sk_X509_pop_free(ctx->intermediates, X509_free); 236 sk_X509_pop_free(ctx->intermediates, X509_free);
235 free(ctx->chains); 237 free(ctx->chains);
236 memset(ctx, 0, sizeof(*ctx)); 238
237} 239}
238 240
239static int 241static int
@@ -453,10 +455,11 @@ x509_verify_ctx_validate_legacy_chain(struct x509_verify_ctx *ctx,
453/* Add a validated chain to our list of valid chains */ 455/* Add a validated chain to our list of valid chains */
454static int 456static int
455x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx, 457x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx,
456 struct x509_verify_chain *chain) 458 struct x509_verify_chain *chain, char *name)
457{ 459{
458 size_t depth; 460 size_t depth;
459 X509 *last = x509_verify_chain_last(chain); 461 X509 *last = x509_verify_chain_last(chain);
462 X509 *leaf = x509_verify_chain_leaf(chain);
460 463
461 depth = sk_X509_num(chain->certs); 464 depth = sk_X509_num(chain->certs);
462 if (depth > 0) 465 if (depth > 0)
@@ -488,6 +491,13 @@ x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx,
488 return x509_verify_cert_error(ctx, last, depth, 491 return x509_verify_cert_error(ctx, last, depth,
489 X509_V_ERR_OUT_OF_MEM, 0); 492 X509_V_ERR_OUT_OF_MEM, 0);
490 } 493 }
494
495 if (!x509_verify_cert_valid(ctx, leaf, NULL))
496 return 0;
497
498 if (!x509_verify_cert_hostname(ctx, leaf, name))
499 return 0;
500
491 ctx->chains_count++; 501 ctx->chains_count++;
492 ctx->error = X509_V_OK; 502 ctx->error = X509_V_OK;
493 ctx->error_depth = depth; 503 ctx->error_depth = depth;
@@ -539,7 +549,7 @@ x509_verify_parent_signature(X509 *parent, X509 *child, int *error)
539static int 549static int
540x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert, 550x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert,
541 int is_root_cert, X509 *candidate, struct x509_verify_chain *current_chain, 551 int is_root_cert, X509 *candidate, struct x509_verify_chain *current_chain,
542 int full_chain) 552 int full_chain, char *name)
543{ 553{
544 int depth = sk_X509_num(current_chain->certs); 554 int depth = sk_X509_num(current_chain->certs);
545 struct x509_verify_chain *new_chain; 555 struct x509_verify_chain *new_chain;
@@ -590,14 +600,14 @@ x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert,
590 x509_verify_chain_free(new_chain); 600 x509_verify_chain_free(new_chain);
591 return 0; 601 return 0;
592 } 602 }
593 if (!x509_verify_ctx_add_chain(ctx, new_chain)) { 603 if (!x509_verify_ctx_add_chain(ctx, new_chain, name)) {
594 x509_verify_chain_free(new_chain); 604 x509_verify_chain_free(new_chain);
595 return 0; 605 return 0;
596 } 606 }
597 goto done; 607 goto done;
598 } 608 }
599 609
600 x509_verify_build_chains(ctx, candidate, new_chain, full_chain); 610 x509_verify_build_chains(ctx, candidate, new_chain, full_chain, name);
601 611
602 done: 612 done:
603 x509_verify_chain_free(new_chain); 613 x509_verify_chain_free(new_chain);
@@ -621,7 +631,7 @@ x509_verify_cert_error(struct x509_verify_ctx *ctx, X509 *cert, size_t depth,
621 631
622static void 632static void
623x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert, 633x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
624 struct x509_verify_chain *current_chain, int full_chain) 634 struct x509_verify_chain *current_chain, int full_chain, char *name)
625{ 635{
626 X509 *candidate; 636 X509 *candidate;
627 int i, depth, count, ret, is_root; 637 int i, depth, count, ret, is_root;
@@ -679,7 +689,7 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
679 x509_verify_cert_self_signed(candidate); 689 x509_verify_cert_self_signed(candidate);
680 x509_verify_consider_candidate(ctx, cert, 690 x509_verify_consider_candidate(ctx, cert,
681 is_root, candidate, current_chain, 691 is_root, candidate, current_chain,
682 full_chain); 692 full_chain, name);
683 } 693 }
684 X509_free(candidate); 694 X509_free(candidate);
685 } 695 }
@@ -692,7 +702,7 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
692 x509_verify_cert_self_signed(candidate); 702 x509_verify_cert_self_signed(candidate);
693 x509_verify_consider_candidate(ctx, cert, 703 x509_verify_consider_candidate(ctx, cert,
694 is_root, candidate, current_chain, 704 is_root, candidate, current_chain,
695 full_chain); 705 full_chain, name);
696 } 706 }
697 } 707 }
698 } 708 }
@@ -704,7 +714,7 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
704 if (x509_verify_potential_parent(ctx, candidate, cert)) { 714 if (x509_verify_potential_parent(ctx, candidate, cert)) {
705 x509_verify_consider_candidate(ctx, cert, 715 x509_verify_consider_candidate(ctx, cert,
706 0, candidate, current_chain, 716 0, candidate, current_chain,
707 full_chain); 717 full_chain, name);
708 } 718 }
709 } 719 }
710 } 720 }
@@ -1116,16 +1126,18 @@ x509_verify(struct x509_verify_ctx *ctx, X509 *leaf, char *name)
1116 ctx->xsc->current_cert = leaf; 1126 ctx->xsc->current_cert = leaf;
1117 } 1127 }
1118 1128
1119 if (!x509_verify_cert_valid(ctx, leaf, NULL))
1120 goto err;
1121
1122 if (!x509_verify_cert_hostname(ctx, leaf, name))
1123 goto err;
1124
1125 if ((current_chain = x509_verify_chain_new()) == NULL) { 1129 if ((current_chain = x509_verify_chain_new()) == NULL) {
1126 ctx->error = X509_V_ERR_OUT_OF_MEM; 1130 ctx->error = X509_V_ERR_OUT_OF_MEM;
1127 goto err; 1131 goto err;
1128 } 1132 }
1133
1134 /*
1135 * Add the leaf to the chain and try to build chains from it.
1136 * Note that unlike Go's verifier, we have not yet checked
1137 * anything about the leaf, This is intentional, so that we
1138 * report failures in chain building before we report problems
1139 * with the leaf.
1140 */
1129 if (!x509_verify_chain_append(current_chain, leaf, &ctx->error)) { 1141 if (!x509_verify_chain_append(current_chain, leaf, &ctx->error)) {
1130 x509_verify_chain_free(current_chain); 1142 x509_verify_chain_free(current_chain);
1131 goto err; 1143 goto err;
@@ -1133,13 +1145,14 @@ x509_verify(struct x509_verify_ctx *ctx, X509 *leaf, char *name)
1133 do { 1145 do {
1134 retry_chain_build = 0; 1146 retry_chain_build = 0;
1135 if (x509_verify_ctx_cert_is_root(ctx, leaf, full_chain)) { 1147 if (x509_verify_ctx_cert_is_root(ctx, leaf, full_chain)) {
1136 if (!x509_verify_ctx_add_chain(ctx, current_chain)) { 1148 if (!x509_verify_ctx_add_chain(ctx, current_chain,
1149 name)) {
1137 x509_verify_chain_free(current_chain); 1150 x509_verify_chain_free(current_chain);
1138 goto err; 1151 goto err;
1139 } 1152 }
1140 } else { 1153 } else {
1141 x509_verify_build_chains(ctx, leaf, current_chain, 1154 x509_verify_build_chains(ctx, leaf, current_chain,
1142 full_chain); 1155 full_chain, name);
1143 if (full_chain && ctx->chains_count == 0) { 1156 if (full_chain && ctx->chains_count == 0) {
1144 /* 1157 /*
1145 * Save the error state from the xsc 1158 * Save the error state from the xsc
diff --git a/src/regress/lib/libcrypto/x509/Makefile b/src/regress/lib/libcrypto/x509/Makefile
index 919bef31d1..ca66df19cd 100644
--- a/src/regress/lib/libcrypto/x509/Makefile
+++ b/src/regress/lib/libcrypto/x509/Makefile
@@ -1,6 +1,7 @@
1# $OpenBSD: Makefile,v 1.12 2022/06/02 12:08:41 tb Exp $ 1# $OpenBSD: Makefile,v 1.13 2022/06/25 20:01:43 beck Exp $
2 2
3PROGS = constraints verify x509attribute x509name x509req_ext callback 3PROGS = constraints verify x509attribute x509name x509req_ext callback
4PROGS += expirecallback
4LDADD = -lcrypto 5LDADD = -lcrypto
5DPADD = ${LIBCRYPTO} 6DPADD = ${LIBCRYPTO}
6 7
@@ -18,6 +19,7 @@ REGRESS_TARGETS += regress-x509attribute
18REGRESS_TARGETS += regress-x509name 19REGRESS_TARGETS += regress-x509name
19REGRESS_TARGETS += regress-x509req_ext 20REGRESS_TARGETS += regress-x509req_ext
20REGRESS_TARGETS += regress-callback 21REGRESS_TARGETS += regress-callback
22REGRESS_TARGETS += regress-expirecallback
21 23
22CLEANFILES += x509name.result callbackout 24CLEANFILES += x509name.result callbackout
23 25
@@ -49,4 +51,7 @@ regress-callback: callback
49 ./callback ${.CURDIR}/../certs 51 ./callback ${.CURDIR}/../certs
50 perl ${.CURDIR}/callback.pl callback.out 52 perl ${.CURDIR}/callback.pl callback.out
51 53
54regress-expirecallback: expirecallback
55 ./expirecallback ${.CURDIR}/../certs
56
52.include <bsd.regress.mk> 57.include <bsd.regress.mk>
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}