TLS: pre-generate and load server selfsigned cert, when one is to be used
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 13 Oct 2020 16:12:33 +0000 (17:12 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 13 Oct 2020 16:12:33 +0000 (17:12 +0100)
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/tls.c
test/confs/2120
test/log/2020
test/log/2120
test/scripts/2000-GnuTLS/2020
test/scripts/2100-OpenSSL/2120

index 9b684e3cd64446d3990146cdd96957c07903f605..b14bca4831e0f43200ae533d4558ba735b2cd436 100644 (file)
@@ -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");
index 700b01d923b9b754d40bce600f7e618a0b41affe..0dfd8e01a321d56c234b884be3f390099d8d059c 100644 (file)
@@ -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;
 }
 
 
index 38d695d200f19584b68bdced93feef8d9c6f1cdc..c088c7d85ff25214c21976d91d28915695b3ff76 100644 (file)
@@ -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
 }
index 9469cb7386d6c119ca2e9cf1358e64477bdbc5cc..ea9111c67a074c5acbe9a08667052adc67989139 100644 (file)
@@ -13,7 +13,6 @@ acl_smtp_rcpt = accept
 log_selector = +tls_peerdn
 
 queue_only
-queue_run_in_order
 
 tls_advertise_hosts = *
 
index fbf9039a2645db25041028d70491964b7b4fd069..1d45378a5f642acfa251eb8ecd1b8e124c4ed782 100644 (file)
@@ -1,11 +1,16 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@thishost.test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp
 1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@thishost.test.ex R=abc T=t1 H=thishost.test.ex [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no DN="C=UK,O=Exim Developers,CN=thishost.test.ex" C="250 OK id=10HmaY-0005vi-00"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
-1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@thishost.test.ex R=abc T=t1 H=thishost.test.ex [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no DN="C=UK,O=Exim Developers,CN=thishost.test.ex" C="250 OK id=10HmaZ-0005vi-00"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
 
 ******** SERVER ********
 1999-03-02 09:44:33 Warning: No server certificate defined; will use a selfsigned one.
  Suggested action: either install a certificate or change tls_advertise_hosts option
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@thishost.test.ex H=localhost (thishost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@thishost.test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@thishost.test.ex H=localhost (thishost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@thishost.test.ex
index 8057655d7de274e4615208d1a4a2250f4b9d65dc..d4a754d288dafa51d16834af1105888618f6c2a2 100644 (file)
@@ -1,12 +1,18 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@thishost.test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp
 1999-03-02 09:44:33 10HmaX-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=Exim Developers/CN=thishost.test.ex
 1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@thishost.test.ex R=abc T=t1 H=thishost.test.ex [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no DN="/C=UK/O=Exim Developers/CN=thishost.test.ex" C="250 OK id=10HmaY-0005vi-00"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
-1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 [127.0.0.1] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=Exim Developers/CN=thishost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@thishost.test.ex R=abc T=t1 H=thishost.test.ex [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no DN="/C=UK/O=Exim Developers/CN=thishost.test.ex" C="250 OK id=10HmaZ-0005vi-00"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
 
 ******** SERVER ********
 1999-03-02 09:44:33 Warning: No server certificate defined; will use a selfsigned one.
  Suggested action: either install a certificate or change tls_advertise_hosts option
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@thishost.test.ex H=localhost (thishost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@thishost.test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@thishost.test.ex H=localhost (thishost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@thishost.test.ex
index 3203ab4ea870ac08e4a4329e82d69f31bdd24a56..28835a5750d22972a34c145d14801ac5468dd024 100644 (file)
@@ -5,7 +5,10 @@ exim -DSERVER=server -bd -oX PORT_D
 exim userx
 Test
 ****
-exim -qf
+exim -q
+****
+sleep 4
+exim -q
 ****
 killdaemon
 no_msglog_check
index 53cd8133324ec0c68a45404410e05dbb72d60502..e4f2c825f1924ea3eeee12211ef354a6ee43853d 100644 (file)
@@ -4,7 +4,10 @@ exim -DSERVER=server -bd -oX PORT_D
 exim userx
 Test
 ****
-exim -qf
+exim -q
+****
+sleep 4
+exim -q
 ****
 killdaemon
 no_msglog_check