/* Copyright (c) University of Cambridge 1995 - 2018 */
/* Copyright (c) Phil Pennock 2012 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* This file provides TLS/SSL support for Exim using the GnuTLS library,
one of the available supported implementations. This file is #included into
# endif
#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030702
+# define HAVE_GNUTLS_EXPORTER
+#endif
+
#ifndef DISABLE_OCSP
# include <gnutls/ocsp.h>
#endif
{
return tls_error(prefix,
state && err == GNUTLS_E_FATAL_ALERT_RECEIVED
- ? US gnutls_alert_get_name(gnutls_alert_get(state->session))
+ ? string_sprintf("rxd alert: %s",
+ US gnutls_alert_get_name(gnutls_alert_get(state->session)))
: US gnutls_strerror(err),
state ? state->host : NULL,
errstr);
#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
{
gnutls_datum_t channel = {.data = NULL, .size = 0};
- uschar * buf;
int rc;
-# ifdef HAVE_GNUTLS_PRF_RFC5705
+# ifdef HAVE_GNUTLS_EXPORTER
+ if (gnutls_protocol_get_version(state->session) >= GNUTLS_TLS1_3)
+ {
+ rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_EXPORTER, &channel);
+ tlsp->channelbind_exporter = TRUE;
+ }
+ else
+# elif defined(HAVE_GNUTLS_PRF_RFC5705)
/* Older libraries may not have GNUTLS_TLS1_3 defined! */
if (gnutls_protocol_get_version(state->session) > GNUTLS_TLS1_2)
{
- buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED);
+ uschar * buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED);
rc = gnutls_prf_rfc5705(state->session,
(size_t)24, "EXPORTER-Channel-Binding", (size_t)0, "",
32, CS buf);
{
int old_pool = store_pool;
/* Declare the taintedness of the binding info. On server, untainted; on
- client, tainted - being the Finish msg from the server. */
+ client, tainted if we used the Finish msg from the server. */
store_pool = POOL_PERM;
tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
- state->host ? GET_TAINTED : GET_UNTAINTED);
+ !tlsp->channelbind_exporter && state->host ? GET_TAINTED : GET_UNTAINTED);
store_pool = old_pool;
DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
}
unsigned when, unsigned int incoming, const gnutls_datum_t * msg)
{
/* Call fn for each extension seen. 3.6.3 onwards */
-return gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg,
+int rc = gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg,
GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO);
+return rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE ? 0 : rc;
}
if (!(kfile = string_nextinlist(&klist, &ksep, NULL, 0)))
return tls_error(US"cert/key setup: out of keys", NULL, NULL, errstr);
- else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) > 0)
+ else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) != OK)
return rc;
else
{
if (!state->lib_state.conn_certs)
{
- if (!Expand_check_tlsvar(tls_certificate, errstr))
+ if ( !Expand_check_tlsvar(tls_certificate, errstr)
+ || f.expand_string_forcedfail)
+ {
+ if (f.expand_string_forcedfail)
+ *errstr = US"expansion of tls_certificate failed";
return DEFER;
+ }
/* certificate is mandatory in server, optional in client */
else
DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n");
- if (state->tls_privatekey && !Expand_check_tlsvar(tls_privatekey, errstr))
+ if ( state->tls_privatekey && !Expand_check_tlsvar(tls_privatekey, errstr)
+ || f.expand_string_forcedfail
+ )
+ {
+ if (f.expand_string_forcedfail)
+ *errstr = US"expansion of tls_privatekey failed";
return DEFER;
+ }
/* tls_privatekey is optional, defaulting to same file as certificate */
tls_ocsp_file,
#endif
errstr)
- ) ) return rc;
+ ) )
+ {
+ DEBUG(D_tls) debug_printf("load-cert: '%s'\n", *errstr);
+ return rc;
+ }
}
}
else
{
/* If the setup of certs/etc failed before handshake, TLS would not have
been offered. The best we can do now is abort. */
- return GNUTLS_E_APPLICATION_ERROR_MIN;
+ DEBUG(D_tls) debug_printf("expansion for SNI-dependent session files failed\n");
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
}
rc = tls_set_remaining_x509(state, &dummy_errstr);
-if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN;
+if (rc != OK) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
return 0;
}
if ((cert_list = gnutls_certificate_get_peers(session, &cert_list_size)))
while (cert_list_size--)
- {
- if ((rc = import_cert(&cert_list[cert_list_size], &crt)) != GNUTLS_E_SUCCESS)
{
- DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n",
- cert_list_size, gnutls_strerror(rc));
- break;
- }
+ if ((rc = import_cert(&cert_list[cert_list_size], &crt)) != GNUTLS_E_SUCCESS)
+ {
+ DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n",
+ cert_list_size, gnutls_strerror(rc));
+ break;
+ }
- state->tlsp->peercert = crt;
- if ((yield = event_raise(state->event_action,
- US"tls:cert", string_sprintf("%d", cert_list_size), &errno)))
- {
- log_write(0, LOG_MAIN,
- "SSL verify denied by event-action: depth=%d: %s",
- cert_list_size, yield);
- return 1; /* reject */
+ state->tlsp->peercert = crt;
+ if ((yield = event_raise(state->event_action,
+ US"tls:cert", string_sprintf("%d", cert_list_size), &errno)))
+ {
+ log_write(0, LOG_MAIN,
+ "SSL verify denied by event-action: depth=%d: %s",
+ cert_list_size, yield);
+ return 1; /* reject */
+ }
+ state->tlsp->peercert = NULL;
}
- state->tlsp->peercert = NULL;
- }
return 0;
}
{
tlsp->resumption = RESUME_SUPPORTED;
-if (continue_hostname) /* no host_lbserver available for tls_client_resmption_key() */
+if (!conn_args->have_lbserver)
{ DEBUG(D_tls) debug_printf("resumption not supported on continued-connection\n"); }
else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, conn_args->host) == OK)
{