summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2020-10-13 17:12:33 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2020-10-13 17:12:33 +0100
commit48e909900663856b9b1225f5df4cd033302f1bcd (patch)
tree6c2983a932c605e8dc885061b1955bd85ce58b86 /src
parent6bacc7090a4d219a48899c5b5b08b33f4811dc7f (diff)
TLS: pre-generate and load server selfsigned cert, when one is to be used
Diffstat (limited to 'src')
-rw-r--r--src/src/tls-gnu.c33
-rw-r--r--src/src/tls-openssl.c24
-rw-r--r--src/src/tls.c45
3 files changed, 70 insertions, 32 deletions
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 9b684e3cd..b14bca483 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -929,7 +929,7 @@ return OK;
-/* 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)
@@ -946,6 +946,7 @@ rc = GNUTLS_E_NO_CERTIFICATE_FOUND;
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;
@@ -970,7 +971,7 @@ now = 1;
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,
@@ -1421,26 +1422,25 @@ return gnutls_priority_init( (gnutls_priority_t *) &state->lib_state.pri_cache,
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);
#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
@@ -1470,6 +1470,18 @@ if ( opt_set_and_noexpand(tls_certificate)
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");
@@ -1482,7 +1494,7 @@ if (opt_set_and_noexpand(tls_verify_certificates))
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 */
@@ -1493,7 +1505,7 @@ if (opt_set_and_noexpand(tls_verify_certificates))
{
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;
}
}
@@ -1520,6 +1532,7 @@ if (!tls_require_ciphers || opt_set_and_noexpand(tls_require_ciphers))
}
else
DEBUG(D_tls) debug_printf("TLS: not preloading cipher list for server\n");
+return lifetime;
}
@@ -1990,7 +2003,7 @@ state->tls_require_ciphers = require_ciphers;
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");
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 700b01d92..0dfd8e01a 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -794,6 +794,9 @@ return rsa_key;
/* Create and install a selfsigned certificate, for use in server mode */
+/*XXX we could arrange to call this during prelo for a null tls_certificate option.
+The normal cache inval + relo will suffice.
+Just need a timer for inval. */
static int
tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
@@ -804,6 +807,7 @@ RSA * rsa;
X509_NAME * name;
uschar * where;
+DEBUG(D_tls) debug_printf("TLS: generating selfsigned server cert\n");
where = US"allocating pkey";
if (!(pkey = EVP_PKEY_new()))
goto err;
@@ -823,7 +827,7 @@ if (!EVP_PKEY_assign_RSA(pkey, rsa))
X509_set_version(x509, 2); /* N+1 - version 3 */
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0);
-X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60); /* 1 hour */
+X509_gmtime_adj(X509_get_notAfter(x509), (long)2 * 60 * 60); /* 2 hour */
X509_set_pubkey(x509, pkey);
name = X509_get_subject_name(x509);
@@ -1619,18 +1623,19 @@ return OK;
}
-static void
+static unsigned
tls_server_creds_init(void)
{
SSL_CTX * ctx;
uschar * dummy_errstr;
+unsigned lifetime = 0;
tls_openssl_init();
state_server.lib_state = null_tls_preload;
if (lib_ctx_new(&ctx, NULL, &dummy_errstr) != OK)
- return;
+ return 0;
state_server.lib_state.lib_ctx = ctx;
/* Preload DH params and EC curve */
@@ -1677,6 +1682,18 @@ if ( opt_set_and_noexpand(tls_certificate)
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_expand_session_files(ctx, &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");
@@ -1717,6 +1734,7 @@ if (opt_set_and_noexpand(tls_require_ciphers))
}
else
DEBUG(D_tls) debug_printf("TLS: not preloading cipher list for server\n");
+return lifetime;
}
diff --git a/src/src/tls.c b/src/src/tls.c
index 38d695d20..c088c7d85 100644
--- a/src/src/tls.c
+++ b/src/src/tls.c
@@ -38,7 +38,7 @@ functions from the OpenSSL or GNU TLS libraries. */
static void tls_per_lib_daemon_init(void);
static void tls_per_lib_daemon_tick(void);
-static void tls_server_creds_init(void);
+static unsigned tls_server_creds_init(void);
static void tls_server_creds_invalidate(void);
static void tls_client_creds_init(transport_instance *, BOOL);
static void tls_client_creds_invalidate(transport_instance *);
@@ -82,6 +82,8 @@ static struct kevent kev[KEV_SIZE];
static int kev_used = 0;
#endif
+static unsigned tls_creds_expire = 0;
+
/*************************************************
* Expand string; give error on failure *
*************************************************/
@@ -291,20 +293,6 @@ struct timespec t = {0};
(void) kevent(fd, NULL, 0, &kev, 1, &t);
#endif
}
-
-/* Called, after a delay for multiple file ops to get done, from
-the daemon when any of the watches added (above) fire.
-
-Dump the set of watches and arrange to reload cached creds (which
-will set up new watches). */
-
-static void
-tls_watch_triggered(void)
-{
-DEBUG(D_tls) debug_printf("watch triggered\n");
-
-tls_daemon_creds_reload();
-}
#endif /*EXIM_HAVE_INOTIFY*/
@@ -343,12 +331,15 @@ tls_watch_fd = -1;
static void
tls_daemon_creds_reload(void)
{
+unsigned lifetime;
+
#ifdef EXIM_HAVE_KEVENT
tls_watch_invalidate();
#endif
tls_server_creds_invalidate();
-tls_server_creds_init();
+tls_creds_expire = (lifetime = tls_server_creds_init())
+ ? time(NULL) + lifetime : 0;
tls_client_creds_reload(TRUE);
}
@@ -372,10 +363,26 @@ tls_daemon_tick(void)
{
tls_per_lib_daemon_tick();
#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
-if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5)
+if (tls_creds_expire && time(NULL) >= tls_creds_expire)
+ {
+ /* The server cert is a selfsign, with limited lifetime. Dump it and
+ generate a new one. Reload the rest of the creds also as the machinery
+ is all there. */
+
+ DEBUG(D_tls) debug_printf("selfsign cert rotate\n");
+ tls_creds_expire = 0;
+ tls_daemon_creds_reload();
+ }
+else if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5)
{
- tls_watch_trigger_time = 0;
- tls_watch_triggered();
+ /* Called, after a delay for multiple file ops to get done, from
+ the daemon when any of the watches added (above) fire.
+ Dump the set of watches and arrange to reload cached creds (which
+ will set up new watches). */
+
+ DEBUG(D_tls) debug_printf("watch triggered\n");
+ tls_watch_trigger_time = tls_creds_expire = 0;
+ tls_daemon_creds_reload();
}
#endif
}