# ifdef SUPPORT_SRV_OCSP_STACK
builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
# endif
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
builtin_macro_create(US"_HAVE_TLS_CA_CACHE");
# endif
}
#endif
+/*************************************************
+* Handle TLS error *
+*************************************************/
+
+/* Called from lots of places when errors occur before actually starting to do
+the TLS handshake, that is, while the session is still in clear. Always returns
+DEFER for a server and FAIL for a client so that most calls can use "return
+tls_error(...)" to do this processing and then give an appropriate return. A
+single function is used for both server and client, because it is called from
+some shared functions.
+
+Argument:
+ prefix text to include in the logged error
+ msg additional error string (may be NULL)
+ usually obtained from gnutls_strerror()
+ host NULL if setting up a server;
+ the connected host if setting up a client
+ errstr pointer to returned error string
+
+Returns: OK/DEFER/FAIL
+*/
+
+static int
+tls_error(const uschar *prefix, const uschar *msg, const host_item *host,
+ uschar ** errstr)
+{
+if (errstr)
+ *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : US"");
+return host ? FAIL : DEFER;
+}
+
+
+static int
+tls_error_gnu(const uschar *prefix, int err, const host_item *host,
+ uschar ** errstr)
+{
+return tls_error(prefix, US gnutls_strerror(err), host, errstr);
+}
+
+static int
+tls_error_sys(const uschar *prefix, int err, const host_item *host,
+ uschar ** errstr)
+{
+return tls_error(prefix, US strerror(err), host, errstr);
+}
+
+
/* ------------------------------------------------------------------------ */
/* Initialisation */
#endif
-static void
-tls_g_init(void)
+static int
+tls_g_init(uschar ** errstr)
{
+int rc;
DEBUG(D_tls) debug_printf("GnuTLS global init required\n");
#if defined(HAVE_GNUTLS_PKCS11) && !defined(GNUTLS_AUTO_PKCS11_MANUAL)
if (!gnutls_allow_auto_pkcs11)
if ((rc = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL)))
- return tls_error_gnu(US"gnutls_pkcs11_init", rc, host, errstr);
+ return tls_error_gnu(US"gnutls_pkcs11_init", rc, NULL, errstr);
#endif
#ifndef GNUTLS_AUTO_GLOBAL_INIT
if ((rc = gnutls_global_init()))
- return tls_error_gnu(US"gnutls_global_init", rc, host, errstr);
+ return tls_error_gnu(US"gnutls_global_init", rc, NULL, errstr);
#endif
#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
#endif
exim_gnutls_base_init_done = TRUE;
+return OK;
}
static void
tls_per_lib_daemon_init(void)
{
+uschar * dummy_errstr;
static BOOL once = FALSE;
if (!exim_gnutls_base_init_done)
- tls_g_init();
+ tls_g_init(&dummy_errstr);
if (!once)
{
}
/* ------------------------------------------------------------------------ */
-/* Static functions */
-
-/*************************************************
-* Handle TLS error *
-*************************************************/
-
-/* Called from lots of places when errors occur before actually starting to do
-the TLS handshake, that is, while the session is still in clear. Always returns
-DEFER for a server and FAIL for a client so that most calls can use "return
-tls_error(...)" to do this processing and then give an appropriate return. A
-single function is used for both server and client, because it is called from
-some shared functions.
-
-Argument:
- prefix text to include in the logged error
- msg additional error string (may be NULL)
- usually obtained from gnutls_strerror()
- host NULL if setting up a server;
- the connected host if setting up a client
- errstr pointer to returned error string
-
-Returns: OK/DEFER/FAIL
-*/
-
-static int
-tls_error(const uschar *prefix, const uschar *msg, const host_item *host,
- uschar ** errstr)
-{
-if (errstr)
- *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : US"");
-return host ? FAIL : DEFER;
-}
-
-
-static int
-tls_error_gnu(const uschar *prefix, int err, const host_item *host,
- uschar ** errstr)
-{
-return tls_error(prefix, US gnutls_strerror(err), host, errstr);
-}
-
-static int
-tls_error_sys(const uschar *prefix, int err, const host_item *host,
- uschar ** errstr)
-{
-return tls_error(prefix, US strerror(err), host, errstr);
-}
-
/*************************************************
* Deal with logging errors during I/O *
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 */
-/* Create and install a selfsigned certificate, for use in server mode */
+/* Create and install a selfsigned certificate, for use in server mode. */
static int
tls_install_selfsign(exim_gnutls_state_st * state, uschar ** errstr)
if (TRUE) goto err;
#endif
+DEBUG(D_tls) debug_printf("TLS: generating selfsigned server cert\n");
where = US"initialising pkey";
if ((rc = gnutls_x509_privkey_init(&pkey))) goto err;
if ( (rc = gnutls_x509_crt_set_version(cert, 3))
|| (rc = gnutls_x509_crt_set_serial(cert, &now, sizeof(now)))
|| (rc = gnutls_x509_crt_set_activation_time(cert, now = time(NULL)))
- || (rc = gnutls_x509_crt_set_expiration_time(cert, now + 60 * 60)) /* 1 hr */
+ || (rc = gnutls_x509_crt_set_expiration_time(cert, (long)2 * 60 * 60)) /* 2 hour */
|| (rc = gnutls_x509_crt_set_key(cert, pkey))
|| (rc = gnutls_x509_crt_set_dn_by_oid(cert,
CCS p, errpos);
}
-static void
+static unsigned
tls_server_creds_init(void)
{
uschar * dummy_errstr;
+unsigned lifetime = 0;
state_server.lib_state = null_tls_preload;
if (gnutls_certificate_allocate_credentials(
(gnutls_certificate_credentials_t *) &state_server.lib_state.x509_cred))
{
state_server.lib_state.x509_cred = NULL;
- return;
+ return lifetime;
}
creds_basic_init(state_server.lib_state.x509_cred, TRUE);
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
/* If tls_certificate has any $ indicating expansions, it is not good.
If tls_privatekey is set but has $, not good. Likewise for tls_ocsp_file.
-If all good (and tls_certificate set), load the cert(s). Do not try
-to handle selfsign generation for now (tls_certificate null/empty;
-XXX will want to do that later though) due to the lifetime/expiry issue. */
+If all good (and tls_certificate set), load the cert(s). */
if ( opt_set_and_noexpand(tls_certificate)
# ifndef DISABLE_OCSP
state_server.lib_state.conn_certs = TRUE;
}
}
+else if ( !tls_certificate && !tls_privatekey
+# ifndef DISABLE_OCSP
+ && !tls_ocsp_file
+# endif
+ )
+ { /* Generate & preload a selfsigned cert. No files to watch. */
+ if ((tls_install_selfsign(&state_server, &dummy_errstr)) == OK)
+ {
+ state_server.lib_state.conn_certs = TRUE;
+ lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */
+ }
+ }
else
DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n");
DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n");
if (creds_load_cabundle(&state_server, tls_verify_certificates,
NULL, &dummy_errstr) != OK)
- return;
+ return lifetime;
state_server.lib_state.cabundle = TRUE;
/* If CAs loaded and tls_crl is non-empty and has no $, load it */
{
DEBUG(D_tls) debug_printf("TLS: preloading CRL for server\n");
if (creds_load_crl(&state_server, tls_crl, &dummy_errstr) != OK)
- return;
+ return lifetime;
state_server.lib_state.crl = TRUE;
}
}
}
else
DEBUG(D_tls) debug_printf("TLS: not preloading cipher list for server\n");
+return lifetime;
}
host_item * dummy_host = (host_item *)1;
uschar * dummy_errstr;
-if (!exim_gnutls_base_init_done)
- tls_g_init();
+if ( !exim_gnutls_base_init_done
+ && tls_g_init(&dummy_errstr) != OK)
+ return;
ob->tls_preload = null_tls_preload;
if (gnutls_certificate_allocate_credentials(
tpt_dummy_state.session = NULL;
tpt_dummy_state.lib_state = ob->tls_preload;
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
if ( opt_set_and_noexpand(ob->tls_certificate)
&& opt_unset_or_noexpand(ob->tls_privatekey))
{
}
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
/* Invalidate the creds cached, by dropping the current ones.
Call when we notice one of the source files has changed. */
int rc;
size_t sz;
-if (!exim_gnutls_base_init_done)
- tls_g_init();
+if ( !exim_gnutls_base_init_done
+ && (rc = tls_g_init(errstr)) != OK)
+ return rc;
if (host)
{
state->host = host;
/* This handles the variables that might get re-expanded after TLS SNI;
-that's tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */
+tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */
DEBUG(D_tls)
debug_printf("Expanding various TLS configuration options for session credentials\n");
+/*
+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 *
*************************************************/
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);
}
void
-tls_get_cache()
+tls_get_cache(void)
{
#ifndef DISABLE_DKIM
exim_gnutls_state_st * state = &state_server;
}
-
-
/*************************************************
* Read bytes from TLS channel *
*************************************************/