* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
/* See the file NOTICE for conditions of use and distribution. */
/* This module provides the TLS (aka SSL) support for Exim using the OpenSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
+#ifdef EXPERIMENTAL_OCSP
+#include <openssl/ocsp.h>
+#endif
+
+#ifdef EXPERIMENTAL_OCSP
+#define EXIM_OCSP_SKEW_SECONDS (300L)
+#define EXIM_OCSP_MAX_AGE (-1L)
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+#define EXIM_HAVE_OPENSSL_TLSEXT
+#endif
/* Structure for collecting random data for seeding. */
static const uschar *sid_ctx = US"exim";
static SSL_CTX *ctx = NULL;
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+static SSL_CTX *ctx_sni = NULL;
+#endif
static SSL *ssl = NULL;
static char ssl_errstring[256];
static int ssl_session_timeout = 200;
static BOOL verify_optional = FALSE;
+static BOOL reexpand_tls_files_for_sni = FALSE;
+typedef struct tls_ext_ctx_cb {
+ uschar *certificate;
+ uschar *privatekey;
+#ifdef EXPERIMENTAL_OCSP
+ uschar *ocsp_file;
+ uschar *ocsp_file_expanded;
+ OCSP_RESPONSE *ocsp_response;
+#endif
+ uschar *dhparam;
+ /* these are cached from first expand */
+ uschar *server_cipher_list;
+ /* only passed down to tls_error: */
+ host_item *host;
+} tls_ext_ctx_cb;
+
+/* should figure out a cleanup of API to handle state preserved per
+implementation, for various reasons, which can be void * in the APIs.
+For now, we hack around it. */
+tls_ext_ctx_cb *static_cbinfo = NULL;
+
+static int
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional);
+
+/* Callbacks */
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
+#endif
+#ifdef EXPERIMENTAL_OCSP
+static int tls_stapling_cb(SSL *s, void *arg);
+#endif
/*************************************************
*************************************************/
/* The SSL library functions call this from time to time to indicate what they
-are doing. We copy the string to the debugging output when the level is high
-enough.
+are doing. We copy the string to the debugging output when TLS debugging has
+been requested.
Arguments:
s the SSL connection
}
else
{
- SSL_CTX_set_tmp_dh(ctx, dh);
- DEBUG(D_tls)
- debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n",
- dhexpanded, 8*DH_size(dh));
+ if ((8*DH_size(dh)) > tls_dh_max_bits)
+ {
+ DEBUG(D_tls)
+ debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d",
+ 8*DH_size(dh), tls_dh_max_bits);
+ }
+ else
+ {
+ SSL_CTX_set_tmp_dh(ctx, dh);
+ DEBUG(D_tls)
+ debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n",
+ dhexpanded, 8*DH_size(dh));
+ }
DH_free(dh);
}
BIO_free(bio);
+#ifdef EXPERIMENTAL_OCSP
+/*************************************************
+* Load OCSP information into state *
+*************************************************/
+
+/* Called to load the 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
+ expanded the filename putatively holding an OCSP response
+
+*/
+
+static void
+ocsp_load_response(SSL_CTX *sctx,
+ tls_ext_ctx_cb *cbinfo,
+ const uschar *expanded)
+{
+BIO *bio;
+OCSP_RESPONSE *resp;
+OCSP_BASICRESP *basic_response;
+OCSP_SINGLERESP *single_response;
+ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+X509_STORE *store;
+unsigned long verify_flags;
+int status, reason, i;
+
+cbinfo->ocsp_file_expanded = string_copy(expanded);
+if (cbinfo->ocsp_response)
+ {
+ OCSP_RESPONSE_free(cbinfo->ocsp_response);
+ cbinfo->ocsp_response = NULL;
+ }
+
+bio = BIO_new_file(CS cbinfo->ocsp_file_expanded, "rb");
+if (!bio)
+ {
+ DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
+ cbinfo->ocsp_file_expanded);
+ return;
+ }
+
+resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+BIO_free(bio);
+if (!resp)
+ {
+ DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
+ return;
+ }
+
+status = OCSP_response_status(resp);
+if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+ {
+ DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n",
+ OCSP_response_status_str(status), status);
+ return;
+ }
+
+basic_response = OCSP_response_get1_basic(resp);
+if (!basic_response)
+ {
+ DEBUG(D_tls)
+ debug_printf("OCSP response parse error: unable to extract basic response.\n");
+ return;
+ }
+
+store = SSL_CTX_get_cert_store(sctx);
+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 */
+
+i = OCSP_basic_verify(basic_response, NULL, store, verify_flags);
+if (i <= 0)
+ {
+ DEBUG(D_tls) {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
+ }
+ return;
+ }
+
+/* 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. */
+single_response = OCSP_resp_get0(basic_response, 0);
+if (!single_response)
+ {
+ DEBUG(D_tls)
+ debug_printf("Unable to get first response from OCSP basic response.\n");
+ return;
+ }
+
+status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd);
+/* how does this status differ from the one above? */
+if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+ {
+ DEBUG(D_tls) debug_printf("OCSP response not valid (take 2): %s (%d)\n",
+ OCSP_response_status_str(status), status);
+ return;
+ }
+
+if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+ {
+ DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+ return;
+ }
+
+cbinfo->ocsp_response = resp;
+}
+#endif
+
+
+
+
+/*************************************************
+* Expand key and cert file specs *
+*************************************************/
+
+/* Called once during tls_init and possibly againt 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
+ cbinfo various parts of session state
+
+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo)
+{
+uschar *expanded;
+
+if (cbinfo->certificate == NULL)
+ return OK;
+
+if (Ustrstr(cbinfo->certificate, US"tls_sni"))
+ reexpand_tls_files_for_sni = TRUE;
+
+if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded))
+ return DEFER;
+
+if (expanded != NULL)
+ {
+ DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
+ if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_certificate_chain_file file=%s", expanded),
+ cbinfo->host, NULL);
+ }
+
+if (cbinfo->privatekey != NULL &&
+ !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded))
+ 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 != NULL && *expanded != 0)
+ {
+ DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
+ if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
+ return tls_error(string_sprintf(
+ "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL);
+ }
+
+#ifdef EXPERIMENTAL_OCSP
+if (cbinfo->ocsp_file != NULL)
+ {
+ if (!expand_check(cbinfo->ocsp_file, US"tls_ocsp_file", &expanded))
+ return DEFER;
+
+ if (expanded != NULL && *expanded != 0)
+ {
+ DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded);
+ if (cbinfo->ocsp_file_expanded &&
+ (Ustrcmp(expanded, cbinfo->ocsp_file_expanded) == 0))
+ {
+ DEBUG(D_tls)
+ debug_printf("tls_ocsp_file value unchanged, using existing values.\n");
+ } else {
+ ocsp_load_response(sctx, cbinfo, expanded);
+ }
+ }
+ }
+#endif
+
+return OK;
+}
+
+
+
+
+/*************************************************
+* Callback to handle SNI *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if a Server Name
+Indication extension was sent by the client.
+
+API documentation is OpenSSL s_server.c implementation.
+
+Arguments:
+ s SSL* of the current session
+ ad unknown (part of OpenSSL API) (unused)
+ arg Callback of "our" registered data
+
+Returns: SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK}
+*/
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+{
+const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+int rc;
+int old_pool = store_pool;
+
+if (!servername)
+ return SSL_TLSEXT_ERR_OK;
+
+DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
+ reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
+
+/* Make the extension value available for expansion */
+store_pool = POOL_PERM;
+tls_sni = string_copy(US servername);
+store_pool = old_pool;
+
+if (!reexpand_tls_files_for_sni)
+ return SSL_TLSEXT_ERR_OK;
+
+/* Can't find an SSL_CTX_clone() or equivalent, so we do it manually;
+not confident that memcpy wouldn't break some internal reference counting.
+Especially since there's a references struct member, which would be off. */
+
+ctx_sni = SSL_CTX_new(SSLv23_server_method());
+if (!ctx_sni)
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+/* Not sure how many of these are actually needed, since SSL object
+already exists. Might even need this selfsame callback, for reneg? */
+
+SSL_CTX_set_info_callback(ctx_sni, SSL_CTX_get_info_callback(ctx));
+SSL_CTX_set_mode(ctx_sni, SSL_CTX_get_mode(ctx));
+SSL_CTX_set_options(ctx_sni, SSL_CTX_get_options(ctx));
+SSL_CTX_set_timeout(ctx_sni, SSL_CTX_get_timeout(ctx));
+SSL_CTX_set_tlsext_servername_callback(ctx_sni, tls_servername_cb);
+SSL_CTX_set_tlsext_servername_arg(ctx_sni, cbinfo);
+if (cbinfo->server_cipher_list)
+ SSL_CTX_set_cipher_list(ctx_sni, CS cbinfo->server_cipher_list);
+#ifdef EXPERIMENTAL_OCSP
+if (cbinfo->ocsp_file)
+ {
+ SSL_CTX_set_tlsext_status_cb(ctx_sni, tls_stapling_cb);
+ SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
+ }
+#endif
+
+rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+/* do this after setup_certs, because this can require the certs for verifying
+OCSP information. */
+rc = tls_expand_session_files(ctx_sni, cbinfo);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+DEBUG(D_tls) debug_printf("Switching SSL context.\n");
+SSL_set_SSL_CTX(s, ctx_sni);
+
+return SSL_TLSEXT_ERR_OK;
+}
+#endif /* EXIM_HAVE_OPENSSL_TLSEXT */
+
+
+
+
+#ifdef EXPERIMENTAL_OCSP
+/*************************************************
+* Callback to handle OCSP Stapling *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if the client
+requests OCSP information with a Certificate Status Request.
+
+Documentation via openssl s_server.c and the Apache patch from the OpenSSL
+project.
+
+*/
+
+static int
+tls_stapling_cb(SSL *s, void *arg)
+{
+const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+uschar *response_der;
+int response_der_len;
+
+DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response.\n",
+ cbinfo->ocsp_response ? "have" : "lack");
+if (!cbinfo->ocsp_response)
+ return SSL_TLSEXT_ERR_NOACK;
+
+response_der = NULL;
+response_der_len = i2d_OCSP_RESPONSE(cbinfo->ocsp_response, &response_der);
+if (response_der_len <= 0)
+ return SSL_TLSEXT_ERR_NOACK;
+
+SSL_set_tlsext_status_ocsp_resp(ssl, response_der, response_der_len);
+return SSL_TLSEXT_ERR_OK;
+}
+
+#endif /* EXPERIMENTAL_OCSP */
+
+
+
+
/*************************************************
* Initialize for TLS *
*************************************************/
static int
tls_init(host_item *host, uschar *dhparam, uschar *certificate,
- uschar *privatekey, address_item *addr)
+ uschar *privatekey,
+#ifdef EXPERIMENTAL_OCSP
+ uschar *ocsp_file,
+#endif
+ address_item *addr)
{
long init_options;
+int rc;
BOOL okay;
+tls_ext_ctx_cb *cbinfo;
+
+cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
+cbinfo->certificate = certificate;
+cbinfo->privatekey = privatekey;
+#ifdef EXPERIMENTAL_OCSP
+cbinfo->ocsp_file = ocsp_file;
+#endif
+cbinfo->dhparam = dhparam;
+cbinfo->host = host;
SSL_load_error_strings(); /* basic set up */
OpenSSL_add_ssl_algorithms();
if (!init_dh(dhparam, host)) return DEFER;
-/* Set up certificate and key */
-
-if (certificate != NULL)
- {
- uschar *expanded;
- if (!expand_check(certificate, US"tls_certificate", &expanded))
- return DEFER;
-
- if (expanded != NULL)
- {
- DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
- if (!SSL_CTX_use_certificate_chain_file(ctx, CS expanded))
- return tls_error(string_sprintf(
- "SSL_CTX_use_certificate_chain_file file=%s", expanded), host, NULL);
- }
-
- if (privatekey != NULL &&
- !expand_check(privatekey, US"tls_privatekey", &expanded))
- return DEFER;
+/* Set up certificate and key (and perhaps OCSP info) */
- /* 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. */
+rc = tls_expand_session_files(ctx, cbinfo);
+if (rc != OK) return rc;
- if (expanded != NULL && *expanded != 0)
+/* If we need to handle SNI, do so */
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+if (host == NULL)
+ {
+#ifdef EXPERIMENTAL_OCSP
+ /* We check ocsp_file, not ocsp_response, because we care about if
+ the option exists, not what the current expansion might be, as SNI might
+ change the certificate and OCSP file in use between now and the time the
+ callback is invoked. */
+ if (cbinfo->ocsp_file)
{
- DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
- if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM))
- return tls_error(string_sprintf(
- "SSL_CTX_use_PrivateKey_file file=%s", expanded), host, NULL);
+ SSL_CTX_set_tlsext_status_cb(ctx, tls_stapling_cb);
+ SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
}
+#endif
+ /* We always do this, so that $tls_sni is available even if not used in
+ tls_certificate */
+ SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
+ SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
}
+#endif
/* Set up the RSA callback */
SSL_CTX_set_timeout(ctx, ssl_session_timeout);
DEBUG(D_tls) debug_printf("Initialized TLS\n");
+
+static_cbinfo = cbinfo;
+
return OK;
}
/* Called by both client and server startup
Arguments:
+ sctx SSL_CTX* to initialise
certs certs file or NULL
crl CRL file or NULL
host NULL in a server; the remote host in a client
*/
static int
-setup_certs(uschar *certs, uschar *crl, host_item *host, BOOL optional)
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional)
{
uschar *expcerts, *expcrl;
if (expcerts != NULL)
{
struct stat statbuf;
- if (!SSL_CTX_set_default_verify_paths(ctx))
+ if (!SSL_CTX_set_default_verify_paths(sctx))
return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
if (Ustat(expcerts, &statbuf) < 0)
says no certificate was supplied.) But this is better. */
if ((file == NULL || statbuf.st_size > 0) &&
- !SSL_CTX_load_verify_locations(ctx, CS file, CS dir))
+ !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
if (file != NULL)
{
- SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file));
+ SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file));
}
}
{
/* is it a file or directory? */
uschar *file, *dir;
- X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx);
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(sctx);
if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR)
{
file = NULL;
/* If verification is optional, don't fail if no certificate */
- SSL_CTX_set_verify(ctx,
+ SSL_CTX_set_verify(sctx,
SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
verify_callback);
}
Arguments:
require_ciphers allowed ciphers
- ------------------------------------------------------
- require_mac list of allowed MACs ) Not used
- require_kx list of allowed key_exchange methods ) for
- require_proto list of allowed protocols ) OpenSSL
- ------------------------------------------------------
Returns: OK on success
DEFER for errors before the start of the negotiation
*/
int
-tls_server_start(uschar *require_ciphers, uschar *require_mac,
- uschar *require_kx, uschar *require_proto)
+tls_server_start(const uschar *require_ciphers)
{
int rc;
uschar *expciphers;
+tls_ext_ctx_cb *cbinfo;
/* Check for previous activation */
/* Initialize the SSL library. If it fails, it will already have logged
the error. */
-rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL);
+rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey,
+#ifdef EXPERIMENTAL_OCSP
+ tls_ocsp_file,
+#endif
+ NULL);
if (rc != OK) return rc;
+cbinfo = static_cbinfo;
if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
return FAIL;
/* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
-are separated by underscores. So that I can use either form in my tests, and
-also for general convenience, we turn underscores into hyphens here. */
+were historically separated by underscores. So that I can use either form in my
+tests, and also for general convenience, we turn underscores into hyphens here.
+*/
if (expciphers != NULL)
{
DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL);
+ cbinfo->server_cipher_list = expciphers;
}
/* If this is a host for which certificate verification is mandatory or
if (verify_check_host(&tls_verify_hosts) == OK)
{
- rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE);
+ rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, FALSE);
if (rc != OK) return rc;
verify_optional = FALSE;
}
else if (verify_check_host(&tls_try_verify_hosts) == OK)
{
- rc = setup_certs(tls_verify_certificates, tls_crl, NULL, TRUE);
+ rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, TRUE);
if (rc != OK) return rc;
verify_optional = TRUE;
}
dhparam DH parameter file
certificate certificate file
privatekey private key file
+ sni TLS SNI to send to remote host
verify_certs file for certificate verify
crl file containing CRL
require_ciphers list of allowed ciphers
- ------------------------------------------------------
- require_mac list of allowed MACs ) Not used
- require_kx list of allowed key_exchange methods ) for
- require_proto list of allowed protocols ) OpenSSL
- ------------------------------------------------------
timeout startup timeout
Returns: OK on success
int
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
- uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl,
- uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
- uschar *require_proto, int timeout)
+ uschar *certificate, uschar *privatekey, uschar *sni,
+ uschar *verify_certs, uschar *crl,
+ uschar *require_ciphers, int timeout)
{
static uschar txt[256];
uschar *expciphers;
X509* server_cert;
int rc;
-rc = tls_init(host, dhparam, certificate, privatekey, addr);
+rc = tls_init(host, dhparam, certificate, privatekey,
+#ifdef EXPERIMENTAL_OCSP
+ NULL,
+#endif
+ addr);
if (rc != OK) return rc;
tls_certificate_verified = FALSE;
return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
}
-rc = setup_certs(verify_certs, crl, host, FALSE);
+rc = setup_certs(ctx, verify_certs, crl, host, FALSE);
if (rc != OK) return rc;
if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
SSL_set_fd(ssl, fd);
SSL_set_connect_state(ssl);
+if (sni)
+ {
+ if (!expand_check(sni, US"tls_sni", &tls_sni))
+ return FAIL;
+ if (!Ustrlen(tls_sni))
+ tls_sni = NULL;
+ else
+ {
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+ DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
+ SSL_set_tlsext_host_name(ssl, tls_sni);
+#else
+ DEBUG(D_tls)
+ debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
+ tls_sni);
+#endif
+ }
+ }
+
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
SSL_free(ssl);
ssl = NULL;
tls_active = -1;
+ tls_bits = 0;
tls_cipher = NULL;
tls_peerdn = NULL;
+ tls_sni = NULL;
return smtp_getc();
}
+/*************************************************
+* Let tls_require_ciphers be checked at startup *
+*************************************************/
+
+/* The tls_require_ciphers option, if set, must be something which the
+library can parse.
+
+Returns: NULL on success, or error message
+*/
+
+uschar *
+tls_validate_require_cipher(void)
+{
+SSL_CTX *ctx;
+uschar *s, *expciphers, *err;
+
+/* this duplicates from tls_init(), we need a better "init just global
+state, for no specific purpose" singleton function of our own */
+
+SSL_load_error_strings();
+OpenSSL_add_ssl_algorithms();
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+/* SHA256 is becoming ever more popular. This makes sure it gets added to the
+list of available digests. */
+EVP_add_digest(EVP_sha256());
+#endif
+
+if (!(tls_require_ciphers && *tls_require_ciphers))
+ return NULL;
+
+if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers))
+ return US"failed to expand tls_require_ciphers";
+
+if (!(expciphers && *expciphers))
+ return NULL;
+
+/* normalisation ripped from above */
+s = expciphers;
+while (*s != 0) { if (*s == '_') *s = '-'; s++; }
+
+err = NULL;
+
+ctx = SSL_CTX_new(SSLv23_server_method());
+if (!ctx)
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);
+ }
+
+DEBUG(D_tls)
+ debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers);
+
+if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed", expciphers);
+ }
+
+SSL_CTX_free(ctx);
+
+return err;
+}
+
+
+
+
/*************************************************
* Report the library versions. *
*************************************************/
/*************************************************
-* Pseudo-random number generation *
+* Random number generation *
*************************************************/
/* Pseudo-random number generation. The result is not expected to be
*/
int
-pseudo_random_number(int max)
+vaguely_random_number(int max)
{
unsigned int r;
int i, needed_len;
needed_len = i;
/* We do not care if crypto-strong */
-(void) RAND_pseudo_bytes(smallbuf, needed_len);
+i = RAND_pseudo_bytes(smallbuf, needed_len);
+if (i < 0)
+ {
+ DEBUG(D_all)
+ debug_printf("OpenSSL RAND_pseudo_bytes() not supported by RAND method, using fallback.\n");
+ return vaguely_random_number_fallback(max);
+ }
+
r = 0;
for (p = smallbuf; needed_len; --needed_len, ++p)
{
BOOL adding, item_parsed;
result = 0L;
-/* Prior to 4.78 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
+/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
* from default because it increases BEAST susceptibility. */
if (option_spec == NULL)