From b5aea5e16720f8b17bcbbf54af966ba034432db9 Mon Sep 17 00:00:00 2001 From: Philip Hazel Date: Tue, 8 Mar 2005 11:38:21 +0000 Subject: [PATCH 1/1] Installed a modified version of Nikos Mavrogiannopoulos' patch that 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 | 9 ++- doc/doc-txt/NewStuff | 33 +++++++- src/ACKNOWLEDGMENTS | 7 +- src/README.UPDATING | 16 +++- src/src/tls-gnu.c | 175 +++++++++++++++++++++--------------------- 5 files changed, 147 insertions(+), 93 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index fff308912..5aa2f92d0 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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 diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index fc613073c..827c5d1dc 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -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 ------------ diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index 0bc76a1ab..093a0adfc 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -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 diff --git a/src/README.UPDATING b/src/README.UPDATING index 8abad1b62..b83ca2ab4 100644 --- a/src/README.UPDATING +++ b/src/README.UPDATING @@ -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 ------------ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index c81484c5b..944fb0762 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -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; -- 2.30.2