Pull Andreas Metzler's fix for gnutls_certificate_verify_peers (bug 1095)
[exim.git] / src / src / transport.c
index 03d73f9ba28751a5b860991c137c591c3c74a1b4..0557318d0dea5ddc98b649046d4d20a771d3e00a 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/transport.c,v 1.17 2006/10/30 22:06:33 tom Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* General functions concerned with transportation, and generic options for all
@@ -324,7 +322,7 @@ Returns:      the yield of transport_write_block()
 */
 
 BOOL
-transport_write_string(int fd, char *format, ...)
+transport_write_string(int fd, const char *format, ...)
 {
 va_list ap;
 va_start(ap, format);
@@ -426,6 +424,7 @@ for (ptr = start; ptr < end; ptr++)
 
     if (use_crlf) *chunk_ptr++ = '\r';
     *chunk_ptr++ = '\n';
+    transport_newlines++;
 
     /* The check_string test (formerly "from hack") replaces the specific
     string at the start of a line with an escape string (e.g. "From " becomes
@@ -941,16 +940,14 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 ||
 }
 
 
-#ifdef EXPERIMENTAL_DOMAINKEYS
+#ifndef DISABLE_DKIM
 
-/**********************************************************************************
-*    External interface to write the message, while signing it with domainkeys    *
-**********************************************************************************/
+/***************************************************************************************************
+*    External interface to write the message, while signing it with DKIM and/or Domainkeys         *
+***************************************************************************************************/
 
 /* This function is a wrapper around transport_write_message(). It is only called
-   from the smtp transport if
-   (1) Domainkeys support is compiled in.
-   (2) The dk_private_key option on the smtp transport is set.
+   from the smtp transport if DKIM or Domainkeys support is compiled in.
    The function sets up a replacement fd into a -K file, then calls the normal
    function. This way, the exact bits that exim would have put "on the wire" will
    end up in the file (except for TLS encapsulation, which is the very
@@ -959,39 +956,48 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 ||
 
 Arguments:     as for internal_transport_write_message() above, with additional
                arguments:
-               uschar *dk_private_key         The private key to use (filename or plain data)
-               uschar *dk_domain              Override domain (normally NULL)
-               uschar *dk_selector            The selector to use.
-               uschar *dk_canon               The canonalization scheme to use, "simple" or "nofws"
-               uschar *dk_headers             Colon-separated header list to include in the signing
-                                              process.
-               uschar *dk_strict              What to do if signing fails: 1/true  => throw error
-                                                                           0/false => send anyway
+               uschar *dkim_private_key         DKIM: The private key to use (filename or plain data)
+               uschar *dkim_domain              DKIM: The domain to use
+               uschar *dkim_selector            DKIM: The selector to use.
+               uschar *dkim_canon               DKIM: The canonalization scheme to use, "simple" or "relaxed"
+               uschar *dkim_strict              DKIM: What to do if signing fails: 1/true  => throw error
+                                                                                   0/false => send anyway
+               uschar *dkim_sign_headers        DKIM: List of headers that should be included in signature
+                                                generation
 
 Returns:       TRUE on success; FALSE (with errno) for any failure
 */
 
 BOOL
-dk_transport_write_message(address_item *addr, int fd, int options,
+dkim_transport_write_message(address_item *addr, int fd, int options,
   int size_limit, uschar *add_headers, uschar *remove_headers,
   uschar *check_string, uschar *escape_string, rewrite_rule *rewrite_rules,
-  int rewrite_existflags, uschar *dk_private_key, uschar *dk_domain,
-  uschar *dk_selector, uschar *dk_canon, uschar *dk_headers, uschar *dk_strict)
+  int rewrite_existflags, uschar *dkim_private_key, uschar *dkim_domain,
+  uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers
+  )
 {
-  int dk_fd;
+  int dkim_fd;
   int save_errno = 0;
   BOOL rc;
-  uschar dk_spool_name[256];
+  uschar dkim_spool_name[256];
   char sbuf[2048];
   int sread = 0;
   int wwritten = 0;
-  uschar *dk_signature = NULL;
+  uschar *dkim_signature = NULL;
   off_t size = 0;
 
-  (void)string_format(dk_spool_name, 256, "%s/input/%s/%s-%d-K",
+  if (!( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) )) {
+    /* If we can't sign, just call the original function. */
+    return transport_write_message(addr, fd, options,
+              size_limit, add_headers, remove_headers,
+              check_string, escape_string, rewrite_rules,
+              rewrite_existflags);
+  }
+
+  (void)string_format(dkim_spool_name, 256, "%s/input/%s/%s-%d-K",
           spool_directory, message_subdir, message_id, (int)getpid());
-  dk_fd = Uopen(dk_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE);
-  if (dk_fd < 0)
+  dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE);
+  if (dkim_fd < 0)
     {
     /* Can't create spool file. Ugh. */
     rc = FALSE;
@@ -1000,7 +1006,7 @@ dk_transport_write_message(address_item *addr, int fd, int options,
     }
 
   /* Call original function */
-  rc = transport_write_message(addr, dk_fd, options,
+  rc = transport_write_message(addr, dkim_fd, options,
     size_limit, add_headers, remove_headers,
     check_string, escape_string, rewrite_rules,
     rewrite_existflags);
@@ -1012,55 +1018,54 @@ dk_transport_write_message(address_item *addr, int fd, int options,
     goto CLEANUP;
     }
 
-  /* Rewind file and feed it to the goats^W DK lib */
-  lseek(dk_fd, 0, SEEK_SET);
-  dk_signature = dk_exim_sign(dk_fd,
-                              dk_private_key,
-                              dk_domain,
-                              dk_selector,
-                              dk_canon);
-
-  if (dk_signature != NULL)
-    {
-    /* Send the signature first */
-    int siglen = Ustrlen(dk_signature);
-    while(siglen > 0)
-      {
-      #ifdef SUPPORT_TLS
-      if (tls_active == fd) wwritten = tls_write(dk_signature, siglen); else
-      #endif
-      wwritten = write(fd,dk_signature,siglen);
-      if (wwritten == -1)
-        {
-        /* error, bail out */
-        save_errno = errno;
-        rc = FALSE;
-        goto CLEANUP;
+  if ( (dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL) ) {
+    /* Rewind file and feed it to the goats^W DKIM lib */
+    lseek(dkim_fd, 0, SEEK_SET);
+    dkim_signature = dkim_exim_sign(dkim_fd,
+                                    dkim_private_key,
+                                    dkim_domain,
+                                    dkim_selector,
+                                    dkim_canon,
+                                    dkim_sign_headers);
+    if (dkim_signature == NULL) {
+      if (dkim_strict != NULL) {
+        uschar *dkim_strict_result = expand_string(dkim_strict);
+        if (dkim_strict_result != NULL) {
+          if ( (strcmpic(dkim_strict,US"1") == 0) ||
+               (strcmpic(dkim_strict,US"true") == 0) ) {
+            /* Set errno to something halfway meaningful */
+            save_errno = EACCES;
+            log_write(0, LOG_MAIN, "DKIM: message could not be signed, and dkim_strict is set. Deferring message delivery.");
+            rc = FALSE;
+            goto CLEANUP;
+          }
         }
-      siglen -= wwritten;
-      dk_signature += wwritten;
       }
     }
-  else if (dk_strict != NULL)
-    {
-    uschar *dk_strict_result = expand_string(dk_strict);
-    if (dk_strict_result != NULL)
-      {
-      if ( (strcmpic(dk_strict,US"1") == 0) ||
-           (strcmpic(dk_strict,US"true") == 0) )
-        {
-        save_errno = errno;
-        rc = FALSE;
-        goto CLEANUP;
+    else {
+      int siglen = Ustrlen(dkim_signature);
+      while(siglen > 0) {
+        #ifdef SUPPORT_TLS
+        if (tls_active == fd) wwritten = tls_write(dkim_signature, siglen); else
+        #endif
+        wwritten = write(fd,dkim_signature,siglen);
+        if (wwritten == -1) {
+          /* error, bail out */
+          save_errno = errno;
+          rc = FALSE;
+          goto CLEANUP;
         }
+        siglen -= wwritten;
+        dkim_signature += wwritten;
       }
     }
+  }
 
   /* Fetch file positition (the size) */
-  size = lseek(dk_fd,0,SEEK_CUR);
+  size = lseek(dkim_fd,0,SEEK_CUR);
 
   /* Rewind file */
-  lseek(dk_fd, 0, SEEK_SET);
+  lseek(dkim_fd, 0, SEEK_SET);
 
 #ifdef HAVE_LINUX_SENDFILE
   /* We can use sendfile() to shove the file contents
@@ -1073,7 +1078,7 @@ dk_transport_write_message(address_item *addr, int fd, int options,
     off_t offset = 0;
     while((copied >= 0) && (offset<size))
       {
-      copied = sendfile(fd, dk_fd, &offset, (size - offset));
+      copied = sendfile(fd, dkim_fd, &offset, (size - offset));
       }
     if (copied < 0)
       {
@@ -1085,11 +1090,11 @@ dk_transport_write_message(address_item *addr, int fd, int options,
 #endif
 
   /* Send file down the original fd */
-  while((sread = read(dk_fd,sbuf,2048)) > 0)
+  while((sread = read(dkim_fd,sbuf,2048)) > 0)
     {
     char *p = sbuf;
     /* write the chunk */
-    DK_WRITE:
+    DKIM_WRITE:
     #ifdef SUPPORT_TLS
     if (tls_active == fd) wwritten = tls_write(US p, sread); else
     #endif
@@ -1106,7 +1111,7 @@ dk_transport_write_message(address_item *addr, int fd, int options,
       /* short write, try again */
       p += wwritten;
       sread -= wwritten;
-      goto DK_WRITE;
+      goto DKIM_WRITE;
       }
     }
 
@@ -1119,14 +1124,16 @@ dk_transport_write_message(address_item *addr, int fd, int options,
 
   CLEANUP:
   /* unlink -K file */
-  (void)close(dk_fd);
-  Uunlink(dk_spool_name);
+  (void)close(dkim_fd);
+  Uunlink(dkim_spool_name);
   errno = save_errno;
   return rc;
 }
+
 #endif
 
 
+
 /*************************************************
 *    External interface to write the message     *
 *************************************************/
@@ -1433,6 +1440,8 @@ host_item *host;
 open_db dbblock;
 open_db *dbm_file;
 
+DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname);
+
 /* Open the database for this transport */
 
 sprintf(CS buffer, "wait-%.200s", tpname);
@@ -1498,7 +1507,11 @@ for (host = hostlist; host!= NULL; host = host->next)
 
   /* If this message is already in a record, no need to update. */
 
-  if (already) continue;
+  if (already)
+    {
+    DEBUG(D_transport) debug_printf("already listed for %s\n", host->name);
+    continue;
+    }
 
 
   /* If this record is full, write it out with a new name constructed
@@ -1534,6 +1547,7 @@ for (host = hostlist; host!= NULL; host = host->next)
   /* Update the database */
 
   dbfn_write(dbm_file, host->name, host_record, sizeof(dbdata_wait) + host_length);
+  DEBUG(D_transport) debug_printf("added to list for %s\n", host->name);
   }
 
 /* All now done */