[exim] Generating gnutls parameters offline instead of onlin…

Etusivu
Poista viesti
Vastaa
Lähettäjä: Andreas Metzler
Päiväys:  
Vastaanottaja: exim-users
Aihe: [exim] Generating gnutls parameters offline instead of online when receiving STARTTLS?
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
*/