* 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
/*
|| 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
}
-#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:
{
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)
{
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;
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,
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 */
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);
-/* 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);
}
}
}
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;
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;
}
-#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 =
tls_out.certificate_verified = FALSE;
client_verify_callback_called = FALSE;
-if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
- &expciphers, errstr))
+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 FAIL;
+ if (expciphers && *expciphers == '\0')
+ expciphers = NULL;
+ }
+#endif
+if (!expciphers &&
+ !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+ &expciphers, errstr))
return FAIL;
/* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
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
receive_ferror = smtp_ferror;
receive_smtp_buffered = smtp_buffered;
+ if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+ SSL_shutdown(server_ssl);
+
+ sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
SSL_free(server_ssl);
+ SSL_CTX_free(server_ctx);
+ server_static_cbinfo->verify_stack = NULL;
+ server_ctx = NULL;
server_ssl = NULL;
tls_in.active = -1;
tls_in.bits = 0;
{
ERR_error_string(ERR_get_error(), ssl_errstring);
log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
- ssl_xfer_error = 1;
+ ssl_xfer_error = TRUE;
return FALSE;
}
else if (error != SSL_ERROR_NONE)
{
DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
- ssl_xfer_error = 1;
+ ssl_xfer_error = TRUE;
return FALSE;
}
{
int outbytes, error, left;
SSL *ssl = is_server ? server_ssl : client_ssl;
-static uschar * corked = NULL;
-static int c_size = 0, c_len = 0;
+static gstring * corked = NULL;
DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
- buff, (ulong)len, more ? ", more" : "");
+ 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
if (is_server && (more || corked))
{
- corked = string_catn(corked, &c_size, &c_len, buff, len);
+ corked = string_catn(corked, buff, len);
if (more)
return len;
- buff = CUS corked;
- len = c_len;
- corked = NULL; c_size = c_len = 0;
+ buff = CUS corked->s;
+ len = corked->ptr;
+ corked = NULL;
}
for (left = len; left > 0;)
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:
+ 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(BOOL is_server, int shutdown)
{
+SSL_CTX **ctxp = is_server ? &server_ctx : &client_ctx;
SSL **sslp = is_server ? &server_ssl : &client_ssl;
int *fdp = is_server ? &tls_in.active : &tls_out.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);
+ }
}
+if (is_server)
+ {
+ sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
+ server_static_cbinfo->verify_stack = NULL;
+ }
+
+SSL_CTX_free(*ctxp);
SSL_free(*sslp);
+*ctxp = NULL;
*sslp = NULL;
-
*fdp = -1;
}
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