* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* Copyright (c) Phil Pennock 2012 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* 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
# endif /* AVOID_GNUTLS_PKCS11 */
#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030404
+# define HAVE_GNUTLS_PRF_RFC5705
+#endif
the connected host if setting up a client
errstr pointer to returned error string
-Returns: OK/DEFER/FAIL
+Returns: DEFER/FAIL
*/
static int
}
+/* Returns: DEFER/FAIL */
static int
tls_error_gnu(exim_gnutls_state_st * state, const uschar *prefix, int err,
uschar ** errstr)
{
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);
const uschar * msg;
uschar * errstr;
+if (rc == GNUTLS_E_INVALID_SESSION && errno == 0)
+ {
+ DEBUG(D_tls) debug_printf("- INVALID_SESSION with zero errno\n");
+ return;
+ }
+
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)))
static void
extract_exim_vars_from_tls_state(exim_gnutls_state_st * state)
{
-#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
-int old_pool;
-int rc;
-gnutls_datum_t channel;
-#endif
tls_support * tlsp = state->tlsp;
tlsp->active.sock = state->fd_out;
tlsp->channelbinding = NULL;
#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
-channel.data = NULL;
-channel.size = 0;
-if ((rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_UNIQUE, &channel)))
- { DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); }
-else
{
- /* Declare the taintedness of the binding info. On server, untainted; on
- client, tainted - being the Finish msg from the server. */
+ gnutls_datum_t channel = {.data = NULL, .size = 0};
+ int rc;
- old_pool = store_pool;
- store_pool = POOL_PERM;
- tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
- !!state->host);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+# 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)
+ {
+ 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);
+ channel.data = buf;
+ channel.size = 32;
+ }
+ else
+# endif
+ rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_UNIQUE, &channel);
+
+ if (rc)
+ { DEBUG(D_tls) debug_printf("extracting channel binding: %s\n", gnutls_strerror(rc)); }
+ else
+ {
+ int old_pool = store_pool;
+ /* Declare the taintedness of the binding info. On server, untainted; on
+ 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,
+ !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");
+ }
}
#endif
waste a bit of effort, but it doesn't seem worth messing around with locking to
prevent this.
-Returns: OK/DEFER/FAIL
+Returns: OK/DEFER (expansion issue)/FAIL (requested none)
*/
static int
else if (Ustrcmp(exp_tls_dhparam, "none") == 0)
{
DEBUG(D_tls) debug_printf("Requested no DH parameters\n");
- return OK;
+ return FAIL;
}
else if (exp_tls_dhparam[0] != '/')
{
if ( (rc = gnutls_x509_crt_set_version(cert, 3))
|| (rc = gnutls_x509_crt_set_serial(cert, &now, sizeof(now)))
|| (rc = gnutls_x509_crt_set_activation_time(cert, now = time(NULL)))
- || (rc = gnutls_x509_crt_set_expiration_time(cert, (long)2 * 60 * 60)) /* 2 hour */
+ || (rc = gnutls_x509_crt_set_expiration_time(cert, now + (long)2 * 60 * 60)) /* 2 hour */
|| (rc = gnutls_x509_crt_set_key(cert, pkey))
|| (rc = gnutls_x509_crt_set_dn_by_oid(cert,
/* The format of "data" here doesn't seem to be documented, but appears
to be a 2-byte field with a (redundant, given the "size" arg) total length
then a sequence of one-byte size then string (not nul-term) names. The
- latter is as described in OpenSSL documentation. */
+ latter is as described in OpenSSL documentation.
+ Note that we do not get called for a match_fail, making it hard to log
+ a single bad ALPN being offered (the common case). */
+ {
+ gstring * g = NULL;
DEBUG(D_tls) debug_printf("Seen ALPN extension from client (s=%u):", size);
for (const uschar * s = data+2; s-data < size-1; s += *s + 1)
{
server_seen_alpn++;
+ g = string_append_listele_n(g, ':', s+1, *s);
DEBUG(D_tls) debug_printf(" '%.*s'", (int)*s, s+1);
}
DEBUG(D_tls) debug_printf("\n");
if (server_seen_alpn > 1)
{
+ log_write(0, LOG_MAIN, "TLS ALPN (%Y) rejected", g);
DEBUG(D_tls) debug_printf("TLS: too many ALPNs presented in handshake\n");
return GNUTLS_E_NO_APPLICATION_PROTOCOL;
}
break;
+ }
#endif
}
return 0;
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;
}
# ifdef notdef_crashes
/*XXX crashes */
return gnutls_ext_raw_parse(NULL, tls_server_servercerts_ext, msg, 0);
+# else
+return GNUTLS_E_SUCCESS;
# endif
}
#endif /*SUPPORT_GNUTLS_EXT_RAW_PARSE*/
return tls_server_ticket_cb(sess, htype, when, incoming, msg);
# endif
default:
- return 0;
+ return GNUTLS_E_SUCCESS;
}
}
#endif
debug_printf("TLS: basic cred init, %s\n", server ? "server" : "client");
}
+/* Returns OK/DEFER/FAIL */
static int
creds_load_server_certs(exim_gnutls_state_st * state, const uschar * cert,
const uschar * pkey, const uschar * ocsp, uschar ** errstr)
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
{
}
#endif /* DISABLE_OCSP */
}
-return 0;
+return OK;
}
static int
int rc = tls_add_certfile(state, host, cert, pkey, errstr);
if (rc > 0) return rc;
DEBUG(D_tls) debug_printf("TLS: cert/key registered\n");
-return 0;
+return OK;
}
static int
/* Preload whatever creds are static, onto a transport. The client can then
just copy the pointer as it starts up. */
+/*XXX this is not called for a cmdline send. But one needing to use >1 conn would benefit,
+and there seems little downside. */
+
static void
tls_client_creds_init(transport_instance * t, BOOL watch)
{
-smtp_transport_options_block * ob = t->options_block;
+smtp_transport_options_block * ob = t->drinst.options_block;
+const uschar * trname = t->drinst.name;
exim_gnutls_state_st tpt_dummy_state;
host_item * dummy_host = (host_item *)1;
uschar * dummy_errstr;
const uschar * pkey = ob->tls_privatekey;
DEBUG(D_tls)
- debug_printf("TLS: preloading client certs for transport '%s'\n", t->name);
+ debug_printf("TLS: preloading client certs for transport '%s'\n", trname);
/* The state->lib_state.x509_cred is used for the certs load, and is the sole
structure element used. So we can set up a dummy. The hoat arg only
}
else
DEBUG(D_tls)
- debug_printf("TLS: not preloading client certs, for transport '%s'\n", t->name);
+ debug_printf("TLS: not preloading client certs, for transport '%s'\n", trname);
/* If tls_verify_certificates is non-empty and has no $, load CAs.
If none was configured and we can't handle "system", treat as empty. */
if (!watch || tls_set_watch(ob->tls_verify_certificates, FALSE))
{
DEBUG(D_tls)
- debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name);
+ debug_printf("TLS: preloading CA bundle for transport '%s'\n", trname);
if (creds_load_cabundle(&tpt_dummy_state, ob->tls_verify_certificates,
dummy_host, &dummy_errstr) != OK)
return;
{
if (!watch || tls_set_watch(ob->tls_crl, FALSE))
{
- DEBUG(D_tls) debug_printf("TLS: preloading CRL for transport '%s'\n", t->name);
+ DEBUG(D_tls) debug_printf("TLS: preloading CRL for transport '%s'\n", trname);
if (creds_load_crl(&tpt_dummy_state, ob->tls_crl, &dummy_errstr) != OK)
return;
ob->tls_preload.crl = TRUE;
}
}
else
- DEBUG(D_tls) debug_printf("TLS: not preloading CRL, for transport '%s'\n", t->name);
+ DEBUG(D_tls) debug_printf("TLS: not preloading CRL, for transport '%s'\n", trname);
}
}
else
DEBUG(D_tls)
- debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", t->name);
+ debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", trname);
/* We do not preload tls_require_ciphers to to the transport as it implicitly
depends on DANE or plain usage. */
static void
tls_client_creds_invalidate(transport_instance * t)
{
-smtp_transport_options_block * ob = t->options_block;
+smtp_transport_options_block * ob = t->drinst.options_block;
if (ob->tls_preload.x509_cred)
gnutls_certificate_free_credentials(ob->tls_preload.x509_cred);
ob->tls_preload = null_tls_preload;
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
*/
static int
-tls_set_remaining_x509(exim_gnutls_state_st *state, uschar ** errstr)
+tls_set_remaining_x509(exim_gnutls_state_st * state, uschar ** errstr)
{
-int rc;
-const host_item *host = state->host; /* macro should be reconsidered? */
+int rc = OK;
+const host_item * host = state->host; /* macro should be reconsidered? */
/* Create D-H parameters, or read them from the cache file. This function does
its own SMTP error messaging. This only happens for the server, TLS D-H ignores
if (!state->host)
{
if (!dh_server_params)
- if ((rc = init_server_dh(errstr)) != OK) return rc;
+ if ((rc = init_server_dh(errstr)) == DEFER) return rc;
/* Unnecessary & discouraged with 3.6.0 or later, according to docs. But without it,
no DHE- ciphers are advertised. */
- gnutls_certificate_set_dh_params(state->lib_state.x509_cred, dh_server_params);
+
+ if (rc == OK)
+ gnutls_certificate_set_dh_params(state->lib_state.x509_cred, dh_server_params);
}
/* Link the credentials to the session. */
int old_pool = store_pool;
store_pool = POOL_PERM;
- state = store_get(sizeof(exim_gnutls_state_st), FALSE);
+ state = store_get(sizeof(exim_gnutls_state_st), GET_UNTAINTED);
store_pool = old_pool;
memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
for (s++; (c = *s) && c != ')'; s++) g = string_catn(g, s, 1);
- tlsp->ver = string_copyn(g->s, g->ptr);
+ tlsp->ver = string_copy_from_gstring(g);
for (uschar * p = US tlsp->ver; *p; p++)
if (*p == '-') { *p = '\0'; break; } /* TLS1.0-PKIX -> TLS1.0 */
if (*s) s++; /* now on _ between groups */
while ((c = *s))
{
- for (*++s && ++s; (c = *s) && c != ')'; s++)
- g = string_catn(g, c == '-' ? US"_" : s, 1);
+ if (*++s)
+ for (++s; (c = *s) && c != ')'; s++)
+ g = string_catn(g, c == '-' ? US"_" : s, 1);
/* now on ) closing group */
if ((c = *s) && *++s == '-') g = string_catn(g, US"__", 2);
/* now on _ between groups */
exim_gnutls_peer_err(US"getting size for cert DN failed");
return FAIL; /* should not happen */
}
-dn_buf = store_get_perm(sz, TRUE); /* tainted */
+dn_buf = store_get_perm(sz, GET_TAINTED);
rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz);
exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]");
for (nrec = 0; state->dane_data_len[nrec]; ) nrec++;
nrec++;
- dd = store_get(nrec * sizeof(uschar *), FALSE);
- ddl = store_get(nrec * sizeof(int), FALSE);
+ dd = store_get(nrec * sizeof(uschar *), GET_UNTAINTED);
+ ddl = store_get(nrec * sizeof(int), GET_UNTAINTED);
nrec--;
if ((rc = dane_state_init(&s, 0)))
)
{
DEBUG(D_tls)
- debug_printf("TLS certificate verification failed: cert name mismatch\n");
+ debug_printf("TLS certificate verification failed: cert name mismatch (per GnuTLS)\n");
if (state->verify_requirement >= VERIFY_REQUIRED)
goto badcert;
return TRUE;
/* We now have a UTF-8 string in sni_name */
old_pool = store_pool;
store_pool = POOL_PERM;
-state->received_sni = string_copy_taint(US sni_name, TRUE);
+state->received_sni = string_copy_taint(US sni_name, GET_TAINTED);
store_pool = old_pool;
/* We set this one now so that variable expansions below will work */
{
/* 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;
}
tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
unsigned incoming, const gnutls_datum_t * msg)
{
-DEBUG(D_tls) debug_printf("newticket cb\n");
+DEBUG(D_tls) debug_printf("newticket cb (on server)\n");
tls_in.resumption |= RESUME_CLIENT_REQUESTED;
return 0;
}
{
if (gnutls_session_resumption_requested(state->session))
{
- /* This tells us the client sent a full ticket. We use a
+ /* This tells us the client sent a full (?) ticket. We use a
callback on session-ticket request, elsewhere, to tell
- if a client asked for a ticket. */
+ if a client asked for a ticket.
+ XXX As of GnuTLS 3.0.1 it seems to be returning true even for
+ a pure ticket-req (a zero-length Session Ticket extension
+ in the Client Hello, for 1.2) which mucks up our logic. */
tls_in.resumption |= RESUME_CLIENT_SUGGESTED;
DEBUG(D_tls) debug_printf("client requested resumption\n");
*/
static BOOL
-tls_alpn_plist(const uschar * tls_alpn, const gnutls_datum_t ** plist, unsigned * plen,
+tls_alpn_plist(uschar ** tls_alpn, const gnutls_datum_t ** plist, unsigned * plen,
uschar ** errstr)
{
uschar * exp_alpn;
-if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+if (!expand_check(*tls_alpn, US"tls_alpn", &exp_alpn, errstr))
return FALSE;
if (!exp_alpn)
while (string_nextinlist(&list, &sep, NULL, 0)) cnt++;
- p = store_get(sizeof(gnutls_datum_t) * cnt, is_tainted(exp_alpn));
+ p = store_get(sizeof(gnutls_datum_t) * cnt, exp_alpn);
list = exp_alpn;
for (int i = 0; s = string_nextinlist(&list, &sep, NULL, 0); i++)
{ p[i].data = s; p[i].size = Ustrlen(s); }
static void
tls_server_set_acceptable_alpns(exim_gnutls_state_st * state, uschar ** errstr)
{
+uschar * local_alpn = string_copy(tls_alpn);
int rc;
const gnutls_datum_t * plist;
unsigned plen;
-if (tls_alpn_plist(tls_alpn, &plist, &plen, errstr) && plist)
+if (tls_alpn_plist(&local_alpn, &plist, &plen, errstr) && plist)
{
/* This seems to be only mandatory if the client sends an ALPN extension;
not trying ALPN is ok. Need to decide how to support server-side must-alpn. */
if (tls_in.active.sock >= 0)
{
tls_error(US"STARTTLS received after TLS started", US "", NULL, errstr);
- smtp_printf("554 Already in TLS\r\n", FALSE);
+ smtp_printf("554 Already in TLS\r\n", SP_NO_MORE);
return FAIL;
}
if (!state->tlsp->on_connect)
{
- smtp_printf("220 TLS go ahead\r\n", FALSE);
+ smtp_printf("220 TLS go ahead\r\n", SP_NO_MORE);
fflush(smtp_out);
}
if (sigalrm_seen)
{
tls_error(US"gnutls_handshake", US"timed out", NULL, errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
gnutls_db_remove_session(state->session);
}
else
{
tls_error_gnu(state, US"gnutls_handshake", rc, errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
(void) gnutls_alert_send_appropriate(state->session, rc);
gnutls_deinit(state->session);
- gnutls_certificate_free_credentials(state->lib_state.x509_cred);
- state->lib_state = null_tls_preload;
millisleep(500);
shutdown(state->fd_out, SHUT_WR);
for (int i = 1024; fgetc(smtp_in) != EOF && i > 0; ) i--; /* drain skt */
rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
) if (rr->type == T_TLSA) i++;
-dane_data = store_get(i * sizeof(uschar *), FALSE);
-dane_data_len = store_get(i * sizeof(int), FALSE);
+dane_data = store_get(i * sizeof(uschar *), GET_UNTAINTED);
+dane_data_len = store_get(i * sizeof(int), GET_UNTAINTED);
i = 0;
for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
static void
tls_retrieve_session(tls_support * tlsp, gnutls_session_t session,
- host_item * host, smtp_transport_options_block * ob)
+ smtp_connect_args * conn_args, smtp_transport_options_block * ob)
{
tlsp->resumption = RESUME_SUPPORTED;
-if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+
+if (!conn_args->have_lbserver)
+ { DEBUG(D_tls) debug_printf(
+ "resumption not supported: no LB detection done (continued-conn?)\n"); }
+else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, conn_args->host) == OK)
{
dbdata_tls_session * dt;
int len, rc;
open_db dbblock, * dbm_file;
- DEBUG(D_tls)
- debug_printf("check for resumable session for %s\n", host->address);
tlsp->host_resumable = TRUE;
+ tls_client_resmption_key(tlsp, conn_args, ob);
+
tlsp->resumption |= RESUME_CLIENT_REQUESTED;
if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE)))
{
- /* Key for the db is the IP. We'd like to filter the retrieved session
- for ticket advisory expiry, but 3.6.1 seems to give no access to that */
+ /* We'd like to filter the retrieved session for ticket advisory expiry,
+ but 3.6.1 seems to give no access to that */
- if ((dt = dbfn_read_with_length(dbm_file, host->address, &len)))
+ if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len)))
if (!(rc = gnutls_session_set_data(session,
CUS dt->session, (size_t)len - sizeof(dbdata_tls_session))))
{
dbfn_close(dbm_file);
}
}
+else DEBUG(D_tls) debug_printf("no resumption for this host\n");
}
{
open_db dbblock, * dbm_file;
int dlen = sizeof(dbdata_tls_session) + tkt.size;
- dbdata_tls_session * dt = store_get(dlen, TRUE);
+ dbdata_tls_session * dt = store_get(dlen, GET_TAINTED);
- DEBUG(D_tls) debug_printf("session data size %u\n", (unsigned)tkt.size);
+ DEBUG(D_tls) debug_printf(" session data size %u\n", (unsigned)tkt.size);
memcpy(dt->session, tkt.data, tkt.size);
gnutls_free(tkt.data);
- if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
+ if ((dbm_file = dbfn_open(US"tls", O_RDWR|O_CREAT, &dbblock, FALSE, FALSE)))
{
/* key for the db is the IP */
- dbfn_delete(dbm_file, host->address);
- dbfn_write(dbm_file, host->address, dt, dlen);
+ dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
dbfn_close(dbm_file);
DEBUG(D_tls)
- debug_printf("wrote session db (len %u)\n", (unsigned)dlen);
+ debug_printf(" wrote session db (len %u)\n", (unsigned)dlen);
}
}
- else DEBUG(D_tls)
- debug_printf("extract session data: %s\n", US gnutls_strerror(rc));
+ else
+ { DEBUG(D_tls)
+ debug_printf(" extract session data: %s\n", US gnutls_strerror(rc));
+ }
+ else DEBUG(D_tls)
+ debug_printf(" host not resmable; not saving ticket\n");
}
}
exim_gnutls_state_st * state = gnutls_session_get_ptr(sess);
tls_support * tlsp = state->tlsp;
-DEBUG(D_tls) debug_printf("newticket cb\n");
+DEBUG(D_tls) debug_printf("newticket cb (on client)\n");
if (!tlsp->ticket_received)
tls_save_session(tlsp, sess, state->host);
static void
tls_client_resume_prehandshake(exim_gnutls_state_st * state,
- tls_support * tlsp, host_item * host,
+ tls_support * tlsp, smtp_connect_args * conn_args,
smtp_transport_options_block * ob)
{
gnutls_session_set_ptr(state->session, state);
gnutls_handshake_set_hook_function(state->session,
GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, GNUTLS_HOOK_POST, tls_client_ticket_cb);
-tls_retrieve_session(tlsp, state->session, host, ob);
+tls_retrieve_session(tlsp, state->session, conn_args, ob);
}
static void
host_item * host = conn_args->host; /* for msgs and option-tests */
transport_instance * tb = conn_args->tblock; /* always smtp or NULL */
smtp_transport_options_block * ob = tb
- ? (smtp_transport_options_block *)tb->options_block
+ ? tb->drinst.options_block
: &smtp_transport_option_defaults;
int rc;
exim_gnutls_state_st * state = NULL;
const gnutls_datum_t * plist;
unsigned plen;
- if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+ if (!tls_alpn_plist(&ob->tls_alpn, &plist, &plen, errstr))
return FALSE;
if (plist)
if (gnutls_alpn_set_protocols(state->session, plist, plen, 0) != 0)
#endif
#ifdef EXIM_HAVE_TLS_RESUME
-tls_client_resume_prehandshake(state, tlsp, host, ob);
+tls_client_resume_prehandshake(state, tlsp, conn_args, ob);
#endif
#ifndef DISABLE_EVENT
if (do_shutdown)
{
DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
- do_shutdown > 1 ? " (with response-wait)" : "");
+ do_shutdown > TLS_SHUTDOWN_NOWAIT ? " (with response-wait)" : "");
tls_write(ct_ctx, NULL, 0, FALSE); /* flush write buffer */
#ifdef EXIM_TCP_CORK
- if (do_shutdown > 1)
+ if (do_shutdown == TLS_SHUTDOWN_WAIT)
(void) setsockopt(tlsp->active.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
#endif
+ /* The library seems to have no way to only wait for a peer's
+ shutdown, so handle the same as TLS_SHUTDOWN_WAIT */
+
ALARM(2);
- gnutls_bye(state->session, do_shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
+ gnutls_bye(state->session,
+ do_shutdown > TLS_SHUTDOWN_NOWAIT ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
ALARM_CLR(0);
}
}
gnutls_deinit(state->session);
-gnutls_certificate_free_credentials(state->lib_state.x509_cred);
-state->lib_state = null_tls_preload;
-
tlsp->active.sock = -1;
tlsp->active.tls_ctx = NULL;
/* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
return FALSE;
}
#ifndef DISABLE_DKIM
-dkim_exim_verify_feed(state->xfer_buffer, inbytes);
+smtp_verify_feed(state->xfer_buffer, inbytes);
#endif
state->xfer_buffer_hwm = (int) inbytes;
state->xfer_buffer_lwm = 0;
if (n > lim)
n = lim;
if (n > 0)
- dkim_exim_verify_feed(state->xfer_buffer+state->xfer_buffer_lwm, n);
+ smtp_verify_feed(state->xfer_buffer+state->xfer_buffer_lwm, n);
#endif
}
}
+/* For ATRN provider: transfer the tls_in context to tls_out */
+
+void
+tls_state_in_to_out(int newfd, const uschar * ipaddr, int port)
+{
+exim_gnutls_state_st * state;
+host_item * h;
+int old_pool = store_pool;
+
+store_pool = POOL_PERM;
+state = store_get(sizeof(exim_gnutls_state_st), GET_UNTAINTED);
+h = store_get(sizeof(host_item), GET_UNTAINTED);
+
+memset(h, 0, sizeof(host_item));
+h->name = h->address = string_copy(ipaddr);
+h->port = port;
+
+*state = state_server;
+
+state->fd_in = newfd;
+state->fd_out = newfd;
+state->tlsp = &tls_out;
+state->host = h;
+
+tls_out = tls_in;
+tls_out.active.sock = newfd;
+tls_out.active.tls_ctx = state;
+
+memset(&tls_in, 0, sizeof(tls_in));
+
+gnutls_transport_set_ptr2(state->session,
+ (gnutls_transport_ptr_t)(long) newfd,
+ (gnutls_transport_ptr_t)(long) newfd);
+store_pool = old_pool;
+}
+
+
+
+/* For ATRN customer: transfer the tls_out context to tls_in */
+
+void
+tls_state_out_to_in(int newfd, const uschar * ipaddr, int port)
+{
+host_item * h;
+int old_pool = store_pool;
+
+store_pool = POOL_PERM;
+h = store_get(sizeof(host_item), GET_UNTAINTED);
+store_pool = old_pool;
+memset(h, 0, sizeof(host_item));
+h->name = h->address = string_copy(ipaddr);
+h->port = port;
+
+state_server = *(exim_gnutls_state_st *)tls_out.active.tls_ctx;
+state_server.fd_in = newfd;
+state_server.fd_out = newfd;
+state_server.tlsp = &tls_in;
+state_server.host = h;
+state_server.xfer_buffer = store_malloc(ssl_xfer_buffer_size);
+
+tls_in = tls_out;
+tls_in.on_connect = FALSE;
+tls_in.active.sock = newfd;
+tls_in.active.tls_ctx = &state_server;
+
+memset(&tls_out, 0, sizeof(tls_out));
+
+gnutls_transport_set_ptr2(state_server.session,
+ (gnutls_transport_ptr_t)(long) newfd,
+ (gnutls_transport_ptr_t)(long) newfd);
+}
+
+
/*************************************************
/* See a description in tls-openssl.c for an explanation of why this exists.
-Arguments: a FILE* to print the results to
-Returns: nothing
+Arguments: string to append to
+Returns: string
*/
-void
-tls_version_report(FILE *f)
+gstring *
+tls_version_report(gstring * g)
{
-fprintf(f, "Library version: GnuTLS: Compile: %s\n"
- " Runtime: %s\n",
- LIBGNUTLS_VERSION,
- gnutls_check_version(NULL));
+return string_fmt_append(g,
+ "Library version: GnuTLS: Compile: %s\n"
+ " Runtime: %s\n",
+ LIBGNUTLS_VERSION,
+ gnutls_check_version(NULL));
}
#endif /*!MACRO_PREDEF*/