Hello,
We've recently started to delete gnutls-params file once a day as the
documentation suggests.
And quickly after that we've received <
http://bugs.debian.org/285371>.
This user has very little entropy, therefore it takes a long time to
generate gnutls-params, leaving the respective clients hanging after
STARTTLS.
I've asked on the gnutls-dev mailinglist about that[1] and Simon
Josefsson answered that while our setup was ok, it could be improved
by generating gnutls-params offline.
Due to the way exim works that would be easy to do, just replace
rm spooldirectory/gnutls-params
with
helperbinary_that_generates_spooldirectory/gnutls-params
And it is easy to build such a helperbinary, attached is a
simple[2] one that was basically copied and pasted from
src/tls-gnu.c.
What do people think about this, should I invest a little more time in
this trying to it up to exim standards or is this flawed?
thanks, cu andreas
[1] http://lists.gnupg.org/pipermail/gnutls-dev/2004-December/000800.html
[2] e.g. it does not try to find exim_uid and instead relies on being
run as the correct user.
cu andreas
--
"See, I told you they'd listen to Reason," [SPOILER] Svfurlr fnlf,
fuhggvat qbja gur juveyvat tha.
Neal Stephenson in "Snow Crash"
/*#include "exim.h"*/
/* Heading stuff for GnuTLS */
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
/* #include "mytypes.h" */
/* extracted from mytypes.h *
*/
typedef int BOOL;
typedef unsigned char uschar;
#define FALSE 0
#define TRUE 1
#define US (unsigned char *)
#define CS (char *)
#define CCS (const char *)
#define Ustrcmp(s,t) strcmp(CCS(s),CCS(t))
#ifdef O_BINARY /* This is for Cygwin, */
#define Uopen(s,n,m) open(CCS(s),(n)|O_BINARY,m) /* where all files must */
#else /* be opened as binary */
#define Uopen(s,n,m) open(CCS(s),n,m) /* to avoid problems */
#endif /* with CRLF endings. */
/*
extracted from mytypes.h */
#define UNKNOWN_NAME "unknown"
#define DH_BITS 768
#define RSA_BITS 512
/*************************************************
* Handle TLS error *
*************************************************/
/*
Argument:
prefix text to include in the logged error
err a GnuTLS error number, or 0 if local error
Returns: OK/DEFER/FAIL
*/
static int
gnutls_error(uschar *prefix, int err)
{
if (err==0)
fprintf(stderr,"%s",prefix);
else
fprintf(stderr,"%s %s",prefix,gnutls_strerror(err));
return 1;
}
/*************************************************
* 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;
}
/*************************************************
* Setup up RSA and DH parameters *
*************************************************/
/* Generating the RSA and D-H parameters takes 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
exist, we generate them. This means that it is easy to cause a regeneration.
*/
int
main(int argc, char **argv)
{
int fd, ret, verbose=FALSE;
gnutls_datum m, e, d, p, q, u, prime, generator;
uschar filename[200];
#define GNUTLSPARAMFILE "/gnutls-params-new"
struct stat statbuf;
unsigned int rsa_bits = RSA_BITS;
unsigned int dh_bits = DH_BITS;
gnutls_rsa_params rsa_params = NULL;
gnutls_dh_params dh_params = NULL;
uschar tempfilename[sizeof(filename) + 10];
if (0==geteuid()||0==getegid()) {
fprintf(stderr,"Please run as exim user and exim group.\n");
return 1;
/*
setgid(exim_gid);
setuid(exim_uid);
if (verbose) printf("Dropped privileges\n");
*/
}
while (argc > 1)
{
if (Ustrcmp(argv[1], "-v") == 0) verbose=TRUE;
else break;
argv++;
argc--;
}
if (argc!=2) {
printf("Usage: %s [-v] spooldir\n",argv[0]);
return 1;
}
/* Set up the name of the cache file */
if (strlen(argv[1])+strlen(GNUTLSPARAMFILE)>=200)
return gnutls_error(US"overlong filename", 0);
strcat(filename,argv[1]);
strcat(filename,GNUTLSPARAMFILE);
/* exit if cache file exist */
if (0==(stat(CS filename,&statbuf))) {
if (verbose) printf("File [%s] exists, exiting.\n",filename);
return 0;
}
ret=gnutls_global_init();
if (ret < 0) return gnutls_error(US"tls-init", ret);
/* Initialize the data structures for holding the parameters */
ret = gnutls_rsa_params_init(&rsa_params);
if (ret < 0) return gnutls_error(US"init rsa_params", ret);
ret = gnutls_dh_params_init(&dh_params);
if (ret < 0) return gnutls_error(US"init dh_params", ret);
if (verbose) printf("generating %d bit RSA key...\n", RSA_BITS);
ret = gnutls_rsa_params_generate2(rsa_params, RSA_BITS);
if (ret < 0) return gnutls_error(US"RSA key generation", ret);
if (verbose) printf("generating %d bit Diffie-Hellman key...\n",
DH_BITS);
ret = gnutls_dh_params_generate2(dh_params, DH_BITS);
if (ret < 0) return gnutls_error(US"D-H key generation", 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){
fprintf(stderr, "Failed to open %s for writing, error %s\n",
tempfilename,strerror(errno));
return 1;
}
ret = gnutls_rsa_params_export_raw(rsa_params, &m, &e, &d, &p, &q, &u,
&rsa_bits);
if (ret < 0) return gnutls_error(US"RSA params export", ret);
ret = gnutls_dh_params_export_raw(dh_params, &prime, &generator, &dh_bits);
if (ret < 0) return gnutls_error(US"DH params export", 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))
return gnutls_error(US"TLS cache write failed", 0);
(void)close(fd);
if (rename(CS tempfilename, CS filename) < 0)
fprintf(stderr, "failed to rename %s as %s: %s\n",
tempfilename, filename, strerror(errno));
if (verbose) printf("wrote RSA and D-H parameters to file %s\n",filename);
return 0;
}
/*
* vim:tabstop=2:expandtab:shiftwidth=2
*/