uschar *xfer_buffer;
int xfer_buffer_lwm;
int xfer_buffer_hwm;
- int xfer_eof;
- int xfer_error;
+ BOOL xfer_eof; /*XXX never gets set! */
+ BOOL xfer_error;
} exim_gnutls_state_st;
static const exim_gnutls_state_st exim_gnutls_state_init = {
.xfer_buffer = NULL,
.xfer_buffer_lwm = 0,
.xfer_buffer_hwm = 0,
- .xfer_eof = 0,
- .xfer_error = 0,
+ .xfer_eof = FALSE,
+ .xfer_error = FALSE,
};
/* Not only do we have our own APIs which don't pass around state, assuming
XXX But see gnutls_session_get_ptr()
*/
-static exim_gnutls_state_st state_server, state_client;
+static exim_gnutls_state_st state_server;
/* dh_params are initialised once within the lifetime of a process using TLS;
if we used TLS in a long-lived daemon, we'd have to reconsider this. But we
#endif
tls_support * tlsp = state->tlsp;
-tlsp->active = state->fd_out;
+tlsp->active.sock = state->fd_out;
+tlsp->active.tls_ctx = state;
cipher = gnutls_cipher_get(state->session);
/* returns size in "bytes" */
goto err;
where = US"configuring cert";
-now = 0;
+now = 1;
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)))
const uschar *crl,
const uschar *require_ciphers,
exim_gnutls_state_st **caller_state,
+ tls_support * tlsp,
uschar ** errstr)
{
exim_gnutls_state_st *state;
if (host)
{
- state = &state_client;
+ /* For client-side sessions we allocate a context. This lets us run
+ several in parallel. */
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ state = store_get(sizeof(exim_gnutls_state_st));
+ store_pool = old_pool;
+
memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
- state->tlsp = &tls_out;
+ state->tlsp = tlsp;
DEBUG(D_tls) debug_printf("initialising GnuTLS client session\n");
rc = gnutls_init(&state->session, GNUTLS_CLIENT);
}
{
state = &state_server;
memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
- state->tlsp = &tls_in;
+ state->tlsp = tlsp;
DEBUG(D_tls) debug_printf("initialising GnuTLS server session\n");
rc = gnutls_init(&state->session, GNUTLS_SERVER);
}
(needed for TA but not EE). */
if (usage == ((1<<DANESSL_USAGE_DANE_TA) | (1<<DANESSL_USAGE_DANE_EE)))
- { /* a mixed-usage bundle */
+ { /* a mixed-usage bundle */
int i, j, nrec;
const char ** dd;
int * ddl;
1, 0))
|| (rc = dane_verify_crt_raw(s, certlist, lsize,
gnutls_certificate_type_get(state->session),
- r, 0,
+ r, 0,
# ifdef GNUTLS_BROKEN_DANE_VALIDATION
usage == (1 << DANESSL_USAGE_DANE_EE)
? DANE_VFLAG_ONLY_CHECK_EE_USAGE : 0,
uschar * yield;
exim_gnutls_state_st * state = gnutls_session_get_ptr(session);
-cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
-if (cert_list)
+if ((cert_list = gnutls_certificate_get_peers(session, &cert_list_size)))
while (cert_list_size--)
{
- rc = import_cert(&cert_list[cert_list_size], &crt);
- if (rc != GNUTLS_E_SUCCESS)
+ 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));
exim_gnutls_state_st * state = NULL;
/* Check for previous activation */
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
{
tls_error(US"STARTTLS received after TLS started", "", NULL, errstr);
smtp_printf("554 Already in TLS\r\n", FALSE);
if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
NULL, tls_verify_certificates, tls_crl,
- require_ciphers, &state, errstr)) != OK) return rc;
+ require_ciphers, &state, &tls_in, errstr)) != OK) return rc;
/* If this is a host for which certificate verification is mandatory or
optional, set up appropriately. */
Which implies cert must be requested and supplied, dane
verify must pass, and cert verify irrelevant (incl.
hostnames), and (caller handled) require_tls
+ tlsp record details of channel configuration
errstr error string pointer
-Returns: OK/DEFER/FAIL (because using common functions),
- but for a client, DEFER and FAIL have the same meaning
+Returns: Pointer to TLS session context, or NULL on error
*/
-int
+void *
tls_client_start(int fd, host_item *host,
address_item *addr ARG_UNUSED,
transport_instance * tb,
#ifdef SUPPORT_DANE
dns_answer * tlsa_dnsa,
#endif
- uschar ** errstr)
+ tls_support * tlsp, uschar ** errstr)
{
smtp_transport_options_block *ob =
(smtp_transport_options_block *)tb->options_block;
int rc;
exim_gnutls_state_st * state = NULL;
+uschar *cipher_list = NULL;
+
#ifndef DISABLE_OCSP
BOOL require_ocsp =
verify_check_given_host(&ob->hosts_require_ocsp, host) == OK;
DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd);
-if ((rc = tls_init(host, ob->tls_certificate, ob->tls_privatekey,
+#ifdef SUPPORT_DANE
+if (tlsa_dnsa && ob->dane_require_tls_ciphers)
+ {
+ /* not using expand_check_tlsvar because not yet in state */
+ if (!expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
+ &cipher_list, errstr))
+ return NULL;
+ cipher_list = cipher_list && *cipher_list
+ ? ob->dane_require_tls_ciphers : ob->tls_require_ciphers;
+ }
+#endif
+
+if (!cipher_list)
+ cipher_list = ob->tls_require_ciphers;
+
+if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
- ob->tls_require_ciphers, &state, errstr)) != OK)
- return rc;
+ cipher_list, &state, tlsp, errstr) != OK)
+ return NULL;
{
int dh_min_bits = ob->tls_dh_min_bits;
DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n");
if ((rc = gnutls_ocsp_status_request_enable_client(state->session,
NULL, 0, NULL)) != OK)
- return tls_error(US"cert-status-req",
- gnutls_strerror(rc), state->host, errstr);
- tls_out.ocsp = OCSP_NOT_RESP;
+ {
+ tls_error(US"cert-status-req", gnutls_strerror(rc), state->host, errstr);
+ return NULL;
+ }
+ tlsp->ocsp = OCSP_NOT_RESP;
}
#endif
alarm(0);
if (rc != GNUTLS_E_SUCCESS)
+ {
if (sigalrm_seen)
{
gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED);
- return tls_error(US"gnutls_handshake", "timed out", state->host, errstr);
+ tls_error(US"gnutls_handshake", "timed out", state->host, errstr);
}
else
- return tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host, errstr);
+ tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host, errstr);
+ return NULL;
+ }
DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n");
/* Verify late */
if (!verify_certificate(state, errstr))
- return tls_error(US"certificate verification failed", *errstr, state->host, errstr);
+ {
+ tls_error(US"certificate verification failed", *errstr, state->host, errstr);
+ return NULL;
+ }
#ifndef DISABLE_OCSP
if (require_ocsp)
if (gnutls_ocsp_status_request_is_checked(state->session, 0) == 0)
{
- tls_out.ocsp = OCSP_FAILED;
- return tls_error(US"certificate status check failed", NULL, state->host, errstr);
+ tlsp->ocsp = OCSP_FAILED;
+ tls_error(US"certificate status check failed", NULL, state->host, errstr);
+ return NULL;
}
DEBUG(D_tls) debug_printf("Passed OCSP checking\n");
- tls_out.ocsp = OCSP_VFIED;
+ tlsp->ocsp = OCSP_VFIED;
}
#endif
/* Figure out peer DN, and if authenticated, etc. */
-if ((rc = peer_status(state, errstr)) != OK)
- return rc;
+if (peer_status(state, errstr) != OK)
+ return NULL;
/* Sets various Exim expansion variables; may need to adjust for ACL callouts */
extract_exim_vars_from_tls_state(state);
-return OK;
+return state;
}
daemon, to shut down the TLS library, without actually doing a shutdown (which
would tamper with the TLS session in the parent process).
-Arguments: TRUE if gnutls_bye is to be called
+Arguments:
+ ct_ctx client context pointer, or NULL for the one global server context
+ shutdown 1 if TLS close-alert is to be sent,
+ 2 if also response to be waited for
+
Returns: nothing
*/
void
-tls_close(BOOL is_server, BOOL shutdown)
+tls_close(void * ct_ctx, int shutdown)
{
-exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
-if (!state->tlsp || state->tlsp->active < 0) return; /* TLS was not active */
+if (!state->tlsp || state->tlsp->active.sock < 0) return; /* TLS was not active */
if (shutdown)
{
- DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS\n");
- gnutls_bye(state->session, GNUTLS_SHUT_WR);
+ DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
+ shutdown > 1 ? " (with response-wait)" : "");
+
+ alarm(2);
+ gnutls_bye(state->session, shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
+ alarm(0);
}
gnutls_deinit(state->session);
gnutls_certificate_free_credentials(state->x509_cred);
-state->tlsp->active = -1;
+state->tlsp->active.sock = -1;
+state->tlsp->active.tls_ctx = NULL;
+if (state->xfer_buffer) store_free(state->xfer_buffer);
memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
-
-if ((state_server.session == NULL) && (state_client.session == NULL))
- {
- gnutls_global_deinit();
- exim_gnutls_base_init_done = 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));
-alarm(0);
+if (smtp_receive_timeout > 0) alarm(0);
+
+if (had_command_timeout) /* set by signal handler */
+ smtp_command_timeout_exit(); /* does not return */
+if (had_command_sigterm)
+ smtp_command_sigterm_exit();
+if (had_data_timeout)
+ smtp_data_timeout_exit();
+if (had_data_sigint)
+ smtp_data_sigint_exit();
-/* Timeouts do not get this far; see command_timeout_handler().
- A zero-byte return appears to mean that the TLS session has been
- closed down, not that the socket itself has been closed down. Revert to
- non-TLS handling. */
+/* Timeouts do not get this far. A zero-byte return appears to mean that the
+TLS session has been closed down, not that the socket itself has been closed
+down. Revert to non-TLS handling. */
if (sigalrm_seen)
{
DEBUG(D_tls) debug_printf("Got tls read timeout\n");
- state->xfer_error = 1;
+ state->xfer_error = TRUE;
return FALSE;
}
gnutls_certificate_free_credentials(state->x509_cred);
state->session = NULL;
- state->tlsp->active = -1;
+ state->tlsp->active.sock = -1;
+ state->tlsp->active.tls_ctx = NULL;
state->tlsp->bits = 0;
state->tlsp->certificate_verified = FALSE;
tls_channelbinding_b64 = NULL;
else if (inbytes < 0)
{
record_io_error(state, (int) inbytes, US"recv", NULL);
- state->xfer_error = 1;
+ state->xfer_error = TRUE;
return FALSE;
}
#ifndef DISABLE_DKIM
then the caller must feed DKIM.
Arguments:
+ ct_ctx client context pointer, or NULL for the one global server context
buff buffer of data
len size of buffer
*/
int
-tls_read(BOOL is_server, uschar *buff, size_t len)
+tls_read(void * ct_ctx, uschar *buff, size_t len)
{
-exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
ssize_t inbytes;
if (len > INT_MAX)
/*
Arguments:
- is_server channel specifier
+ ct_ctx client context pointer, or NULL for the one global server context
buff buffer of data
len number of bytes
more more data expected soon
*/
int
-tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
{
ssize_t outbytes;
size_t left = len;
-exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
#ifdef SUPPORT_CORK
static BOOL corked = FALSE;