*************************************************/
/* 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 */
# warning "GnuTLS library version too old; tls:cert event unsupported"
# define DISABLE_EVENT
#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030000
+# define SUPPORT_SELFSIGN /* Uncertain what version is first usable but 2.12.23 is not */
+#endif
#if GNUTLS_VERSION_NUMBER >= 0x030306
# define SUPPORT_CA_DIR
#else
/* peercert is set in peer_status() */
tlsp->peerdn = state->peerdn;
-tlsp->sni = state->received_sni;
+
+/* do not corrupt sni sent by client; record sni rxd by server */
+if (!state->host)
+ tlsp->sni = state->received_sni;
/* record our certificate */
{
const uschar * where;
int rc;
+#ifndef SUPPORT_SELFSIGN
+where = US"library too old";
+rc = GNUTLS_E_NO_CERTIFICATE_FOUND;
+if (TRUE) goto err;
+#endif
+
where = US"initialising pkey";
if ((rc = gnutls_x509_privkey_init(&pkey))) goto err;
where = US"initialising cert";
if ((rc = gnutls_x509_crt_init(&cert))) goto err;
-where = US"generating pkey";
+where = US"generating pkey"; /* Hangs on 2.12.23 */
if ((rc = gnutls_x509_privkey_generate(pkey, GNUTLS_PK_RSA,
#ifdef SUPPORT_PARAM_TO_PK_BITS
# ifndef GNUTLS_SEC_PARAM_MEDIUM
{
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",
/* 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)
{
cipher_list, &state, tlsp, errstr) != OK)
return FALSE;
+
#ifdef MEASURE_TIMING
report_time_since(&t0, US"client tls_init (delta)");
#endif
sigalrm_seen = FALSE;
if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
+errno = 0;
do
inbytes = gnutls_record_recv(state->session, state->xfer_buffer,
MIN(ssl_xfer_buffer_size, lim));
debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n",
state->session, buff, len);
+errno = 0;
do
inbytes = gnutls_record_recv(state->session, buff, len);
while (inbytes == GNUTLS_E_AGAIN);
ssize_t outbytes;
size_t left = len;
exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
-#ifdef SUPPORT_CORK
-if (more && !state->corked) gnutls_record_cork(state->session);
+#ifdef SUPPORT_CORK
+if (more && !state->corked)
+ {
+ DEBUG(D_tls) debug_printf("gnutls_record_cork(session=%p)\n", state->session);
+ gnutls_record_cork(state->session);
+ state->corked = TRUE;
+ }
#endif
DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n",
state->session, buff, left);
+ errno = 0;
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 (outbytes < 0)
{
DEBUG(D_tls) debug_printf("%s: gnutls_record_send err\n", __FUNCTION__);
}
#ifdef SUPPORT_CORK
-if (more != state->corked)
+if (!more && state->corked)
{
- if (!more) (void) gnutls_record_uncork(state->session, 0);
- state->corked = more;
+ DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
+ 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