summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorjsing <>2020-11-11 18:49:34 +0000
committerjsing <>2020-11-11 18:49:34 +0000
commita1afbc94cbbe3c87d24fd146e4abf9fec963cf5c (patch)
treef48ae5f4913a86ab11be677b612ebada5129384a /src/lib
parent9b0af8c264027d5934269d611c73f218e6533a95 (diff)
downloadopenbsd-a1afbc94cbbe3c87d24fd146e4abf9fec963cf5c.tar.gz
openbsd-a1afbc94cbbe3c87d24fd146e4abf9fec963cf5c.tar.bz2
openbsd-a1afbc94cbbe3c87d24fd146e4abf9fec963cf5c.zip
Handle additional certificate error cases in new X.509 verifier.
With the old verifier, the verify callback can always return 1 instructing the verifier to simply continue regardless of a certificate verification failure (e.g. the certificate is expired or revoked). This would result in a chain being built, however the first error encountered would be persisted, which allows the caller to build the chain, have the verification process succeed, yet upon inspecting the error code note that the chain is not valid for some reason. Mimic this behaviour by keeping track of certificate errors while building chains - when we finish verification, find the certificate error closest to the leaf certificate and expose that via the X509_STORE_CTX. There are various corner cases that we also have to handle, like the fact that we keep an certificate error until we find the issuer, at which point we have to clear it. Issue reported by Ilya Shipitcin due to failing haproxy regression tests. With much discussion and input from beck@ and tb@! ok beck@ tb@
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/libcrypto/x509/x509_internal.h3
-rw-r--r--src/lib/libcrypto/x509/x509_verify.c88
2 files changed, 79 insertions, 12 deletions
diff --git a/src/lib/libcrypto/x509/x509_internal.h b/src/lib/libcrypto/x509/x509_internal.h
index 9d69055afa..f6887be5fb 100644
--- a/src/lib/libcrypto/x509/x509_internal.h
+++ b/src/lib/libcrypto/x509/x509_internal.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: x509_internal.h,v 1.3 2020/09/15 11:55:14 beck Exp $ */ 1/* $OpenBSD: x509_internal.h,v 1.4 2020/11/11 18:49:34 jsing Exp $ */
2/* 2/*
3 * Copyright (c) 2020 Bob Beck <beck@openbsd.org> 3 * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
4 * 4 *
@@ -57,6 +57,7 @@ struct x509_constraints_names {
57 57
58struct x509_verify_chain { 58struct x509_verify_chain {
59 STACK_OF(X509) *certs; /* Kept in chain order, includes leaf */ 59 STACK_OF(X509) *certs; /* Kept in chain order, includes leaf */
60 int *cert_errors; /* Verify error for each cert in chain. */
60 struct x509_constraints_names *names; /* All names from all certs */ 61 struct x509_constraints_names *names; /* All names from all certs */
61}; 62};
62 63
diff --git a/src/lib/libcrypto/x509/x509_verify.c b/src/lib/libcrypto/x509/x509_verify.c
index c3923a88d5..c76a5e103e 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.18 2020/11/03 17:43:01 jsing Exp $ */ 1/* $OpenBSD: x509_verify.c,v 1.19 2020/11/11 18:49:34 jsing Exp $ */
2/* 2/*
3 * Copyright (c) 2020 Bob Beck <beck@openbsd.org> 3 * Copyright (c) 2020 Bob Beck <beck@openbsd.org>
4 * 4 *
@@ -15,7 +15,7 @@
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */ 16 */
17 17
18/* x509_verify - inspired by golang's crypto/x509/Verify */ 18/* x509_verify - inspired by golang's crypto/x509.Verify */
19 19
20#include <errno.h> 20#include <errno.h>
21#include <stdio.h> 21#include <stdio.h>
@@ -49,6 +49,9 @@ x509_verify_chain_new(void)
49 goto err; 49 goto err;
50 if ((chain->certs = sk_X509_new_null()) == NULL) 50 if ((chain->certs = sk_X509_new_null()) == NULL)
51 goto err; 51 goto err;
52 if ((chain->cert_errors = calloc(X509_VERIFY_MAX_CHAIN_CERTS,
53 sizeof(int))) == NULL)
54 goto err;
52 if ((chain->names = x509_constraints_names_new()) == NULL) 55 if ((chain->names = x509_constraints_names_new()) == NULL)
53 goto err; 56 goto err;
54 57
@@ -63,6 +66,8 @@ x509_verify_chain_clear(struct x509_verify_chain *chain)
63{ 66{
64 sk_X509_pop_free(chain->certs, X509_free); 67 sk_X509_pop_free(chain->certs, X509_free);
65 chain->certs = NULL; 68 chain->certs = NULL;
69 free(chain->cert_errors);
70 chain->cert_errors = NULL;
66 x509_constraints_names_free(chain->names); 71 x509_constraints_names_free(chain->names);
67 chain->names = NULL; 72 chain->names = NULL;
68} 73}
@@ -85,6 +90,11 @@ x509_verify_chain_dup(struct x509_verify_chain *chain)
85 goto err; 90 goto err;
86 if ((new_chain->certs = X509_chain_up_ref(chain->certs)) == NULL) 91 if ((new_chain->certs = X509_chain_up_ref(chain->certs)) == NULL)
87 goto err; 92 goto err;
93 if ((new_chain->cert_errors = calloc(X509_VERIFY_MAX_CHAIN_CERTS,
94 sizeof(int))) == NULL)
95 goto err;
96 memcpy(new_chain->cert_errors, chain->cert_errors,
97 X509_VERIFY_MAX_CHAIN_CERTS * sizeof(int));
88 if ((new_chain->names = 98 if ((new_chain->names =
89 x509_constraints_names_dup(chain->names)) == NULL) 99 x509_constraints_names_dup(chain->names)) == NULL)
90 goto err; 100 goto err;
@@ -99,18 +109,32 @@ x509_verify_chain_append(struct x509_verify_chain *chain, X509 *cert,
99 int *error) 109 int *error)
100{ 110{
101 int verify_err = X509_V_ERR_UNSPECIFIED; 111 int verify_err = X509_V_ERR_UNSPECIFIED;
112 size_t idx;
102 113
103 if (!x509_constraints_extract_names(chain->names, cert, 114 if (!x509_constraints_extract_names(chain->names, cert,
104 sk_X509_num(chain->certs) == 0, &verify_err)) { 115 sk_X509_num(chain->certs) == 0, &verify_err)) {
105 *error = verify_err; 116 *error = verify_err;
106 return 0; 117 return 0;
107 } 118 }
119
108 X509_up_ref(cert); 120 X509_up_ref(cert);
109 if (!sk_X509_push(chain->certs, cert)) { 121 if (!sk_X509_push(chain->certs, cert)) {
110 X509_free(cert); 122 X509_free(cert);
111 *error = X509_V_ERR_OUT_OF_MEM; 123 *error = X509_V_ERR_OUT_OF_MEM;
112 return 0; 124 return 0;
113 } 125 }
126
127 idx = sk_X509_num(chain->certs) - 1;
128 chain->cert_errors[idx] = *error;
129
130 /*
131 * We've just added the issuer for the previous certificate,
132 * clear its error if appropriate.
133 */
134 if (idx > 1 && chain->cert_errors[idx - 1] ==
135 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
136 chain->cert_errors[idx - 1] = X509_V_OK;
137
114 return 1; 138 return 1;
115} 139}
116 140
@@ -171,10 +195,11 @@ x509_verify_ctx_cert_is_root(struct x509_verify_ctx *ctx, X509 *cert)
171 195
172static int 196static int
173x509_verify_ctx_set_xsc_chain(struct x509_verify_ctx *ctx, 197x509_verify_ctx_set_xsc_chain(struct x509_verify_ctx *ctx,
174 struct x509_verify_chain *chain) 198 struct x509_verify_chain *chain, int set_error)
175{ 199{
176 size_t depth;
177 X509 *last = x509_verify_chain_last(chain); 200 X509 *last = x509_verify_chain_last(chain);
201 size_t depth;
202 int i;
178 203
179 if (ctx->xsc == NULL) 204 if (ctx->xsc == NULL)
180 return 1; 205 return 1;
@@ -189,6 +214,19 @@ x509_verify_ctx_set_xsc_chain(struct x509_verify_ctx *ctx,
189 if (ctx->xsc->chain == NULL) 214 if (ctx->xsc->chain == NULL)
190 return x509_verify_cert_error(ctx, last, depth, 215 return x509_verify_cert_error(ctx, last, depth,
191 X509_V_ERR_OUT_OF_MEM, 0); 216 X509_V_ERR_OUT_OF_MEM, 0);
217
218 if (set_error) {
219 ctx->xsc->error = X509_V_OK;
220 ctx->xsc->error_depth = 0;
221 for (i = 0; i < sk_X509_num(chain->certs); i++) {
222 if (chain->cert_errors[i] != X509_V_OK) {
223 ctx->xsc->error = chain->cert_errors[i];
224 ctx->xsc->error_depth = i;
225 break;
226 }
227 }
228 }
229
192 return 1; 230 return 1;
193} 231}
194 232
@@ -208,6 +246,11 @@ x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx,
208 return x509_verify_cert_error(ctx, last, depth, 246 return x509_verify_cert_error(ctx, last, depth,
209 X509_V_ERR_CERT_CHAIN_TOO_LONG, 0); 247 X509_V_ERR_CERT_CHAIN_TOO_LONG, 0);
210 248
249 /* Clear a get issuer failure for a root certificate. */
250 if (chain->cert_errors[depth] ==
251 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
252 chain->cert_errors[depth] = X509_V_OK;
253
211 /* 254 /*
212 * If we have a legacy xsc, choose a validated chain, 255 * If we have a legacy xsc, choose a validated chain,
213 * and apply the extensions, revocation, and policy checks 256 * and apply the extensions, revocation, and policy checks
@@ -217,7 +260,11 @@ x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx,
217 * knobs that are there for the fiddling. 260 * knobs that are there for the fiddling.
218 */ 261 */
219 if (ctx->xsc != NULL) { 262 if (ctx->xsc != NULL) {
220 if (!x509_verify_ctx_set_xsc_chain(ctx, chain)) 263 /* These may be set in one of the following calls. */
264 ctx->xsc->error = X509_V_OK;
265 ctx->xsc->error_depth = 0;
266
267 if (!x509_verify_ctx_set_xsc_chain(ctx, chain, 0))
221 return 0; 268 return 0;
222 269
223 /* 270 /*
@@ -241,6 +288,18 @@ x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx,
241 288
242 if (!x509_vfy_check_policy(ctx->xsc)) 289 if (!x509_vfy_check_policy(ctx->xsc))
243 return 0; 290 return 0;
291
292 /*
293 * The above checks may have set ctx->xsc->error and
294 * ctx->xsc->error_depth - save these for later on.
295 */
296 if (ctx->xsc->error != X509_V_OK) {
297 if (ctx->xsc->error_depth < 0 ||
298 ctx->xsc->error_depth >= X509_VERIFY_MAX_CHAIN_CERTS)
299 return 0;
300 chain->cert_errors[ctx->xsc->error_depth] =
301 ctx->xsc->error;
302 }
244 } 303 }
245 /* 304 /*
246 * no xsc means we are being called from the non-legacy API, 305 * no xsc means we are being called from the non-legacy API,
@@ -350,8 +409,7 @@ x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert,
350 return 0; 409 return 0;
351 } 410 }
352 if (!x509_verify_chain_append(new_chain, candidate, &ctx->error)) { 411 if (!x509_verify_chain_append(new_chain, candidate, &ctx->error)) {
353 x509_verify_cert_error(ctx, candidate, depth, 412 x509_verify_cert_error(ctx, candidate, depth, ctx->error, 0);
354 ctx->error, 0);
355 x509_verify_chain_free(new_chain); 413 x509_verify_chain_free(new_chain);
356 return 0; 414 return 0;
357 } 415 }
@@ -362,7 +420,7 @@ x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert,
362 * give up. 420 * give up.
363 */ 421 */
364 if (is_root_cert) { 422 if (is_root_cert) {
365 if (!x509_verify_ctx_set_xsc_chain(ctx, new_chain)) { 423 if (!x509_verify_ctx_set_xsc_chain(ctx, new_chain, 0)) {
366 x509_verify_chain_free(new_chain); 424 x509_verify_chain_free(new_chain);
367 return 0; 425 return 0;
368 } 426 }
@@ -931,16 +989,24 @@ x509_verify(struct x509_verify_ctx *ctx, X509 *leaf, char *name)
931 * We could not find a validated chain, and for some reason do not 989 * We could not find a validated chain, and for some reason do not
932 * have an error set. 990 * have an error set.
933 */ 991 */
934 if (ctx->chains_count == 0 && ctx->error == 0) 992 if (ctx->chains_count == 0 && ctx->error == 0) {
935 ctx->error = X509_V_ERR_UNSPECIFIED; 993 ctx->error = X509_V_ERR_UNSPECIFIED;
994 if (ctx->xsc != NULL && ctx->xsc->error != 0)
995 ctx->error = ctx->xsc->error;
996
997 }
936 998
937 /* Clear whatever errors happened if we have any validated chain */ 999 /* Clear whatever errors happened if we have any validated chain */
938 if (ctx->chains_count > 0) 1000 if (ctx->chains_count > 0)
939 ctx->error = X509_V_OK; 1001 ctx->error = X509_V_OK;
940 1002
941 if (ctx->xsc != NULL) { 1003 if (ctx->xsc != NULL) {
942 ctx->xsc->error = ctx->error; 1004 /* Take the first chain we found. */
943 return ctx->xsc->verify_cb(ctx->chains_count, ctx->xsc); 1005 if (ctx->chains_count > 0) {
1006 if (!x509_verify_ctx_set_xsc_chain(ctx, ctx->chains[0], 1))
1007 goto err;
1008 }
1009 return ctx->xsc->verify_cb(ctx->chains_count > 0, ctx->xsc);
944 } 1010 }
945 return (ctx->chains_count); 1011 return (ctx->chains_count);
946 1012