Installed a modified version of Nikos Mavrogiannopoulos' patch that
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 8 Mar 2005 11:38:21 +0000 (11:38 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Tue, 8 Mar 2005 11:38:21 +0000 (11:38 +0000)
stores GnuTLS parameters in a format that can be generated externally.
It is upwards, but not downwards, compatible (warning in
README.UPDATING).

doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/ACKNOWLEDGMENTS
src/README.UPDATING
src/src/tls-gnu.c

index fff3089..5aa2f92 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.85 2005/03/07 09:56:23 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.86 2005/03/08 11:38:21 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -10,6 +10,13 @@ Exim version 4.51
 PH/01. Installed a patch from the Sieve maintainer that allows -bf to be used
        to test Sieve filters that use "vacation".
 
+PH/02. Installed a slightly modified version of Nikos Mavrogiannopoulos' patch
+       that changes the way the GnuTLS parameters are stored in the cache file.
+       The new format can be generated externally. For backward compatibility,
+       if the data in the cache doesn't make sense, Exim assumes it has read an
+       old-format file, and it generates new data and writes a new file. This
+       means that you can't go back to an older release without removing the
+       file.
 
 
 A note about Exim versions 4.44 and 4.50
index fc61307..827c5d1 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.26 2005/02/17 12:24:00 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.27 2005/03/08 11:38:21 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -9,6 +9,37 @@ updated when there is a relatively large batch of changes). The doc/ChangeLog
 file contains a listing of all changes, including bug fixes.
 
 
+Version 4.51
+------------
+
+PH/01. The format in which GnuTLS parameters are written to the gnutls-param
+       file in the spool directory has been changed. This change has been made
+       to alleviate problems that some people had with the generation of the
+       parameters by Exim when /dev/random was exhausted. In this situation,
+       Exim would hang until /dev/random acquired some more entropy.
+
+       The new code exports and imports the DH and RSA parameters in PEM
+       format. This means that the parameters can be generated externally using
+       the certtool command that is part of GnuTLS.
+
+       To replace the parameters with new ones, instead of deleting the file
+       and letting Exim re-create it, you can generate new parameters using
+       certtool and, when this has been done, replace Exim's cache file by
+       renaming. The relevant commands are something like this:
+
+         # rm -f new.params
+         # touch new.params
+         # chown exim:exim new.params
+         # chmod 0400 new.params
+         # certtool --generate-privkey --bits 512 >new.params
+         # echo "" >>new.params
+         # certtool --generate-dh-params --bits 1024 >> new.params
+         # mv new.params params
+
+       If Exim never has to generate the parameters itself, the possibility of
+       stalling is removed.
+
+
 Version 4.50
 ------------
 
index 0bc76a1..093a0ad 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.13 2005/01/13 10:09:36 ph10 Exp $
+$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.14 2005/03/08 11:38:21 ph10 Exp $
 
 EXIM ACKNOWLEDGEMENTS
 
@@ -20,7 +20,7 @@ relatively small patches.
 Philip Hazel
 
 Lists created: 20 November 2002
-Last updated:  04 January 2005
+Last updated:  08 March 2005
 
 
 THE OLD LIST
@@ -167,7 +167,8 @@ Lionel Elie Mamane        Patch for IPv4/IPv6 listen() problem on USAGI Linux
                           Patch for callout caching bug
 Everton da Silva Marques  Suggested patch for SRV handling
                           Suggested patch for SRV/MX lookup retry option
-Nikos Mavroyanopoulos     GnuTLS proof of concept code
+Nikos Mavrogiannopoulos   GnuTLS proof of concept code
+                          Update to RSA and D-H parameter caching code
 Andy Mell                 Fix for rejectlog regeneration bug
 Marc Merlin               Many suggestions and patches for callouts and
                             SMTP error message features
index 8abad1b..b83ca2a 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/src/README.UPDATING,v 1.4 2005/01/17 09:35:16 ph10 Exp $
+$Cambridge: exim/src/README.UPDATING,v 1.5 2005/03/08 11:38:21 ph10 Exp $
 
 This document contains detailed information about incompatibilities that might
 be encountered when upgrading from one release of Exim to another. The
@@ -28,6 +28,20 @@ The rest of this document contains information about changes in 4.xx releases
 that might affect a running system.
 
 
+Version 4.51
+------------
+
+The format in which GnuTLS parameters are cached (in the file gnutls-params in
+the spool directory) has been changed. The new format can also be generated
+externally, so it is now possible to update the values from outside Exim. This
+has been implemented in an upwards, BUT NOT downwards, compatible manner.
+Upgrading should be seamless: when Exim finds that it cannot understand an
+existing cache file, it generates new parameters and writes them to the cache
+in the new format. If, however, you downgrade from 4.51 to a previous release,
+you MUST delete the gnutls-params file in the spool directory, because the
+older Exim will not recognize the new format.
+
+
 Version 4.50
 ------------
 
index c81484c..944fb07 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/tls-gnu.c,v 1.5 2005/02/17 11:58:26 ph10 Exp $ */
+/* $Cambridge: exim/src/src/tls-gnu.c,v 1.6 2005/03/08 11:38:21 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -24,6 +24,8 @@ functions from the GnuTLS library. */
 #define UNKNOWN_NAME "unknown"
 #define DH_BITS      768
 #define RSA_BITS     512
+#define PARAM_SIZE 2*1024
+
 
 /* Values for verify_requirment and initialized */
 
@@ -230,43 +232,6 @@ return TRUE;                            /* accept */
 
 
 
-
-/*************************************************
-*        Write/read datum to/from file           *
-*************************************************/
-
-/* These functions are used for saving and restoring the RSA and D-H parameters
-for use by all Exim processes. Data that is read is placed in malloc'd store
-because that's what happens for newly generated data.
-
-Arguments:
-  fd          the file descriptor
-  d           points to the datum
-
-returns:      FALSE on error (errno set)
-*/
-
-static BOOL
-write_datum(int fd, gnutls_datum *d)
-{
-if (write(fd, &(d->size), sizeof(d->size)) != sizeof(d->size)) return FALSE;
-if (write(fd, d->data, d->size) != d->size) return FALSE;
-return TRUE;
-}
-
-
-static BOOL
-read_datum(int fd, gnutls_datum *d)
-{
-if (read(fd, &(d->size), sizeof(d->size)) != sizeof(d->size)) return FALSE;
-d->data = malloc(d->size);
-if (d->data == NULL) return FALSE;
-if (read(fd, d->data, d->size) != d->size) return FALSE;
-return TRUE;
-}
-
-
-
 /*************************************************
 *          Setup up RSA and DH parameters        *
 *************************************************/
@@ -290,8 +255,9 @@ Returns:     OK/DEFER/FAIL
 static int
 init_rsa_dh(host_item *host)
 {
-int fd, ret;
-gnutls_datum m, e, d, p, q, u, prime, generator;
+int fd;
+int ret = -1;
+gnutls_datum m;
 uschar filename[200];
 
 /* Initialize the data structures for holding the parameters */
@@ -308,20 +274,63 @@ if (!string_format(filename, sizeof(filename), "%s/gnutls-params",
       spool_directory))
   return tls_error(US"overlong filename", host, 0);
 
-/* Open the cache file for reading. If this fails because of a non-existent
-file, compute a new set of parameters, write them to a temporary file, and then
-rename that file as the cache file. Other opening errors are bad. */
+/* Open the cache file for reading and if successful, read it and set up the
+parameters. If we can't set up the RSA parameters, assume that we are dealing
+with an old-style cache file that is in another format, and fall through to
+compute new values. However, if we correctly get RSA parameters, a failure to
+set up D-H parameters is treated as an error. */
 
 fd = Uopen(filename, O_RDONLY, 0);
-if (fd < 0)
+if (fd >= 0)
   {
-  unsigned int rsa_bits = RSA_BITS;
-  unsigned int dh_bits = DH_BITS;
-  uschar tempfilename[sizeof(filename) + 10];
+  struct stat statbuf;
+  if (fstat(fd, &statbuf) < 0)
+    {
+    (void)close(fd);
+    return tls_error(US"TLS cache stat failed", host, 0);
+    }
 
-  if (errno != ENOENT)
-    return tls_error(string_open_failed(errno, "%s for reading", filename),
-      host, 0);
+  m.size = statbuf.st_size;
+  m.data = malloc(m.size);
+  if (m.data == NULL)
+    return tls_error(US"memory allocation failed", host, 0);
+  if (read(fd, m.data, m.size) != m.size)
+    return tls_error(US"TLS cache read failed", host, 0);
+  (void)close(fd);
+
+  ret = gnutls_rsa_params_import_pkcs1(rsa_params, &m, GNUTLS_X509_FMT_PEM);
+  if (ret < 0)
+    {
+    DEBUG(D_tls)
+      debug_printf("RSA params import failed: assume old-style cache file\n");
+    }
+  else
+    {
+    ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
+    if (ret < 0)
+      return tls_error(US"DH params import", host, ret);
+    DEBUG(D_tls) debug_printf("read RSA and D-H parameters from file\n");
+    }
+
+  free(m.data);
+  }
+
+/* If the file does not exist, fall through to compute new data and cache it.
+If there was any other opening error, it is serious. */
+
+else if (errno != ENOENT)
+  return tls_error(string_open_failed(errno, "%s for reading", filename),
+    host, 0);
+
+/* If ret < 0, either the cache file does not exist, or the data it contains
+is not useful. One particular case of this is when upgrading from an older
+release of Exim in which the data was stored in a different format. We don't
+try to be clever and support both formats; we just regenerate new data in this
+case. */
+
+if (ret < 0)
+  {
+  uschar tempfilename[sizeof(filename) + 10];
 
   DEBUG(D_tls) debug_printf("generating %d bit RSA key...\n", RSA_BITS);
   ret = gnutls_rsa_params_generate2(rsa_params, RSA_BITS);
@@ -342,23 +351,40 @@ if (fd < 0)
       host, 0);
   (void)fchown(fd, exim_uid, exim_gid);   /* Probably not necessary */
 
-  ret = gnutls_rsa_params_export_raw(rsa_params, &m, &e, &d, &p, &q, &u,
-    &rsa_bits);
+  /* export the parameters in a format that can be generated using GNUTLS'
+   * certtool or other programs.
+   *
+   * The commands for certtool are:
+   * $ certtool --generate-privkey --bits 512 >params
+   * $ echo "" >>params
+   * $ 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);
+
+  ret = gnutls_rsa_params_export_pkcs1(rsa_params, GNUTLS_X509_FMT_PEM,
+    m.data, &m.size);
   if (ret < 0) return tls_error(US"RSA params export", host, ret);
 
-  ret = gnutls_dh_params_export_raw(dh_params, &prime, &generator, &dh_bits);
+  /* Do not write the null termination byte. */
+
+  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);
+
+  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);
 
-  if (!write_datum(fd, &m) ||
-      !write_datum(fd, &e) ||
-      !write_datum(fd, &d) ||
-      !write_datum(fd, &p) ||
-      !write_datum(fd, &q) ||
-      !write_datum(fd, &u) ||
-      !write_datum(fd, &prime) ||
-      !write_datum(fd, &generator))
+  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)
@@ -368,31 +394,6 @@ if (fd < 0)
   DEBUG(D_tls) debug_printf("wrote RSA and D-H parameters to file\n");
   }
 
-/* File opened for reading; get the data */
-
-else
-  {
-  if (!read_datum(fd, &m) ||
-      !read_datum(fd, &e) ||
-      !read_datum(fd, &d) ||
-      !read_datum(fd, &p) ||
-      !read_datum(fd, &q) ||
-      !read_datum(fd, &u) ||
-      !read_datum(fd, &prime) ||
-      !read_datum(fd, &generator))
-    return tls_error(US"TLS cache read failed", host, 0);
-
-  (void)close(fd);
-
-  ret = gnutls_rsa_params_import_raw(rsa_params, &m, &e, &d, &p, &q, &u);
-  if (ret < 0) return tls_error(US"RSA params import", host, ret);
-
-  ret = gnutls_dh_params_import_raw(dh_params, &prime, &generator);
-  if (ret < 0) return tls_error(US"DH params import", host, ret);
-
-  DEBUG(D_tls) debug_printf("read RSA and D-H parameters from file\n");
-  }
-
 DEBUG(D_tls) debug_printf("initialized RSA and D-H parameters\n");
 return OK;
 }
@@ -524,7 +525,7 @@ if (cas != NULL)
 /* Associate the parameters with the x509 credentials structure. */
 
 gnutls_certificate_set_dh_params(x509_cred, dh_params);
-gnutls_certificate_set_rsa_params(x509_cred, rsa_params);
+gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
 
 DEBUG(D_tls) debug_printf("initialized certificate stuff\n");
 return OK;