#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
/* Structure for collecting random data for seeding. */
static const uschar *sid_ctx = US"exim";
static SSL_CTX *ctx = NULL;
+static SSL_CTX *ctx_sni = NULL;
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 */
+static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
+#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
+#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}
+*/
+
+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;
+}
+
+
+
+
+#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();
SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+/* Automatically re-try reads/writes after renegotiation. */
+(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+
/* Apply administrator-supplied work-arounds.
Historically we applied just one requested option,
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, but when bug 994 requested a second, we
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 */
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_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;
}
ver = US"TLSv1";
break;
+#ifdef TLS1_1_VERSION
+ case TLS1_1_VERSION:
+ ver = US"TLSv1.1";
+ break;
+#endif
+
+#ifdef TLS1_2_VERSION
+ case TLS1_2_VERSION:
+ ver = US"TLSv1.2";
+ break;
+#endif
+
default:
ver = US"UNKNOWN";
}
/* 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;
}
/* Prepare for new connection */
if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
-SSL_clear(ssl);
+
+/* Warning: we used to SSL_clear(ssl) here, it was removed.
+ *
+ * With the SSL_clear(), we get strange interoperability bugs with
+ * OpenSSL 1.0.1b and TLS1.1/1.2. It looks as though this may be a bug in
+ * OpenSSL itself, as a clear should not lead to inability to follow protocols.
+ *
+ * The SSL_clear() call is to let an existing SSL* be reused, typically after
+ * session shutdown. In this case, we have a brand new object and there's no
+ * obvious reason to immediately clear it. I'm guessing that this was
+ * originally added because of incomplete initialisation which the clear fixed,
+ * in some historic release.
+ */
/* Set context and tell client to go ahead, except in the case of TLS startup
on connection, where outputting anything now upsets the clients and tends to
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
+ {
+ DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
+ SSL_set_tlsext_host_name(ssl, tls_sni);
+ }
+ }
+
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
int error;
int inbytes;
- DEBUG(D_tls) debug_printf("Calling SSL_read(%lx, %lx, %u)\n", (long)ssl,
- (long)ssl_xfer_buffer, ssl_xfer_buffer_size);
+ DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
+ ssl_xfer_buffer, ssl_xfer_buffer_size);
if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
inbytes = SSL_read(ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size);
SSL_free(ssl);
ssl = NULL;
tls_active = -1;
+ tls_bits = 0;
tls_cipher = NULL;
tls_peerdn = NULL;
+ tls_sni = NULL;
return smtp_getc();
}
ssl_xfer_error = 1;
return EOF;
}
+
#ifndef DISABLE_DKIM
dkim_exim_verify_feed(ssl_xfer_buffer, inbytes);
#endif
int inbytes;
int error;
-DEBUG(D_tls) debug_printf("Calling SSL_read(%lx, %lx, %u)\n", (long)ssl,
- (long)buff, (unsigned int)len);
+DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
+ buff, (unsigned int)len);
inbytes = SSL_read(ssl, CS buff, len);
error = SSL_get_error(ssl, inbytes);
int error;
int left = len;
-DEBUG(D_tls) debug_printf("tls_do_write(%lx, %d)\n", (long)buff, left);
+DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left);
while (left > 0)
{
- DEBUG(D_tls) debug_printf("SSL_write(SSL, %lx, %d)\n", (long)buff, left);
+ DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
outbytes = SSL_write(ssl, CS buff, left);
error = SSL_get_error(ssl, outbytes);
DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
/*************************************************
-* 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)
{
to apply.
This list is current as of:
- ==> 1.0.0c <== */
+ ==> 1.0.1b <== */
static struct exim_openssl_option exim_openssl_options[] = {
/* KEEP SORTED ALPHABETICALLY! */
#ifdef SSL_OP_ALL
#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
{ US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
#endif
+#ifdef SSL_OP_NO_COMPRESSION
+ { US"no_compression", SSL_OP_NO_COMPRESSION },
+#endif
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
{ US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
#endif
#ifdef SSL_OP_NO_TLSv1
{ US"no_tlsv1", SSL_OP_NO_TLSv1 },
#endif
+#ifdef SSL_OP_NO_TLSv1_1
+#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
+ { US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
+#endif
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ { US"no_tlsv1_2", SSL_OP_NO_TLSv1_2 },
+#endif
#ifdef SSL_OP_SINGLE_DH_USE
{ US"single_dh_use", SSL_OP_SINGLE_DH_USE },
#endif
static int exim_openssl_options_size =
sizeof(exim_openssl_options)/sizeof(struct exim_openssl_option);
+
static BOOL
tls_openssl_one_option_parse(uschar *name, long *value)
{
BOOL adding, item_parsed;
result = 0L;
-/* We grandfather in as default the one option which we used to set always. */
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-result |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
-#endif
+/* 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)
{