[Buzilla 376] Preliminary DKIM support
[exim.git] / src / src / tls-openssl.c
index 951e46c253c75c48f4829667dfde820639200c02..a9b67dcdd3876e1bdb537febecba522a701ef99a 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/tls-openssl.c,v 1.2 2004/11/25 15:29:37 ph10 Exp $ */
+/* $Cambridge: exim/src/src/tls-openssl.c,v 1.12 2007/04/13 15:13:47 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* This module provides the TLS (aka SSL) support for Exim using the OpenSSL
@@ -182,9 +182,6 @@ else
   tls_peerdn = txt;
   }
 
-
-debug_printf("+++verify_callback_called=%d\n", verify_callback_called);
-
 if (!verify_callback_called) tls_certificate_verified = TRUE;
 verify_callback_called = TRUE;
 
@@ -293,8 +290,8 @@ Returns:          OK/DEFER/FAIL
 */
 
 static int
-tls_init(host_item *host, uschar *dhparam, uschar *certificate, uschar *privatekey,
-  address_item *addr)
+tls_init(host_item *host, uschar *dhparam, uschar *certificate,
+  uschar *privatekey, address_item *addr)
 {
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
@@ -346,8 +343,7 @@ if (!RAND_status())
 /* Set up the information callback, which outputs if debugging is at a suitable
 level. */
 
-if (!(SSL_CTX_set_info_callback(ctx, (void (*)())info_callback)))
-  return tls_error(US"SSL_CTX_set_info_callback", host);
+SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
 
 /* The following patch was supplied by Robert Roselius */
 
@@ -389,7 +385,11 @@ if (certificate != NULL)
       !expand_check(privatekey, US"tls_privatekey", &expanded))
     return DEFER;
 
-  if (expanded != NULL)
+  /* 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 the private
+  key is in the same file as the certificate. */
+
+  if (expanded != NULL && *expanded != 0)
     {
     DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
     if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM))
@@ -526,34 +526,51 @@ if (expcerts != NULL)
 
   #if OPENSSL_VERSION_NUMBER > 0x00907000L
 
+  /* This bit of code is now the version supplied by Lars Mainka. (I have
+   * merely reformatted it into the Exim code style.)
+
+   * "From here I changed the code to add support for multiple crl's
+   * in pem format in one file or to support hashed directory entries in
+   * pem format instead of a file. This method now uses the library function
+   * X509_STORE_load_locations to add the CRL location to the SSL context.
+   * OpenSSL will then handle the verify against CA certs and CRLs by
+   * itself in the verify callback." */
+
   if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER;
   if (expcrl != NULL && *expcrl != 0)
     {
-    BIO *crl_bio;
-    X509_CRL *crl_x509;
-    X509_STORE *cvstore;
-
-    cvstore = SSL_CTX_get_cert_store(ctx);  /* cert validation store */
-
-    crl_bio = BIO_new(BIO_s_file_internal());
-    if (crl_bio != NULL)
+    struct stat statbufcrl;
+    if (Ustat(expcrl, &statbufcrl) < 0)
       {
-      if (BIO_read_filename(crl_bio, expcrl))
+      log_write(0, LOG_MAIN|LOG_PANIC,
+        "failed to stat %s for certificates revocation lists", expcrl);
+      return DEFER;
+      }
+    else
+      {
+      /* is it a file or directory? */
+      uschar *file, *dir;
+      X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx);
+      if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR)
         {
-        crl_x509 = PEM_read_bio_X509_CRL(crl_bio, NULL, NULL, NULL);
-        BIO_free(crl_bio);
-        X509_STORE_add_crl(cvstore, crl_x509);
-        X509_CRL_free(crl_x509);
-        X509_STORE_set_flags(cvstore,
-          X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+        file = NULL;
+        dir = expcrl;
+        DEBUG(D_tls) debug_printf("SSL CRL value is a directory %s\n", dir);
         }
       else
         {
-        BIO_free(crl_bio);
-        return tls_error(US"BIO_read_filename", host);
+        file = expcrl;
+        dir = NULL;
+        DEBUG(D_tls) debug_printf("SSL CRL value is a file %s\n", file);
         }
+      if (X509_STORE_load_locations(cvstore, CS file, CS dir) == 0)
+        return tls_error(US"X509_STORE_load_locations", host);
+
+      /* setting the flags to check against the complete crl chain */
+
+      X509_STORE_set_flags(cvstore,
+        X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
       }
-    else return tls_error(US"BIO_new", host);
     }
 
   #endif  /* OPENSSL_VERSION_NUMBER > 0x00907000L */
@@ -580,6 +597,11 @@ a TLS session.
 
 Arguments:
   require_ciphers   allowed ciphers
+  ------------------------------------------------------
+  require_mac      list of allowed MACs                 ) Not used
+  require_kx       list of allowed key_exchange methods )   for
+  require_proto    list of allowed protocols            ) OpenSSL
+  ------------------------------------------------------
 
 Returns:            OK on success
                     DEFER for errors before the start of the negotiation
@@ -588,7 +610,8 @@ Returns:            OK on success
 */
 
 int
-tls_server_start(uschar *require_ciphers)
+tls_server_start(uschar *require_ciphers, uschar *require_mac,
+  uschar *require_kx, uschar *require_proto)
 {
 int rc;
 uschar *expciphers;
@@ -666,7 +689,8 @@ if (!tls_on_connect)
 /* Now negotiate the TLS session. We put our own timer on it, since it seems
 that the OpenSSL library doesn't. */
 
-SSL_set_fd(ssl, fileno(smtp_out));
+SSL_set_wfd(ssl, fileno(smtp_out));
+SSL_set_rfd(ssl, fileno(smtp_in));
 SSL_set_accept_state(ssl);
 
 DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
@@ -709,6 +733,7 @@ receive_getc = tls_getc;
 receive_ungetc = tls_ungetc;
 receive_feof = tls_feof;
 receive_ferror = tls_ferror;
+receive_smtp_buffered = tls_smtp_buffered;
 
 tls_active = fileno(smtp_out);
 return OK;
@@ -727,12 +752,19 @@ return OK;
 Argument:
   fd               the fd of the connection
   host             connected host (for messages)
+  addr             the first address
   dhparam          DH parameter file
   certificate      certificate file
   privatekey       private key file
   verify_certs     file for certificate verify
   crl              file containing CRL
   require_ciphers  list of allowed ciphers
+  ------------------------------------------------------
+  require_mac      list of allowed MACs                 ) Not used
+  require_kx       list of allowed key_exchange methods )   for
+  require_proto    list of allowed protocols            ) OpenSSL
+  ------------------------------------------------------
+  timeout          startup timeout
 
 Returns:           OK on success
                    FAIL otherwise - note that tls_error() will not give DEFER
@@ -742,7 +774,8 @@ Returns:           OK on success
 int
 tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
   uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl,
-  uschar *require_ciphers, int timeout)
+  uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
+  uschar *require_proto, int timeout)
 {
 static uschar txt[256];
 uschar *expciphers;
@@ -854,6 +887,7 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     receive_ungetc = smtp_ungetc;
     receive_feof = smtp_feof;
     receive_ferror = smtp_ferror;
+    receive_smtp_buffered = smtp_buffered;
 
     SSL_free(ssl);
     ssl = NULL;