Update version number and copyright year.
[exim.git] / src / src / tls-gnu.c
index aedf09bc23fe7d7b79d09536bc45ef28e120e887..891d1ea0057f4553c898557eba186d23d8bd5bf4 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/tls-gnu.c,v 1.8 2005/06/16 14:10:13 ph10 Exp $ */
+/* $Cambridge: exim/src/src/tls-gnu.c,v 1.17 2007/01/08 10:50:18 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* This module provides TLS (aka SSL) support for Exim using the GnuTLS
@@ -22,8 +22,7 @@ functions from the GnuTLS library. */
 
 
 #define UNKNOWN_NAME "unknown"
-#define DH_BITS      768
-#define RSA_BITS     512
+#define DH_BITS      1024
 #define PARAM_SIZE 2*1024
 
 
@@ -37,7 +36,6 @@ enum { INITIALIZED_NOT, INITIALIZED_SERVER, INITIALIZED_CLIENT };
 static BOOL initialized = INITIALIZED_NOT;
 static host_item *client_host;
 
-static gnutls_rsa_params rsa_params = NULL;
 static gnutls_dh_params dh_params = NULL;
 
 static gnutls_certificate_server_credentials x509_cred = NULL;
@@ -57,7 +55,6 @@ static const int kx_priority[16] = {
   GNUTLS_KX_RSA,
   GNUTLS_KX_DHE_DSS,
   GNUTLS_KX_DHE_RSA,
-  GNUTLS_KX_RSA_EXPORT,
   0 };
 
 static int default_cipher_priority[16] = {
@@ -233,10 +230,10 @@ return TRUE;                            /* accept */
 
 
 /*************************************************
-*          Setup up RSA and DH parameters        *
+*            Setup up DH parameters              *
 *************************************************/
 
-/* Generating the RSA and D-H parameters takes a long time. They only need to
+/* Generating the D-H parameters may take 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.
@@ -253,18 +250,15 @@ Returns:     OK/DEFER/FAIL
 */
 
 static int
-init_rsa_dh(host_item *host)
+init_dh(host_item *host)
 {
 int fd;
-int ret = -1;
+int ret;
 gnutls_datum m;
 uschar filename[200];
 
 /* Initialize the data structures for holding the parameters */
 
-ret = gnutls_rsa_params_init(&rsa_params);
-if (ret < 0) return tls_error(US"init rsa_params", host, ret);
-
 ret = gnutls_dh_params_init(&dh_params);
 if (ret < 0) return tls_error(US"init dh_params", host, ret);
 
@@ -275,10 +269,7 @@ if (!string_format(filename, sizeof(filename), "%s/gnutls-params",
   return tls_error(US"overlong filename", host, 0);
 
 /* 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. */
+parameters. */
 
 fd = Uopen(filename, O_RDONLY, 0);
 if (fd >= 0)
@@ -298,19 +289,9 @@ if (fd >= 0)
     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");
-    }
+  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 D-H parameters from file\n");
 
   free(m.data);
   }
@@ -318,7 +299,13 @@ if (fd >= 0)
 /* 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)
+else if (errno == ENOENT)
+  {
+  ret = -1;
+  DEBUG(D_tls)
+    debug_printf("parameter cache file %s does not exist\n", filename);
+  }
+else
   return tls_error(string_open_failed(errno, "%s for reading", filename),
     host, 0);
 
@@ -332,10 +319,6 @@ 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);
-  if (ret < 0) return tls_error(US"RSA key generation", host, ret);
-
   DEBUG(D_tls) debug_printf("generating %d bit Diffie-Hellman key...\n",
     DH_BITS);
   ret = gnutls_dh_params_generate2(dh_params, DH_BITS);
@@ -355,9 +338,7 @@ if (ret < 0)
    * certtool or other programs.
    *
    * The commands for certtool are:
-   * $ certtool --generate-privkey --bits 512 >params
-   * $ echo "" >>params
-   * $ certtool --generate-dh-params --bits 1024 >> params
+   * $ certtool --generate-dh-params --bits 1024 > params
    */
 
   m.size = PARAM_SIZE;
@@ -365,16 +346,6 @@ if (ret < 0)
   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);
-
-  /* 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);
@@ -391,10 +362,10 @@ if (ret < 0)
     return tls_error(string_sprintf("failed to rename %s as %s: %s",
       tempfilename, filename, strerror(errno)), host, 0);
 
-  DEBUG(D_tls) debug_printf("wrote RSA and D-H parameters to file\n");
+  DEBUG(D_tls) debug_printf("wrote D-H parameters to file %s\n", filename);
   }
 
-DEBUG(D_tls) debug_printf("initialized RSA and D-H parameters\n");
+DEBUG(D_tls) debug_printf("initialized D-H parameters\n");
 return OK;
 }
 
@@ -430,10 +401,10 @@ initialized = (host == NULL)? INITIALIZED_SERVER : INITIALIZED_CLIENT;
 rc = gnutls_global_init();
 if (rc < 0) return tls_error(US"tls-init", host, rc);
 
-/* Create RSA and D-H parameters, or read them from the cache file. This
-function does its own SMTP error messaging. */
+/* Create D-H parameters, or read them from the cache file. This function does
+its own SMTP error messaging. */
 
-rc = init_rsa_dh(host);
+rc = init_dh(host);
 if (rc != OK) return rc;
 
 /* Create the credentials structure */
@@ -447,12 +418,19 @@ may be required for different sessions. */
 if (!expand_check(certificate, US"tls_certificate", &cert_expanded))
   return DEFER;
 
+key_expanded = NULL;
 if (privatekey != NULL)
   {
   if (!expand_check(privatekey, US"tls_privatekey", &key_expanded))
     return DEFER;
   }
-else key_expanded = cert_expanded;
+
+/* If expansion was forced to fail, key_expanded will be NULL. If the result of
+the expansion is an empty string, ignore it also, and assume that the private
+key is in the same file as the certificate. */
+
+if (key_expanded == NULL || *key_expanded == 0)
+  key_expanded = cert_expanded;
 
 /* Set the certificate and private keys */
 
@@ -525,7 +503,6 @@ if (cas != NULL)
 /* Associate the parameters with the x509 credentials structure. */
 
 gnutls_certificate_set_dh_params(x509_cred, dh_params);
-gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
 
 DEBUG(D_tls) debug_printf("initialized certificate stuff\n");
 return OK;
@@ -832,7 +809,8 @@ if (!tls_on_connect)
 /* Now negotiate the TLS session. We put our own timer on it, since it seems
 that the GnuTLS library doesn't. */
 
-gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fileno(smtp_out));
+gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr)fileno(smtp_in),
+                                       (gnutls_transport_ptr)fileno(smtp_out));
 
 sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
@@ -856,8 +834,8 @@ if (rc < 0)
 
   if (!sigalrm_seen)
     {
-    fclose(smtp_out);
-    fclose(smtp_in);
+    (void)fclose(smtp_out);
+    (void)fclose(smtp_in);
     }
 
   return FAIL;