* 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
+#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
/*
# if OPENSSL_VERSION_NUMBER >= 0x010100000L
# define EXIM_HAVE_OPENSSL_CHECKHOST
# define EXIM_HAVE_OPENSSL_DH_BITS
+# define EXIM_HAVE_OPENSSL_TLS_METHOD
# 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 {
Simple case: client, `client_ctx`
As a client, we can be doing a callout or cut-through delivery while receiving
a message. So we have a client context, which should have options initialised
- from the SMTP Transport.
+ from the SMTP Transport. We may also concurrently want to make TLS connections
+ to utility daemons, so client-contexts are allocated and passed around in call
+ args rather than using a gobal.
Server:
There are two cases: with and without ServerNameIndication from the client.
configuration.
*/
-static SSL_CTX *client_ctx = NULL;
+typedef struct {
+ SSL_CTX * ctx;
+ SSL * ssl;
+} exim_openssl_client_tls_ctx;
+
static SSL_CTX *server_ctx = NULL;
-static SSL *client_ssl = NULL;
static SSL *server_ssl = NULL;
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
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 ( tlsp == &tls_out
&& ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
- /* client, wanting hostname check */
+ /* client, wanting hostname check */
{
#ifdef EXIM_HAVE_OPENSSL_CHECKHOST
}
-#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*/
/*************************************************
SNI handling.
Separately we might try to replace using OCSP_basic_verify() - which seems to not
-be a public interface into the OpenSSL library (there's no manual entry) -
+be a public interface into the OpenSSL library (there's no manual entry) -
But what with? We also use OCSP_basic_verify in the client stapling callback.
And there we NEED it; we must verify that status... unless the
library does it for us anyway? */
}
supply_response:
- cbinfo->u_ocsp.server.response = resp;
+ cbinfo->u_ocsp.server.response = resp; /*XXX stack?*/
return;
bad:
- if (running_in_test_harness)
+ if (f.running_in_test_harness)
{
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";
goto err;
X509_set_version(x509, 2); /* N+1 - version 3 */
-ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
+ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60); /* 1 hour */
X509_set_pubkey(x509, 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 */
+ /* server */
if (tls_install_selfsign(sctx, errstr) != OK)
return DEFER;
}
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;
not confident that memcpy wouldn't break some internal reference counting.
Especially since there's a references struct member, which would be off. */
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(server_sni = SSL_CTX_new(TLS_server_method())))
+#else
if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
+#endif
{
ERR_error_string(ERR_get_error(), ssl_errstring);
DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
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)
{
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. */
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(ctx = SSL_CTX_new(host ? TLS_client_method() : TLS_server_method())))
+#else
if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
+#endif
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
else
DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
-/* Disable session cache unconditionally */
-
+/* We'd like to disable session cache unconditionally, but foolish Outlook
+Express clients then give up the first TLS connection and make a second one
+(which works). Only when there is an IMAP service on the same machine.
+Presumably OE is trying to use the cache for A on B. Leave it enabled for
+now, until we work out a decent way of presenting control to the config. It
+will never be used because we use a new context every time. */
+#ifdef notdef
(void) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+#endif
/* Initialize with DH parameters if supplied */
/* Initialize ECDH temp key parameter selection */
}
# endif
-if (host == NULL) /* server */
+if (!host) /* server */
{
# ifndef DISABLE_OCSP
/* We check u_ocsp.server.file, not server.response, because we care about if
static void
construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits)
{
-/* With OpenSSL 1.0.0a, this needs to be const but the documentation doesn't
+/* With OpenSSL 1.0.0a, 'c' needs to be const but the documentation doesn't
yet reflect that. It should be a safe change anyway, even 0.9.8 versions have
the accessor functions use const in the prototype. */
-const SSL_CIPHER *c;
-const uschar *ver;
-ver = (const uschar *)SSL_get_version(ssl);
+const uschar * ver = CUS SSL_get_version(ssl);
+const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
-c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
SSL_CIPHER_get_bits(c, bits);
string_format(cipherbuf, bsize, "%s:%s:%u", ver,
* Set up for verifying certificates *
*************************************************/
+#ifndef DISABLE_OCSP
/* Load certs from file, return TRUE on success */
static BOOL
BIO * bp;
X509 * x;
+while (sk_X509_num(verify_stack) > 0)
+ X509_free(sk_X509_pop(verify_stack));
+
if (!(bp = BIO_new_file(CS file, "r"))) return FALSE;
while ((x = PEM_read_bio_X509(bp, NULL, 0, NULL)))
sk_X509_push(verify_stack, x);
BIO_free(bp);
return TRUE;
}
+#endif
-/* Called by both client and server startup
+/* Called by both client and server startup; on the server possibly
+repeated after a Server Name Indication.
Arguments:
sctx SSL_CTX* to initialise
)
{
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
{
STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
+ SSL_CTX_set_client_CA_list(sctx, names);
DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
sk_X509_NAME_num(names));
- SSL_CTX_set_client_CA_list(sctx, names);
}
}
}
/* Check for previous activation */
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
{
tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
smtp_printf("554 Already in TLS\r\n", FALSE);
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;
/* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
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.
+
+XXX SSL_CTX_set_cipher_list() is replaced by SSL_CTX_set_ciphersuites()
+for TLS 1.3 . Since we do not call it at present we get the default list:
+TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
*/
if (expciphers)
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;
if (verify_check_host(&tls_verify_hosts) == OK)
{
rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
- FALSE, verify_callback_server, errstr);
+ FALSE, verify_callback_server, errstr);
if (rc != OK) return rc;
server_verify_optional = FALSE;
}
else if (verify_check_host(&tls_try_verify_hosts) == OK)
{
rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
- TRUE, verify_callback_server, errstr);
+ TRUE, verify_callback_server, errstr);
if (rc != OK) return rc;
server_verify_optional = TRUE;
}
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;
+ssl_xfer_eof = ssl_xfer_error = FALSE;
receive_getc = tls_getc;
receive_getbuf = tls_getbuf;
receive_ferror = tls_ferror;
receive_smtp_buffered = tls_smtp_buffered;
-tls_in.active = fileno(smtp_out);
+tls_in.active.sock = fileno(smtp_out);
+tls_in.active.tls_ctx = NULL; /* not using explicit ctx for server-side */
return OK;
}
if ( ( !ob->tls_verify_hosts
&& (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts)
)
- || (verify_check_given_host(&ob->tls_verify_hosts, host) == OK)
+ || verify_check_given_host(&ob->tls_verify_hosts, host) == OK
)
client_verify_optional = FALSE;
else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK)
}
-#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*/
Argument:
fd the fd of the connection
- host connected host (for messages)
- addr the first address
+ host connected host (for messages and option-tests)
+ addr the first address (for some randomness; can be NULL)
tb transport (always smtp)
tlsa_dnsa tlsa lookup, if DANE, else null
+ tlsp record details of channel configuration here; must be non-NULL
errstr error string pointer
-Returns: OK on success
- FAIL otherwise - note that tls_error() will not give DEFER
- because this is not a server
+Returns: Pointer to TLS session context, or NULL on error
*/
-int
+void *
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)
+ tls_support * tlsp, uschar ** errstr)
{
-smtp_transport_options_block * ob =
- (smtp_transport_options_block *)tb->options_block;
+smtp_transport_options_block * ob = tb
+ ? (smtp_transport_options_block *)tb->options_block
+ : &smtp_transport_option_defaults;
+exim_openssl_client_tls_ctx * exim_client_ctx;
static uschar peerdn[256];
uschar * expciphers;
int rc;
BOOL require_ocsp = FALSE;
#endif
-#ifdef EXPERIMENTAL_DANE
-tls_out.tlsa_usage = 0;
+rc = store_pool;
+store_pool = POOL_PERM;
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+store_pool = rc;
+
+#ifdef SUPPORT_DANE
+tlsp->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 =
}
#endif
-rc = tls_init(&client_ctx, host, NULL,
+rc = tls_init(&exim_client_ctx->ctx, host, NULL,
ob->tls_certificate, ob->tls_privatekey,
#ifndef DISABLE_OCSP
(void *)(long)request_ocsp,
#endif
addr, &client_static_cbinfo, errstr);
-if (rc != OK) return rc;
+if (rc != OK) return NULL;
-tls_out.certificate_verified = FALSE;
+tlsp->certificate_verified = FALSE;
client_verify_callback_called = FALSE;
-if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
- &expciphers, errstr))
- return FAIL;
+expciphers = NULL;
+#ifdef SUPPORT_DANE
+if (tlsa_dnsa)
+ {
+ /* We fall back to tls_require_ciphers if unset, empty or forced failure, but
+ other failures should be treated as problems. */
+ if (ob->dane_require_tls_ciphers &&
+ !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
+ &expciphers, errstr))
+ return NULL;
+ if (expciphers && *expciphers == '\0')
+ expciphers = NULL;
+ }
+#endif
+if (!expciphers &&
+ !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+ &expciphers, errstr))
+ return NULL;
/* 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
uschar *s = expciphers;
while (*s) { if (*s == '_') *s = '-'; s++; }
DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
- if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers))
- return tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
+ if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
+ {
+ tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
+ return NULL;
+ }
}
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
if (tlsa_dnsa)
{
- SSL_CTX_set_verify(client_ctx,
+ SSL_CTX_set_verify(exim_client_ctx->ctx,
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_callback_client_dane);
if (!DANESSL_library_init())
- return tls_error(US"library init", host, NULL, errstr);
- if (DANESSL_CTX_init(client_ctx) <= 0)
- return tls_error(US"context init", host, NULL, errstr);
+ {
+ tls_error(US"library init", host, NULL, errstr);
+ return NULL;
+ }
+ if (DANESSL_CTX_init(exim_client_ctx->ctx) <= 0)
+ {
+ tls_error(US"context init", host, NULL, errstr);
+ return NULL;
+ }
}
else
#endif
- if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob,
- client_static_cbinfo, errstr)) != OK)
- return rc;
+ if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
+ client_static_cbinfo, errstr) != OK)
+ return NULL;
-if ((client_ssl = SSL_new(client_ctx)) == NULL)
- return tls_error(US"SSL_new", host, NULL, errstr);
-SSL_set_session_id_context(client_ssl, sid_ctx, Ustrlen(sid_ctx));
-SSL_set_fd(client_ssl, fd);
-SSL_set_connect_state(client_ssl);
+if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
+ {
+ tls_error(US"SSL_new", host, NULL, errstr);
+ return NULL;
+ }
+SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_fd(exim_client_ctx->ssl, fd);
+SSL_set_connect_state(exim_client_ctx->ssl);
if (ob->tls_sni)
{
- if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni, errstr))
- return FAIL;
- if (!tls_out.sni)
+ if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
+ return NULL;
+ if (!tlsp->sni)
{
DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
}
- else if (!Ustrlen(tls_out.sni))
- tls_out.sni = NULL;
+ else if (!Ustrlen(tlsp->sni))
+ tlsp->sni = NULL;
else
{
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
- DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_out.sni);
- SSL_set_tlsext_host_name(client_ssl, tls_out.sni);
+ DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+ SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
#else
log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
- tls_out.sni);
+ tlsp->sni);
#endif
}
}
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
if (tlsa_dnsa)
- if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa, errstr)) != OK)
- return rc;
+ if (dane_tlsa_load(exim_client_ctx->ssl, host, tlsa_dnsa, errstr) != OK)
+ return NULL;
#endif
#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;
if (request_ocsp)
{
- SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp);
+ SSL_set_tlsext_status_type(exim_client_ctx->ssl, TLSEXT_STATUSTYPE_ocsp);
client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
- tls_out.ocsp = OCSP_NOT_RESP;
+ tlsp->ocsp = OCSP_NOT_RESP;
}
#endif
#ifndef DISABLE_EVENT
-client_static_cbinfo->event_action = tb->event_action;
+client_static_cbinfo->event_action = tb ? tb->event_action : NULL;
#endif
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
sigalrm_seen = FALSE;
alarm(ob->command_timeout);
-rc = SSL_connect(client_ssl);
+rc = SSL_connect(exim_client_ctx->ssl);
alarm(0);
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
if (tlsa_dnsa)
- DANESSL_cleanup(client_ssl);
+ DANESSL_cleanup(exim_client_ctx->ssl);
#endif
if (rc <= 0)
- return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL,
- errstr);
+ {
+ tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
+ return NULL;
+ }
DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
-peer_cert(client_ssl, &tls_out, peerdn, sizeof(peerdn));
+peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
-construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
-tls_out.cipher = cipherbuf;
+construct_cipher_name(exim_client_ctx->ssl, cipherbuf, sizeof(cipherbuf), &tlsp->bits);
+tlsp->cipher = cipherbuf;
/* Record the certificate we presented */
{
- X509 * crt = SSL_get_certificate(client_ssl);
- tls_out.ourcert = crt ? X509_dup(crt) : NULL;
+ X509 * crt = SSL_get_certificate(exim_client_ctx->ssl);
+ tlsp->ourcert = crt ? X509_dup(crt) : NULL;
}
-tls_out.active = fd;
-return OK;
+tlsp->active.sock = fd;
+tlsp->active.tls_ctx = exim_client_ctx;
+return exim_client_ctx;
}
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 (smtp_receive_timeout > 0) alarm(0);
+
+if (had_command_timeout) /* set by signal handler */
+ smtp_command_timeout_exit(); /* does not return */
+if (had_command_sigterm)
+ smtp_command_sigterm_exit();
+if (had_data_timeout)
+ smtp_data_timeout_exit();
+if (had_data_sigint)
+ smtp_data_sigint_exit();
/* 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)
+switch(error)
{
- DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
+ case SSL_ERROR_NONE:
+ break;
- 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;
+ case SSL_ERROR_ZERO_RETURN:
+ DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
- return FALSE;
- }
+ 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;
-/* Handle genuine errors */
+ if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+ SSL_shutdown(server_ssl);
-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;
- }
+#ifndef DISABLE_OCSP
+ sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
+ server_static_cbinfo->verify_stack = NULL;
+#endif
+ SSL_free(server_ssl);
+ SSL_CTX_free(server_ctx);
+ server_ctx = NULL;
+ server_ssl = NULL;
+ tls_in.active.sock = -1;
+ tls_in.active.tls_ctx = NULL;
+ tls_in.bits = 0;
+ tls_in.cipher = NULL;
+ tls_in.peerdn = NULL;
+ tls_in.sni = NULL;
-else if (error != SSL_ERROR_NONE)
- {
- DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
- ssl_xfer_error = 1;
- return FALSE;
+ return FALSE;
+
+ /* Handle genuine errors */
+ case 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 = TRUE;
+ return FALSE;
+
+ default:
+ DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
+ DEBUG(D_tls) if (error == SSL_ERROR_SYSCALL)
+ debug_printf(" - syscall %s\n", strerror(errno));
+ ssl_xfer_error = TRUE;
+ return FALSE;
}
#ifndef DISABLE_DKIM
/*
Arguments:
+ ct_ctx client context pointer, or NULL for the one global server context
buff buffer of data
len size of buffer
Returns: the number of bytes read
- -1 after a failed read
+ -1 after a failed read, including EOF
Only used by the client-side TLS.
*/
int
-tls_read(BOOL is_server, uschar *buff, size_t len)
+tls_read(void * ct_ctx, uschar *buff, size_t len)
{
-SSL *ssl = is_server ? server_ssl : client_ssl;
+SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
int inbytes;
int error;
/*
Arguments:
- is_server channel specifier
+ ct_ctx client context pointer, or NULL for the one global server context
buff buffer of data
len number of bytes
more further data expected soon
*/
int
-tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
{
int outbytes, error, left;
-SSL *ssl = is_server ? server_ssl : client_ssl;
+SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
static gstring * corked = NULL;
DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
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))
+if (!ct_ctx && (more || corked))
{
corked = string_catn(corked, buff, len);
if (more)
for (left = len; left > 0;)
{
- DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
+ DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, 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);
daemon, to shut down the TLS library, without actually doing a shutdown (which
would tamper with the SSL session in the parent process).
-Arguments: TRUE if SSL_shutdown is to be called
+Arguments:
+ ct_ctx client TLS context pointer, or NULL for the one global server context
+ shutdown 1 if TLS close-alert is to be sent,
+ 2 if also response to be waited for
+
Returns: nothing
Used by both server-side and client-side TLS.
*/
void
-tls_close(BOOL is_server, BOOL shutdown)
+tls_close(void * ct_ctx, int shutdown)
{
-SSL **sslp = is_server ? &server_ssl : &client_ssl;
-int *fdp = is_server ? &tls_in.active : &tls_out.active;
+exim_openssl_client_tls_ctx * o_ctx = ct_ctx;
+SSL_CTX **ctxp = o_ctx ? &o_ctx->ctx : &server_ctx;
+SSL **sslp = o_ctx ? &o_ctx->ssl : &server_ssl;
+int *fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
if (*fdp < 0) return; /* TLS was not active */
if (shutdown)
{
- DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n");
- SSL_shutdown(*sslp);
+ int rc;
+ DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
+ shutdown > 1 ? " (with response-wait)" : "");
+
+ if ( (rc = SSL_shutdown(*sslp)) == 0 /* send "close notify" alert */
+ && shutdown > 1)
+ {
+ alarm(2);
+ rc = SSL_shutdown(*sslp); /* wait for response */
+ alarm(0);
+ }
+
+ if (rc < 0) DEBUG(D_tls)
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ debug_printf("SSL_shutdown: %s\n", ssl_errstring);
+ }
+ }
+
+#ifndef DISABLE_OCSP
+if (!o_ctx) /* server side */
+ {
+ sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
+ server_static_cbinfo->verify_stack = NULL;
}
+#endif
+SSL_CTX_free(*ctxp);
SSL_free(*sslp);
+*ctxp = NULL;
*sslp = NULL;
-
*fdp = -1;
}
err = NULL;
-ctx = SSL_CTX_new(SSLv23_server_method());
-if (!ctx)
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(ctx = SSL_CTX_new(TLS_server_method())))
+#else
+if (!(ctx = SSL_CTX_new(SSLv23_server_method())))
+#endif
{
ERR_error_string(ERR_get_error(), ssl_errstring);
return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);