Lower EXIM_CLIENT_DH_MIN_BITS 1024 -> 512.
[exim.git] / src / src / tls-gnu.c
index 0ac72ad25b17e468e9833e061b2056ebbb31b5c8..db0e2115f570b2d5bd42218b7220da7deaa90c31 100644 (file)
@@ -152,7 +152,7 @@ callbacks. */
 #endif
 
 #ifndef EXIM_CLIENT_DH_MIN_BITS
-#define EXIM_CLIENT_DH_MIN_BITS 1024
+#define EXIM_CLIENT_DH_MIN_BITS 512
 #endif
 
 /* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we
@@ -358,9 +358,6 @@ file is never present. If two processes both compute some new parameters, you
 waste a bit of effort, but it doesn't seem worth messing around with locking to
 prevent this.
 
-Argument:
-  host       NULL for server, server for client (for error handling)
-
 Returns:     OK/DEFER/FAIL
 */
 
@@ -370,8 +367,12 @@ init_server_dh(void)
 int fd, rc;
 unsigned int dh_bits;
 gnutls_datum m;
-uschar filename[PATH_MAX];
+uschar filename_buf[PATH_MAX];
+uschar *filename = NULL;
 size_t sz;
+uschar *exp_tls_dhparam;
+BOOL use_file_in_spool = FALSE;
+BOOL use_fixed_file = FALSE;
 host_item *host = NULL; /* dummy for macros */
 
 DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n");
@@ -379,6 +380,46 @@ DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n");
 rc = gnutls_dh_params_init(&dh_server_params);
 exim_gnutls_err_check(US"gnutls_dh_params_init");
 
+m.data = NULL;
+m.size = 0;
+
+if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam))
+  return DEFER;
+
+if (!exp_tls_dhparam)
+  {
+  DEBUG(D_tls) debug_printf("Loading default hard-coded DH params\n");
+  m.data = US std_dh_prime_default();
+  m.size = Ustrlen(m.data);
+  }
+else if (Ustrcmp(exp_tls_dhparam, "historic") == 0)
+  use_file_in_spool = TRUE;
+else if (Ustrcmp(exp_tls_dhparam, "none") == 0)
+  {
+  DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
+  return OK;
+  }
+else if (exp_tls_dhparam[0] != '/')
+  {
+  m.data = US std_dh_prime_named(exp_tls_dhparam);
+  if (m.data == NULL)
+    return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL);
+  m.size = Ustrlen(m.data);
+  }
+else
+  {
+  use_fixed_file = TRUE;
+  filename = exp_tls_dhparam;
+  }
+
+if (m.data)
+  {
+  rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM);
+  exim_gnutls_err_check(US"gnutls_dh_params_import_pkcs3");
+  DEBUG(D_tls) debug_printf("Loaded fixed standard D-H parameters\n");
+  return OK;
+  }
+
 #ifdef HAVE_GNUTLS_SEC_PARAM_CONSTANTS
 /* If you change this constant, also change dh_param_fn_ext so that we can use a
 different filename and ensure we have sufficient bits. */
@@ -404,9 +445,13 @@ if (dh_bits > tls_dh_max_bits)
   dh_bits = tls_dh_max_bits;
   }
 
-if (!string_format(filename, sizeof(filename),
-      "%s/gnutls-params-%d", spool_directory, dh_bits))
-  return tls_error(US"overlong filename", NULL, NULL);
+if (use_file_in_spool)
+  {
+  if (!string_format(filename_buf, sizeof(filename_buf),
+        "%s/gnutls-params-%d", spool_directory, dh_bits))
+    return tls_error(US"overlong filename", NULL, NULL);
+  filename = filename_buf;
+  }
 
 /* Open the cache file for reading and if successful, read it and set up the
 parameters. */
@@ -483,6 +528,7 @@ case. */
 if (rc < 0)
   {
   uschar *temp_fn;
+  unsigned int dh_bits_gen = dh_bits;
 
   if ((PATH_MAX - Ustrlen(filename)) < 10)
     return tls_error(US"Filename too long to generate replacement",
@@ -494,8 +540,26 @@ if (rc < 0)
     return tls_error(US"Unable to open temp file", strerror(errno), NULL);
   (void)fchown(fd, exim_uid, exim_gid);   /* Probably not necessary */
 
-  DEBUG(D_tls) debug_printf("generating %d bits Diffie-Hellman key ...\n", dh_bits);
-  rc = gnutls_dh_params_generate2(dh_server_params, dh_bits);
+  /* GnuTLS overshoots!
+   * If we ask for 2236, we might get 2237 or more.
+   * But there's no way to ask GnuTLS how many bits there really are.
+   * We can ask how many bits were used in a TLS session, but that's it!
+   * The prime itself is hidden behind too much abstraction.
+   * So we ask for less, and proceed on a wing and a prayer.
+   * First attempt, subtracted 3 for 2233 and got 2240.
+   */
+  if (dh_bits >= EXIM_CLIENT_DH_MIN_BITS + 10)
+    {
+    dh_bits_gen = dh_bits - 10;
+    DEBUG(D_tls)
+      debug_printf("being paranoid about DH generation, make it '%d' bits'\n",
+          dh_bits_gen);
+    }
+
+  DEBUG(D_tls)
+    debug_printf("requesting generation of %d bit Diffie-Hellman prime ...\n",
+        dh_bits_gen);
+  rc = gnutls_dh_params_generate2(dh_server_params, dh_bits_gen);
   exim_gnutls_err_check(US"gnutls_dh_params_generate2");
 
   /* gnutls_dh_params_export_pkcs3() will tell us the exact size, every time,
@@ -514,12 +578,13 @@ if (rc < 0)
     return tls_error(US"memory allocation failed", strerror(errno), NULL);
   /* this will return a size 1 less than the allocation size above */
   rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM,
-      m.data, &m.size);
+      m.data, &sz);
   if (rc != GNUTLS_E_SUCCESS)
     {
     free(m.data);
     exim_gnutls_err_check(US"gnutls_dh_params_export_pkcs3() real");
     }
+  m.size = sz; /* shrink by 1, probably */
 
   sz = write_to_fd_buf(fd, m.data, (size_t) m.size);
   if (sz != m.size)
@@ -1166,7 +1231,7 @@ if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0)
     return FALSE;
     }
   DEBUG(D_tls)
-    debug_printf("TLS verify failure overriden (host in tls_try_verify_hosts)\n");
+    debug_printf("TLS verify failure overridden (host in tls_try_verify_hosts)\n");
   }
 else
   {