diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2020-10-13 17:12:33 +0100 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2020-10-13 17:12:33 +0100 |
commit | 48e909900663856b9b1225f5df4cd033302f1bcd (patch) | |
tree | 6c2983a932c605e8dc885061b1955bd85ce58b86 /src | |
parent | 6bacc7090a4d219a48899c5b5b08b33f4811dc7f (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.c | 33 | ||||
-rw-r--r-- | src/src/tls-openssl.c | 24 | ||||
-rw-r--r-- | src/src/tls.c | 45 |
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 } |