I've seen this problem to be open quite long time, and I believe it occurs
because exim tries to generate Diffie Hellman parameters on the fly when they
don't exist. This situation may occur when the gnutls-params file is missing.
I propose some solutions.
1. Return an error if the gnutls-params file does not exist. (sol1.patch)
2. Generate the parameters in a non-blocking way using /dev/urandom.
(sol2.patch)
3. Read static parameters if the file does not exist.
I believe the third solution is the most elegant. Generating these parameters
on the fly (sol2) even if /dev/urandom is used is time consuming and not
really appropriate for a server. The idea is to have them pregenerated.
Using static parameters (sol3) does not harm in any way.
If somebody wants different ones he can generate them.
So the
--- tls-gnu.c.org 2007-10-21 23:51:28.000000000 +0300
+++ tls-gnu.c 2007-10-21 23:56:04.000000000 +0300
@@ -362,54 +362,7 @@
case. */
if (ret < 0)
- {
- uschar tempfilename[sizeof(filename) + 10];
-
- DEBUG(D_tls) debug_printf("generating %d bit Diffie-Hellman key...\n",
- DH_BITS);
- ret = gnutls_dh_params_generate2(dh_params, DH_BITS);
- if (ret < 0) return tls_error(US"D-H key generation", host, ret);
-
- /* Write the parameters to a file in the spool directory so that we
- can use them from other Exim processes. */
-
- sprintf(CS tempfilename, "%s-%d", filename, (int)getpid());
- fd = Uopen(tempfilename, O_WRONLY|O_CREAT, 0400);
- if (fd < 0)
- return tls_error(string_open_failed(errno, "%s for writing", filename),
- host, 0);
- (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */
-
- /* export the parameters in a format that can be generated using GNUTLS'
- * certtool or other programs.
- *
- * The commands for certtool are:
- * $ certtool --generate-dh-params --bits 1024 > params
- */
-
- m.size = PARAM_SIZE;
- m.data = malloc(m.size);
- if (m.data == NULL)
- return tls_error(US"memory allocation failed", host, 0);
-
- m.size = PARAM_SIZE;
- ret = gnutls_dh_params_export_pkcs3(dh_params, GNUTLS_X509_FMT_PEM, m.data,
- &m.size);
- if (ret < 0) return tls_error(US"DH params export", host, ret);
-
- m.size = Ustrlen(m.data);
- if (write(fd, m.data, m.size) != m.size || write(fd, "\n", 1) != 1)
- return tls_error(US"TLS cache write failed", host, 0);
-
- free(m.data);
- (void)close(fd);
-
- if (rename(CS tempfilename, CS filename) < 0)
- return tls_error(string_sprintf("failed to rename %s as %s: %s",
- tempfilename, filename, strerror(errno)), host, 0);
-
- DEBUG(D_tls) debug_printf("wrote D-H parameters to file %s\n", filename);
- }
+ return tls_error(US"DH parameters file \"%s\" was not found. It must be present to use TLS.", filename);
DEBUG(D_tls) debug_printf("initialized D-H parameters\n");
return OK;
--- tls-gnu.c.org 2007-10-21 23:51:28.000000000 +0300
+++ tls-gnu.c 2007-10-21 23:58:12.000000000 +0300
@@ -17,6 +17,7 @@
/* Heading stuff for GnuTLS */
+#include <gcrypt.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
@@ -444,6 +445,10 @@
initialized = (host == NULL)? INITIALIZED_SERVER : INITIALIZED_CLIENT;
+/* To use /dev/urandom even for key generation
+ */
+gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+
rc = gnutls_global_init();
if (rc < 0) return tls_error(US"tls-init", host, rc);
--- tls-gnu.c.org 2007-10-21 23:51:28.000000000 +0300
+++ tls-gnu.c 2007-10-22 00:10:00.000000000 +0300
@@ -279,6 +279,13 @@
* Setup up DH parameters *
*************************************************/
+const static char pkcs3[] =
+"-----BEGIN DH PARAMETERS-----\n"
+"MIGGAoGA7q8Kua2zjdacM/gK+o/F6GByYYd1/zwLnqIxTJwlZXbWdN90luqB0zg7\n"
+"SBPWksbg4NXY4lC5i+SOSVwdYIna0V3H17RhVNa2zo70rWmxXUmCVZspe88YhcUp\n"
+"9WZmDlfsaO28PAVybMAv1Mv0l26qmv1ROP6DdkNbn8YdL8DrBuMCAQI=\n"
+"-----END DH PARAMETERS-----\n";
+
/* Generating the D-H parameters may take a long time. They only need to
be re-generated every so often, depending on security policy. What we do is to
keep these parameters in a file in the spool directory. If the file does not
@@ -294,7 +301,6 @@
Returns: OK/DEFER/FAIL
*/
-
static int
init_dh(host_item *host)
{
@@ -363,52 +369,10 @@
if (ret < 0)
{
- uschar tempfilename[sizeof(filename) + 10];
-
- DEBUG(D_tls) debug_printf("generating %d bit Diffie-Hellman key...\n",
- DH_BITS);
- ret = gnutls_dh_params_generate2(dh_params, DH_BITS);
- if (ret < 0) return tls_error(US"D-H key generation", host, ret);
-
- /* Write the parameters to a file in the spool directory so that we
- can use them from other Exim processes. */
-
- sprintf(CS tempfilename, "%s-%d", filename, (int)getpid());
- fd = Uopen(tempfilename, O_WRONLY|O_CREAT, 0400);
- if (fd < 0)
- return tls_error(string_open_failed(errno, "%s for writing", filename),
- host, 0);
- (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */
-
- /* export the parameters in a format that can be generated using GNUTLS'
- * certtool or other programs.
- *
- * The commands for certtool are:
- * $ certtool --generate-dh-params --bits 1024 > params
- */
-
- m.size = PARAM_SIZE;
- m.data = malloc(m.size);
- if (m.data == NULL)
- return tls_error(US"memory allocation failed", host, 0);
+ gnutls_datum dparams = { pkcs3, sizeof (pkcs3) };
- m.size = PARAM_SIZE;
- ret = gnutls_dh_params_export_pkcs3(dh_params, GNUTLS_X509_FMT_PEM, m.data,
- &m.size);
- if (ret < 0) return tls_error(US"DH params export", host, ret);
-
- m.size = Ustrlen(m.data);
- if (write(fd, m.data, m.size) != m.size || write(fd, "\n", 1) != 1)
- return tls_error(US"TLS cache write failed", host, 0);
-
- free(m.data);
- (void)close(fd);
-
- if (rename(CS tempfilename, CS filename) < 0)
- return tls_error(string_sprintf("failed to rename %s as %s: %s",
- tempfilename, filename, strerror(errno)), host, 0);
-
- DEBUG(D_tls) debug_printf("wrote D-H parameters to file %s\n", filename);
+ ret = gnutls_dh_params_import_pkcs3(dh_params, &dparams, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) return tls_error(US"DH params import", host, ret);
}
DEBUG(D_tls) debug_printf("initialized D-H parameters\n");