*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Copyright (c) Phil Pennock 2012 */
# endif
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
-# if GNUTLS_VERSION_NUMBER < 0x030603
-# error GNUTLS version too early for session-resumption
+#ifndef DISABLE_TLS_RESUME
+# if GNUTLS_VERSION_NUMBER >= 0x030603
+# define EXIM_HAVE_TLS_RESUME
+# else
+# warning "GnuTLS library version too old; resumption unsupported"
# endif
#endif
void
options_tls(void)
{
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifndef DISABLE_TLS_RESUME
builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING );
# endif
# ifdef EXIM_HAVE_TLS1_3
static BOOL exim_testharness_disable_ocsp_validity_check = FALSE;
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
static gnutls_datum_t server_sessticket_key;
#endif
static int exim_sni_handling_cb(gnutls_session_t session);
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
static int
tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
unsigned incoming, const gnutls_datum_t * msg);
void
tls_daemon_init(void)
{
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
/* We are dependent on the GnuTLS implementation of the Session Ticket
encryption; both the strength and the key rotation period. We hope that
the strength at least matches that of the ciphersuite (but GnuTLS does not
const uschar * msg;
uschar * errstr;
-if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED)
- msg = string_sprintf("A TLS fatal alert has been received: %s",
- US gnutls_alert_get_name(gnutls_alert_get(state->session)));
-else
- msg = US gnutls_strerror(rc);
+msg = rc == GNUTLS_E_FATAL_ALERT_RECEIVED
+ ? string_sprintf("A TLS fatal alert has been received: %s",
+ US gnutls_alert_get_name(gnutls_alert_get(state->session)))
+#ifdef GNUTLS_E_PREMATURE_TERMINATION
+ : rc == GNUTLS_E_PREMATURE_TERMINATION && errno
+ ? string_sprintf("%s: syscall: %s", US gnutls_strerror(rc), strerror(errno))
+#endif
+ : US gnutls_strerror(rc);
(void) tls_error(when, msg, state->host, &errstr);
debug_printf("D-H parameter cache file \"%s\" does not exist\n", filename);
}
else
- return tls_error(string_open_failed(errno, "\"%s\" for reading", filename),
+ return tls_error(string_open_failed("\"%s\" for reading", filename),
NULL, NULL, errstr);
/* If ret < 0, either the cache file does not exist, or the data it contains
"Handshake Protocol: Certificate" record.
So we need to spot the Certificate handshake message, parse it and spot any status_request extension(s)
-This is different to tls1.2 - where it is a separate record (wireshake term) / handshake message (gnutls term).
+This is different to tls1.2 - where it is a separate record (wireshark term) / handshake message (gnutls term).
*/
-#if defined(EXPERIMENTAL_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
+#if defined(EXIM_HAVE_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
/* Callback for certificate-status, on server. We sent stapled OCSP. */
static int
tls_server_certstatus_cb(gnutls_session_t session, unsigned int htype,
# endif
case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
return tls_server_certstatus_cb(sess, htype, when, incoming, msg);
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifdef EXIM_HAVE_TLS_RESUME
case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
return tls_server_ticket_cb(sess, htype, when, incoming, msg);
# endif
}
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
static int
tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
unsigned incoming, const gnutls_datum_t * msg)
#endif
}
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_server_resume_prehandshake(state);
#endif
tls_in.ext_master_secret = TRUE;
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_server_resume_posthandshake(state);
#endif
{
state->exp_tls_verify_cert_hostnames =
#ifdef SUPPORT_I18N
- string_domain_utf8_to_alabel(host->name, NULL);
+ string_domain_utf8_to_alabel(host->certname, NULL);
#else
- host->name;
+ host->certname;
#endif
DEBUG(D_tls)
debug_printf("TLS: server cert verification includes hostname: \"%s\".\n",
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
/* On the client, get any stashed session for the given IP from hints db
and apply it to the ssl-connection for attempted resumption. Although
there is a gnutls_session_ticket_enable_client() interface it is
tls_save_session(tlsp, state->session, host);
}
-#endif /* EXPERIMENTAL_TLS_RESUME */
+#endif /* !DISABLE_TLS_RESUME */
/*************************************************
/* If dane is flagged, have either request or require dane for this host, and
a TLSA record found. Therefore, dane verify required. Which implies cert must
be requested and supplied, dane verify must pass, and cert verify irrelevant
-(incl. hostnames), and (caller handled) require_tls */
+(incl. hostnames), and (caller handled) require_tls and sni=$domain */
if (conn_args->dane && ob->dane_require_tls_ciphers)
{
}
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_client_resume_prehandshake(state, tlsp, host, ob);
#endif
}
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_client_resume_posthandshake(state, tlsp, host);
#endif
if (outbytes < 0)
{
- DEBUG(D_tls) debug_printf("%s: gnutls_record_send err\n", __FUNCTION__);
- record_io_error(state, outbytes, US"send", NULL);
+#ifdef GNUTLS_E_PREMATURE_TERMINATION
+ if ( outbytes == GNUTLS_E_PREMATURE_TERMINATION && errno == ECONNRESET
+ && !ct_ctx && f.smtp_in_quit
+ )
+ { /* Outlook, dammit */
+ if (LOGGING(protocol_detail))
+ log_write(0, LOG_MAIN, "[%s] after QUIT, client reset TCP before"
+ " SMTP response and TLS close\n", sender_host_address);
+ else
+ DEBUG(D_tls) debug_printf("[%s] SSL_write: after QUIT,"
+ " client reset TCP before TLS close\n", sender_host_address);
+ }
+ else
+#endif
+ {
+ DEBUG(D_tls) debug_printf("%s: gnutls_record_send err\n", __FUNCTION__);
+ record_io_error(state, outbytes, US"send", NULL);
+ }
return -1;
}
if (outbytes == 0)