/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2019 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Portions Copyright (c) The OpenSSL Project 1999 */
#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
# define EXIM_HAVE_SESSION_TICKET
# define EXIM_HAVE_OPESSL_TRACE
# define EXIM_HAVE_OPESSL_GET0_SERIAL
+# define EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS
+# define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN
# ifndef DISABLE_OCSP
# define EXIM_HAVE_OCSP
# endif
#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L)
# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+# define EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS
#endif
#if !defined(LIBRESSL_VERSION_NUMBER) \
# define OPENSSL_HAVE_NUM_TICKETS
# define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
# define EXIM_HAVE_EXP_CHNL_BNGNG
+# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER
+# define EXIM_HAVE_OPENSSL_SET1_GROUPS
# else
# define OPENSSL_BAD_SRVR_OURCERT
# endif
# endif
#endif
+#define TESTSUITE_TICKET_LIFE 10 /* seconds */
/*************************************************
* OpenSSL option parse *
*************************************************/
uschar * privatekey;
BOOL is_server;
#ifndef DISABLE_OCSP
- STACK_OF(X509) *verify_stack; /* chain for verifying the proof */
union {
struct {
uschar *file;
const uschar *file_expanded;
ocsp_resplist *olist;
+ STACK_OF(X509) *verify_stack; /* chain for verifying the proof */
} server;
struct {
X509_STORE *verify_store; /* non-null if status requested */
+ uschar *verify_errstr; /* only if _required */
BOOL verify_required;
} client;
} u_ocsp;
exim_openssl_state_st state_server = {.is_server = TRUE};
static int
-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host,
- uschar ** errstr );
+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
}
else
DEBUG(D_tls)
- debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+ debug_printf(" Diffie-Hellman initialized from %s with %d-bit prime\n",
dhexpanded ? dhexpanded : US"default", dh_bitsize);
}
else
DEBUG(D_tls)
- debug_printf("dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n",
+ debug_printf(" dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n",
dhexpanded ? dhexpanded : US"default", dh_bitsize, tls_dh_max_bits);
#if OPENSSL_VERSION_NUMBER < 0x30000000L
* Initialize for ECDH *
*************************************************/
+/* "auto" needs to be handled carefully.
+OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1
+OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto
+ (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
+OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
+ https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
+
+*/
+
+static uschar *
+init_ecdh_auto(SSL_CTX * sctx)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+DEBUG(D_tls) debug_printf(
+ " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
+return US"prime256v1";
+
+#else
+# if defined SSL_CTRL_SET_ECDH_AUTO
+
+DEBUG(D_tls) debug_printf(
+ " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n");
+SSL_CTX_set_ecdh_auto(sctx, 1);
+return NULL;
+
+# else
+
+DEBUG(D_tls) debug_printf(
+ " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n");
+return NULL;
+
+# endif
+#endif
+}
+
/* Load parameters for ECDH encryption. Server only.
For now, we stick to NIST P-256 because: it's simple and easy to configure;
return TRUE;
#else
-uschar * exp_curve;
-int nid;
-BOOL rv;
-
# ifndef EXIM_HAVE_ECDH
DEBUG(D_tls)
- debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
+ debug_printf(" No OpenSSL API to define ECDH parameters, skipping\n");
return TRUE;
# else
+uschar * exp_curve;
+int ngroups, rc, sep;
+const uschar * curves_list, * curve;
+# ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS
+int nids[16];
+# else
+int nids[1];
+# endif
+
if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr))
return FALSE;
+
+/* Is the option deliberately empty? */
+
if (!exp_curve || !*exp_curve)
return TRUE;
-/* "auto" needs to be handled carefully.
- * OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1
- * OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto
- * (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
- * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
- * https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
- */
-if (Ustrcmp(exp_curve, "auto") == 0)
- {
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
- DEBUG(D_tls) debug_printf(
- "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
- exp_curve = US"prime256v1";
-#else
-# if defined SSL_CTRL_SET_ECDH_AUTO
- DEBUG(D_tls) debug_printf(
- "ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n");
- SSL_CTX_set_ecdh_auto(sctx, 1);
- return TRUE;
-# else
- DEBUG(D_tls) debug_printf(
- "ECDH OpenSSL 1.1.0+: temp key parameter settings: default selection\n");
- return TRUE;
-# endif
-#endif
- }
+/* Limit the list to hardwired array size. Drop out if any element is "suto". */
-DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve);
-if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef
-# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
- && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef
-# endif
- )
- {
- tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
- NULL, NULL, errstr);
- return FALSE;
- }
+curves_list = exp_curve;
+sep = 0;
+for (ngroups = 0;
+ ngroups < nelem(nids)
+ && (curve = string_nextinlist(&curves_list, &sep, NULL, 0));
+ )
+ if (Ustrcmp(curve, "auto") == 0)
+ {
+ DEBUG(D_tls) if (ngroups > 0)
+ debug_printf(" tls_eccurve 'auto' item takes precedence\n");
+ if ((exp_curve = init_ecdh_auto(sctx))) break; /* have a curve name to set */
+ return TRUE; /* all done */
+ }
+ else
+ ngroups++;
-# if OPENSSL_VERSION_NUMBER < 0x30000000L
+/* Translate to NIDs */
+
+curves_list = exp_curve;
+for (ngroups = 0; curve = string_nextinlist(&curves_list, &sep, NULL, 0);
+ ngroups++)
+ if ( (nids[ngroups] = OBJ_sn2nid (CCS curve)) == NID_undef
+# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
+ && (nids[ngroups] = EC_curve_nist2nid(CCS curve)) == NID_undef
+# endif
+ )
+ {
+ uschar * s = string_sprintf("Unknown curve name in tls_eccurve '%s'", curve);
+ DEBUG(D_tls) debug_printf("TLS error: %s\n", s);
+ if (errstr) *errstr = s;
+ return FALSE;
+ }
+
+# ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS
+/* Set the groups */
+
+if ((rc = SSL_CTX_set1_groups(sctx, nids, ngroups)) == 0)
+ tls_error(string_sprintf("Error enabling '%s' group(s)", exp_curve), NULL, NULL, errstr);
+else
+ DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group(s)\n", exp_curve);
+
+# else /* Cannot handle a list; only 1 element nids array */
{
EC_KEY * ecdh;
- if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+ if (!(ecdh = EC_KEY_new_by_curve_name(nids[0])))
{
tls_error(US"Unable to create ec curve", NULL, NULL, errstr);
return FALSE;
/* The "tmp" in the name here refers to setting a temporary key
not to the stability of the interface. */
- if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0))
+ if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh)) == 0)
tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), NULL, NULL, errstr);
else
- DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+ DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve);
EC_KEY_free(ecdh);
}
+# endif /*!EXIM_HAVE_OPENSSL_SET1_GROUPS*/
-#else /* v 3.0.0 + */
-
-if ((rv = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0)
- tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr);
-else
- DEBUG(D_tls) debug_printf("ECDH: enabled '%s' group\n", exp_curve);
-
-#endif
-
-return !rv;
+return !!rc;
# endif /*EXIM_HAVE_ECDH*/
#endif /*OPENSSL_NO_ECDH*/
*/
static void
-info_callback(SSL *s, int where, int ret)
+info_callback(const 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));
}
}
else if (depth != 0)
{
DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn);
-#ifndef DISABLE_OCSP
- if (tlsp == &tls_out && client_static_state->u_ocsp.client.verify_store)
- { /* client, wanting stapling */
- /* Add the server cert's signing chain as the one
- for the verification of the OCSP stapled information. */
-
- if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
- cert))
- ERR_clear_error();
- sk_X509_push(client_static_state->verify_stack, cert);
- }
-#endif
#ifndef DISABLE_EVENT
if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
return 0; /* reject, with peercert set */
uschar * name;
int rc;
while ((name = string_nextinlist(&list, &sep, NULL, 0)))
+ {
+ DEBUG(D_tls|D_lookup) debug_printf_indent("%s suitable for cert, per OpenSSL?", name);
if ((rc = X509_check_host(cert, CCS name, 0,
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
| X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
tlsp == &tls_out ? deliver_host_address : sender_host_address);
name = NULL;
}
+ DEBUG(D_tls|D_lookup) debug_printf_indent(" yes\n");
break;
}
+ else DEBUG(D_tls|D_lookup) debug_printf_indent(" no\n");
+ }
if (!name)
#else
if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
#endif
if (preverify_ok == 1)
- {
tls_out.dane_verified = TRUE;
-#ifndef DISABLE_OCSP
- if (client_static_state->u_ocsp.client.verify_store)
- { /* client, wanting stapling */
- /* Add the server cert's signing chain as the one
- for the verification of the OCSP stapled information. */
-
- if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
- cert))
- ERR_clear_error();
- sk_X509_push(client_static_state->verify_stack, cert);
- }
-#endif
- }
else
{
int err = X509_STORE_CTX_get_error(x509ctx);
#ifndef DISABLE_OCSP
+static void
+time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+{
+BIO_printf(bp, "\t%s: ", str);
+ASN1_GENERALIZEDTIME_print(bp, time);
+BIO_puts(bp, "\n");
+}
+
/*************************************************
* Load OCSP information into state *
*************************************************/
OCSP_SINGLERESP * single_response;
ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
STACK_OF(X509) * sk;
-unsigned long verify_flags;
int status, reason, i;
DEBUG(D_tls)
goto bad;
}
-sk = state->verify_stack;
-verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */
+sk = state->u_ocsp.server.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
handle the "system" case too, but there seems to be no library
function for getting a stack from a store.
[ In OpenSSL 1.1 - ? X509_STORE_CTX_get0_chain(ctx) ? ]
+[ 3.0.0 - sk = X509_STORE_get1_all_certs(store) ]
We do not free the stack since it could be needed a second time for
SNI handling.
Separately 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) -
+(in 3.0.0 + is is public)
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)
{
ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
- debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
+ debug_printf("OCSP response has bad signature: %s\n", US ssl_errstring);
}
goto bad;
}
if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
{
- DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+ DEBUG(D_tls)
+ {
+ BIO * bp = BIO_new(BIO_s_mem());
+ uschar * s = NULL;
+ int len;
+ time_print(bp, "This OCSP Update", thisupd);
+ if (nextupd) time_print(bp, "Next OCSP Update", nextupd);
+ if ((len = (int) BIO_get_mem_data(bp, CSS &s)) > 0) debug_printf("%.*s", len, s);
+ debug_printf("OCSP status invalid times.\n");
+ }
goto bad;
}
static void
-ocsp_free_response_list(exim_openssl_state_st * cbinfo)
+ocsp_free_response_list(exim_openssl_state_st * state)
{
-for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist;
+for (ocsp_resplist * olist = state->u_ocsp.server.olist; olist;
olist = olist->next)
OCSP_RESPONSE_free(olist->resp);
-cbinfo->u_ocsp.server.olist = NULL;
+state->u_ocsp.server.olist = NULL;
}
#endif /*!DISABLE_OCSP*/
) )
reexpand_tls_files_for_sni = TRUE;
- if (!expand_check(state->certificate, US"tls_certificate", &expanded, errstr))
+ if ( !expand_check(state->certificate, US"tls_certificate", &expanded, errstr)
+ || f.expand_string_forcedfail)
+ {
+ if (f.expand_string_forcedfail)
+ *errstr = US"expansion of tls_certificate failed";
return DEFER;
+ }
if (expanded)
if (state->is_server)
if (olist && !*olist)
olist = NULL;
+ /* If doing a re-expand after SNI, avoid reloading the OCSP
+ responses when the list of filenames has not changed.
+ The creds-invali on content change wipes file_expanded, so that
+ always reloads here. */
+
if ( state->u_ocsp.server.file_expanded && olist
&& (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
{
if ((err = tls_add_certfile(sctx, state, expanded, errstr)))
return err;
- if ( state->privatekey
- && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr))
+ if ( state->privatekey
+ && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr)
+ || f.expand_string_forcedfail)
+ {
+ if (f.expand_string_forcedfail)
+ *errstr = US"expansion of tls_privatekey failed";
return DEFER;
+ }
/* If expansion was forced to fail, key_expanded will be NULL. If the result
of the expansion is an empty string, ignore it also, and assume the private
DEBUG(D_tls)
{
- SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+ SSL_CTX_set_info_callback(ctx, info_callback);
#if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE)
/* this needs a debug build of OpenSSL */
- SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace);
+ SSL_CTX_set_msg_callback(ctx, SSL_trace);
#endif
#ifdef OPENSSL_HAVE_KEYLOG_CB
- SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
+ SSL_CTX_set_keylog_callback(ctx, keylog_callback);
#endif
}
if (opt_unset_or_noexpand(tls_dhparam))
{
- DEBUG(D_tls) debug_printf("TLS: preloading DH params for server\n");
+ DEBUG(D_tls) debug_printf("TLS: preloading DH params '%s' for server\n", tls_dhparam);
if (init_dh(ctx, tls_dhparam, &dummy_errstr))
state_server.lib_state.dh = TRUE;
}
DEBUG(D_tls) debug_printf("TLS: not preloading DH params for server\n");
if (opt_unset_or_noexpand(tls_eccurve))
{
- DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for server\n");
+ DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve '%s' for server\n", tls_eccurve);
if (init_ecdh(ctx, &dummy_errstr))
state_server.lib_state.ecdh = TRUE;
}
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))
&& 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 */
&& 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;
}
#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
/* Invalidate the creds cached, by dropping the current ones.
Call when we notice one of the source files has changed. */
-
+
static void
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
}
/* Extreme debug
+ * */
#ifndef DISABLE_OCSP
-void
-x509_store_dump_cert_s_names(X509_STORE * store)
+static void
+debug_print_sn(const X509 * cert)
{
-STACK_OF(X509_OBJECT) * roots= store->objs;
+X509_NAME * sn = X509_get_subject_name((X509 *)cert);
static uschar name[256];
+if (X509_NAME_oneline(sn, CS name, sizeof(name)))
+ {
+ name[sizeof(name)-1] = '\0';
+ debug_printf(" %s\n", name);
+ }
+}
-for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
+static void
+x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk)
+{
+if (!sk)
+ debug_printf(" (null)\n");
+else
{
- X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i);
- if(tmp_obj->type == X509_LU_X509)
- {
- X509_NAME * sn = X509_get_subject_name(tmp_obj->data.x509);
- if (X509_NAME_oneline(sn, CS name, sizeof(name)))
- {
- name[sizeof(name)-1] = '\0';
- debug_printf(" %s\n", name);
- }
- }
+ int idx = sk_X509_num(sk);
+ if (!idx)
+ debug_printf(" (empty)\n");
+ else
+ while (--idx >= 0) debug_print_sn(sk_X509_value(sk, idx));
}
}
-#endif
+
+static void
+x509_store_dump_cert_s_names(X509_STORE * store)
+{
+# ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS
+if (!store)
+ debug_printf(" (no store)\n");
+else
+ {
+ STACK_OF(X509) * sk = X509_STORE_get1_all_certs(store);
+ x509_stack_dump_cert_s_names(sk);
+ sk_X509_pop_free(sk, X509_free);
+ }
+# endif
+}
+#endif /*!DISABLE_OCSP*/
+/*
*/
exim_tk_old = exim_tk;
}
-if (f.running_in_test_harness) ssl_session_timeout = 6;
+if (f.running_in_test_harness) ssl_session_timeout = TESTSUITE_TICKET_LIFE;
DEBUG(D_tls) debug_printf("OpenSSL: %s STEK\n", exim_tk.name[0] ? "rotating" : "creating");
if (RAND_bytes(exim_tk.aes_key, sizeof(exim_tk.aes_key)) <= 0) return;
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
static int
-tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+tls_servername_cb(SSL * s, int * ad ARG_UNUSED, void * arg)
{
-const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
-exim_openssl_state_st *state = (exim_openssl_state_st *) arg;
+const char * servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+exim_openssl_state_st * state = (exim_openssl_state_st *) arg;
int rc;
int old_pool = store_pool;
-uschar * dummy_errstr;
+uschar * errstr;
if (!servername)
return SSL_TLSEXT_ERR_OK;
not confident that memcpy wouldn't break some internal reference counting.
Especially since there's a references struct member, which would be off. */
-if (lib_ctx_new(&server_sni, NULL, &dummy_errstr) != OK)
+if (lib_ctx_new(&server_sni, NULL, &errstr) != OK)
goto bad;
/* Not sure how many of these are actually needed, since SSL object
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);
- }
+ }
-if ( !init_dh(server_sni, state->dhparam, &dummy_errstr)
- || !init_ecdh(server_sni, &dummy_errstr)
+if ( !init_dh(server_sni, state->dhparam, &errstr)
+ || !init_ecdh(server_sni, &errstr)
)
goto bad;
#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,
- &dummy_errstr)) != OK)
+ uschar * v_certs = tls_verify_certificates;
+ if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL,
+ &errstr)) != OK)
goto bad;
- if (expcerts && *expcerts)
+ if (v_certs && *v_certs)
setup_cert_verify(server_sni, FALSE, verify_callback_server);
}
/* do this after setup_certs, because this can require the certs for verifying
OCSP information. */
-if ((rc = tls_expand_session_files(server_sni, state, &dummy_errstr)) != OK)
+if ((rc = tls_expand_session_files(server_sni, state, &errstr)) != OK)
goto bad;
DEBUG(D_tls) debug_printf("Switching SSL context.\n");
SSL_set_SSL_CTX(s, server_sni);
return SSL_TLSEXT_ERR_OK;
-bad: return SSL_TLSEXT_ERR_ALERT_FATAL;
+bad:
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s", errstr);
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
}
#endif /* EXIM_HAVE_OPENSSL_TLSEXT */
tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen,
const uschar * in, unsigned int inlen, void * arg)
{
+gstring * g = NULL;
+
server_seen_alpn = TRUE;
DEBUG(D_tls)
{
}
}
-/* More than one name from clilent, or name did not match our list. */
+/* More than one name from client, or name did not match our list. */
/* This will be fatal to the TLS conn; would be nice to kill TCP also.
Maybe as an option in future; for now leave control to the config (must-tls). */
-DEBUG(D_tls) debug_printf("TLS ALPN rejected\n");
+for (int pos = 0, siz; pos < inlen; pos += siz+1)
+ {
+ siz = in[pos];
+ if (pos + 1 + siz > inlen) siz = inlen - pos - 1;
+ g = string_append_listele_n(g, ':', in + pos + 1, siz);
+ }
+log_write(0, LOG_MAIN, "TLS ALPN (%Y) rejected", g);
+gstring_release_unused(g);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
#endif /* EXIM_HAVE_ALPN */
static void
-time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+add_chain_to_store(X509_STORE * store, STACK_OF(X509) * sk,
+ const char * debug_text)
{
-BIO_printf(bp, "\t%s: ", str);
-ASN1_GENERALIZEDTIME_print(bp, time);
-BIO_puts(bp, "\n");
+int idx;
+
+DEBUG(D_tls)
+ {
+ debug_printf("chain for %s:\n", debug_text);
+ x509_stack_dump_cert_s_names(sk);
+ }
+if (sk)
+ if ((idx = sk_X509_num(sk)) > 0)
+ while (--idx >= 0)
+ X509_STORE_add_cert(store, sk_X509_value(sk, idx));
+
}
static int
DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);
if(!p)
- { /* Expect this when we requested ocsp but got none */
+ { /* Expect this when we requested ocsp but got none */
if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED)
{
DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
return 1;
}
+
if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
log_write(0, LOG_MAIN, "Required TLS certificate status not received");
else
DEBUG(D_tls) debug_printf(" null\n");
- return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
- }
+
+ if (!cbinfo->u_ocsp.client.verify_required)
+ return 1;
+ cbinfo->u_ocsp.client.verify_errstr =
+ US"(SSL_connect) Required TLS certificate status not received";
+ return 0;
+ }
if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
{
*/
{
BIO * bp = NULL;
+ X509_STORE * verify_store = NULL;
+ BOOL have_verified_OCSP_signer = FALSE;
#ifndef EXIM_HAVE_OCSP_RESP_COUNT
STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
#endif
DEBUG(D_tls) bp = BIO_new(BIO_s_mem());
- /*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */
+ /* Use the CA & chain that verified the server cert to verify the stapled info */
+ /*XXX could we do an event here, for observability of ocsp? What reasonable data could we give access to? */
+ /* Dates would be a start. Do we need another opaque variable type, as for certs, plus an extract expansion? */
+
+ {
+ /* If this routine is not available, we've avoided [in tls_client_start()]
+ asking for certificate-status under DANE, so this callback won't run for
+ that combination. It still will for non-DANE. */
+
+#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER
+ X509 * signer;
- /* 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 ( tls_out.dane_verified
+ && (have_verified_OCSP_signer =
+ OCSP_resp_get0_signer(bs, &signer, SSL_get0_verified_chain(ssl)) == 1))
+ {
+ DEBUG(D_tls)
+ debug_printf("signer for OCSP basicres is in the verified chain;"
+ " shortcut its verification\n");
+ }
+ else
+#endif
+ {
+ STACK_OF(X509) * verified_chain;
- if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
- cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+ verify_store = X509_STORE_new();
+
+ SSL_get0_chain_certs(ssl, &verified_chain);
+ add_chain_to_store(verify_store, verified_chain,
+ "'current cert' per SSL_get0_chain_certs()");
+#ifdef EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN
+ verified_chain = SSL_get0_verified_chain(ssl);
+ add_chain_to_store(verify_store, verified_chain,
+ "SSL_get0_verified_chain()");
+#endif
+ }
+ }
+
+ DEBUG(D_tls)
+ {
+ debug_printf("Untrusted intermediate cert stack (from SSL_get_peer_cert_chain()):\n");
+ x509_stack_dump_cert_s_names(SSL_get_peer_cert_chain(ssl));
+
+ debug_printf("will use this CA store for verifying basicresp:\n");
+ x509_store_dump_cert_s_names(verify_store);
+
+ /* OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */
+
+ debug_printf("certs contained in basicresp:\n");
+ x509_stack_dump_cert_s_names(
+#ifdef EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS
+ OCSP_resp_get0_certs(bs)
+#else
+ bs->certs
+#endif
+ );
+
+#ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS
+/* could do via X509_STORE_get0_objects(); not worth it just for debug info */
+ {
+ X509 * signer;
+ if (OCSP_resp_get0_signer(bs, &signer, X509_STORE_get1_all_certs(verify_store)) == 1)
+ {
+ debug_printf("found signer for basicres:\n");
+ debug_print_sn(signer);
+ }
+ else
+ {
+ debug_printf("failed to find signer for basicres:\n");
+ ERR_print_errors(bp);
+ }
+ }
+#endif
+
+ }
+
+ ERR_clear_error();
+
+ /* Under DANE the trust-anchor (at least in TA mode) is indicated by the TLSA
+ record in DNS, and probably is not the root of the chain of certificates. So
+ accept a partial chain for that case (and hope that anchor is visible for
+ verifying the OCSP stapling).
+ XXX for EE mode it won't even be that. Does that make OCSP useless for EE?
+
+ Worse, for LetsEncrypt-mode (ocsp signer is leaf-signer) under DANE, the
+ data used within OpenSSL for the signer has nil pointers for signing
+ algorithms - and a crash results. Avoid this by shortcutting verification,
+ having determined that the OCSP signer is in the (DANE-)validated set.
+ */
+
+#ifndef OCSP_PARTIAL_CHAIN /* defined for 3.0.0 onwards */
+# define OCSP_PARTIAL_CHAIN 0
+#endif
+
+ if ((i = OCSP_basic_verify(bs, SSL_get_peer_cert_chain(ssl),
+ verify_store,
+#ifdef SUPPORT_DANE
+ tls_out.dane_verified
+ ? have_verified_OCSP_signer
+ ? OCSP_NOVERIFY | OCSP_NOEXPLICIT
+ : OCSP_PARTIAL_CHAIN | OCSP_NOEXPLICIT
+ :
+#endif
+ OCSP_NOEXPLICIT)) <= 0)
+ {
+ DEBUG(D_tls) debug_printf("OCSP_basic_verify() fail: returned %d\n", i);
if (ERR_peek_error())
{
tls_out.ocsp = OCSP_FAILED;
if (LOGGING(tls_cipher))
{
- const uschar * errstr = CUS ERR_reason_error_string(ERR_peek_error());
static uschar peerdn[256];
+ const uschar * errstr;;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ ERR_peek_error_all(NULL, NULL, NULL, CCSS &errstr, NULL);
+ if (!errstr)
+#endif
+ errstr = CUS ERR_reason_error_string(ERR_peek_error());
+
X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(ssl)),
CS peerdn, sizeof(peerdn));
log_write(0, LOG_MAIN,
"[%s] %s Received TLS cert (DN: '%.*s') status response, "
"itself unverifiable: %s",
- sender_host_address, sender_host_name,
- (int)sizeof(peerdn), peerdn,
- errstr);
+ deliver_host_address, deliver_host,
+ (int)sizeof(peerdn), peerdn, errstr);
}
DEBUG(D_tls)
{
BIO_printf(bp, "OCSP response verify failure\n");
ERR_print_errors(bp);
+ {
+ uschar * s = NULL;
+ int len = (int) BIO_get_mem_data(bp, CSS &s);
+ if (len > 0) debug_printf("%.*s", len, s);
+ BIO_reset(bp);
+ }
OCSP_RESPONSE_print(bp, rsp, 0);
}
goto failed;
else
DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
" in the root CA certificate; ignoring\n");
+ }
DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
{
tls_out.ocsp = OCSP_FAILED;
DEBUG(D_tls) ERR_print_errors(bp);
+ cbinfo->u_ocsp.client.verify_errstr =
+ US"(SSL_connect) Server certificate status is out-of-date";
log_write(0, LOG_MAIN, "OCSP dates invalid");
goto failed;
}
case V_OCSP_CERTSTATUS_GOOD:
continue; /* the idx loop */
case V_OCSP_CERTSTATUS_REVOKED:
+ cbinfo->u_ocsp.client.verify_errstr =
+ US"(SSL_connect) Server certificate revoked";
log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
reason != -1 ? "; reason: " : "",
reason != -1 ? OCSP_crl_reason_str(reason) : "");
DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
break;
default:
+ cbinfo->u_ocsp.client.verify_errstr =
+ US"(SSL_connect) Server certificate has unknown status";
log_write(0, LOG_MAIN,
"Server certificate status unknown, in OCSP stapling");
break;
uschar *ocsp_file,
#endif
address_item *addr, exim_openssl_state_st ** caller_state,
- tls_support * tlsp,
- uschar ** errstr)
+ tls_support * tlsp, uschar ** errstr)
{
SSL_CTX * ctx;
exim_openssl_state_st * state;
}
#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");
else
{
#ifndef DISABLE_OCSP
- if (!host)
+ if (!host) /* server */
{
state->u_ocsp.server.file = ocsp_file;
state->u_ocsp.server.file_expanded = NULL;
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
# ifndef DISABLE_OCSP
- if (!(state->verify_stack = sk_X509_new_null()))
+ if (!host && !(state->u_ocsp.server.verify_stack = sk_X509_new_null()))
{
DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n");
return FAIL;
DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n");
return FAIL;
}
+
SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
SSL_CTX_set_tlsext_status_arg(ctx, state);
}
# endif
-#endif
+#endif /*EXIM_HAVE_OPENSSL_TLSEXT*/
state->verify_cert_hostnames = NULL;
*************************************************/
#ifndef DISABLE_OCSP
-/* Load certs from file, return TRUE on success */
+/* In the server, load certs from file, return TRUE on success */
static BOOL
chain_from_pem_file(const uschar * file, STACK_OF(X509) ** vp)
Arguments:
sctx SSL_CTX* to initialise
- certs certs file, expanded
+ certsp certs file, returned expanded
crl CRL file or NULL
host NULL in a server; the remote host in a client
errstr error string pointer
*/
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
{
STACK_OF(X509) * verify_stack =
#ifndef DISABLE_OCSP
- !host ? state_server.verify_stack :
+ !host ? state_server.u_ocsp.server.verify_stack :
#endif
NULL;
STACK_OF(X509) ** vp = &verify_stack;
file = expcerts; dir = NULL;
#ifndef DISABLE_OCSP
- /* In the server if we will be offering an OCSP proof, load chain from
+ /* In the server if we will be offering an OCSP proof; load chain from
file for verifying the OCSP proof at load time. */
/*XXX Glitch! The file here is tls_verify_certs: the chain for verifying the client cert.
This is inconsistent with the need to verify the OCSP proof of the server cert.
*/
+/* *debug_printf("file for checking server ocsp stapling is: %s\n", file); */
if ( !host
&& statbuf.st_size > 0
&& state_server.u_ocsp.server.file
if (state_server.lib_state.pri_string)
{ DEBUG(D_tls) debug_printf("TLS: cipher list was preloaded\n"); }
-else
+else
{
if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
return FAIL;
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: ;
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)
{
if (tlsp->host_resumable)
{
- const uschar * key = tlsp->resume_index;
dbdata_tls_session * dt;
int len;
open_db dbblock, * dbm_file;
#ifdef EXIM_HAVE_SESSION_TICKET
SSL_SESSION_get_ticket_lifetime_hint(ss);
#else /* Use, fairly arbitrilarily, what we as server would */
- f.running_in_test_harness ? 6 : ssl_session_timeout;
+ f.running_in_test_harness ? TESTSUITE_TICKET_LIFE : ssl_session_timeout;
#endif
- if (lifetime + dt->time_stamp < time(NULL))
+ time_t now = time(NULL), expires = lifetime + dt->time_stamp;
+ if (expires < now)
{
- DEBUG(D_tls) debug_printf("session expired\n");
+ DEBUG(D_tls) debug_printf("session expired (by " TIME_T_FMT "s from %lus)\n", now - expires, lifetime);
dbfn_delete(dbm_file, tlsp->resume_index);
}
else if (SSL_set_session(ssl, ss))
{
- DEBUG(D_tls) debug_printf("good session\n");
+ DEBUG(D_tls) debug_printf("good session (" TIME_T_FMT "s left of %lus)\n", expires - now, lifetime);
tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
tlsp->verify_override = dt->verify_override;
tlsp->ocsp = dt->ocsp;
tls_error(US"set ex_data", host, NULL, errstr);
return FALSE;
}
- debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state);
+ /* debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state); */
}
tlsp->resumption = RESUME_SUPPORTED;
#ifndef DISABLE_OCSP
{
# ifdef SUPPORT_DANE
- /*XXX this should be moved to caller, to be common across gnutls/openssl */
if ( conn_args->dane
&& ob->hosts_request_ocsp[0] == '*'
&& ob->hosts_request_ocsp[1] == '\0'
# endif
request_ocsp =
verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
+
+# if defined(SUPPORT_DANE) && !defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER)
+ if (conn_args->dane && (require_ocsp || request_ocsp))
+ {
+ DEBUG(D_tls) debug_printf("OpenSSL version to early to combine OCSP"
+ " and DANE; disabling OCSP\n");
+ require_ocsp = request_ocsp = FALSE;
+ }
+# endif
}
#endif
tls_error(US"context init", host, NULL, errstr);
return FALSE;
}
+ DEBUG(D_tls) debug_printf("since dane-mode conn, not loading the usual CA bundle\n");
}
else
if (rc <= 0)
{
- tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
+#ifndef DISABLE_OCSP
+ if (client_static_state->u_ocsp.client.verify_errstr)
+ { if (errstr) *errstr = client_static_state->u_ocsp.client.verify_errstr; }
+ else
+#endif
+ tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
return FALSE;
}
/* Handle genuine errors */
case SSL_ERROR_SSL:
+ {
+ uschar * conn_info = smtp_get_connection_info();
+ if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5;
+ /* I'd like to get separated H= here, but too hard for now */
ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
- log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
+ log_write(0, LOG_MAIN, "TLS error (SSL_read): on %s %s", conn_info, ssl_errstring);
ssl_xfer_error = TRUE;
return FALSE;
+ }
default:
DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
{
#ifndef DISABLE_DKIM
int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm;
-debug_printf("tls_get_cache\n");
if (n > lim)
n = lim;
if (n > 0)
if (!o_ctx) /* server side */
{
#ifndef DISABLE_OCSP
- sk_X509_pop_free(state_server.verify_stack, X509_free);
- state_server.verify_stack = NULL;
+ sk_X509_pop_free(state_server.u_ocsp.server.verify_stack, X509_free);
+ state_server.u_ocsp.server.verify_stack = NULL;
#endif
receive_getc = smtp_getc;