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 fff308912396b6bd317a35ec9bec83decace0c9d..5aa2f92d0d42ce7008278ab2837eb346553737a8 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
 -------------------------------------------
 
 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/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
 
 
 A note about Exim versions 4.44 and 4.50
index fc613073cdf91bc636d2af0f942db3dd131ed5ce..827c5d1dce664164bab87beb954e99829498907b 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
 --------------------
 
 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.
 
 
 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
 ------------
 
 Version 4.50
 ------------
 
index 0bc76a1ab5b5a1aeda20dde047ccee3f3c309e15..093a0adfceee96ae18e534ef0f23bf1cd04e751e 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
 
 
 EXIM ACKNOWLEDGEMENTS
 
@@ -20,7 +20,7 @@ relatively small patches.
 Philip Hazel
 
 Lists created: 20 November 2002
 Philip Hazel
 
 Lists created: 20 November 2002
-Last updated:  04 January 2005
+Last updated:  08 March 2005
 
 
 THE OLD LIST
 
 
 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
                           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
 Andy Mell                 Fix for rejectlog regeneration bug
 Marc Merlin               Many suggestions and patches for callouts and
                             SMTP error message features
index 8abad1b62e2a84346ae0070334a96bf756248318..b83ca2ab48f1b55e786d1246243ca9e8aebb8b0a 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
 
 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.
 
 
 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
 ------------
 
 Version 4.50
 ------------
 
index c81484c5b6c5defb38ca770cda5ae862ef9c99bf..944fb076296d76853b7d4645e91e9827b3acc870 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    *
 
 /*************************************************
 *     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 UNKNOWN_NAME "unknown"
 #define DH_BITS      768
 #define RSA_BITS     512
+#define PARAM_SIZE 2*1024
+
 
 /* Values for verify_requirment and initialized */
 
 
 /* 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        *
 *************************************************/
 /*************************************************
 *          Setup up RSA and DH parameters        *
 *************************************************/
@@ -290,8 +255,9 @@ Returns:     OK/DEFER/FAIL
 static int
 init_rsa_dh(host_item *host)
 {
 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 */
 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);
 
       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);
 
 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);
 
   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 */
 
       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);
 
   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 (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);
 
     return tls_error(US"TLS cache write failed", host, 0);
 
+  free(m.data);
   (void)close(fd);
 
   if (rename(CS tempfilename, CS filename) < 0)
   (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");
   }
 
   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;
 }
 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);
 /* 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;
 
 DEBUG(D_tls) debug_printf("initialized certificate stuff\n");
 return OK;