X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/fc624b8cb4c3312d7450dfa86adfa3fe8dd9cbeb..107077d7fd6736711bf5cd980221723401d37c51:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 3adadb80b..efa6004a3 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 - 2023 */ /* 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 @@ -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 @@ -333,6 +338,9 @@ before, for now. */ # endif /* AVOID_GNUTLS_PKCS11 */ #endif +#if GNUTLS_VERSION_NUMBER >= 0x030404 +# define HAVE_GNUTLS_PRF_RFC5705 +#endif @@ -371,7 +379,7 @@ Argument: the connected host if setting up a client errstr pointer to returned error string -Returns: OK/DEFER/FAIL +Returns: DEFER/FAIL */ static int @@ -384,13 +392,15 @@ return host ? FAIL : DEFER; } +/* 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); @@ -624,11 +634,6 @@ Argument: 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; @@ -646,21 +651,46 @@ only available for use for authenticators while this TLS session is running. */ 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 @@ -697,7 +727,7 @@ file is never present. If two processes both compute some new parameters, you 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 @@ -735,7 +765,7 @@ else if (Ustrcmp(exp_tls_dhparam, "historic") == 0) 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] != '/') { @@ -986,7 +1016,7 @@ 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))) - || (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, @@ -1089,21 +1119,28 @@ switch (tls_id) /* 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; @@ -1115,8 +1152,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; } @@ -1241,6 +1279,7 @@ DEBUG(D_tls) 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) @@ -1264,7 +1303,7 @@ while (cfile = string_nextinlist(&clist, &csep, NULL, 0)) 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 { @@ -1342,7 +1381,7 @@ while (cfile = string_nextinlist(&clist, &csep, NULL, 0)) } #endif /* DISABLE_OCSP */ } -return 0; +return OK; } static int @@ -1352,7 +1391,7 @@ creds_load_client_certs(exim_gnutls_state_st * state, const host_item * host, 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 @@ -1583,6 +1622,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) { @@ -1778,8 +1820,13 @@ D-H generation. */ 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 */ @@ -1791,8 +1838,14 @@ if (!state->lib_state.conn_certs) 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 */ @@ -1834,7 +1887,11 @@ if (!state->lib_state.conn_certs) tls_ocsp_file, #endif errstr) - ) ) return rc; + ) ) + { + DEBUG(D_tls) debug_printf("load-cert: '%s'\n", *errstr); + return rc; + } } } else @@ -1945,10 +2002,10 @@ Returns: OK/DEFER/FAIL */ 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 @@ -1957,11 +2014,13 @@ client-side params. */ 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. */ @@ -2015,7 +2074,7 @@ if (host) 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)); @@ -2245,7 +2304,7 @@ old_pool = store_pool; 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 */ @@ -2335,7 +2394,7 @@ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) 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)]"); @@ -2415,8 +2474,8 @@ else 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))) @@ -2563,7 +2622,7 @@ else ) { 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; @@ -2662,7 +2721,7 @@ if (sni_type != GNUTLS_NAME_DNS) /* 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 */ @@ -2678,11 +2737,12 @@ if ((rc = tls_expand_session_files(state, &dummy_errstr)) != OK) { /* 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; } @@ -2711,25 +2771,25 @@ exim_gnutls_state_st * state = gnutls_session_get_ptr(session); 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; } @@ -2850,12 +2910,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) @@ -2873,7 +2933,7 @@ else 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); } @@ -2885,11 +2945,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. */ @@ -3053,17 +3114,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 */ @@ -3192,8 +3255,8 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; 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; @@ -3251,25 +3314,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)))) { @@ -3306,7 +3372,7 @@ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET) { 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); memcpy(dt->session, tkt.data, tkt.size); @@ -3315,8 +3381,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) @@ -3351,14 +3416,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 @@ -3456,7 +3521,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) @@ -3548,7 +3613,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 @@ -3727,17 +3792,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); } @@ -3753,9 +3822,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 */ @@ -4234,17 +4300,18 @@ return NULL; /* 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*/