*************************************************/
/* 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
"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
if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
do
rc = gnutls_handshake(state->session);
-while (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
+while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
ALARM_CLR(0);
if (rc != GNUTLS_E_SUCCESS)
tls_in.ext_master_secret = TRUE;
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_server_resume_posthandshake(state);
#endif
-#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 */
/*************************************************
}
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_client_resume_prehandshake(state, tlsp, host, ob);
#endif
ALARM(ob->command_timeout);
do
rc = gnutls_handshake(state->session);
-while (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
+while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
ALARM_CLR(0);
if (rc != GNUTLS_E_SUCCESS)
}
#endif
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
tls_client_resume_posthandshake(state, tlsp, host);
#endif
sigalrm_seen = FALSE;
if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
-inbytes = gnutls_record_recv(state->session, state->xfer_buffer, MIN(ssl_xfer_buffer_size, lim));
+do
+ inbytes = gnutls_record_recv(state->session, state->xfer_buffer,
+ MIN(ssl_xfer_buffer_size, lim));
+while (inbytes == GNUTLS_E_AGAIN);
if (smtp_receive_timeout > 0) ALARM_CLR(0);
debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n",
state->session, buff, len);
-inbytes = gnutls_record_recv(state->session, buff, len);
+do
+ inbytes = gnutls_record_recv(state->session, buff, len);
+while (inbytes == GNUTLS_E_AGAIN);
if (inbytes > 0) return inbytes;
if (inbytes == 0)
DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n",
state->session, buff, left);
- outbytes = gnutls_record_send(state->session, buff, left);
+ do
+ outbytes = gnutls_record_send(state->session, buff, left);
+ while (outbytes == GNUTLS_E_AGAIN);
DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes);
if (!more && state->corked)
{
DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
- do {
- outbytes = gnutls_record_uncork(state->session, 0);
- if (outbytes < 0)
- {
- record_io_error(state, len, US"uncork", NULL);
- return -1;
- }
- } while (gnutls_record_check_corked(state->session) > 0);
+ do
+ /* We can't use GNUTLS_RECORD_WAIT here, as it retries on
+ GNUTLS_E_AGAIN || GNUTLS_E_INTR, which would break our timeout set by alarm().
+ The GNUTLS_E_AGAIN should not happen ever, as our sockets are blocking anyway.
+ But who knows. (That all relies on the fact that GNUTLS_E_INTR and GNUTLS_E_AGAIN
+ match the EINTR and EAGAIN errno values.) */
+ outbytes = gnutls_record_uncork(state->session, 0);
+ while (outbytes == GNUTLS_E_AGAIN);
+
+ if (outbytes < 0)
+ {
+ record_io_error(state, len, US"uncork", NULL);
+ return -1;
+ }
+
state->corked = FALSE;
}
#endif