X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/80dceaeced16838c894429d5c15d8699ddec5542..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 1215f852e..c98760202 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2,10 +2,11 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* 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-only */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into @@ -121,6 +122,10 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # endif #endif +#if GNUTLS_VERSION_NUMBER >= 0x030702 +# define HAVE_GNUTLS_EXPORTER +#endif + #ifndef DISABLE_OCSP # include #endif @@ -646,14 +651,20 @@ tlsp->channelbinding = NULL; #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); @@ -670,11 +681,11 @@ tlsp->channelbinding = NULL; { 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"); } @@ -1132,8 +1143,9 @@ tls_server_clienthello_cb(gnutls_session_t session, unsigned int htype, 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; } @@ -1600,6 +1612,9 @@ return lifetime; /* 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) { @@ -2867,12 +2882,12 @@ NULL plist return for silent no-ALPN. */ 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) @@ -2902,11 +2917,12 @@ return TRUE; 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. */ @@ -3070,17 +3086,19 @@ if (rc != GNUTLS_E_SUCCESS) 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 */ @@ -3268,25 +3286,28 @@ however avoid storing and retrieving session information. */ 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 on continued-connection\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)))) { @@ -3332,8 +3353,7 @@ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET) if ((dbm_file = dbfn_open(US"tls", O_RDWR, &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) @@ -3368,14 +3388,14 @@ return 0; 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 @@ -3473,7 +3493,7 @@ if (ob->tls_alpn) 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) @@ -3565,7 +3585,7 @@ if (request_ocsp) #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 @@ -3744,17 +3764,21 @@ if (!tlsp || tlsp->active.sock < 0) return; /* TLS was not active */ 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); } @@ -3770,9 +3794,6 @@ if (!ct_ctx) /* server */ } 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 */