X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9641b6648d2d2d87e14856f9c3383deb86772757..62b97c2ecf148ee86053d82e5509e4c3a5a20054:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index c63e56c0e..2e09882d2 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -48,6 +48,7 @@ functions from the OpenSSL library. */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define EXIM_HAVE_OCSP_RESP_COUNT # define OPENSSL_AUTO_SHA256 +# define OPENSSL_MIN_PROTO_VERSION #else # define EXIM_HAVE_EPHEM_RSA_KEX # define EXIM_HAVE_RAND_PSEUDO @@ -438,12 +439,14 @@ exim_openssl_state_st *client_static_state = NULL; /*XXX should not use static; exim_openssl_state_st state_server = {.is_server = TRUE}; static int -setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, +setup_certs(SSL_CTX * sctx, uschar ** certs, uschar * crl, host_item * host, uschar ** errstr ); /* Callbacks */ #ifndef DISABLE_OCSP static int tls_server_stapling_cb(SSL *s, void *arg); +static void x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk); +static void x509_store_dump_cert_s_names(X509_STORE * store); #endif @@ -951,36 +954,35 @@ Returns: nothing */ static void -info_callback(SSL *s, int where, int ret) +info_callback(SSL * s, int where, int ret) { DEBUG(D_tls) { - const uschar * str; - - if (where & SSL_ST_CONNECT) - str = US"SSL_connect"; - else if (where & SSL_ST_ACCEPT) - str = US"SSL_accept"; - else - str = US"SSL info (undefined)"; + gstring * g = NULL; + + if (where & SSL_ST_CONNECT) g = string_append_listele(g, ',', US"SSL_connect"); + if (where & SSL_ST_ACCEPT) g = string_append_listele(g, ',', US"SSL_accept"); + if (where & SSL_CB_LOOP) g = string_append_listele(g, ',', US"state_chg"); + if (where & SSL_CB_EXIT) g = string_append_listele(g, ',', US"hshake_exit"); + if (where & SSL_CB_READ) g = string_append_listele(g, ',', US"read"); + if (where & SSL_CB_WRITE) g = string_append_listele(g, ',', US"write"); + if (where & SSL_CB_ALERT) g = string_append_listele(g, ',', US"alert"); + if (where & SSL_CB_HANDSHAKE_START) g = string_append_listele(g, ',', US"hshake_start"); + if (where & SSL_CB_HANDSHAKE_DONE) g = string_append_listele(g, ',', US"hshake_done"); if (where & SSL_CB_LOOP) - debug_printf("%s: %s\n", str, SSL_state_string_long(s)); + debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s)); else if (where & SSL_CB_ALERT) - debug_printf("SSL3 alert %s:%s:%s\n", - str = where & SSL_CB_READ ? US"read" : US"write", + debug_printf("SSL %s %s:%s\n", g->s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); else if (where & SSL_CB_EXIT) { - if (ret == 0) - debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s)); - else if (ret < 0) - debug_printf("%s: error in %s\n", str, SSL_state_string_long(s)); + if (ret <= 0) + debug_printf("SSL %s: %s in %s\n", g->s, + ret == 0 ? "failed" : "error", SSL_state_string_long(s)); } - else if (where & SSL_CB_HANDSHAKE_START) - debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s)); - else if (where & SSL_CB_HANDSHAKE_DONE) - debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s)); + else if (where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)) + debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s)); } } @@ -1311,7 +1313,6 @@ OCSP_BASICRESP * basic_response; OCSP_SINGLERESP * single_response; ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd; STACK_OF(X509) * sk; -unsigned long verify_flags; int status, reason, i; DEBUG(D_tls) @@ -1376,20 +1377,20 @@ if (!(basic_response = OCSP_response_get1_basic(resp))) goto bad; } -sk = state->verify_stack; -verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */ +sk = state->verify_stack; /* set by setup_certs() / chain_from_pem_file() */ /* May need to expose ability to adjust those flags? OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT OCSP_TRUSTOTHER OCSP_NOINTERN */ -/* This does a full verify on the OCSP proof before we load it for serving -up; possibly overkill - just date-checks might be nice enough. +/* This does a partial verify (only the signer link, not the whole chain-to-CA) +on the OCSP proof before we load it for serving 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. +use it for the chain verification, when OCSP_NOVERIFY is set. +The content from the wire "basic_response" and a cert-stack "sk" are all +that is used. We have a stack, loaded in setup_certs() if tls_verify_certificates was a file (not a directory, or "system"). It is unfortunate we @@ -1406,7 +1407,7 @@ But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ -if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0) +if ((i = OCSP_basic_verify(basic_response, sk, NULL, OCSP_NOVERIFY)) < 0) { DEBUG(D_tls) { @@ -1751,53 +1752,10 @@ else DEBUG(D_tls) debug_printf("TLS: not preloading ECDH curve for server\n"); #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) -/* If we can, preload the server-side cert, key and ocsp */ - -if ( opt_set_and_noexpand(tls_certificate) -# ifndef DISABLE_OCSP - && opt_unset_or_noexpand(tls_ocsp_file) -#endif - && opt_unset_or_noexpand(tls_privatekey)) - { - /* Set watches on the filenames. The implementation does de-duplication - so we can just blindly do them all. */ - - if ( tls_set_watch(tls_certificate, TRUE) -# ifndef DISABLE_OCSP - && tls_set_watch(tls_ocsp_file, TRUE) -#endif - && tls_set_watch(tls_privatekey, TRUE)) - { - state_server.certificate = tls_certificate; - state_server.privatekey = tls_privatekey; -#ifndef DISABLE_OCSP - state_server.u_ocsp.server.file = tls_ocsp_file; -#endif - - DEBUG(D_tls) debug_printf("TLS: preloading server certs\n"); - if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) - state_server.lib_state.conn_certs = TRUE; - } - } -else if ( !tls_certificate && !tls_privatekey -# ifndef DISABLE_OCSP - && !tls_ocsp_file -#endif - ) - { /* Generate & preload a selfsigned cert. No files to watch. */ - if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) - { - state_server.lib_state.conn_certs = TRUE; - lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */ - } - } -else - DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n"); - - /* If we can, preload the Authorities for checking client certs against. Actual choice to do verify is made (tls_{,try_}verify_hosts) -at TLS conn startup */ +at TLS conn startup. +Do this before the server ocsp so that its info can verify the ocsp. */ if ( opt_set_and_noexpand(tls_verify_certificates) && opt_unset_or_noexpand(tls_crl)) @@ -1808,15 +1766,60 @@ if ( opt_set_and_noexpand(tls_verify_certificates) && tls_set_watch(tls_verify_certificates, FALSE) && tls_set_watch(tls_crl, FALSE)) { + uschar * v_certs = tls_verify_certificates; DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n"); - if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr) - == OK) + if (setup_certs(ctx, &v_certs, tls_crl, NULL, &dummy_errstr) == OK) state_server.lib_state.cabundle = TRUE; - } + + /* If we can, preload the server-side cert, key and ocsp */ + + if ( opt_set_and_noexpand(tls_certificate) +# ifndef DISABLE_OCSP + && opt_unset_or_noexpand(tls_ocsp_file) +# endif + && opt_unset_or_noexpand(tls_privatekey)) + { + /* Set watches on the filenames. The implementation does de-duplication + so we can just blindly do them all. */ + + if ( tls_set_watch(tls_certificate, TRUE) +# ifndef DISABLE_OCSP + && tls_set_watch(tls_ocsp_file, TRUE) +# endif + && tls_set_watch(tls_privatekey, TRUE)) + { + state_server.certificate = tls_certificate; + state_server.privatekey = tls_privatekey; +#ifndef DISABLE_OCSP + state_server.u_ocsp.server.file = tls_ocsp_file; +# endif + + DEBUG(D_tls) debug_printf("TLS: preloading server certs\n"); + if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) + state_server.lib_state.conn_certs = TRUE; + } + } + else if ( !tls_certificate && !tls_privatekey +# ifndef DISABLE_OCSP + && !tls_ocsp_file +# endif + ) + { /* Generate & preload a selfsigned cert. No files to watch. */ + if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) + { + state_server.lib_state.conn_certs = TRUE; + lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */ + } + } + else + DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n"); + } } else DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n"); + + #endif /* EXIM_HAVE_INOTIFY */ @@ -1898,10 +1901,11 @@ if ( opt_set_and_noexpand(ob->tls_verify_certificates) && tls_set_watch(ob->tls_crl, FALSE) ) { + uschar * v_certs = ob->tls_verify_certificates; DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name); - if (setup_certs(ctx, ob->tls_verify_certificates, + if (setup_certs(ctx, &v_certs, ob->tls_crl, dummy_host, &dummy_errstr) == OK) ob->tls_preload.cabundle = TRUE; } @@ -1923,7 +1927,9 @@ tls_server_creds_invalidate(void) { SSL_CTX_free(state_server.lib_state.lib_ctx); state_server.lib_state = null_tls_preload; +#ifndef DISABLE_OCSP state_server.u_ocsp.server.file_expanded = NULL; +#endif } @@ -2209,7 +2215,11 @@ already exists. Might even need this selfsame callback, for reneg? */ SSL_CTX * ctx = state_server.lib_state.lib_ctx; SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx)); SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx)); +#ifdef OPENSSL_MIN_PROTO_VERSION + SSL_CTX_set_min_proto_version(server_sni, SSL3_VERSION); +#endif SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx)); + SSL_CTX_clear_options(server_sni, ~SSL_CTX_get_options(ctx)); SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, state); @@ -2233,14 +2243,12 @@ if (state->u_ocsp.server.file) #endif { - uschar * expcerts; - if ( !expand_check(tls_verify_certificates, US"tls_verify_certificates", - &expcerts, &dummy_errstr) - || (rc = setup_certs(server_sni, expcerts, tls_crl, NULL, + uschar * v_certs = tls_verify_certificates; + if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL, &dummy_errstr)) != OK) goto bad; - if (expcerts && *expcerts) + if (v_certs && *v_certs) setup_cert_verify(server_sni, FALSE, verify_callback_server); } @@ -2725,10 +2733,17 @@ if (init_options) } #endif - DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(ctx, init_options))) - return tls_error(string_sprintf( +#ifdef OPENSSL_MIN_PROTO_VERSION + SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); +#endif + DEBUG(D_tls) debug_printf("setting SSL CTX options: %016lx\n", init_options); + SSL_CTX_set_options(ctx, init_options); + { + uint64_t readback = SSL_CTX_clear_options(ctx, ~init_options); + if (readback != init_options) + return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr); + } } else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); @@ -3005,7 +3020,7 @@ repeated after a Server Name Indication. Arguments: sctx SSL_CTX* to initialise - certs certs file, expanded + certs certs file, returned expanded crl CRL file or NULL host NULL in a server; the remote host in a client errstr error string pointer @@ -3014,15 +3029,16 @@ Returns: OK/DEFER/FAIL */ static int -setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, +setup_certs(SSL_CTX * sctx, uschar ** certsp, uschar * crl, host_item * host, uschar ** errstr) { -uschar *expcerts, *expcrl; +uschar * expcerts, * expcrl; -if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr)) +if (!expand_check(*certsp, US"tls_verify_certificates", &expcerts, errstr)) return DEFER; DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); +*certsp = expcerts; if (expcerts && *expcerts) { /* Tell the library to use its compiled-in location for the system default @@ -3318,20 +3334,20 @@ else goto skip_certs; { - uschar * expcerts; - if (!expand_check(tls_verify_certificates, US"tls_verify_certificates", - &expcerts, errstr)) - return DEFER; - DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); + uschar * v_certs = tls_verify_certificates; if (state_server.lib_state.cabundle) - { DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); } + { + DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); + setup_cert_verify(ctx, server_verify_optional, verify_callback_server); + } else - if ((rc = setup_certs(ctx, expcerts, tls_crl, NULL, errstr)) != OK) + { + if ((rc = setup_certs(ctx, &v_certs, tls_crl, NULL, errstr)) != OK) return rc; - - if (expcerts && *expcerts) - setup_cert_verify(ctx, server_verify_optional, verify_callback_server); + if (v_certs && *v_certs) + setup_cert_verify(ctx, server_verify_optional, verify_callback_server); + } } skip_certs: ; @@ -3594,20 +3610,20 @@ else return OK; { - uschar * expcerts; - if (!expand_check(ob->tls_verify_certificates, US"tls_verify_certificates", - &expcerts, errstr)) - return DEFER; - DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); + uschar * v_certs = ob->tls_verify_certificates; if (state->lib_state.cabundle) - { DEBUG(D_tls) debug_printf("TLS: CA bundle was preloaded\n"); } + { + DEBUG(D_tls) debug_printf("TLS: CA bundle for tpt was preloaded\n"); + setup_cert_verify(ctx, client_verify_optional, verify_callback_client); + } else - if ((rc = setup_certs(ctx, expcerts, ob->tls_crl, host, errstr)) != OK) + { + if ((rc = setup_certs(ctx, &v_certs, ob->tls_crl, host, errstr)) != OK) return rc; - - if (expcerts && *expcerts) - setup_cert_verify(ctx, client_verify_optional, verify_callback_client); + if (v_certs && *v_certs) + setup_cert_verify(ctx, client_verify_optional, verify_callback_client); + } } if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)