+ if (old_cert) tlsp->peercert = old_cert; /* restore 1st failing cert */
+ return 1; /* reject (leaving peercert set) */
+ }
+ DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+ "(host in tls_try_verify_hosts)\n");
+ tlsp->verify_override = TRUE;
+ }
+ X509_free(tlsp->peercert);
+ tlsp->peercert = old_cert;
+ }
+return 0;
+}
+#endif
+
+/*************************************************
+* Callback for verification *
+*************************************************/
+
+/* The SSL library does certificate verification if set up to do so. This
+callback has the current yes/no state is in "state". If verification succeeded,
+we set the certificate-verified flag. If verification failed, what happens
+depends on whether the client is required to present a verifiable certificate
+or not.
+
+If verification is optional, we change the state to yes, but still log the
+verification error. For some reason (it really would help to have proper
+documentation of OpenSSL), this callback function then gets called again, this
+time with state = 1. We must take care not to set the private verified flag on
+the second time through.
+
+Note: this function is not called if the client fails to present a certificate
+when asked. We get here only if a certificate has been received. Handling of
+optional verification for this case is done when requesting SSL to verify, by
+setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
+
+May be called multiple times for different issues with a certificate, even
+for a given "depth" in the certificate chain.
+
+Arguments:
+ preverify_ok current yes/no state as 1/0
+ x509ctx certificate information.
+ tlsp per-direction (client vs. server) support data
+ calledp has-been-called flag
+ optionalp verification-is-optional flag
+
+Returns: 0 if verification should fail, otherwise 1
+*/
+
+static int
+verify_callback(int preverify_ok, X509_STORE_CTX * x509ctx,
+ tls_support * tlsp, BOOL * calledp, BOOL * optionalp)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+uschar dn[256];
+
+if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
+ {
+ DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n");
+ log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address);
+ return 0;
+ }
+dn[sizeof(dn)-1] = '\0';
+
+tlsp->verify_override = FALSE;
+if (preverify_ok == 0)
+ {
+ uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])",
+ *verify_mode, sender_host_address)
+ : US"";
+ log_write(0, LOG_MAIN, "[%s] SSL verify error%s: depth=%d error=%s cert=%s",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address,
+ extra, depth,
+ X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), dn);
+ *calledp = TRUE;
+ if (!*optionalp)
+ {
+ if (!tlsp->peercert)
+ tlsp->peercert = X509_dup(cert); /* record failing cert */
+ return 0; /* reject */
+ }
+ DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+ "tls_try_verify_hosts)\n");
+ tlsp->verify_override = TRUE;
+ }
+
+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 */
+#endif
+ }
+else
+ {
+ const uschar * verify_cert_hostnames;
+
+ if ( tlsp == &tls_out
+ && ((verify_cert_hostnames = client_static_state->verify_cert_hostnames)))
+ /* client, wanting hostname check */
+ {
+
+#ifdef EXIM_HAVE_OPENSSL_CHECKHOST
+# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
+# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0
+# endif
+# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS
+# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0
+# endif
+ int sep = 0;
+ const uschar * list = verify_cert_hostnames;
+ uschar * name;
+ int rc;
+ while ((name = string_nextinlist(&list, &sep, NULL, 0)))
+ if ((rc = X509_check_host(cert, CCS name, 0,
+ X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
+ | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
+ NULL)))
+ {
+ if (rc < 0)
+ {
+ log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address);
+ name = NULL;
+ }
+ break;
+ }
+ if (!name)
+#else
+ if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
+#endif
+ {
+ uschar * extra = verify_mode
+ ? string_sprintf(" (during %c-verify for [%s])",
+ *verify_mode, sender_host_address)
+ : US"";
+ log_write(0, LOG_MAIN,
+ "[%s] SSL verify error%s: certificate name mismatch: DN=\"%s\" H=\"%s\"",
+ tlsp == &tls_out ? deliver_host_address : sender_host_address,
+ extra, dn, verify_cert_hostnames);
+ *calledp = TRUE;
+ if (!*optionalp)
+ {
+ if (!tlsp->peercert)
+ tlsp->peercert = X509_dup(cert); /* record failing cert */
+ return 0; /* reject */
+ }
+ DEBUG(D_tls) debug_printf("SSL verify name failure overridden (host in "
+ "tls_try_verify_hosts)\n");
+ tlsp->verify_override = TRUE;
+ }
+ }
+
+#ifndef DISABLE_EVENT
+ if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
+ return 0; /* reject, with peercert set */
+#endif
+
+ DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
+ *calledp ? "" : " authenticated", dn);
+ *calledp = TRUE;
+ }
+
+return 1; /* accept, at least for this level */
+}
+
+static int
+verify_callback_client(int preverify_ok, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(preverify_ok, x509ctx, &tls_out,
+ &client_verify_callback_called, &client_verify_optional);
+}
+
+static int
+verify_callback_server(int preverify_ok, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(preverify_ok, x509ctx, &tls_in,
+ &server_verify_callback_called, &server_verify_optional);
+}
+
+
+#ifdef SUPPORT_DANE
+
+/* This gets called *by* the dane library verify callback, which interposes
+itself.
+*/
+static int
+verify_callback_client_dane(int preverify_ok, X509_STORE_CTX * x509ctx)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+uschar dn[256];
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+#ifndef DISABLE_EVENT
+BOOL dummy_called, optional = FALSE;
+#endif
+
+if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
+ {
+ DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n");
+ log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+ deliver_host_address);
+ return 0;
+ }
+dn[sizeof(dn)-1] = '\0';
+
+DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
+ preverify_ok ? "ok":"BAD", depth, dn);
+
+#ifndef DISABLE_EVENT
+ if (verify_event(&tls_out, cert, depth, dn,
+ &dummy_called, &optional, US"DANE"))
+ return 0; /* reject, with peercert set */
+#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);
+ DEBUG(D_tls)
+ debug_printf(" - err %d '%s'\n", err, X509_verify_cert_error_string(err));
+ if (err == X509_V_ERR_APPLICATION_VERIFICATION)
+ preverify_ok = 1;
+ }
+return preverify_ok;
+}
+
+#endif /*SUPPORT_DANE*/
+
+
+#ifndef DISABLE_OCSP
+/*************************************************
+* Load OCSP information into state *
+*************************************************/
+/* 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.
+
+ASSUMES: single response, for single cert.
+
+Arguments:
+ state various parts of session state
+ filename the filename putatively holding an OCSP response
+ is_pem file is PEM format; otherwise is DER
+*/
+
+static void
+ocsp_load_response(exim_openssl_state_st * state, const uschar * filename,
+ BOOL is_pem)
+{
+BIO * bio;
+OCSP_RESPONSE * resp;
+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)
+ debug_printf("tls_ocsp_file (%s) '%s'\n", is_pem ? "PEM" : "DER", filename);
+
+if (!filename || !*filename) return;
+
+ERR_clear_error();
+if (!(bio = BIO_new_file(CS filename, "rb")))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "Failed to open OCSP response file \"%s\": %.100s",
+ filename, ERR_reason_error_string(ERR_get_error()));
+ return;
+ }
+
+if (is_pem)
+ {
+ uschar * data, * freep;
+ char * dummy;
+ long len;
+ if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "Failed to read PEM file \"%s\": %.100s",
+ filename, ERR_reason_error_string(ERR_get_error()));
+ return;
+ }
+ freep = data;
+ resp = d2i_OCSP_RESPONSE(NULL, CUSS &data, len);
+ OPENSSL_free(freep);
+ }
+else
+ resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+BIO_free(bio);
+
+if (!resp)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "Error reading OCSP response from \"%s\": %s",
+ filename, ERR_reason_error_string(ERR_get_error()));
+ return;
+ }
+
+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;
+ }
+
+#ifdef notdef
+ {
+ BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
+ OCSP_RESPONSE_print(bp, resp, 0); /* extreme debug: stapling content */
+ BIO_free(bp);
+ }
+#endif
+
+if (!(basic_response = OCSP_response_get1_basic(resp)))
+ {
+ DEBUG(D_tls)
+ debug_printf("OCSP response parse error: unable to extract basic response.\n");
+ goto bad;
+ }
+
+sk = state->verify_stack;
+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 */
+
+/* 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.
+
+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.
+
+We have a stack, loaded in setup_certs() if tls_verify_certificates
+was a file (not a directory, or "system"). It is unfortunate we
+cannot used the connection context store, as that would neatly
+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) ? ]
+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) -
+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)
+ {
+ 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);
+ }
+ goto bad;
+ }
+
+/* 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
+proves false, we need to extract a cert id from our issued cert
+(tls_certificate) and use that for OCSP_resp_find_status() (which finds the
+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.
+
+XXX that will change when we add support for (TLS1.3) whole-chain stapling
+*/
+
+if (!(single_response = OCSP_resp_get0(basic_response, 0)))
+ {
+ DEBUG(D_tls)
+ debug_printf("Unable to get first response from OCSP basic response.\n");
+ goto bad;
+ }
+
+status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd);
+if (status != V_OCSP_CERTSTATUS_GOOD)
+ {
+ DEBUG(D_tls) debug_printf("OCSP response bad cert status: %s (%d) %s (%d)\n",
+ OCSP_cert_status_str(status), status,
+ OCSP_crl_reason_str(reason), reason);
+ 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");
+ goto bad;
+ }
+
+supply_response:
+ /* Add the resp to the list used by tls_server_stapling_cb() */
+ {
+ ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
+ while (oentry = *op)
+ op = &oentry->next;
+ *op = oentry = store_get(sizeof(ocsp_resplist), FALSE);
+ oentry->next = NULL;
+ oentry->resp = resp;
+ }
+return;
+
+bad:
+ if (f.running_in_test_harness)
+ {
+ extern char ** environ;
+ if (environ) for (uschar ** p = USS environ; *p; p++)
+ if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
+ {
+ DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
+ goto supply_response;
+ }
+ }
+return;
+}
+
+
+static void
+ocsp_free_response_list(exim_openssl_state_st * cbinfo)
+{
+for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist;
+ olist = olist->next)
+ OCSP_RESPONSE_free(olist->resp);
+cbinfo->u_ocsp.server.olist = NULL;
+}
+#endif /*!DISABLE_OCSP*/
+
+
+
+
+
+static int
+tls_add_certfile(SSL_CTX * sctx, exim_openssl_state_st * cbinfo, uschar * file,
+ uschar ** errstr)
+{
+DEBUG(D_tls) debug_printf("tls_certificate file '%s'\n", file);
+if (!SSL_CTX_use_certificate_chain_file(sctx, CS file))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_certificate_chain_file file=%s", file),
+ cbinfo->host, NULL, errstr);
+return 0;
+}
+
+static int
+tls_add_pkeyfile(SSL_CTX * sctx, exim_openssl_state_st * cbinfo, uschar * file,
+ uschar ** errstr)
+{
+DEBUG(D_tls) debug_printf("tls_privatekey file '%s'\n", file);
+if (!SSL_CTX_use_PrivateKey_file(sctx, CS file, SSL_FILETYPE_PEM))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_PrivateKey_file file=%s", file), cbinfo->host, NULL, errstr);
+return 0;
+}
+
+
+
+
+/* Called once during tls_init and possibly again during TLS setup, for a
+new context, if Server Name Indication was used and tls_sni was seen in
+the certificate string.
+
+Arguments:
+ sctx the SSL_CTX* to update
+ state various parts of session state
+ errstr error string pointer
+
+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(SSL_CTX * sctx, exim_openssl_state_st * state,
+ uschar ** errstr)
+{
+uschar * expanded;
+
+if (!state->certificate)
+ {
+ if (!state->is_server) /* client */
+ return OK;
+ /* server */
+ if (tls_install_selfsign(sctx, errstr) != OK)
+ return DEFER;
+ }
+else
+ {
+ int err;
+
+ if ( !reexpand_tls_files_for_sni
+ && ( Ustrstr(state->certificate, US"tls_sni")
+ || Ustrstr(state->certificate, US"tls_in_sni")
+ || Ustrstr(state->certificate, US"tls_out_sni")
+ ) )
+ reexpand_tls_files_for_sni = TRUE;
+
+ if (!expand_check(state->certificate, US"tls_certificate", &expanded, errstr))
+ return DEFER;
+
+ if (expanded)
+ if (state->is_server)
+ {
+ const uschar * file_list = expanded;
+ int sep = 0;
+ uschar * file;
+#ifndef DISABLE_OCSP
+ const uschar * olist = state->u_ocsp.server.file;
+ int osep = 0;
+ uschar * ofile;
+ BOOL fmt_pem = FALSE;
+
+ if (olist)
+ if (!expand_check(olist, US"tls_ocsp_file", USS &olist, errstr))
+ return DEFER;
+ if (olist && !*olist)
+ olist = NULL;
+
+ if ( state->u_ocsp.server.file_expanded && olist
+ && (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
+ {
+ DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+ olist = NULL;
+ }
+ else
+ {
+ ocsp_free_response_list(state);
+ state->u_ocsp.server.file_expanded = olist;
+ }
+#endif
+
+ while (file = string_nextinlist(&file_list, &sep, NULL, 0))
+ {
+ if ((err = tls_add_certfile(sctx, state, file, errstr)))
+ return err;
+
+#ifndef DISABLE_OCSP
+ if (olist)
+ if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
+ {
+ if (Ustrncmp(ofile, US"PEM ", 4) == 0)
+ {
+ fmt_pem = TRUE;
+ ofile += 4;
+ }
+ else if (Ustrncmp(ofile, US"DER ", 4) == 0)
+ {
+ fmt_pem = FALSE;
+ ofile += 4;
+ }
+ ocsp_load_response(state, ofile, fmt_pem);
+ }
+ else
+ DEBUG(D_tls) debug_printf("ran out of ocsp file list\n");
+#endif
+ }
+ }
+ else /* would there ever be a need for multiple client certs? */
+ if ((err = tls_add_certfile(sctx, state, expanded, errstr)))
+ return err;
+
+ if ( state->privatekey
+ && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr))
+ 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
+ key is in the same file as the certificate. */
+
+ if (expanded && *expanded)
+ if (state->is_server)
+ {
+ const uschar * file_list = expanded;
+ int sep = 0;
+ uschar * file;
+
+ while (file = string_nextinlist(&file_list, &sep, NULL, 0))
+ if ((err = tls_add_pkeyfile(sctx, state, file, errstr)))
+ return err;
+ }
+ else /* would there ever be a need for multiple client certs? */
+ if ((err = tls_add_pkeyfile(sctx, state, expanded, errstr)))
+ return err;
+ }
+
+return OK;
+}
+
+
+
+
+/**************************************************
+* One-time init credentials for server and client *
+**************************************************/
+
+static int
+server_load_ciphers(SSL_CTX * ctx, exim_openssl_state_st * state,
+ uschar * ciphers, uschar ** errstr)
+{
+for (uschar * s = ciphers; *s; s++ ) if (*s == '_') *s = '-';
+DEBUG(D_tls) debug_printf("required ciphers: %s\n", ciphers);
+if (!SSL_CTX_set_cipher_list(ctx, CS ciphers))
+ return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
+state->server_cipher_list = ciphers;
+return OK;
+}
+
+
+
+static int
+lib_ctx_new(SSL_CTX ** ctxp, host_item * host, uschar ** errstr)
+{
+SSL_CTX * ctx;
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(ctx = SSL_CTX_new(host ? TLS_client_method() : TLS_server_method())))
+#else
+if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
+#endif
+ return tls_error(US"SSL_CTX_new", host, NULL, errstr);
+
+/* Set up the information callback, which outputs if debugging is at a suitable
+level. */
+
+DEBUG(D_tls)
+ {
+ SSL_CTX_set_info_callback(ctx, (void (*)())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);
+#endif
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+ SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
+#endif
+ }
+
+/* Automatically re-try reads/writes after renegotiation. */
+(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+*ctxp = ctx;
+return OK;
+}
+
+
+static unsigned
+tls_server_creds_init(void)
+{
+SSL_CTX * ctx;
+uschar * dummy_errstr;
+unsigned lifetime = 0;
+
+tls_openssl_init();
+
+state_server.lib_state = null_tls_preload;
+
+if (lib_ctx_new(&ctx, NULL, &dummy_errstr) != OK)
+ return 0;
+state_server.lib_state.lib_ctx = ctx;
+
+/* Preload DH params and EC curve */
+
+if (opt_unset_or_noexpand(tls_dhparam))
+ {
+ DEBUG(D_tls) debug_printf("TLS: preloading DH params for server\n");
+ if (init_dh(ctx, tls_dhparam, NULL, &dummy_errstr))
+ state_server.lib_state.dh = TRUE;
+ }
+if (opt_unset_or_noexpand(tls_eccurve))
+ {
+ DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for server\n");
+ if (init_ecdh(ctx, NULL, &dummy_errstr))
+ state_server.lib_state.ecdh = TRUE;
+ }
+
+#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 */
+
+if ( opt_set_and_noexpand(tls_verify_certificates)
+ && opt_unset_or_noexpand(tls_crl))
+ {
+ /* Watch the default dir also as they are always included */
+
+ if ( tls_set_watch(CUS X509_get_default_cert_file(), FALSE)
+ && tls_set_watch(tls_verify_certificates, FALSE)
+ && tls_set_watch(tls_crl, FALSE))
+ {
+ 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)
+ state_server.lib_state.cabundle = TRUE;
+ }
+ }
+else
+ DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n");
+#endif /* EXIM_HAVE_INOTIFY */
+
+
+/* If we can, preload the ciphers control string */
+
+if (opt_set_and_noexpand(tls_require_ciphers))
+ {
+ DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
+ if (server_load_ciphers(ctx, &state_server, tls_require_ciphers,
+ &dummy_errstr) == OK)
+ state_server.lib_state.pri_string = TRUE;
+ }
+else
+ DEBUG(D_tls) debug_printf("TLS: not preloading cipher list for server\n");
+return lifetime;
+}
+
+
+
+
+/* Preload whatever creds are static, onto a transport. The client can then
+just copy the pointer as it starts up.
+Called from the daemon after a cache-invalidate with watch set; called from
+a queue-run startup with watch clear. */
+
+static void
+tls_client_creds_init(transport_instance * t, BOOL watch)
+{
+smtp_transport_options_block * ob = t->options_block;
+exim_openssl_state_st tpt_dummy_state;
+host_item * dummy_host = (host_item *)1;
+uschar * dummy_errstr;
+SSL_CTX * ctx;
+
+tls_openssl_init();
+
+ob->tls_preload = null_tls_preload;
+if (lib_ctx_new(&ctx, dummy_host, &dummy_errstr) != OK)
+ return;
+ob->tls_preload.lib_ctx = ctx;
+
+tpt_dummy_state.lib_state = ob->tls_preload;
+
+if (opt_unset_or_noexpand(tls_dhparam))
+ {
+ DEBUG(D_tls) debug_printf("TLS: preloading DH params for transport '%s'\n", t->name);
+ if (init_dh(ctx, tls_dhparam, NULL, &dummy_errstr))
+ ob->tls_preload.dh = TRUE;
+ }
+if (opt_unset_or_noexpand(tls_eccurve))
+ {
+ DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for transport '%s'\n", t->name);
+ if (init_ecdh(ctx, NULL, &dummy_errstr))
+ ob->tls_preload.ecdh = TRUE;
+ }
+
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
+if ( opt_set_and_noexpand(ob->tls_certificate)
+ && opt_unset_or_noexpand(ob->tls_privatekey))
+ {
+ if ( !watch
+ || ( tls_set_watch(ob->tls_certificate, FALSE)
+ && tls_set_watch(ob->tls_privatekey, FALSE)
+ ) )
+ {
+ uschar * pkey = ob->tls_privatekey;
+
+ DEBUG(D_tls)
+ debug_printf("TLS: preloading client certs for transport '%s'\n",t->name);
+
+ if ( tls_add_certfile(ctx, &tpt_dummy_state, ob->tls_certificate,
+ &dummy_errstr) == 0
+ && tls_add_pkeyfile(ctx, &tpt_dummy_state,
+ pkey ? pkey : ob->tls_certificate,
+ &dummy_errstr) == 0
+ )
+ ob->tls_preload.conn_certs = TRUE;
+ }
+ }
+else
+ DEBUG(D_tls)
+ debug_printf("TLS: not preloading client certs, for transport '%s'\n", t->name);
+
+
+if ( opt_set_and_noexpand(ob->tls_verify_certificates)
+ && opt_unset_or_noexpand(ob->tls_crl))
+ {
+ if ( !watch
+ || tls_set_watch(CUS X509_get_default_cert_file(), FALSE)
+ && tls_set_watch(ob->tls_verify_certificates, FALSE)
+ && tls_set_watch(ob->tls_crl, FALSE)
+ )
+ {
+ DEBUG(D_tls)
+ debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name);
+
+ if (setup_certs(ctx, ob->tls_verify_certificates,
+ ob->tls_crl, dummy_host, &dummy_errstr) == OK)
+ ob->tls_preload.cabundle = TRUE;
+ }
+ }
+else
+ DEBUG(D_tls)
+ debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", t->name);
+
+#endif /*EXIM_HAVE_INOTIFY*/
+}
+
+
+#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;
+}
+
+
+static void
+tls_client_creds_invalidate(transport_instance * t)
+{
+smtp_transport_options_block * ob = t->options_block;
+SSL_CTX_free(ob->tls_preload.lib_ctx);
+ob->tls_preload = null_tls_preload;
+}
+
+#else
+
+static void
+tls_server_creds_invalidate(void)
+{ return; }
+
+static void
+tls_client_creds_invalidate(transport_instance * t)
+{ return; }
+
+#endif /*EXIM_HAVE_INOTIFY*/
+
+
+/* Extreme debug
+#ifndef DISABLE_OCSP
+void
+x509_store_dump_cert_s_names(X509_STORE * store)
+{
+STACK_OF(X509_OBJECT) * roots= store->objs;
+static uschar name[256];
+
+for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
+ {
+ 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);
+ }
+ }
+ }
+}
+#endif
+*/
+
+
+#ifndef DISABLE_TLS_RESUME
+/* Manage the keysets used for encrypting the session tickets, on the server. */
+
+typedef struct { /* Session ticket encryption key */
+ uschar name[16];
+
+ const EVP_CIPHER * aes_cipher;
+ uschar aes_key[32]; /* size needed depends on cipher. aes_128 implies 128/8 = 16? */
+ const EVP_MD * hmac_hash;
+ uschar hmac_key[16];
+ time_t renew;
+ time_t expire;
+} exim_stek;
+
+static exim_stek exim_tk; /* current key */
+static exim_stek exim_tk_old; /* previous key */
+
+static void
+tk_init(void)
+{
+time_t t = time(NULL);
+
+if (exim_tk.name[0])
+ {
+ if (exim_tk.renew >= t) return;
+ exim_tk_old = exim_tk;
+ }
+
+if (f.running_in_test_harness) ssl_session_timeout = 6;
+
+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;
+if (RAND_bytes(exim_tk.hmac_key, sizeof(exim_tk.hmac_key)) <= 0) return;
+if (RAND_bytes(exim_tk.name+1, sizeof(exim_tk.name)-1) <= 0) return;
+
+exim_tk.name[0] = 'E';
+exim_tk.aes_cipher = EVP_aes_256_cbc();
+exim_tk.hmac_hash = EVP_sha256();
+exim_tk.expire = t + ssl_session_timeout;
+exim_tk.renew = t + ssl_session_timeout/2;
+}
+
+static exim_stek *
+tk_current(void)
+{
+if (!exim_tk.name[0]) return NULL;
+return &exim_tk;
+}
+
+static exim_stek *
+tk_find(const uschar * name)
+{
+return memcmp(name, exim_tk.name, sizeof(exim_tk.name)) == 0 ? &exim_tk
+ : memcmp(name, exim_tk_old.name, sizeof(exim_tk_old.name)) == 0 ? &exim_tk_old
+ : NULL;
+}
+
+/* Callback for session tickets, on server */
+static int
+ticket_key_callback(SSL * ssl, uschar key_name[16],
+ uschar * iv, EVP_CIPHER_CTX * c_ctx, HMAC_CTX * hctx, int enc)
+{
+tls_support * tlsp = state_server.tlsp;
+exim_stek * key;
+
+if (enc)
+ {
+ DEBUG(D_tls) debug_printf("ticket_key_callback: create new session\n");
+ tlsp->resumption |= RESUME_CLIENT_REQUESTED;