reported the original. Fix to report (as far as possible) the ACL
result replacing the original.
+JH/23 Fix memory leak during multi-message connections using STARTTLS under
+ OpenSSL. Certificate information is loaded for every new TLS startup,
+ and the resources needed to be freed.
+
Exim version 4.90
-----------------
the data structures if necessary. */
#ifdef SUPPORT_TLS
- tls_close(TRUE, FALSE);
+ tls_close(TRUE, TLS_NO_SHUTDOWN);
#endif
/* Reset SIGHUP and SIGCHLD in the child in both cases. */
if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
{
#ifdef SUPPORT_TLS
- tls_close(FALSE, FALSE);
+ tls_close(FALSE, TLS_NO_SHUTDOWN);
#endif
(void) close(cutthrough.fd);
release_cutthrough_connection(US"passed to transport proc");
if (smtp_input)
{
#ifdef SUPPORT_TLS
- tls_close(TRUE, FALSE); /* Shut down the TLS library */
+ tls_close(TRUE, TLS_NO_SHUTDOWN); /* Shut down the TLS library */
#endif
(void)close(fileno(smtp_in));
(void)close(fileno(smtp_out));
dns_answer *,
# endif
uschar **);
-extern void tls_close(BOOL, BOOL);
+extern void tls_close(BOOL, int);
extern BOOL tls_could_read(void);
extern int tls_export_cert(uschar *, size_t, void *);
extern int tls_feof(void);
#define UTF8_VERT_2DASH "\xE2\x95\x8E"
+/* Options on tls_close */
+#define TLS_NO_SHUTDOWN 0
+#define TLS_SHUTDOWN_NOWAIT 1
+#define TLS_SHUTDOWN_WAIT 2
+
/* End of macros.h */
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
#ifdef SUPPORT_TLS
-tls_close(TRUE, TRUE);
+tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
#endif
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
smtp_printf("554 Security failure\r\n", FALSE);
break;
}
- tls_close(TRUE, TRUE);
+ tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
break;
#endif
daemon, to shut down the TLS library, without actually doing a shutdown (which
would tamper with the TLS session in the parent process).
-Arguments: TRUE if gnutls_bye is to be called
+Arguments:
+ shutdown 1 if TLS close-alert is to be sent,
+ 2 if also response to be waited for
+
Returns: nothing
*/
void
-tls_close(BOOL is_server, BOOL shutdown)
+tls_close(BOOL is_server, int shutdown)
{
exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
if (shutdown)
{
- DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS\n");
- gnutls_bye(state->session, GNUTLS_SHUT_WR);
+ DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
+ shutdown > 1 ? " (with response-wait)" : "");
+
+ alarm(2);
+ gnutls_bye(state->session, shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
+ alarm(0);
}
gnutls_deinit(state->session);
uschar *certificate;
uschar *privatekey;
BOOL is_server;
+ STACK_OF(X509_NAME) * acceptable_certnames;
#ifndef DISABLE_OCSP
STACK_OF(X509) *verify_stack; /* chain for verifying the proof */
union {
cbinfo->certificate = certificate;
cbinfo->privatekey = privatekey;
cbinfo->is_server = host==NULL;
+cbinfo->acceptable_certnames = NULL;
#ifndef DISABLE_OCSP
cbinfo->verify_stack = NULL;
if (!host)
BIO * bp;
X509 * x;
+while (sk_X509_num(verify_stack) > 0)
+ X509_free(sk_X509_pop(verify_stack));
+
if (!(bp = BIO_new_file(CS file, "r"))) return FALSE;
while ((x = PEM_read_bio_X509(bp, NULL, 0, NULL)))
sk_X509_push(verify_stack, x);
-/* Called by both client and server startup
+/* Called by both client and server startup; on the server possibly
+repeated after a Server Name Indication.
Arguments:
sctx SSL_CTX* to initialise
{ file = NULL; dir = expcerts; }
else
{
+ /*XXX somewhere down here we leak memory per-STARTTLS, on a multi-message conn, server-side */
file = expcerts; dir = NULL;
#ifndef DISABLE_OCSP
/* In the server if we will be offering an OCSP proof, load chain from
*/
if (file)
{
- STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
+ tls_ext_ctx_cb * cbinfo = host
+ ? client_static_cbinfo : server_static_cbinfo;
+ STACK_OF(X509_NAME) * names;
+ if ((names = cbinfo->acceptable_certnames))
+ {
+ sk_X509_NAME_pop_free(names, X509_NAME_free);
+ cbinfo->acceptable_certnames = NULL;
+ }
+ names = SSL_load_client_CA_file(CS file);
+
+ SSL_CTX_set_client_CA_list(sctx, names);
DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
sk_X509_NAME_num(names));
- SSL_CTX_set_client_CA_list(sctx, names);
+ cbinfo->acceptable_certnames = names;
}
}
}
receive_ferror = smtp_ferror;
receive_smtp_buffered = smtp_buffered;
+ if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+ SSL_shutdown(server_ssl);
+
+ sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
+ sk_X509_NAME_pop_free(server_static_cbinfo->acceptable_certnames, X509_NAME_free);
SSL_free(server_ssl);
+ SSL_CTX_free(server_ctx);
+ server_static_cbinfo->verify_stack = NULL;
+ server_static_cbinfo->acceptable_certnames = NULL;
+ server_ctx = NULL;
server_ssl = NULL;
tls_in.active = -1;
tls_in.bits = 0;
daemon, to shut down the TLS library, without actually doing a shutdown (which
would tamper with the SSL session in the parent process).
-Arguments: TRUE if SSL_shutdown is to be called
+Arguments:
+ shutdown 1 if TLS close-alert is to be sent,
+ 2 if also response to be waited for
+
Returns: nothing
Used by both server-side and client-side TLS.
*/
void
-tls_close(BOOL is_server, BOOL shutdown)
+tls_close(BOOL is_server, int shutdown)
{
+SSL_CTX **ctxp = is_server ? &server_ctx : &client_ctx;
SSL **sslp = is_server ? &server_ssl : &client_ssl;
int *fdp = is_server ? &tls_in.active : &tls_out.active;
if (shutdown)
{
- DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n");
- SSL_shutdown(*sslp);
+ int rc;
+ DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
+ shutdown > 1 ? " (with response-wait)" : "");
+
+ if ( (rc = SSL_shutdown(*sslp)) == 0 /* send "close notify" alert */
+ && shutdown > 1)
+ {
+ alarm(2);
+ rc = SSL_shutdown(*sslp); /* wait for response */
+ alarm(0);
+ }
+
+ if (rc < 0) DEBUG(D_tls)
+ {
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ debug_printf("SSL_shutdown: %s\n", ssl_errstring);
+ }
+ }
+
+if (is_server)
+ {
+ sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
+ sk_X509_NAME_pop_free(server_static_cbinfo->acceptable_certnames,
+ X509_NAME_free);
+ server_static_cbinfo->verify_stack = NULL;
+ server_static_cbinfo->acceptable_certnames = NULL;
}
+SSL_CTX_free(*ctxp);
SSL_free(*sslp);
+*ctxp = NULL;
*sslp = NULL;
-
*fdp = -1;
}
(void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
#ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
/* Close the socket, and return the appropriate value, first setting
if ((rc = read(pfd[0], buf, bsize)) <= 0)
{
fd_bits = 0;
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
}
else
{
a new EHLO. If we don't get a good response, we don't attempt to pass
the socket on. */
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_WAIT);
smtp_peer_options = smtp_peer_options_wrap;
sx.ok = !sx.smtps
&& smtp_write_command(&sx.outblock, SCMD_FLUSH,
close(pfd[0]);
/* tidy the inter-proc to disconn the proxy proc */
waitpid(pid, NULL, 0);
- tls_close(FALSE, FALSE);
+ tls_close(FALSE, TLS_NO_SHUTDOWN);
(void)close(sx.inblock.sock);
continue_transport = NULL;
continue_hostname = NULL;
END_OFF:
#ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
/* Close the socket, and return the appropriate value, first setting
if (tls_out.active == fd)
{
(void) tls_write(FALSE, US"QUIT\r\n", 6, FALSE);
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
}
else
#else
debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
random_local_part = NULL;
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
(void)close(sx.inblock.sock);
if (sx.inblock.sock >= 0)
{
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
(void)close(sx.inblock.sock);
cutthrough_response(fd, '2', NULL, 1);
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
(void)close(fd);