diff -urN ../exim-4.87.1.orig/doc/ChangeLog ./doc/ChangeLog --- ../exim-4.87.1.orig/doc/ChangeLog 2016-12-17 19:18:18.000000000 +0200 +++ ./doc/ChangeLog 2017-02-21 01:51:52.000000000 +0200 @@ -199,6 +199,10 @@ is unavailable so we cannot tell if this is a numbered or keyed extraction. Accept either. +JH/32 Bug 1909: Fix OCSP proof verification for cases where the proof is + signed directly by the cert-signing cert, rather than an intermediate + OCSP-signing cert. This is the model used by LetsEncrypt. + Exim version 4.86 diff -urN ../exim-4.87.1.orig/src/tls-openssl.c ./src/tls-openssl.c --- ../exim-4.87.1.orig/src/tls-openssl.c 2016-12-17 19:18:18.000000000 +0200 +++ ./src/tls-openssl.c 2017-02-21 01:52:57.000000000 +0200 @@ -156,6 +156,7 @@ } server; struct { X509_STORE *verify_store; /* non-null if status requested */ + STACK_OF(X509) *verify_stack; BOOL verify_required; } client; } u_ocsp; @@ -424,6 +425,7 @@ if (!X509_STORE_add_cert(client_static_cbinfo->u_ocsp.client.verify_store, cert)) ERR_clear_error(); + sk_X509_push(client_static_cbinfo->u_ocsp.client.verify_stack, cert); } #endif #ifndef DISABLE_EVENT @@ -778,6 +780,34 @@ * Load OCSP information into state * *************************************************/ +static STACK_OF(X509) * +cert_stack_from_store(X509_STORE * store) +{ +STACK_OF(X509_OBJECT) * roots= store->objs; +STACK_OF(X509) * sk = sk_X509_new_null(); +int i; + +for(i = sk_X509_OBJECT_num(roots) - 1; i >= 0; i--) + { + X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i); + if(tmp_obj->type == X509_LU_X509) + { + X509 * x = tmp_obj->data.x509; + sk_X509_push(sk, x); + } + } +return sk; +} + +static void +cert_stack_free(STACK_OF(X509) * sk) +{ +while (sk_X509_num(sk) > 0) (void) sk_X509_pop(sk); +sk_X509_free(sk); +} + + + /* Called to load the server OCSP response from the given file into memory, once caller has determined this is needed. Checks validity. Debugs a message if invalid. @@ -794,12 +824,13 @@ static void ocsp_load_response(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo, const uschar *expanded) { -BIO *bio; -OCSP_RESPONSE *resp; -OCSP_BASICRESP *basic_response; -OCSP_SINGLERESP *single_response; -ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; -X509_STORE *store; +BIO * bio; +OCSP_RESPONSE * resp; +OCSP_BASICRESP * basic_response; +OCSP_SINGLERESP * single_response; +ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd; +X509_STORE * store; +STACK_OF(X509) * sk; unsigned long verify_flags; int status, reason, i; @@ -810,8 +841,7 @@ cbinfo->u_ocsp.server.response = NULL; } -bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb"); -if (!bio) +if (!(bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb"))) { DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n", cbinfo->u_ocsp.server.file_expanded); @@ -826,16 +856,14 @@ return; } -status = OCSP_response_status(resp); -if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) +if ((status = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL) { DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n", OCSP_response_status_str(status), status); goto bad; } -basic_response = OCSP_response_get1_basic(resp); -if (!basic_response) +if (!(basic_response = OCSP_response_get1_basic(resp))) { DEBUG(D_tls) debug_printf("OCSP response parse error: unable to extract basic response.\n"); @@ -843,21 +871,38 @@ } store = SSL_CTX_get_cert_store(sctx); +sk = cert_stack_from_store(store); verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */ /* May need to expose ability to adjust those flags? OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT OCSP_TRUSTOTHER OCSP_NOINTERN */ -i = OCSP_basic_verify(basic_response, NULL, store, verify_flags); -if (i <= 0) +/* This does a full verify on the OCSP proof before we load it for serviing +up; possibly overkill - just date-checks might be nice enough. + +OCSP_basic_verify takes a "store" arg, but does not +use it for the chain verification, which is all we do +when OCSP_NOVERIFY is set. The content from the wire +"basic_response" and a cert-stack "sk" are all that is used. + +Seperately we might try to replace using OCSP_basic_verify() - which seems to not +be a public interface into the OpenSSL library (there's no manual entry) - +But what with? We also use OCSP_basic_verify in the client stapling callback. +And there we NEED it; we miust verify that status... unless the +library does it for us anyway? */ + +if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0) { - DEBUG(D_tls) { + DEBUG(D_tls) + { ERR_error_string(ERR_get_error(), ssl_errstring); debug_printf("OCSP response verify failure: %s\n", US ssl_errstring); } + cert_stack_free(sk); goto bad; } +cert_stack_free(sk); /* Here's the simplifying assumption: there's only one response, for the one certificate we use, and nothing for anything else in a chain. If this @@ -866,8 +911,8 @@ right cert in the stack and then calls OCSP_single_get0_status()). I'm hoping to avoid reworking a bunch more of how we handle state here. */ -single_response = OCSP_resp_get0(basic_response, 0); -if (!single_response) + +if (!(single_response = OCSP_resp_get0(basic_response, 0))) { DEBUG(D_tls) debug_printf("Unable to get first response from OCSP basic response.\n"); @@ -1202,7 +1247,7 @@ /* Use the chain that verified the server cert to verify the stapled info */ /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */ - if ((i = OCSP_basic_verify(bs, NULL, + if ((i = OCSP_basic_verify(bs, cbinfo->u_ocsp.client.verify_stack, cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; @@ -1332,7 +1377,10 @@ cbinfo->u_ocsp.server.response = NULL; } else + { cbinfo->u_ocsp.client.verify_store = NULL; + cbinfo->u_ocsp.client.verify_stack = NULL; + } #endif cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; @@ -1458,6 +1506,11 @@ DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); return FAIL; } + if (!(cbinfo->u_ocsp.client.verify_stack = sk_X509_new_null())) + { + DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n"); + return FAIL; + } SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb); SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo); }