X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fsrc%2Ftls-gnu.c;h=24114f05e213085d4a0cbc052f878ffa5a8ad8a7;hb=302e7912253bd1f1a88f04d9a8a71d8a3c9b93fc;hp=34ebc0903114a5414ac8c0c48bc2a8b46cdf2a6c;hpb=17ba0f52b8df4e6ece849deac1b9d6b88bdc26c6;p=exim.git diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 34ebc0903..24114f05e 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Copyright (c) Phil Pennock 2012 */ @@ -53,6 +54,9 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # warning "GnuTLS library version too old; tls:cert event unsupported" # define DISABLE_EVENT #endif +#if GNUTLS_VERSION_NUMBER >= 0x030000 +# define SUPPORT_SELFSIGN /* Uncertain what version is first usable but 2.12.23 is not */ +#endif #if GNUTLS_VERSION_NUMBER >= 0x030306 # define SUPPORT_CA_DIR #else @@ -177,10 +181,17 @@ typedef struct exim_gnutls_state { enum peer_verify_requirement verify_requirement; int fd_in; int fd_out; - BOOL peer_cert_verified; - BOOL peer_dane_verified; - BOOL trigger_sni_changes; - BOOL have_set_peerdn; + + BOOL peer_cert_verified:1; + BOOL peer_dane_verified:1; + BOOL trigger_sni_changes:1; + BOOL have_set_peerdn:1; + BOOL xfer_eof:1; /*XXX never gets set! */ + BOOL xfer_error:1; +#ifdef SUPPORT_CORK + BOOL corked:1; +#endif + const struct host_item *host; /* NULL if server */ gnutls_x509_crt_t peercert; uschar *peerdn; @@ -213,8 +224,6 @@ typedef struct exim_gnutls_state { uschar *xfer_buffer; int xfer_buffer_lwm; int xfer_buffer_hwm; - BOOL xfer_eof; /*XXX never gets set! */ - BOOL xfer_error; } exim_gnutls_state_st; static const exim_gnutls_state_st exim_gnutls_state_init = { @@ -819,13 +828,19 @@ gnutls_x509_privkey_t pkey = NULL; const uschar * where; int rc; +#ifndef SUPPORT_SELFSIGN +where = US"library too old"; +rc = GNUTLS_E_NO_CERTIFICATE_FOUND; +if (TRUE) goto err; +#endif + where = US"initialising pkey"; if ((rc = gnutls_x509_privkey_init(&pkey))) goto err; where = US"initialising cert"; if ((rc = gnutls_x509_crt_init(&cert))) goto err; -where = US"generating pkey"; +where = US"generating pkey"; /* Hangs on 2.12.23 */ if ((rc = gnutls_x509_privkey_generate(pkey, GNUTLS_PK_RSA, #ifdef SUPPORT_PARAM_TO_PK_BITS # ifndef GNUTLS_SEC_PARAM_MEDIUM @@ -1939,7 +1954,7 @@ else const char ** dd; int * ddl; - for(nrec = 0; state->dane_data_len[nrec]; ) nrec++; + for (nrec = 0; state->dane_data_len[nrec]; ) nrec++; nrec++; dd = store_get(nrec * sizeof(uschar *), FALSE); @@ -2302,11 +2317,12 @@ if (TRUE) } else debug_printf("To get keying info for TLS1.3 is hard:\n" - " set environment variable SSLKEYLOGFILE to a filename writable by uid exim\n" - " add SSLKEYLOGFILE to keep_environment in the exim config\n" - " run exim as root\n" - " if using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n" - " (works for TLS1.2 also, and saves cut-paste into file)" + " Set environment variable SSLKEYLOGFILE to a filename relative to the spool directory,\n" + " and make sure it is writable by the Exim runtime user.\n" + " Add SSLKEYLOGFILE to keep_environment in the exim config.\n" + " Start Exim as root.\n" + " If using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n" + " (works for TLS1.2 also, and saves cut-paste into file).\n" " Trying to use add_environment for this will not work\n"); #endif } @@ -3136,7 +3152,7 @@ tls_refill(unsigned lim) exim_gnutls_state_st * state = &state_server; ssize_t inbytes; -DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%p, %p, %u)\n", +DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, buffersize=%u)\n", state->session, state->xfer_buffer, ssl_xfer_buffer_size); sigalrm_seen = FALSE; @@ -3299,7 +3315,7 @@ if (state->xfer_buffer_lwm < state->xfer_buffer_hwm) state->xfer_buffer_hwm - state->xfer_buffer_lwm); DEBUG(D_tls) - debug_printf("Calling gnutls_record_recv(%p, %p, " SIZE_T_FMT ")\n", + debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n", state->session, buff, len); do @@ -3347,10 +3363,14 @@ 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 = ct_ctx ? ct_ctx : &state_server; -#ifdef SUPPORT_CORK -static BOOL corked = FALSE; -if (more && !corked) gnutls_record_cork(state->session); +#ifdef SUPPORT_CORK +if (more && !state->corked) + { + DEBUG(D_tls) debug_printf("gnutls_record_cork(session=%p)\n", state->session); + gnutls_record_cork(state->session); + state->corked = TRUE; + } #endif DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__, @@ -3358,14 +3378,15 @@ DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__, while (left > 0) { - DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %p, " SIZE_T_FMT ")\n", - buff, left); + DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n", + 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) { DEBUG(D_tls) debug_printf("%s: gnutls_record_send err\n", __FUNCTION__); @@ -3391,10 +3412,25 @@ if (len > INT_MAX) } #ifdef SUPPORT_CORK -if (more != corked) +if (!more && state->corked) { - if (!more) (void) gnutls_record_uncork(state->session, 0); - corked = more; + DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session); + do + /* We can't use GNUTLS_RECORD_WAIT here, as it retries on + GNUTLS_E_AGAIN || GNUTLS_E_INTR, which would break our timeout set by alarm(). + The GNUTLS_E_AGAIN should not happen ever, as our sockets are blocking anyway. + But who knows. (That all relies on the fact that GNUTLS_E_INTR and GNUTLS_E_AGAIN + match the EINTR and EAGAIN errno values.) */ + outbytes = gnutls_record_uncork(state->session, 0); + while (outbytes == GNUTLS_E_AGAIN); + + if (outbytes < 0) + { + record_io_error(state, len, US"uncork", NULL); + return -1; + } + + state->corked = FALSE; } #endif