*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2019 */
-/* Copyright (c) The Exim Maintainers 2020 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
/* Portions Copyright (c) The OpenSSL Project 1999 */
{ US"no_tlsv1", SSL_OP_NO_TLSv1 },
#endif
#ifdef SSL_OP_NO_TLSv1_1
-# if SSL_OP_NO_TLSv1_1 == 0x00000400L
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+# if SSL_OP_NO_TLSv1_1 == 0x00000400L
/* Error in chosen value in 1.0.1a; see first item in CHANGES for 1.0.1b */
-# warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
-# else
+# warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
+# define NO_SSL_OP_NO_TLSv1_1
+# endif
+# endif
+# ifndef NO_SSL_OP_NO_TLSv1_1
{ US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
# endif
#endif
*************************************************/
/* If dhparam is set, expand it, and load up the parameters for DH encryption.
+Server only.
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)
+init_dh(SSL_CTX * sctx, uschar * dhparam, uschar ** errstr)
{
-BIO *bio;
-DH *dh;
-uschar *dhexpanded;
-const char *pem;
+BIO * bio;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+DH * dh;
+#else
+EVP_PKEY * pkey;
+#endif
+uschar * dhexpanded;
+const char * pem;
int dh_bitsize;
if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
if (!(bio = BIO_new_file(CS dhexpanded, "r")))
{
tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
- host, US strerror(errno), errstr);
+ NULL, US strerror(errno), errstr);
return FALSE;
}
}
if (!(pem = std_dh_prime_named(dhexpanded)))
{
tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
- host, US strerror(errno), errstr);
+ NULL, US strerror(errno), errstr);
return FALSE;
}
bio = BIO_new_mem_buf(CS pem, -1);
}
-if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
+if (!(
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)
+#else
+ pkey = PEM_read_bio_Parameters_ex(bio, NULL, NULL, NULL)
+#endif
+ ) )
{
BIO_free(bio);
tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
- host, NULL, errstr);
+ NULL, 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
+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. */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+# 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 */
+This predates OpenSSL_1_1_0 (before a, b, ...) so is in all 1.1.0 */
dh_bitsize = DH_bits(dh);
-#else
+# else
dh_bitsize = 8 * DH_size(dh);
+# endif
+#else /* 3.0.0 + */
+dh_bitsize = EVP_PKEY_get_bits(pkey);
#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)
+/* 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. Likewise for a failing attempt to set one. */
+
+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);
+ if (
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ SSL_CTX_set_tmp_dh(sctx, dh)
+#else
+ SSL_CTX_set0_tmp_dh_pkey(sctx, pkey)
+#endif
+ == 0)
+ {
+ ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+ log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (D-H param setting '%s'): %s",
+ dhexpanded ? dhexpanded : US"default", ssl_errstring);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ /* EVP_PKEY_free(pkey); crashes */
+#endif
+ }
+ else
+ DEBUG(D_tls)
+ debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+ dhexpanded ? dhexpanded : US"default", dh_bitsize);
}
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);
- }
+ 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
DH_free(dh);
-BIO_free(bio);
+#endif
+/* The EVP_PKEY ownership stays with the ctx; do not free it */
+BIO_free(bio);
return TRUE;
}
* Initialize for ECDH *
*************************************************/
-/* Load parameters for ECDH encryption.
+/* Load parameters for ECDH encryption. Server only.
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
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)
+init_ecdh(SSL_CTX * sctx, 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");
)
{
tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
- host, NULL, errstr);
+ NULL, 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;
- }
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+ {
+ EC_KEY * ecdh;
+ if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+ {
+ 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. */
+ /* 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);
+ if ((rv = 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);
+ EC_KEY_free(ecdh);
+ }
+
+#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' curve\n", exp_curve);
+ DEBUG(D_tls) debug_printf("ECDH: enabled '%s' group\n", exp_curve);
+
+#endif
-EC_KEY_free(ecdh);
return !rv;
# endif /*EXIM_HAVE_ECDH*/
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))
+ if (init_dh(ctx, tls_dhparam, &dummy_errstr))
state_server.lib_state.dh = TRUE;
}
+else
+ 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");
- if (init_ecdh(ctx, NULL, &dummy_errstr))
+ if (init_ecdh(ctx, &dummy_errstr))
state_server.lib_state.ecdh = TRUE;
}
+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 */
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))
SSL_CTX_set_tlsext_servername_arg(server_sni, state);
}
-if ( !init_dh(server_sni, state->dhparam, NULL, &dummy_errstr)
- || !init_ecdh(server_sni, NULL, &dummy_errstr)
+if ( !init_dh(server_sni, state->dhparam, &dummy_errstr)
+ || !init_ecdh(server_sni, &dummy_errstr)
)
goto bad;
const X509 * cert_sent = SSL_get_certificate(s);
const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent);
const BIGNUM * cert_bn = ASN1_INTEGER_to_BN(cert_serial, NULL);
- const X509_NAME * cert_issuer = X509_get_issuer_name(cert_sent);
- uschar * chash;
- uint chash_len;
for (; olist; olist = olist->next)
{
/* Initialize with DH parameters if supplied */
/* Initialize ECDH temp key parameter selection */
-if (state->lib_state.dh)
- { DEBUG(D_tls) debug_printf("TLS: DH params were preloaded\n"); }
-else
- if (!init_dh(ctx, state->dhparam, host, errstr)) return DEFER;
+if (!host)
+ {
+ if (state->lib_state.dh)
+ { DEBUG(D_tls) debug_printf("TLS: DH params were preloaded\n"); }
+ else
+ if (!init_dh(ctx, state->dhparam, errstr)) return DEFER;
-if (state->lib_state.ecdh)
- { DEBUG(D_tls) debug_printf("TLS: ECDH curve was preloaded\n"); }
-else
- if (!init_ecdh(ctx, host, errstr)) return DEFER;
+ if (state->lib_state.ecdh)
+ { DEBUG(D_tls) debug_printf("TLS: ECDH curve was preloaded\n"); }
+ else
+ if (!init_ecdh(ctx, errstr)) return DEFER;
+ }
/* Set up certificate and key (and perhaps OCSP info) */
else
goto skip_certs;
- {
+ {
uschar * expcerts;
if (!expand_check(tls_verify_certificates, US"tls_verify_certificates",
&expcerts, errstr))
if (expcerts && *expcerts)
setup_cert_verify(ctx, server_verify_optional, verify_callback_server);
- }
+ }
skip_certs: ;
#ifndef DISABLE_TLS_RESUME
receive_ungetc = tls_ungetc;
receive_feof = tls_feof;
receive_ferror = tls_ferror;
-receive_smtp_buffered = tls_smtp_buffered;
tls_in.active.sock = fileno(smtp_out);
tls_in.active.tls_ctx = NULL; /* not using explicit ctx for server-side */
else
return OK;
- {
+ {
uschar * expcerts;
if (!expand_check(ob->tls_verify_certificates, US"tls_verify_certificates",
&expcerts, errstr))
if (expcerts && *expcerts)
setup_cert_verify(ctx, client_verify_optional, verify_callback_client);
- }
+ }
if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
{
BOOL
-tls_could_read(void)
+tls_could_getc(void)
{
return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm
|| SSL_pending(state_server.lib_state.lib_ssl) > 0;
receive_ungetc = smtp_ungetc;
receive_feof = smtp_feof;
receive_ferror = smtp_ferror;
- receive_smtp_buffered = smtp_buffered;
tls_in.active.tls_ctx = NULL;
tls_in.sni = NULL;
/* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */