Ensure OpenSSL entropy state reset across forks.
[exim.git] / src / src / tls-openssl.c
index a8a62fe8cbbc2ad7e263abe9342f7a9c585af0ee..18cb787a5a6addd64540a954b3e40cee255da27c 100644 (file)
@@ -46,13 +46,31 @@ static BOOL client_verify_callback_called = FALSE;
 static BOOL server_verify_callback_called = FALSE;
 static const uschar *sid_ctx = US"exim";
 
+/* We have three different contexts to care about.
+
+Simple case: client, `client_ctx`
+ As a client, we can be doing a callout or cut-through delivery while receiving
+ a message.  So we have a client context, which should have options initialised
+ from the SMTP Transport.
+
+Server:
+ There are two cases: with and without ServerNameIndication from the client.
+ Given TLS SNI, we can be using different keys, certs and various other
+ configuration settings, because they're re-expanded with $tls_sni set.  This
+ allows vhosting with TLS.  This SNI is sent in the handshake.
+ A client might not send SNI, so we need a fallback, and an initial setup too.
+ So as a server, we start out using `server_ctx`.
+ If SNI is sent by the client, then we as server, mid-negotiation, try to clone
+ `server_sni` from `server_ctx` and then initialise settings by re-expanding
+ configuration.
+*/
+
 static SSL_CTX *client_ctx = NULL;
 static SSL_CTX *server_ctx = NULL;
 static SSL     *client_ssl = NULL;
 static SSL     *server_ssl = NULL;
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
-static SSL_CTX *client_sni = NULL;
 static SSL_CTX *server_sni = NULL;
 #endif
 
@@ -671,7 +689,7 @@ if (cbinfo->server_cipher_list)
 if (cbinfo->ocsp_file)
   {
   SSL_CTX_set_tlsext_status_cb(server_sni, tls_stapling_cb);
-  SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
+  SSL_CTX_set_tlsext_status_arg(server_sni, cbinfo);
   }
 #endif
 
@@ -726,7 +744,7 @@ response_der_len = i2d_OCSP_RESPONSE(cbinfo->ocsp_response, &response_der);
 if (response_der_len <= 0)
   return SSL_TLSEXT_ERR_NOACK;
 
-SSL_set_tlsext_status_ocsp_resp(ssl, response_der, response_der_len);
+SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len);
 return SSL_TLSEXT_ERR_OK;
 }
 
@@ -770,6 +788,8 @@ cbinfo->certificate = certificate;
 cbinfo->privatekey = privatekey;
 #ifdef EXPERIMENTAL_OCSP
 cbinfo->ocsp_file = ocsp_file;
+cbinfo->ocsp_file_expanded = NULL;
+cbinfo->ocsp_response = NULL;
 #endif
 cbinfo->dhparam = dhparam;
 cbinfo->host = host;
@@ -870,8 +890,8 @@ if (host == NULL)
   callback is invoked. */
   if (cbinfo->ocsp_file)
     {
-    SSL_CTX_set_tlsext_status_cb(ctx, tls_stapling_cb);
-    SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
+    SSL_CTX_set_tlsext_status_cb(server_ctx, tls_stapling_cb);
+    SSL_CTX_set_tlsext_status_arg(server_ctx, cbinfo);
     }
 #endif
   /* We always do this, so that $tls_sni is available even if not used in
@@ -988,7 +1008,7 @@ uschar *expcerts, *expcrl;
 if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
   return DEFER;
 
-if (expcerts != NULL)
+if (expcerts != NULL && *expcerts != '\0')
   {
   struct stat statbuf;
   if (!SSL_CTX_set_default_verify_paths(sctx))
@@ -1338,7 +1358,11 @@ if (sni)
   {
   if (!expand_check(sni, US"tls_sni", &tls_out.sni))
     return FAIL;
-  if (!Ustrlen(tls_out.sni))
+  if (tls_out.sni == NULL)
+    {
+    DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
+    }
+  else if (!Ustrlen(tls_out.sni))
     tls_out.sni = NULL;
   else
     {
@@ -1348,7 +1372,7 @@ if (sni)
 #else
     DEBUG(D_tls)
       debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
-          tls_sni);
+          tls_out.sni);
 #endif
     }
   }
@@ -1729,12 +1753,26 @@ vaguely_random_number(int max)
 {
 unsigned int r;
 int i, needed_len;
+static pid_t pidlast = 0;
+pid_t pidnow;
 uschar *p;
 uschar smallbuf[sizeof(r)];
 
 if (max <= 1)
   return 0;
 
+pidnow = getpid();
+if (pidnow != pidlast)
+  {
+  /* Although OpenSSL documents that "OpenSSL makes sure that the PRNG state
+  is unique for each thread", this doesn't apparently apply across processes,
+  so our own warning from vaguely_random_number_fallback() applies here too.
+  Fix per PostgreSQL. */
+  if (pidlast != 0)
+    RAND_cleanup();
+  pidlast = pidnow;
+  }
+
 /* OpenSSL auto-seeds from /dev/random, etc, but this a double-check. */
 if (!RAND_status())
   {