X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/40c90bca9f7e2952bd64faebceb53538f80805a7..30c3236bdc2fba05051cd77d52fb1cb29cb70afb:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index a5a680fd2..7b16fc811 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -13,7 +13,7 @@ tls.c when USE_GNUTLS has been set. The code herein is a revamp of GnuTLS integration using the current APIs; the original tls-gnu.c was based on a patch which was contributed by Nikos -Mavroyanopoulos. The revamp is partially a rewrite, partially cut&paste as +Mavrogiannopoulos. The revamp is partially a rewrite, partially cut&paste as appropriate. APIs current as of GnuTLS 2.12.18; note that the GnuTLS manual is for GnuTLS 3, @@ -493,8 +493,7 @@ else if (Ustrcmp(exp_tls_dhparam, "none") == 0) } else if (exp_tls_dhparam[0] != '/') { - m.data = US std_dh_prime_named(exp_tls_dhparam); - if (m.data == NULL) + if (!(m.data = US std_dh_prime_named(exp_tls_dhparam))) return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL); m.size = Ustrlen(m.data); } @@ -548,8 +547,7 @@ if (use_file_in_spool) /* Open the cache file for reading and if successful, read it and set up the parameters. */ -fd = Uopen(filename, O_RDONLY, 0); -if (fd >= 0) +if ((fd = Uopen(filename, O_RDONLY, 0)) >= 0) { struct stat statbuf; FILE *fp; @@ -624,8 +622,7 @@ if (rc < 0) CS filename, NULL); temp_fn = string_copy(US "%s.XXXXXXX"); - fd = mkstemp(CS temp_fn); /* modifies temp_fn */ - if (fd < 0) + if ((fd = mkstemp(CS temp_fn)) < 0) /* modifies temp_fn */ return tls_error(US"Unable to open temp file", strerror(errno), NULL); (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */ @@ -675,23 +672,19 @@ if (rc < 0) } m.size = sz; /* shrink by 1, probably */ - sz = write_to_fd_buf(fd, m.data, (size_t) m.size); - if (sz != m.size) + if ((sz = write_to_fd_buf(fd, m.data, (size_t) m.size)) != m.size) { free(m.data); return tls_error(US"TLS cache write D-H params failed", strerror(errno), NULL); } free(m.data); - sz = write_to_fd_buf(fd, US"\n", 1); - if (sz != 1) + if ((sz = write_to_fd_buf(fd, US"\n", 1)) != 1) return tls_error(US"TLS cache write D-H params final newline failed", strerror(errno), NULL); - rc = close(fd); - if (rc) - return tls_error(US"TLS cache write close() failed", - strerror(errno), NULL); + if ((rc = close(fd))) + return tls_error(US"TLS cache write close() failed", strerror(errno), NULL); if (Urename(temp_fn, filename) < 0) return tls_error(string_sprintf("failed to rename \"%s\" as \"%s\"", @@ -1730,7 +1723,7 @@ Arguments: Returns: OK on success DEFER for errors before the start of the negotiation - FAIL for errors during the negotation; the server can't + FAIL for errors during the negotiation; the server can't continue running. */ @@ -1829,16 +1822,27 @@ alarm(0); if (rc != GNUTLS_E_SUCCESS) { - tls_error(US"gnutls_handshake", - sigalrm_seen ? "timed out" : gnutls_strerror(rc), NULL); /* It seems that, except in the case of a timeout, we have to close the connection right here; otherwise if the other end is running OpenSSL it hangs until the server times out. */ - if (!sigalrm_seen) + if (sigalrm_seen) + { + tls_error(US"gnutls_handshake", "timed out", NULL); + gnutls_db_remove_session(state->session); + } + else { + tls_error(US"gnutls_handshake", gnutls_strerror(rc), NULL); + (void) gnutls_alert_send_appropriate(state->session, rc); + gnutls_deinit(state->session); + gnutls_certificate_free_credentials(state->x509_cred); + millisleep(500); + shutdown(state->fd_out, SHUT_WR); + for (rc = 1024; fgetc(smtp_in) != EOF && rc > 0; ) rc--; /* drain skt */ (void)fclose(smtp_out); (void)fclose(smtp_in); + smtp_out = smtp_in = NULL; } return FAIL; @@ -1863,8 +1867,7 @@ if ( state->verify_requirement != VERIFY_NONE /* Figure out peer DN, and if authenticated, etc. */ -rc = peer_status(state); -if (rc != OK) return rc; +if ((rc = peer_status(state)) != OK) return rc; /* Sets various Exim expansion variables; always safe within server */ @@ -2040,8 +2043,13 @@ do alarm(0); if (rc != GNUTLS_E_SUCCESS) - return tls_error(US"gnutls_handshake", - sigalrm_seen ? "timed out" : gnutls_strerror(rc), state->host); + if (sigalrm_seen) + { + gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED); + return tls_error(US"gnutls_handshake", "timed out", state->host); + } + else + return tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host); DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); @@ -2123,6 +2131,8 @@ if (shutdown) } gnutls_deinit(state->session); +gnutls_certificate_free_credentials(state->x509_cred); + state->tlsp->active = -1; memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); @@ -2148,12 +2158,12 @@ Only used by the server-side TLS. This feeds DKIM and should be used for all message-body reads. -Arguments: none +Arguments: lim Maximum amount to read/bufffer Returns: the next character or EOF */ int -tls_getc(void) +tls_getc(unsigned lim) { exim_gnutls_state_st *state = &state_server; if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) @@ -2165,14 +2175,22 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); inbytes = gnutls_record_recv(state->session, state->xfer_buffer, - ssl_xfer_buffer_size); + MIN(ssl_xfer_buffer_size, lim)); alarm(0); - /* A zero-byte return appears to mean that the TLS session has been + /* Timeouts do not get this far; see command_timeout_handler(). + A zero-byte return appears to mean that the TLS session has been closed down, not that the socket itself has been closed down. Revert to non-TLS handling. */ - if (inbytes == 0) + if (sigalrm_seen) + { + DEBUG(D_tls) debug_printf("Got tls read timeout\n"); + state->xfer_error = 1; + return EOF; + } + + else if (inbytes == 0) { DEBUG(D_tls) debug_printf("Got TLS_EOF\n"); @@ -2184,6 +2202,8 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) receive_smtp_buffered = smtp_buffered; gnutls_deinit(state->session); + gnutls_certificate_free_credentials(state->x509_cred); + state->session = NULL; state->tlsp->active = -1; state->tlsp->bits = 0; @@ -2193,7 +2213,7 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) state->tlsp->peercert = NULL; state->tlsp->peerdn = NULL; - return smtp_getc(); + return smtp_getc(lim); } /* Handle genuine errors */