* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Portions Copyright (c) The OpenSSL Project 1999 */
#ifndef DISABLE_OCSP
# include <openssl/ocsp.h>
#endif
-#ifdef EXPERIMENTAL_DANE
-# include <danessl.h>
+#ifdef SUPPORT_DANE
+# include "danessl.h"
#endif
# define EXIM_HAVE_RAND_PSEUDO
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
-# define EXIM_HAVE_SHA256
+# define EXIM_HAVE_SHA256 /*MMMM*/
#endif
/*
#ifndef LIBRESSL_VERSION_NUMBER
# if OPENSSL_VERSION_NUMBER >= 0x010100000L
# define EXIM_HAVE_OPENSSL_CHECKHOST
+# define EXIM_HAVE_OPENSSL_DH_BITS
# endif
# if OPENSSL_VERSION_NUMBER >= 0x010000000L \
&& (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
|| LIBRESSL_VERSION_NUMBER >= 0x20010000L
# if !defined(OPENSSL_NO_ECDH)
# if OPENSSL_VERSION_NUMBER >= 0x0090800fL
-# define EXIM_HAVE_ECDH
+# define EXIM_HAVE_ECDH /*MMMM*/
# endif
# if OPENSSL_VERSION_NUMBER >= 0x10002000L
# define EXIM_HAVE_OPENSSL_EC_NIST2NID
# define DISABLE_OCSP
#endif
+#ifdef EXIM_HAVE_OPENSSL_CHECKHOST
+# include <openssl/x509v3.h>
+#endif
+
/* Structure for collecting random data for seeding. */
typedef struct randstuff {
typedef struct tls_ext_ctx_cb {
uschar *certificate;
uschar *privatekey;
-#ifndef DISABLE_OCSP
BOOL is_server;
+#ifndef DISABLE_OCSP
STACK_OF(X509) *verify_stack; /* chain for verifying the proof */
union {
struct {
-#ifdef EXIM_HAVE_EPHEM_RSA_KEX
/*************************************************
* Callback to generate RSA key *
*************************************************/
/*
Arguments:
- s SSL connection
+ s SSL connection (not used)
export not used
keylength keylength
}
return rsa_key;
}
-#endif
if (preverify_ok == 0)
{
- log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s",
- tlsp == &tls_out ? deliver_host_address : sender_host_address,
- depth,
- X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)),
- dn);
+ 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 (rc < 0)
{
log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
- deliver_host_address);
+ tlsp == &tls_out ? deliver_host_address : sender_host_address);
name = NULL;
}
break;
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: certificate name mismatch: "
- "DN=\"%s\" H=\"%s\"",
- deliver_host_address, dn, verify_cert_hostnames);
+ "[%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)
{
}
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
/* This gets called *by* the dane library verify callback, which interposes
itself.
#endif
if (preverify_ok == 1)
- tls_out.dane_verified =
- tls_out.certificate_verified = TRUE;
+ {
+ tls_out.dane_verified = tls_out.certificate_verified = TRUE;
+#ifndef DISABLE_OCSP
+ if (client_static_cbinfo->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_cbinfo->u_ocsp.client.verify_store,
+ cert))
+ ERR_clear_error();
+ sk_X509_push(client_static_cbinfo->verify_stack, cert);
+ }
+#endif
+ }
else
{
int err = X509_STORE_CTX_get_error(x509ctx);
return preverify_ok;
}
-#endif /*EXPERIMENTAL_DANE*/
+#endif /*SUPPORT_DANE*/
/*************************************************
DH *dh;
uschar *dhexpanded;
const char *pem;
+int dh_bitsize;
if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
return FALSE;
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 ((8*DH_size(dh)) > tls_dh_max_bits)
+if (dh_bitsize > 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);
+ 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", 8*DH_size(dh));
+ dhexpanded ? dhexpanded : US"default", dh_bitsize);
}
DH_free(dh);
#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 = "prime256v1";
+ exp_curve = US"prime256v1";
#else
# if defined SSL_CTRL_SET_ECDH_AUTO
DEBUG(D_tls) debug_printf(
}
supply_response:
- cbinfo->u_ocsp.server.response = resp;
+ cbinfo->u_ocsp.server.response = resp; /*XXX stack?*/
return;
bad:
{
extern char ** environ;
uschar ** p;
- if (environ) for (p = USS environ; *p != NULL; p++)
+ if (environ) for (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 err;
where = US"generating pkey";
- /* deprecated, use RSA_generate_key_ex() */
-if (!(rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)))
+if (!(rsa = rsa_callback(NULL, 0, 1024)))
goto err;
where = US"assigning pkey";
+static int
+tls_add_certfile(SSL_CTX * sctx, tls_ext_ctx_cb * 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, tls_ext_ctx_cb * 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;
+}
+
+
/*************************************************
* Expand key and cert file specs *
*************************************************/
if (!cbinfo->certificate)
{
- if (cbinfo->host) /* client */
+ if (!cbinfo->is_server) /* client */
return OK;
/* server */
if (tls_install_selfsign(sctx, errstr) != OK)
}
else
{
+ int err;
+
if (Ustrstr(cbinfo->certificate, US"tls_sni") ||
Ustrstr(cbinfo->certificate, US"tls_in_sni") ||
Ustrstr(cbinfo->certificate, US"tls_out_sni")
if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr))
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, errstr);
- }
+ if (expanded)
+ if (cbinfo->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_certfile(sctx, cbinfo, file, errstr)))
+ return err;
+ }
+ else /* would there ever be a need for multiple client certs? */
+ if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr)))
+ return err;
if (cbinfo->privatekey != NULL &&
!expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
key is in the same file as the certificate. */
if (expanded && *expanded)
- {
- 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, errstr);
- }
+ if (cbinfo->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, cbinfo, file, errstr)))
+ return err;
+ }
+ else /* would there ever be a need for multiple client certs? */
+ if ((err = tls_add_pkeyfile(sctx, cbinfo, expanded, errstr)))
+ return err;
}
#ifndef DISABLE_OCSP
if (cbinfo->is_server && cbinfo->u_ocsp.server.file)
{
+ /*XXX stack*/
if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr))
return DEFER;
tls_server_stapling_cb(SSL *s, void *arg)
{
const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
-uschar *response_der;
+uschar *response_der; /*XXX blob */
int response_der_len;
+/*XXX stack: use SSL_get_certificate() to see which cert; from that work
+out which ocsp blob to send. Unfortunately, SSL_get_certificate is known
+buggy in current OpenSSL; it returns the last cert loaded always rather than
+the one actually presented. So we can't support a stack of OCSP proofs at
+this time. */
+
DEBUG(D_tls)
debug_printf("Received TLS status request (OCSP stapling); %s response\n",
cbinfo->u_ocsp.server.response ? "have" : "lack");
return SSL_TLSEXT_ERR_NOACK;
response_der = NULL;
-response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response,
+response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, /*XXX stack*/
&response_der);
if (response_der_len <= 0)
return SSL_TLSEXT_ERR_NOACK;
int status, reason;
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
- DEBUG(D_tls) bp = BIO_new_fp(stderr, BIO_NOCLOSE);
+ DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
/*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */
cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
{
tls_out.ocsp = OCSP_FAILED;
- if (LOGGING(tls_cipher))
- log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
+ if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
+ "Received TLS cert status response, itself unverifiable: %s",
+ ERR_reason_error_string(ERR_peek_error()));
BIO_printf(bp, "OCSP response verify failure\n");
ERR_print_errors(bp);
+ OCSP_RESPONSE_print(bp, rsp, 0);
goto failed;
}
tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
uschar *privatekey,
#ifndef DISABLE_OCSP
- uschar *ocsp_file,
+ uschar *ocsp_file, /*XXX stack, in server*/
#endif
address_item *addr, tls_ext_ctx_cb ** cbp, uschar ** errstr)
{
+SSL_CTX * ctx;
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;
+cbinfo->is_server = host==NULL;
#ifndef DISABLE_OCSP
cbinfo->verify_stack = NULL;
-if ((cbinfo->is_server = host==NULL))
+if (!host)
{
cbinfo->u_ocsp.server.file = ocsp_file;
cbinfo->u_ocsp.server.file_expanded = NULL;
By disabling with openssl_options, we can let admins re-enable with the
existing knob. */
-*ctxp = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method());
-
-if (!*ctxp) return tls_error(US"SSL_CTX_new", host, NULL, errstr);
+if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
+ return tls_error(US"SSL_CTX_new", host, NULL, errstr);
/* It turns out that we need to seed the random number generator this early in
order to get the full complement of ciphers to work. It took me roughly a day
gettimeofday(&r.tv, NULL);
r.p = getpid();
- RAND_seed((uschar *)(&r), sizeof(r));
- RAND_seed((uschar *)big_buffer, big_buffer_size);
- if (addr != NULL) RAND_seed((uschar *)addr, sizeof(addr));
+ RAND_seed(US (&r), sizeof(r));
+ RAND_seed(US big_buffer, big_buffer_size);
+ if (addr != NULL) RAND_seed(US addr, sizeof(addr));
if (!RAND_status())
return tls_error(US"RAND_status", host,
/* Set up the information callback, which outputs if debugging is at a suitable
level. */
-DEBUG(D_tls) SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback);
+DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
/* Automatically re-try reads/writes after renegotiation. */
-(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY);
+(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
/* Apply administrator-supplied work-arounds.
Historically we applied just one requested option,
No OpenSSL version number checks: the options we accept depend upon the
availability of the option value macros from OpenSSL. */
-okay = tls_openssl_options_parse(openssl_options, &init_options);
-if (!okay)
+if (!tls_openssl_options_parse(openssl_options, &init_options))
return tls_error(US"openssl_options parsing failed", host, NULL, errstr);
if (init_options)
{
DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options);
- if (!(SSL_CTX_set_options(*ctxp, init_options)))
+ if (!(SSL_CTX_set_options(ctx, init_options)))
return tls_error(string_sprintf(
"SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr);
}
else
DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
+/* Disable session cache unconditionally */
+
+(void) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+
/* Initialize with DH parameters if supplied */
/* Initialize ECDH temp key parameter selection */
-if ( !init_dh(*ctxp, dhparam, host, errstr)
- || !init_ecdh(*ctxp, host, errstr)
+if ( !init_dh(ctx, dhparam, host, errstr)
+ || !init_ecdh(ctx, host, errstr)
)
return DEFER;
/* Set up certificate and key (and perhaps OCSP info) */
-if ((rc = tls_expand_session_files(*ctxp, cbinfo, errstr)) != OK)
+if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK)
return rc;
/* If we need to handle SNI or OCSP, do so */
callback is invoked. */
if (cbinfo->u_ocsp.server.file)
{
- SSL_CTX_set_tlsext_status_cb(server_ctx, tls_server_stapling_cb);
- SSL_CTX_set_tlsext_status_arg(server_ctx, cbinfo);
+ SSL_CTX_set_tlsext_status_cb(ctx, tls_server_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(*ctxp, tls_servername_cb);
- SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo);
+ SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
+ SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
}
# ifndef DISABLE_OCSP
else /* client */
DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n");
return FAIL;
}
- SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb);
- SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo);
+ SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
+ SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
}
# endif
#endif
#ifdef EXIM_HAVE_EPHEM_RSA_KEX
/* Set up the RSA callback */
-SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
+SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback);
#endif
/* Finally, set the timeout, and we are done */
-SSL_CTX_set_timeout(*ctxp, ssl_session_timeout);
+SSL_CTX_set_timeout(ctx, ssl_session_timeout);
DEBUG(D_tls) debug_printf("Initialized TLS\n");
*cbp = cbinfo;
+*ctxp = ctx;
return OK;
}
)
{
log_write(0, LOG_MAIN|LOG_PANIC,
- "failed to load cert hain from %s", file);
+ "failed to load cert chain from %s", file);
return DEFER;
}
#endif
if (tls_in.active >= 0)
{
tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
- smtp_printf("554 Already in TLS\r\n");
+ smtp_printf("554 Already in TLS\r\n", FALSE);
return FAIL;
}
rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
#ifndef DISABLE_OCSP
- tls_ocsp_file,
+ tls_ocsp_file, /*XXX stack*/
#endif
NULL, &server_static_cbinfo, errstr);
if (rc != OK) return rc;
optional, set up appropriately. */
tls_in.certificate_verified = FALSE;
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
tls_in.dane_verified = FALSE;
#endif
server_verify_callback_called = FALSE;
SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx));
if (!tls_in.on_connect)
{
- smtp_printf("220 TLS go ahead\r\n");
+ smtp_printf("220 TLS go ahead\r\n", FALSE);
fflush(smtp_out);
}
smtp_read_response()/ip_recv().
Hence no need to duplicate for _in and _out.
*/
-ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size);
+if (!ssl_xfer_buffer) ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size);
ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0;
ssl_xfer_eof = ssl_xfer_error = 0;
receive_getc = tls_getc;
+receive_getbuf = tls_getbuf;
receive_get_cache = tls_get_cache;
receive_ungetc = tls_ungetc;
receive_feof = tls_feof;
}
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
static int
dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa, uschar ** errstr)
{
log_write(0, LOG_MAIN, "DANE error: No usable TLSA records");
return DEFER;
}
-#endif /*EXPERIMENTAL_DANE*/
+#endif /*SUPPORT_DANE*/
int
tls_client_start(int fd, host_item *host, address_item *addr,
transport_instance * tb,
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
dns_answer * tlsa_dnsa,
#endif
uschar ** errstr)
BOOL require_ocsp = FALSE;
#endif
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
tls_out.tlsa_usage = 0;
#endif
#ifndef DISABLE_OCSP
{
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
if ( tlsa_dnsa
&& ob->hosts_request_ocsp[0] == '*'
&& ob->hosts_request_ocsp[1] == '\0'
verify_check_given_host(&ob->hosts_require_ocsp, host) == OK))
request_ocsp = TRUE;
else
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
if (!request_ocsp)
# endif
request_ocsp =
return tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
}
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
if (tlsa_dnsa)
{
SSL_CTX_set_verify(client_ctx,
}
}
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
if (tlsa_dnsa)
if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa, errstr)) != OK)
return rc;
#ifndef DISABLE_OCSP
/* Request certificate status at connection-time. If the server
does OCSP stapling we will get the callback (set in tls_init()) */
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
if (request_ocsp)
{
const uschar * s;
rc = SSL_connect(client_ssl);
alarm(0);
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
if (tlsa_dnsa)
DANESSL_cleanup(client_ssl);
#endif
+static BOOL
+tls_refill(unsigned lim)
+{
+int error;
+int inbytes;
+
+DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
+ ssl_xfer_buffer, ssl_xfer_buffer_size);
+
+if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
+ MIN(ssl_xfer_buffer_size, lim));
+error = SSL_get_error(server_ssl, inbytes);
+alarm(0);
+
+/* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been
+closed down, not that the socket itself has been closed down. Revert to
+non-SSL handling. */
+
+if (error == SSL_ERROR_ZERO_RETURN)
+ {
+ DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
+
+ receive_getc = smtp_getc;
+ receive_getbuf = smtp_getbuf;
+ receive_get_cache = smtp_get_cache;
+ receive_ungetc = smtp_ungetc;
+ receive_feof = smtp_feof;
+ receive_ferror = smtp_ferror;
+ receive_smtp_buffered = smtp_buffered;
+
+ SSL_free(server_ssl);
+ server_ssl = NULL;
+ tls_in.active = -1;
+ tls_in.bits = 0;
+ tls_in.cipher = NULL;
+ tls_in.peerdn = NULL;
+ tls_in.sni = NULL;
+
+ return FALSE;
+ }
+
+/* Handle genuine errors */
+
+else if (error == SSL_ERROR_SSL)
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
+ ssl_xfer_error = 1;
+ return FALSE;
+ }
+
+else if (error != SSL_ERROR_NONE)
+ {
+ DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
+ ssl_xfer_error = 1;
+ return FALSE;
+ }
+
+#ifndef DISABLE_DKIM
+dkim_exim_verify_feed(ssl_xfer_buffer, inbytes);
+#endif
+ssl_xfer_buffer_hwm = inbytes;
+ssl_xfer_buffer_lwm = 0;
+return TRUE;
+}
+
+
/*************************************************
* TLS version of getc *
*************************************************/
tls_getc(unsigned lim)
{
if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
- {
- int error;
- int inbytes;
-
- DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
- ssl_xfer_buffer, ssl_xfer_buffer_size);
-
- if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
- inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
- MIN(ssl_xfer_buffer_size, lim));
- error = SSL_get_error(server_ssl, inbytes);
- alarm(0);
+ if (!tls_refill(lim))
+ return ssl_xfer_error ? EOF : smtp_getc(lim);
- /* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been
- closed down, not that the socket itself has been closed down. Revert to
- non-SSL handling. */
-
- if (error == SSL_ERROR_ZERO_RETURN)
- {
- DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
-
- receive_getc = smtp_getc;
- receive_get_cache = smtp_get_cache;
- receive_ungetc = smtp_ungetc;
- receive_feof = smtp_feof;
- receive_ferror = smtp_ferror;
- receive_smtp_buffered = smtp_buffered;
-
- SSL_free(server_ssl);
- server_ssl = NULL;
- tls_in.active = -1;
- tls_in.bits = 0;
- tls_in.cipher = NULL;
- tls_in.peerdn = NULL;
- tls_in.sni = NULL;
-
- return smtp_getc(lim);
- }
+/* Something in the buffer; return next uschar */
- /* Handle genuine errors */
+return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
+}
- else if (error == SSL_ERROR_SSL)
- {
- ERR_error_string(ERR_get_error(), ssl_errstring);
- log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
- ssl_xfer_error = 1;
- return EOF;
- }
+uschar *
+tls_getbuf(unsigned * len)
+{
+unsigned size;
+uschar * buf;
- else if (error != SSL_ERROR_NONE)
+if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
+ if (!tls_refill(*len))
{
- DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
- ssl_xfer_error = 1;
- return EOF;
+ if (!ssl_xfer_error) return smtp_getbuf(len);
+ *len = 0;
+ return NULL;
}
-#ifndef DISABLE_DKIM
- dkim_exim_verify_feed(ssl_xfer_buffer, inbytes);
-#endif
- ssl_xfer_buffer_hwm = inbytes;
- ssl_xfer_buffer_lwm = 0;
- }
-
-/* Something in the buffer; return next uschar */
-
-return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
+if ((size = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm) > *len)
+ size = *len;
+buf = &ssl_xfer_buffer[ssl_xfer_buffer_lwm];
+ssl_xfer_buffer_lwm += size;
+*len = size;
+return buf;
}
+
void
tls_get_cache()
{
}
+BOOL
+tls_could_read(void)
+{
+return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm || SSL_pending(server_ssl) > 0;
+}
+
/*************************************************
* Read bytes from TLS channel *
is_server channel specifier
buff buffer of data
len number of bytes
+ more further data expected soon
Returns: the number of bytes after a successful write,
-1 after a failed write
*/
int
-tls_write(BOOL is_server, const uschar *buff, size_t len)
+tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
{
-int outbytes;
-int error;
-int left = len;
+int outbytes, error, left;
SSL *ssl = is_server ? server_ssl : client_ssl;
+static gstring * corked = NULL;
+
+DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
+ buff, (unsigned long)len, more ? ", more" : "");
+
+/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
+"more" is notified. This hack is only ok if small amounts are involved AND only
+one stream does it, in one context (i.e. no store reset). Currently it is used
+for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
+
+if (is_server && (more || corked))
+ {
+ corked = string_catn(corked, buff, len);
+ if (more)
+ return len;
+ buff = CUS corked->s;
+ len = corked->ptr;
+ corked = NULL;
+ }
-DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left);
-while (left > 0)
+for (left = len; left > 0;)
{
DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
outbytes = SSL_write(ssl, CS buff, left);
gettimeofday(&r.tv, NULL);
r.p = getpid();
- RAND_seed((uschar *)(&r), sizeof(r));
+ RAND_seed(US (&r), sizeof(r));
}
/* We're after pseudo-random, not random; if we still don't have enough data
in the internal PRNG then our options are limited. We could sleep and hope
uschar keep_c;
BOOL adding, item_parsed;
-result = 0L;
+result = SSL_OP_NO_TICKET;
/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
* from default because it increases BEAST susceptibility. */
#ifdef SSL_OP_NO_SSLv2
result |= SSL_OP_SINGLE_DH_USE;
#endif
-if (option_spec == NULL)
+if (!option_spec)
{
*results = result;
return TRUE;