-#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 * ctx, HMAC_CTX * hctx, int enc)
-{
-tls_support * tlsp = server_static_cbinfo->tlsp;
-exim_stek * key;
-
-if (enc)
- {
- DEBUG(D_tls) debug_printf("ticket_key_callback: create new session\n");
- tlsp->resumption |= RESUME_CLIENT_REQUESTED;
-
- if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0)
- return -1; /* insufficient random */
-
- if (!(key = tk_current())) /* current key doesn't exist or isn't valid */
- return 0; /* key couldn't be created */
- memcpy(key_name, key->name, 16);
- DEBUG(D_tls) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - time(NULL));
-
- /*XXX will want these dependent on the ssl session strength */
- HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
- key->hmac_hash, NULL);
- EVP_EncryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv);
-
- DEBUG(D_tls) debug_printf("ticket created\n");
- return 1;
- }
-else
- {
- time_t now = time(NULL);
-
- DEBUG(D_tls) debug_printf("ticket_key_callback: retrieve session\n");
- tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
-
- if (!(key = tk_find(key_name)) || key->expire < now)
- {
- DEBUG(D_tls)
- {
- debug_printf("ticket not usable (%s)\n", key ? "expired" : "not found");
- if (key) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - now);
- }
- return 0;
- }
-
- HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
- key->hmac_hash, NULL);
- EVP_DecryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv);
-
- DEBUG(D_tls) debug_printf("ticket usable, STEK expire " TIME_T_FMT "\n", key->expire - now);
-
- /* The ticket lifetime and renewal are the same as the STEK lifetime and
- renewal, which is overenthusiastic. A factor of, say, 3x longer STEK would
- be better. To do that we'd have to encode ticket lifetime in the name as
- we don't yet see the restored session. Could check posthandshake for TLS1.3
- and trigger a new ticket then, but cannot do that for TLS1.2 */
- return key->renew < now ? 2 : 1;
- }
-}
-#endif
-
-
-
-/*************************************************
-* Initialize for DH *
-*************************************************/
-
-/* If dhparam is set, expand it, and load up the parameters for DH encryption.
-
-Arguments:
- sctx The current SSL CTX (inbound or outbound)
- dhparam DH parameter file or fixed parameter identity string
- host connected host, if client; NULL if server
- errstr error string pointer
-
-Returns: TRUE if OK (nothing to set up, or setup worked)
-*/
-
-static BOOL
-init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host, uschar ** errstr)
-{
-BIO *bio;
-DH *dh;
-uschar *dhexpanded;
-const char *pem;
-int dh_bitsize;
-
-if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
- return FALSE;
-
-if (!dhexpanded || !*dhexpanded)
- bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1);
-else if (dhexpanded[0] == '/')
- {
- if (!(bio = BIO_new_file(CS dhexpanded, "r")))
- {
- tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
- host, US strerror(errno), errstr);
- return FALSE;
- }
- }
-else
- {
- if (Ustrcmp(dhexpanded, "none") == 0)
- {
- DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
- return TRUE;
- }
-
- if (!(pem = std_dh_prime_named(dhexpanded)))
- {
- tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
- host, US strerror(errno), errstr);
- return FALSE;
- }
- bio = BIO_new_mem_buf(CS pem, -1);
- }
-
-if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
- {
- BIO_free(bio);
- tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
- host, NULL, errstr);
- return FALSE;
- }
-
-/* note: our default limit of 2236 is not a multiple of 8; the limit comes from
- * an NSS limit, and the GnuTLS APIs handle bit-sizes fine, so we went with
- * 2236. But older OpenSSL can only report in bytes (octets), not bits.
- * If someone wants to dance at the edge, then they can raise the limit or use
- * current libraries. */
-#ifdef EXIM_HAVE_OPENSSL_DH_BITS
-/* Added in commit 26c79d5641d; `git describe --contains` says OpenSSL_1_1_0-pre1~1022
- * This predates OpenSSL_1_1_0 (before a, b, ...) so is in all 1.1.0 */
-dh_bitsize = DH_bits(dh);
-#else
-dh_bitsize = 8 * DH_size(dh);
-#endif
-
-/* Even if it is larger, we silently return success rather than cause things
- * to fail out, so that a too-large DH will not knock out all TLS; it's a
- * debatable choice. */
-if (dh_bitsize > tls_dh_max_bits)
- {
- DEBUG(D_tls)
- debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n",
- dh_bitsize, tls_dh_max_bits);
- }
-else
- {
- SSL_CTX_set_tmp_dh(sctx, dh);
- DEBUG(D_tls)
- debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
- dhexpanded ? dhexpanded : US"default", dh_bitsize);
- }
-
-DH_free(dh);
-BIO_free(bio);
-
-return TRUE;
-}
-
-
-
-
-/*************************************************
-* Initialize for ECDH *
-*************************************************/
-
-/* Load parameters for ECDH encryption.
-
-For now, we stick to NIST P-256 because: it's simple and easy to configure;
-it avoids any patent issues that might bite redistributors; despite events in
-the news and concerns over curve choices, we're not cryptographers, we're not
-pretending to be, and this is "good enough" to be better than no support,
-protecting against most adversaries. Given another year or two, there might
-be sufficient clarity about a "right" way forward to let us make an informed
-decision, instead of a knee-jerk reaction.
-
-Longer-term, we should look at supporting both various named curves and
-external files generated with "openssl ecparam", much as we do for init_dh().
-We should also support "none" as a value, to explicitly avoid initialisation.
-
-Patches welcome.
-
-Arguments:
- sctx The current SSL CTX (inbound or outbound)
- host connected host, if client; NULL if server
- errstr error string pointer
-
-Returns: TRUE if OK (nothing to set up, or setup worked)
-*/
-
-static BOOL
-init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr)
-{
-#ifdef OPENSSL_NO_ECDH
-return TRUE;
-#else
-
-EC_KEY * ecdh;
-uschar * exp_curve;
-int nid;
-BOOL rv;
-
-if (host) /* No ECDH setup for clients, only for servers */
- return TRUE;
-
-# ifndef EXIM_HAVE_ECDH
-DEBUG(D_tls)
- debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
-return TRUE;
-# else
-
-if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr))
- return FALSE;
-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
- }
-
-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),
- host, NULL, errstr);
- return FALSE;
- }
-
-if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
- {
- tls_error(US"Unable to create ec curve", host, 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))
- tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL, errstr);
-else
- DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
-
-EC_KEY_free(ecdh);
-return !rv;
-
-# endif /*EXIM_HAVE_ECDH*/
-#endif /*OPENSSL_NO_ECDH*/
-}
-
-
-
-
-#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:
- sctx the SSL_CTX* to update
- cbinfo 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(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo,
- 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 (!(bio = BIO_new_file(CS filename, "rb")))
- {
- DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
- filename);
- return;
- }
-
-if (is_pem)
- {
- uschar * data, * freep;
- char * dummy;
- long len;
- if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
- {
- DEBUG(D_tls) debug_printf("Failed to read PEM file \"%s\"\n",
- filename);
- return;
- }
-debug_printf("read pem file\n");
- 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)
- {
- DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
- 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 = cbinfo->verify_stack;
-verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */