]> git.netwichtig.de Git - user/henk/code/exim.git/blobdiff - src/src/tls-openssl.c
BUGFIX: forced-fail smtp option tls_sni would dereference NULL
[user/henk/code/exim.git] / src / src / tls-openssl.c
index eeab9c1303bbb27d1f88d7e5f2e50d7499e28ceb..17cc72133daf5e1afd9f2aed0678c59d37a52eb2 100644 (file)
@@ -46,7 +46,9 @@ static BOOL verify_callback_called = FALSE;
 static const uschar *sid_ctx = US"exim";
 
 static SSL_CTX *ctx = NULL;
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static SSL_CTX *ctx_sni = NULL;
+#endif
 static SSL *ssl = NULL;
 
 static char ssl_errstring[256];
@@ -273,60 +275,85 @@ DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s));
 /* If dhparam is set, expand it, and load up the parameters for DH encryption.
 
 Arguments:
-  dhparam   DH parameter file
+  dhparam   DH parameter file or fixed parameter identity string
   host      connected host, if client; NULL if server
 
 Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
 static BOOL
-init_dh(uschar *dhparam, host_item *host)
+init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host)
 {
-BOOL yield = TRUE;
 BIO *bio;
 DH *dh;
 uschar *dhexpanded;
+const char *pem;
 
 if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded))
   return FALSE;
 
-if (dhexpanded == NULL) return TRUE;
-
-if ((bio = BIO_new_file(CS dhexpanded, "r")) == NULL)
+if (dhexpanded == NULL || *dhexpanded == '\0')
   {
-  tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
-    host, (uschar *)strerror(errno));
-  yield = FALSE;
+  bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1);
   }
-else
+else if (dhexpanded[0] == '/')
   {
-  if ((dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)) == NULL)
+  bio = BIO_new_file(CS dhexpanded, "r");
+  if (bio == NULL)
     {
     tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
-      host, NULL);
-    yield = FALSE;
+          host, US strerror(errno));
+    return FALSE;
     }
-  else
+  }
+else
+  {
+  if (Ustrcmp(dhexpanded, "none") == 0)
     {
-    if ((8*DH_size(dh)) > tls_dh_max_bits)
-      {
-      DEBUG(D_tls)
-        debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d",
-            8*DH_size(dh), tls_dh_max_bits);
-      }
-    else
-      {
-      SSL_CTX_set_tmp_dh(ctx, dh);
-      DEBUG(D_tls)
-        debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n",
-          dhexpanded, 8*DH_size(dh));
-      }
-    DH_free(dh);
+    DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
+    return TRUE;
     }
+
+  pem = std_dh_prime_named(dhexpanded);
+  if (!pem)
+    {
+    tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
+        host, US strerror(errno));
+    return FALSE;
+    }
+  bio = BIO_new_mem_buf(CS pem, -1);
+  }
+
+dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+if (dh == NULL)
+  {
   BIO_free(bio);
+  tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
+      host, NULL);
+  return FALSE;
+  }
+
+/* Even if it is larger, we silently return success rather than cause things
+ * to fail out, so that a too-large DH will not knock out all TLS; it's a
+ * debatable choice. */
+if ((8*DH_size(dh)) > tls_dh_max_bits)
+  {
+  DEBUG(D_tls)
+    debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d",
+        8*DH_size(dh), tls_dh_max_bits);
+  }
+else
+  {
+  SSL_CTX_set_tmp_dh(sctx, dh);
+  DEBUG(D_tls)
+    debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+      dhexpanded ? dhexpanded : US"default", 8*DH_size(dh));
   }
 
-return yield;
+DH_free(dh);
+BIO_free(bio);
+
+return TRUE;
 }
 
 
@@ -617,6 +644,9 @@ OCSP information. */
 rc = tls_expand_session_files(ctx_sni, cbinfo);
 if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
 
+rc = init_dh(ctx_sni, cbinfo->dhparam, NULL);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
 SSL_set_SSL_CTX(s, ctx_sni);
 
@@ -714,7 +744,13 @@ list of available digests. */
 EVP_add_digest(EVP_sha256());
 #endif
 
-/* Create a context */
+/* Create a context.
+The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
+negotiation in the different methods; as far as I can tell, the only
+*_{server,client}_method which allows negotiation is SSLv23, which exists even
+when OpenSSL is built without SSLv2 support.
+By disabling with openssl_options, we can let admins re-enable with the
+existing knob. */
 
 ctx = SSL_CTX_new((host == NULL)?
   SSLv23_server_method() : SSLv23_client_method());
@@ -777,7 +813,7 @@ else
 
 /* Initialize with DH parameters if supplied */
 
-if (!init_dh(dhparam, host)) return DEFER;
+if (!init_dh(ctx, dhparam, host)) return DEFER;
 
 /* Set up certificate and key (and perhaps OCSP info) */
 
@@ -1253,12 +1289,22 @@ if (sni)
   {
   if (!expand_check(sni, US"tls_sni", &tls_sni))
     return FAIL;
-  if (!Ustrlen(tls_sni))
+  if (tls_sni == NULL)
+    {
+    DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
+    }
+  else if (!Ustrlen(tls_sni))
     tls_sni = NULL;
   else
     {
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
     DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
     SSL_set_tlsext_host_name(ssl, tls_sni);
+#else
+    DEBUG(D_tls)
+      debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
+          tls_sni);
+#endif
     }
   }
 
@@ -1836,6 +1882,9 @@ BOOL adding, item_parsed;
 result = 0L;
 /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
  * from default because it increases BEAST susceptibility. */
+#ifdef SSL_OP_NO_SSLv2
+result |= SSL_OP_NO_SSLv2;
+#endif
 
 if (option_spec == NULL)
   {