X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-gnu.c;h=2d7041f3e4077e41a4cee7e8f0061c2b3266f0cb;hb=fd5ad03aa7da47965ce3e23294661c8f3c602d33;hp=b14bca4831e0f43200ae533d4558ba735b2cd436;hpb=48e909900663856b9b1225f5df4cd033302f1bcd;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index b14bca483..2d7041f3e 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -119,6 +119,12 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # endif #endif +#if GNUTLS_VERSION_NUMBER >= 0x030200 +# ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE +# define EXIM_HAVE_ALPN +# endif +#endif + #ifndef DISABLE_OCSP # include #endif @@ -278,10 +284,14 @@ static BOOL gnutls_buggy_ocsp = FALSE; static BOOL exim_testharness_disable_ocsp_validity_check = FALSE; #endif +#ifdef EXIM_HAVE_ALPN +static BOOL server_seen_alpn = FALSE; +#endif #ifdef EXIM_HAVE_TLS_RESUME static gnutls_datum_t server_sessticket_key; #endif + /* ------------------------------------------------------------------------ */ /* macros */ @@ -851,7 +861,7 @@ if (rc < 0) return tls_error(US"Filename too long to generate replacement", filename, NULL, errstr); - temp_fn = string_copy(US"%s.XXXXXXX"); + temp_fn = string_copy(US"exim-dh.XXXXXXX"); if ((fd = mkstemp(CS temp_fn)) < 0) /* modifies temp_fn */ return tls_error_sys(US"Unable to open temp file", errno, NULL, errstr); (void)exim_chown(temp_fn, exim_uid, exim_gid); /* Probably not necessary */ @@ -1062,10 +1072,18 @@ tls_server_clienthello_ext(void * ctx, unsigned tls_id, const unsigned char *data, unsigned size) { /* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */ -if (tls_id == 5) /* status_request */ +switch (tls_id) { - DEBUG(D_tls) debug_printf("Seen status_request extension from client\n"); - tls_in.ocsp = OCSP_NOT_RESP; + case 5: /* status_request */ + DEBUG(D_tls) debug_printf("Seen status_request extension from client\n"); + tls_in.ocsp = OCSP_NOT_RESP; + break; +#ifdef EXIM_HAVE_ALPN + case 16: /* Application Layer Protocol Notification */ + DEBUG(D_tls) debug_printf("Seen ALPN extension from client\n"); + server_seen_alpn = TRUE; + break; +#endif } return 0; } @@ -1126,12 +1144,12 @@ tls_server_certstatus_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, const gnutls_datum_t * msg) { DEBUG(D_tls) debug_printf("Sending certificate-status\n"); /*XXX we get this for tls1.2 but not for 1.3 */ -#ifdef SUPPORT_SRV_OCSP_STACK +# ifdef SUPPORT_SRV_OCSP_STACK tls_in.ocsp = exim_testharness_disable_ocsp_validity_check ? OCSP_VFY_NOT_TRIED : OCSP_VFIED; /* We know that GnuTLS verifies responses */ -#else +# else tls_in.ocsp = OCSP_VFY_NOT_TRIED; -#endif +# endif return 0; } @@ -2791,6 +2809,26 @@ if (gnutls_session_is_resumed(state->session)) } } #endif + + +#ifdef EXIM_HAVE_ALPN +static void +tls_server_set_acceptable_alpns(exim_gnutls_state_st * state) +{ +int rc; +gnutls_datum_t protocols[2] = {[0] = {.data = US"smtp", .size = 4}, + [1] = {.data = US"esmtp", .size = 5}}; + +/* Set non-mandatory set of protocol names */ +if (!(rc = gnutls_alpn_set_protocols(state->session, protocols, 2, 0))) + gnutls_handshake_set_hook_function(state->session, + GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, tls_server_hook_cb); +else + DEBUG(D_tls) + debug_printf("setting alpn protocols: %s\n", US gnutls_strerror(rc)); +} +#endif + /* ------------------------------------------------------------------------ */ /* Exported functions */ @@ -2847,6 +2885,10 @@ DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n"); #endif } +#ifdef EXIM_HAVE_ALPN +tls_server_set_acceptable_alpns(state); +#endif + #ifdef EXIM_HAVE_TLS_RESUME tls_server_resume_prehandshake(state); #endif @@ -2962,6 +3004,32 @@ tls_server_resume_posthandshake(state); DEBUG(D_tls) post_handshake_debug(state); +#ifdef EXIM_HAVE_ALPN +if (server_seen_alpn) + { + /* The client offered ALPN. We were set up with a nonmandatory list; + see what was negotiated. We require a match now, given that something + was offered. */ + gnutls_datum_t p = {.size = 0}; + int rc = gnutls_alpn_get_selected_protocol(state->session, &p); + if (!rc || rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + { + if (p.size == 0) + { + *errstr = US"ALPN rejected"; + return FAIL; + } + else + DEBUG(D_tls) + debug_printf("ALPN negotiated: %.*s\n", (int)p.size, p.data); + } + else + DEBUG(D_tls) + debug_printf("getting alpn protocol: %s\n", US gnutls_strerror(rc)); + + } +#endif + /* Verify after the fact */ if (!verify_certificate(state, errstr)) @@ -3489,6 +3557,25 @@ return TRUE; +/* +Arguments: + ct_ctx client TLS context pointer, or NULL for the one global server context +*/ + +void +tls_shutdown_wr(void * ct_ctx) +{ +exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server; +tls_support * tlsp = state->tlsp; + +if (!tlsp || tlsp->active.sock < 0) return; /* TLS was not active */ + +tls_write(ct_ctx, NULL, 0, FALSE); /* flush write buffer */ + +HDEBUG(D_transport|D_tls|D_acl|D_v) debug_printf_indent(" SMTP(TLS shutdown)>>\n"); +gnutls_bye(state->session, GNUTLS_SHUT_WR); +} + /************************************************* * Close down a TLS session * *************************************************/ @@ -3499,27 +3586,30 @@ would tamper with the TLS session in the parent process). 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 + do_shutdown 0 no data-flush or TLS close-alert + 1 if TLS close-alert is to be sent, + 2 if also response to be waited for (2s timeout) Returns: nothing */ void -tls_close(void * ct_ctx, int shutdown) +tls_close(void * ct_ctx, int do_shutdown) { exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server; tls_support * tlsp = state->tlsp; if (!tlsp || tlsp->active.sock < 0) return; /* TLS was not active */ -if (shutdown) +if (do_shutdown) { DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n", - shutdown > 1 ? " (with response-wait)" : ""); + do_shutdown > 1 ? " (with response-wait)" : ""); + + tls_write(ct_ctx, NULL, 0, FALSE); /* flush write buffer */ ALARM(2); - gnutls_bye(state->session, shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR); + gnutls_bye(state->session, do_shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR); ALARM_CLR(0); } @@ -3667,7 +3757,7 @@ return buf; void -tls_get_cache() +tls_get_cache(void) { #ifndef DISABLE_DKIM exim_gnutls_state_st * state = &state_server; @@ -3686,8 +3776,6 @@ return state_server.xfer_buffer_lwm < state_server.xfer_buffer_hwm } - - /************************************************* * Read bytes from TLS channel * *************************************************/