X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/aded22555eeb31bc032f9bc58a83762981a58391..d3a0dde57754d2b434957c126e1a22e2094cbbf1:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index bceb57395..af815d224 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -67,6 +67,9 @@ require current GnuTLS, then we'll drop support for the ancient libraries). #if GNUTLS_VERSION_NUMBER >= 0x030109 # define SUPPORT_CORK #endif +#if GNUTLS_VERSION_NUMBER >= 0x03010a +# define SUPPORT_GNUTLS_SESS_DESC +#endif #if GNUTLS_VERSION_NUMBER >= 0x030506 && !defined(DISABLE_OCSP) # define SUPPORT_SRV_OCSP_STACK #endif @@ -229,7 +232,7 @@ static gnutls_dh_params_t dh_server_params = NULL; static const int ssl_session_timeout = 200; -static const char * const exim_default_gnutls_priority = "NORMAL"; +static const uschar * const exim_default_gnutls_priority = US"NORMAL"; /* Guard library core initialisation */ @@ -1278,7 +1281,6 @@ int rc; size_t sz; const char *errpos; uschar *p; -BOOL want_default_priorities; if (!exim_gnutls_base_init_done) { @@ -1387,32 +1389,24 @@ and replaces gnutls_require_kx, gnutls_require_mac & gnutls_require_protocols. This was backwards incompatible, but means Exim no longer needs to track all algorithms and provide string forms for them. */ -want_default_priorities = TRUE; - +p = NULL; if (state->tls_require_ciphers && *state->tls_require_ciphers) { if (!expand_check_tlsvar(tls_require_ciphers, errstr)) return DEFER; if (state->exp_tls_require_ciphers && *state->exp_tls_require_ciphers) { - DEBUG(D_tls) debug_printf("GnuTLS session cipher/priority \"%s\"\n", - state->exp_tls_require_ciphers); - - rc = gnutls_priority_init(&state->priority_cache, - CS state->exp_tls_require_ciphers, &errpos); - want_default_priorities = FALSE; p = state->exp_tls_require_ciphers; + DEBUG(D_tls) debug_printf("GnuTLS session cipher/priority \"%s\"\n", p); } } -if (want_default_priorities) +if (!p) { + p = exim_default_gnutls_priority; DEBUG(D_tls) - debug_printf("GnuTLS using default session cipher/priority \"%s\"\n", - exim_default_gnutls_priority); - rc = gnutls_priority_init(&state->priority_cache, - exim_default_gnutls_priority, &errpos); - p = US exim_default_gnutls_priority; + debug_printf("GnuTLS using default session cipher/priority \"%s\"\n", p); } +rc = gnutls_priority_init(&state->priority_cache, CCS p, &errpos); exim_gnutls_err_check(rc, string_sprintf( "gnutls_priority_init(%s) failed at offset %ld, \"%.6s..\"", @@ -1496,23 +1490,61 @@ state->peerdn = NULL; cipher = gnutls_cipher_get(state->session); protocol = gnutls_protocol_get_version(state->session); mac = gnutls_mac_get(state->session); -kx = gnutls_kx_get(state->session); - -string_format(cipherbuf, sizeof(cipherbuf), - "%s:%s:%d", - gnutls_protocol_get_name(protocol), - gnutls_cipher_suite_get_name(kx, cipher, mac), - (int) gnutls_cipher_get_key_size(cipher) * 8); - -/* I don't see a way that spaces could occur, in the current GnuTLS -code base, but it was a concern in the old code and perhaps older GnuTLS -releases did return "TLS 1.0"; play it safe, just in case. */ -for (p = cipherbuf; *p != '\0'; ++p) - if (isspace(*p)) - *p = '-'; +kx = +#ifdef GNUTLS_TLS1_3 + protocol >= GNUTLS_TLS1_3 ? 0 : +#endif + gnutls_kx_get(state->session); + old_pool = store_pool; -store_pool = POOL_PERM; -state->ciphersuite = string_copy(cipherbuf); + { + store_pool = POOL_PERM; + +#ifdef SUPPORT_GNUTLS_SESS_DESC + { + gstring * g = NULL; + uschar * s = US gnutls_session_get_desc(state->session), c; + + /* Nikos M suggests we use this by preference. It returns like: + (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) + + For partial back-compat, put a colon after the TLS version, replace the + )-( grouping with __, replace in-group - with _ and append the :keysize. */ + + /* debug_printf("peer_status: gnutls_session_get_desc %s\n", s); */ + + for (s++; (c = *s) && c != ')'; s++) g = string_catn(g, s, 1); + g = string_catn(g, US":", 1); + 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); + /* now on ) closing group */ + if ((c = *s) && *++s == '-') g = string_catn(g, US"__", 2); + /* now on _ between groups */ + } + g = string_catn(g, US":", 1); + g = string_cat(g, string_sprintf("%d", (int) gnutls_cipher_get_key_size(cipher) * 8)); + state->ciphersuite = string_from_gstring(g); + } +#else + state->ciphersuite = string_sprintf("%s:%s:%d", + gnutls_protocol_get_name(protocol), + gnutls_cipher_suite_get_name(kx, cipher, mac), + (int) gnutls_cipher_get_key_size(cipher) * 8); + + /* I don't see a way that spaces could occur, in the current GnuTLS + code base, but it was a concern in the old code and perhaps older GnuTLS + releases did return "TLS 1.0"; play it safe, just in case. */ + + for (uschar * p = state->ciphersuite; *p; p++) if (isspace(*p)) *p = '-'; +#endif + +/* debug_printf("peer_status: ciphersuite %s\n", state->ciphersuite); */ + + state->tlsp->cipher = state->ciphersuite; + state->tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8; + } store_pool = old_pool; state->tlsp->cipher = state->ciphersuite; @@ -2562,8 +2594,12 @@ DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%p, %p, %u)\n", sigalrm_seen = 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)); + +do + inbytes = gnutls_record_recv(state->session, state->xfer_buffer, + MIN(ssl_xfer_buffer_size, lim)); +while (inbytes == GNUTLS_E_AGAIN); + if (smtp_receive_timeout > 0) ALARM_CLR(0); if (had_command_timeout) /* set by signal handler */ @@ -2618,7 +2654,7 @@ else if (inbytes == 0) else if (inbytes < 0) { -debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__); + DEBUG(D_tls) debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__); record_io_error(state, (int) inbytes, US"recv", NULL); state->xfer_error = TRUE; return FALSE; @@ -2740,17 +2776,20 @@ DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%p, %p, " SIZE_T_FMT ")\n", state->session, buff, len); -inbytes = gnutls_record_recv(state->session, buff, len); +do + inbytes = gnutls_record_recv(state->session, buff, len); +while (inbytes == GNUTLS_E_AGAIN); + if (inbytes > 0) return inbytes; if (inbytes == 0) { DEBUG(D_tls) debug_printf("Got TLS_EOF\n"); } else -{ -debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__); -record_io_error(state, (int)inbytes, US"recv", NULL); -} + { + DEBUG(D_tls) debug_printf("%s: err from gnutls_record_recv(\n", __FUNCTION__); + record_io_error(state, (int)inbytes, US"recv", NULL); + } return -1; } @@ -2792,7 +2831,10 @@ while (left > 0) { DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %p, " SIZE_T_FMT ")\n", buff, left); - outbytes = gnutls_record_send(state->session, buff, left); + + do + outbytes = gnutls_record_send(state->session, buff, left); + while (outbytes == GNUTLS_E_AGAIN); DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes); if (outbytes < 0)