[Buzilla 376] Preliminary DKIM support
authorTom Kistner <tom@duncanthrax.net>
Fri, 28 Sep 2007 12:21:57 +0000 (12:21 +0000)
committerTom Kistner <tom@duncanthrax.net>
Fri, 28 Sep 2007 12:21:57 +0000 (12:21 +0000)
22 files changed:
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/acl.c
src/src/config.h.defaults
src/src/dkim-exim.c [new file with mode: 0755]
src/src/dkim-exim.h [new file with mode: 0755]
src/src/drtables.c
src/src/exim.c
src/src/exim.h
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/lookups/Makefile
src/src/lookups/dkim.c [new file with mode: 0755]
src/src/lookups/dkim.h [new file with mode: 0755]
src/src/receive.c
src/src/sieve.c
src/src/smtp_in.c
src/src/spool_in.c
src/src/transport.c
src/src/transports/smtp.c
src/src/transports/smtp.h

index 698a2b20792dd4d9282fca1a3b3305b6aed00665..ac5a4d822934c8e34c8f0dc726d0cd9a21343141 100644 (file)
@@ -1,4 +1,4 @@
-# $Cambridge: exim/src/OS/Makefile-Base,v 1.10 2006/02/07 14:20:58 ph10 Exp $
+# $Cambridge: exim/src/OS/Makefile-Base,v 1.11 2007/09/28 12:21:57 tom Exp $
 
 # This file is the basis of the main makefile for Exim and friends. The
 # makefile at the top level arranges to build the main makefile by calling
@@ -298,7 +298,7 @@ convert4r4: Makefile ../src/convert4r4.src
 
 OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o
 OBJ_WITH_OLD_DEMIME = demime.o
-OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o
+OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o dkim-exim.o
 
 # Targets for final binaries; the main one has a build number which is
 # updated each time. We don't bother with that for the auxiliaries.
@@ -599,6 +599,7 @@ bmi_spam.o:      $(HDRS) bmi_spam.c
 spf.o:           $(HDRS) spf.h spf.c
 srs.o:           $(HDRS) srs.h srs.c
 dk.o:            $(HDRS) dk.h dk.c
+dkim-exim.o:     $(HDRS) dkim-exim.h dkim-exim.c
 
 # The module containing tables of available lookups, routers, auths, and
 # transports must be rebuilt if any of them are. However, because the makefiles
index c016e50c73f5f8e3920eda1d237e5fc59c7e2319..e30a8f9ba61f2567853a2ac890064184c795bde3 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Cambridge: exim/src/scripts/MakeLinks,v 1.12 2007/08/23 10:16:51 ph10 Exp $
+# $Cambridge: exim/src/scripts/MakeLinks,v 1.13 2007/09/28 12:21:57 tom Exp $
 
 # Script to build links for all the exim source files from the system-
 # specific build directory. It should be run from within that directory.
@@ -61,6 +61,8 @@ ln -s ../../src/lookups/cdb.h            cdb.h
 ln -s ../../src/lookups/cdb.c            cdb.c
 ln -s ../../src/lookups/dbmdb.h          dbmdb.h
 ln -s ../../src/lookups/dbmdb.c          dbmdb.c
+ln -s ../../src/lookups/dkim.h           dkim.h
+ln -s ../../src/lookups/dkim.c           dkim.c
 ln -s ../../src/lookups/dnsdb.h          dnsdb.h
 ln -s ../../src/lookups/dnsdb.c          dnsdb.c
 ln -s ../../src/lookups/dsearch.h        dsearch.h
@@ -280,5 +282,7 @@ ln -s ../src/srs.c             srs.c
 ln -s ../src/srs.h             srs.h
 ln -s ../src/dk.c              dk.c
 ln -s ../src/dk.h              dk.h
+ln -s ../src/dkim-exim.c       dkim-exim.c
+ln -s ../src/dkim-exim.h       dkim-exim.h
 
 # End of MakeLinks
index bc374b46113552acf38b5b8004b83fcdfe53ccfc..04e7858215187b801639d1b9c3ee965ff7df8645 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.79 2007/08/29 13:58:25 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.80 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -174,6 +174,9 @@ enum {
   #ifdef EXPERIMENTAL_DOMAINKEYS
   CONTROL_DK_VERIFY,
   #endif
+  #ifdef EXPERIMENTAL_DKIM
+  CONTROL_DKIM_VERIFY,
+  #endif
   CONTROL_ERROR,
   CONTROL_CASEFUL_LOCAL_PART,
   CONTROL_CASELOWER_LOCAL_PART,
@@ -207,6 +210,9 @@ static uschar *controls[] = {
   #ifdef EXPERIMENTAL_DOMAINKEYS
   US"dk_verify",
   #endif
+  #ifdef EXPERIMENTAL_DKIM
+  US"dkim_verify",
+  #endif
   US"error",
   US"caseful_local_part",
   US"caselower_local_part",
@@ -550,6 +556,11 @@ static unsigned int control_forbids[] = {
     (1<<ACL_WHERE_NOTSMTP_START),
   #endif
 
+  #ifdef EXPERIMENTAL_DKIM
+  (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)|      /* dkim_verify */
+    (1<<ACL_WHERE_NOTSMTP_START),
+  #endif
+
   0,                                               /* error */
 
   (unsigned int)
@@ -628,6 +639,9 @@ static control_def controls_list[] = {
 #endif
 #ifdef EXPERIMENTAL_DOMAINKEYS
   { US"dk_verify",               CONTROL_DK_VERIFY, FALSE },
+#endif
+#ifdef EXPERIMENTAL_DKIM
+  { US"dkim_verify",             CONTROL_DKIM_VERIFY, FALSE },
 #endif
   { US"caseful_local_part",      CONTROL_CASEFUL_LOCAL_PART, FALSE },
   { US"caselower_local_part",    CONTROL_CASELOWER_LOCAL_PART, FALSE },
@@ -2613,6 +2627,12 @@ for (; cb != NULL; cb = cb->next)
       break;
       #endif
 
+      #ifdef EXPERIMENTAL_DKIM
+      case CONTROL_DKIM_VERIFY:
+      dkim_do_verify = 1;
+      break;
+      #endif
+
       case CONTROL_ERROR:
       return ERROR;
 
index 1949cb7f4c6c7ba63627c60db372b6186f42ceb1..db752dad1d3d7c5732e2c547f70b5fad1f6ed82c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/config.h.defaults,v 1.14 2007/01/22 16:29:54 ph10 Exp $ */
+/* $Cambridge: exim/src/src/config.h.defaults,v 1.15 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -151,6 +151,7 @@ it's a default value. */
 #define EXPERIMENTAL_SPF
 #define EXPERIMENTAL_SRS
 #define EXPERIMENTAL_DOMAINKEYS
+#define EXPERIMENTAL_DKIM
 #define EXPERIMENTAL_BRIGHTMAIL
 
 /* Things that are not routinely changed but are nevertheless configurable
diff --git a/src/src/dkim-exim.c b/src/src/dkim-exim.c
new file mode 100755 (executable)
index 0000000..3b6e4e6
--- /dev/null
@@ -0,0 +1,507 @@
+/* $Cambridge: exim/src/src/dkim-exim.c,v 1.1 2007/09/28 12:21:57 tom Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Code for DKIM support. Other DKIM relevant code is in
+   receive.c, transport.c and transports/smtp.c */
+
+#include "exim.h"
+
+#ifdef EXPERIMENTAL_DKIM
+
+/* Globals related to the DKIM reference library. */
+DKIMContext          *dkim_context           = NULL;
+DKIMSignOptions      *dkim_sign_options      = NULL;
+DKIMVerifyOptions    *dkim_verify_options    = NULL;
+int                   dkim_verify_result     = DKIM_NEUTRAL;
+int                   dkim_internal_status   = DKIM_SUCCESS;
+
+/* Global char buffer for getc/ungetc functions. We need
+   to accumulate some chars to be able to match EOD and
+   doubled SMTP dots. Those must not be fed to the validation
+   engine. */
+int dkimbuff[6] = {256,256,256,256,256,256};
+
+/* receive_getc() wrapper that feeds DKIM while Exim reads
+   the message. */
+int dkim_receive_getc(void) {
+  int i;
+
+#ifdef EXPERIMENTAL_DOMAINKEYS
+  int c = dk_receive_getc();
+#else
+  int c = receive_getc();
+#endif
+
+  if ((dkim_context != NULL) &&
+      (dkim_internal_status == DKIM_SUCCESS)) {
+    /* Send oldest byte */
+    if (dkimbuff[0] < 256) {
+      DKIMVerifyProcess(dkim_context,(char *)&dkimbuff[0],1);
+      /* debug_printf("%c",(int)dkimbuff[0]); */
+    }
+    /* rotate buffer */
+    for (i=0;i<5;i++) dkimbuff[i]=dkimbuff[i+1];
+    dkimbuff[5]=c;
+    /* look for our candidate patterns */
+    if ( (dkimbuff[1] == '\r') &&
+         (dkimbuff[2] == '\n') &&
+         (dkimbuff[3] == '.') &&
+         (dkimbuff[4] == '\r') &&
+         (dkimbuff[5] == '\n') ) {
+      /* End of DATA */
+      dkimbuff[1] = 256;
+      dkimbuff[2] = 256;
+      dkimbuff[3] = 256;
+      dkimbuff[4] = 256;
+      dkimbuff[5] = 256;
+    }
+    if ( (dkimbuff[2] == '\r') &&
+         (dkimbuff[3] == '\n') &&
+         (dkimbuff[4] == '.') &&
+         (dkimbuff[5] == '.') ) {
+      /* doubled dot, skip this char */
+      dkimbuff[5] = 256;
+    }
+  }
+
+  return c;
+}
+
+/* When exim puts a char back in the fd, we
+   must rotate our buffer back. */
+int dkim_receive_ungetc(int c) {
+
+  if ((dkim_context != NULL) &&
+      (dkim_internal_status == DKIM_SUCCESS)) {
+    int i;
+    /* rotate buffer back */
+    for (i=5;i>0;i--) dkimbuff[i]=dkimbuff[i-1];
+    dkimbuff[0]=256;
+  }
+
+#ifdef EXPERIMENTAL_DOMAINKEYS
+  return dk_receive_ungetc(c);
+#else
+  return receive_ungetc(c);
+#endif
+}
+
+
+void dkim_exim_verify_init(void) {
+  int old_pool = store_pool;
+
+  /* Bail out unless we got perfect conditions */
+  if (!(smtp_input &&
+        !smtp_batched_input &&
+        dkim_do_verify)) {
+    return;
+  }
+
+  store_pool = POOL_PERM;
+
+  dkim_context = NULL;
+  dkim_verify_options = NULL;
+
+  dkim_context = store_get(sizeof(DKIMContext));
+  dkim_verify_options = store_get(sizeof(DKIMVerifyOptions));
+
+  if (!dkim_context ||
+      !dkim_verify_options) {
+    debug_printf("DKIM: Can't allocate memory for verifying.\n");
+    dkim_context = NULL;
+  }
+
+  memset(dkim_context,0,sizeof(DKIMContext));
+  memset(dkim_verify_options,0,sizeof(DKIMVerifyOptions));
+
+  dkim_verify_options->nHonorBodyLengthTag = 1; /* Honor the l= tag */
+  dkim_verify_options->nCheckPolicy = 1;        /* Fetch sender's policy */
+  dkim_verify_options->nSubjectRequired = 1;    /* Do not require Subject header inclusion */
+
+  dkim_verify_options->pfnSelectorCallback = NULL;
+  dkim_verify_options->pfnPolicyCallback = NULL;
+
+  dkim_status_wrap( DKIMVerifyInit(dkim_context, dkim_verify_options),
+                    "error calling DKIMVerifyInit()" );
+
+  if (dkim_internal_status != DKIM_SUCCESS) {
+    /* Invalidate context */
+    dkim_context = NULL;
+  }
+
+  store_pool = old_pool;
+}
+
+
+void dkim_exim_verify_finish(void) {
+  int i;
+  int old_pool = store_pool;
+
+  if (!dkim_do_verify ||
+      (!(smtp_input && !smtp_batched_input)) ||
+      (dkim_context == NULL) ||
+      (dkim_internal_status != DKIM_SUCCESS)) return;
+
+  store_pool = POOL_PERM;
+
+  /* Flush eventual remaining input chars */
+  for (i=0;i<6;i++)
+    if (dkimbuff[i] < 256)
+      DKIMVerifyProcess(dkim_context,(char *)&dkimbuff[i],1);
+
+  /* Fetch global result. Can be one of:
+      DKIM_SUCCESS
+      DKIM_PARTIAL_SUCCESS
+      DKIM_NEUTRAL
+      DKIM_FAIL
+  */
+  dkim_verify_result = DKIMVerifyResults(dkim_context);
+
+  store_pool = old_pool;
+}
+
+
+/* Lookup result for a given domain (or identity) */
+int dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
+  int sig_count = 0;
+  int i,rc;
+  char policy[512];
+  DKIMVerifyDetails *dkim_verify_details = NULL;
+
+  if (!dkim_do_verify ||
+      (!(smtp_input && !smtp_batched_input)) ||
+      (dkim_context == NULL) ||
+      (dkim_internal_status != DKIM_SUCCESS)) {
+    rc = DKIM_EXIM_UNVERIFIED;
+    goto YIELD;
+  }
+
+  DKIMVerifyGetDetails(dkim_context,
+                       &sig_count,
+                       &dkim_verify_details,
+                       policy);
+
+
+  rc = DKIM_EXIM_UNSIGNED;
+
+  debug_printf("DKIM: We have %d signature(s)\n",sig_count);
+  for (i=0;i<sig_count;i++) {
+    debug_printf( "DKIM: [%d] ", i + 1 );
+    if (!dkim_verify_details[i].Domain) {
+      debug_printf("parse error (no domain)\n");
+      continue;
+    }
+
+    if (dkim_verify_details[i].nResult >= 0) {
+      debug_printf( "GOOD d=%s i=%s\n",
+                    dkim_verify_details[i].Domain,
+                    dkim_verify_details[i].IdentityDomain );
+    }
+    else {
+      debug_printf( "FAIL d=%s i=%s c=%d\n",
+                    dkim_verify_details[i].Domain,
+                    dkim_verify_details[i].IdentityDomain,
+                    dkim_verify_details[i].nResult
+                    );
+
+    }
+
+    if ( (strcmpic(domain,dkim_verify_details[i].Domain) == 0) ||
+         (strcmpic(domain,dkim_verify_details[i].IdentityDomain) == 0) ) {
+      if (dkim_verify_details[i].nResult >= 0) {
+        rc = DKIM_EXIM_GOOD;
+        /* TODO: Add From: domain check */
+      }
+      else {
+        /* Return DEFER for temp. error types */
+        if (dkim_verify_details[i].nResult == DKIM_SELECTOR_DNS_TEMP_FAILURE) {
+          rc = DKIM_EXIM_DEFER;
+        }
+        else {
+          rc = DKIM_EXIM_FAIL;
+        }
+      }
+    }
+  }
+
+  YIELD:
+  switch (rc) {
+    case DKIM_EXIM_FAIL:
+      *result = "bad";
+    break;
+    case DKIM_EXIM_DEFER:
+      *result = "defer";
+    break;
+    case DKIM_EXIM_UNVERIFIED:
+      *result = "unverified";
+    break;
+    case DKIM_EXIM_UNSIGNED:
+      *result = "unsigned";
+    break;
+    case DKIM_EXIM_GOOD:
+      *result = "good";
+    break;
+  }
+
+  return rc;
+}
+
+
+
+uschar *dkim_exim_sign_headers = NULL;
+int dkim_exim_header_callback(const char* header) {
+  int sep = 0;
+  uschar *hdr_ptr = dkim_exim_sign_headers;
+  uschar *hdr_itr = NULL;
+  uschar  hdr_buf[512];
+  uschar *hdr_name = string_copy(US header);
+  char *colon_pos = strchr(hdr_name,':');
+
+  if (colon_pos == NULL) return 0;
+  *colon_pos = '\0';
+
+  debug_printf("DKIM: header '%s' ",hdr_name);
+  while ((hdr_itr = string_nextinlist(&hdr_ptr, &sep,
+                                      hdr_buf,
+                                      sizeof(hdr_buf))) != NULL) {
+    if (strcmpic((uschar *)hdr_name,hdr_itr) == 0) {
+      debug_printf("included in signature.\n");
+      return 1;
+    }
+  }
+  debug_printf("NOT included in signature.\n");
+  return 0;
+}
+
+uschar *dkim_exim_sign(int dkim_fd,
+                       uschar *dkim_private_key,
+                       uschar *dkim_domain,
+                       uschar *dkim_selector,
+                       uschar *dkim_canon,
+                       uschar *dkim_sign_headers) {
+
+  uschar *rc = NULL;
+  char buf[4096];
+  int seen_lf = 0;
+  int seen_lfdot = 0;
+  int save_errno = 0;
+  int sread;
+  char *signature;
+  int old_pool = store_pool;
+  store_pool = POOL_PERM;
+
+  dkim_context = NULL;
+  dkim_sign_options = NULL;
+
+  dkim_context = store_get(sizeof(DKIMContext));
+  dkim_sign_options = store_get(sizeof(DKIMSignOptions));
+
+  dkim_sign_options->nIncludeBodyLengthTag = 0;
+  dkim_sign_options->nIncludeCopiedHeaders = 0;
+  dkim_sign_options->nHash = DKIM_HASH_SHA256;
+  dkim_sign_options->nIncludeTimeStamp = 0;
+  dkim_sign_options->nIncludeQueryMethod = 0;
+  dkim_sign_options->pfnHeaderCallback = dkim_exim_header_callback;
+  dkim_sign_options->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
+
+
+  dkim_domain = expand_string(dkim_domain);
+  if (dkim_domain == NULL) {
+    /* expansion error, do not send message. */
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+          "dkim_domain: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  /* Set up $dkim_domain expansion variable. */
+  dkim_signing_domain = dkim_domain;
+  Ustrncpy((uschar *)dkim_sign_options->szDomain,dkim_domain,255);
+
+
+  /* Get selector to use. */
+  dkim_selector = expand_string(dkim_selector);
+  if (dkim_selector == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+      "dkim_selector: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  /* Set up $dkim_selector expansion variable. */
+  dkim_signing_selector = dkim_selector;
+  Ustrncpy((uschar *)dkim_sign_options->szSelector,dkim_selector,79);
+
+  /* Expand provided options */
+  dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
+  if (dkim_canon == NULL) {
+    /* expansion error, do not send message. */
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+          "dkim_canon: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+  if (Ustrcmp(dkim_canon, "relaxed") == 0)
+    dkim_sign_options->nCanon = DKIM_SIGN_RELAXED;
+  else if (Ustrcmp(dkim_canon, "simple") == 0)
+    dkim_sign_options->nCanon = DKIM_SIGN_SIMPLE;
+  else {
+    log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
+    dkim_sign_options->nCanon = DKIM_SIGN_RELAXED;
+  }
+
+  /* Expand signing headers once */
+  if (dkim_sign_headers != NULL) {
+    dkim_sign_headers = expand_string(dkim_sign_headers);
+    if (dkim_sign_headers == NULL) {
+      log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+        "dkim_sign_headers: %s", expand_string_message);
+      rc = NULL;
+      goto CLEANUP;
+    }
+  }
+
+  if (dkim_sign_headers == NULL) {
+    /* Use RFC defaults */
+    dkim_sign_headers = US"from:sender:reply-to:subject:date:"
+                          "message-id:to:cc:mime-version:content-type:"
+                          "content-transfer-encoding:content-id:"
+                          "content-description:resent-date:resent-from:"
+                          "resent-sender:resent-to:resent-cc:resent-message-id:"
+                          "in-reply-to:references:"
+                          "list-id:list-help:list-unsubscribe:"
+                          "list-subscribe:list-post:list-owner:list-archive";
+  }
+  dkim_exim_sign_headers = dkim_sign_headers;
+
+  /* Get private key to use. */
+  dkim_private_key = expand_string(dkim_private_key);
+  if (dkim_private_key == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
+      "dkim_private_key: %s", expand_string_message);
+    rc = NULL;
+    goto CLEANUP;
+  }
+
+  if ( (Ustrlen(dkim_private_key) == 0) ||
+       (Ustrcmp(dkim_private_key,"0") == 0) ||
+       (Ustrcmp(dkim_private_key,"false") == 0) ) {
+    /* don't sign, but no error */
+    rc = US"";
+    goto CLEANUP;
+  }
+
+  if (dkim_private_key[0] == '/') {
+    int privkey_fd = 0;
+    /* Looks like a filename, load the private key. */
+    memset(big_buffer,0,big_buffer_size);
+    privkey_fd = open(CS dkim_private_key,O_RDONLY);
+    (void)read(privkey_fd,big_buffer,16383);
+    (void)close(privkey_fd);
+    dkim_private_key = big_buffer;
+  }
+
+  /* Initialize signing context. */
+  dkim_status_wrap( DKIMSignInit(dkim_context, dkim_sign_options),
+                    "error calling DKIMSignInit()" );
+
+  if (dkim_internal_status != DKIM_SUCCESS) {
+    /* Invalidate context */
+    dkim_context = NULL;
+    goto CLEANUP;
+  }
+
+  while((sread = read(dkim_fd,&buf,4096)) > 0) {
+    int pos = 0;
+    char c;
+
+    while (pos < sread) {
+      c = buf[pos++];
+
+      if ((c == '.') && seen_lfdot) {
+        /* escaped dot, write "\n.", continue */
+        dkim_internal_status = DKIMSignProcess(dkim_context,"\n.",2);
+        seen_lf = 0;
+        seen_lfdot = 0;
+        continue;
+      }
+
+      if (seen_lfdot) {
+        /* EOM, write "\n" and break */
+        dkim_internal_status = DKIMSignProcess(dkim_context,"\n",1);
+        break;
+      }
+
+      if ((c == '.') && seen_lf) {
+        seen_lfdot = 1;
+        continue;
+      }
+
+      if (seen_lf) {
+        /* normal lf, just send it */
+        dkim_internal_status = DKIMSignProcess(dkim_context,"\n",1);
+        seen_lf = 0;
+      }
+
+      if (c == '\n') {
+        seen_lf = 1;
+        continue;
+      }
+
+      /* write the char */
+      dkim_internal_status = DKIMSignProcess(dkim_context,&c,1);
+    }
+  }
+
+  /* Handle failed read above. */
+  if (sread == -1) {
+    debug_printf("DKIM: Error reading -K file.\n");
+    save_errno = errno;
+    rc = NULL;
+    goto CLEANUP;
+  }
+
+  if (!dkim_status_wrap(dkim_internal_status,
+                        "error while processing message data")) {
+    rc = NULL;
+    goto CLEANUP;
+  }
+
+  if (!dkim_status_wrap( DKIMSignGetSig2( dkim_context, dkim_private_key, &signature ),
+                         "error while signing message" ) ) {
+    rc = NULL;
+    goto CLEANUP;
+  }
+
+  log_write(0, LOG_MAIN, "Message signed with DKIM: %s\n",signature);
+
+  rc = store_get(strlen(signature)+3);
+  Ustrcpy(rc,US signature);
+  Ustrcat(rc,US"\r\n");
+
+  CLEANUP:
+  if (dkim_context != NULL) {
+    dkim_context = NULL;
+  }
+  store_pool = old_pool;
+  errno = save_errno;
+  return rc;
+}
+
+unsigned int dkim_status_wrap(int stat, uschar *text) {
+  char *p = DKIMGetErrorString(stat);
+
+  if (stat != DKIM_SUCCESS) {
+    debug_printf("DKIM: %s",text?text:US"");
+    if (p) debug_printf(" (%s)",p);
+    debug_printf("\n");
+  }
+  dkim_internal_status = stat;
+  return (dkim_internal_status==DKIM_SUCCESS)?1:0;
+}
+
+#endif
diff --git a/src/src/dkim-exim.h b/src/src/dkim-exim.h
new file mode 100755 (executable)
index 0000000..b974d95
--- /dev/null
@@ -0,0 +1,35 @@
+/* $Cambridge: exim/src/src/dkim-exim.h,v 1.1 2007/09/28 12:21:57 tom Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Code for DKIM support. Other DKIM relevant code is in
+   receive.c, transport.c and transports/smtp.c */
+
+/* Exim interface to DKIM results */
+
+#define DKIM_EXIM_FAIL         -2     /* Message has a bad signature from that domain or identity. */
+#define DKIM_EXIM_DEFER        -1     /* Message has an unverified signature from that domain */
+#define DKIM_EXIM_UNVERIFIED    0     /* Message was not validated with the DK engine */
+#define DKIM_EXIM_UNSIGNED      1     /* Message has no signature from that domain or identity */
+#define DKIM_EXIM_GOOD          2     /* Message has good signature from that domain or identity */
+
+
+#ifdef EXPERIMENTAL_DKIM
+#include <dkim.h>
+
+int     dkim_exim_verify_result(uschar *,uschar **,uschar **);
+
+/* Internal prototypes */
+int     dkim_receive_getc(void);
+int     dkim_receive_ungetc(int);
+void    dkim_exim_verify_init(void);
+void    dkim_exim_verify_finish(void);
+uschar *dkim_exim_sign(int, uschar *, uschar *, uschar *, uschar *, uschar *);
+unsigned int dkim_status_wrap(int, uschar *);
+
+#endif
index 38fce1bb0a464611ab5dfd932e5b9d19c7599750..b95d4fc952a0f1ba72d13e8bad0823c82d1bcbb3 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/drtables.c,v 1.8 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/drtables.c,v 1.9 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -105,6 +105,11 @@ be NULL for methods that don't need them. */
 #include "lookups/whoson.h"
 #endif
 
+#ifdef EXPERIMENTAL_DKIM
+#include "lookups/dkim.h"
+#endif
+
+
 /* The second field in each item below is a set of bit flags:
 
   lookup_querystyle     => this is a query-style lookup,
@@ -171,6 +176,23 @@ of the key strings. */
 #endif
   },
 
+/* DKIM lookups */
+
+  {
+  US"dkim",                      /* lookup name */
+  lookup_querystyle,             /* query style */
+#ifdef EXPERIMENTAL_DKIM
+  dkim_open,                     /* open function */
+  NULL,                          /* check function */
+  dkim_find,                     /* find function */
+  NULL,                          /* no close function */
+  NULL,                          /* no tidy function */
+  NULL                           /* no quoting function */
+#else
+  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+#endif
+  },
+
 /* Using DNS TXT records as a database */
 
   {
index 77e5fa4acf9ceb3f24f05466ff88b7e538861a20..2da43ff8855378068c83df84166265956c7bfbb5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.58 2007/09/04 08:18:12 nm4 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.59 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -920,6 +920,9 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_DOMAINKEYS
   fprintf(f, " Experimental_DomainKeys");
 #endif
+#ifdef EXPERIMENTAL_DKIM
+  fprintf(f, " Experimental_DKIM");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Lookups:");
index 8fa6959c82f4c97dd2d77e6a624bee68bd72c9e1..9118e4d8923da17f469afd7f2158d9843bc98b41 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.h,v 1.22 2007/01/22 16:29:54 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.h,v 1.23 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -449,6 +449,9 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly.
 #ifdef EXPERIMENTAL_DOMAINKEYS
 #include "dk.h"
 #endif
+#ifdef EXPERIMENTAL_DKIM
+#include "dkim-exim.h"
+#endif
 
 /* The following stuff must follow the inclusion of config.h because it
 requires various things that are set therein. */
index 40281bd8dfdcf46a9deb4ae75fd6538627098b88..296aa25a6e541bc7fd81c0e025b75ef11aec1987 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.38 2007/08/22 10:10:23 ph10 Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.39 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -83,6 +83,11 @@ extern BOOL    dk_transport_write_message(address_item *, int, int,
                    int, uschar *, uschar *, uschar *, uschar *, rewrite_rule *,
                    int, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *);
 #endif
+#ifdef EXPERIMENTAL_DKIM
+extern BOOL    dkim_transport_write_message(address_item *, int, int,
+                   int, uschar *, uschar *, uschar *, uschar *, rewrite_rule *,
+                   int, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *);
+#endif
 extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
 extern void    dns_build_reverse(uschar *, uschar *);
 extern void    dns_init(BOOL, BOOL);
index 74e87e6d359a63906ee86975a43f9987f11f236b..c46199da66cc5ccf02cadc52833068fd27d4f34d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.78 2007/08/23 11:01:49 ph10 Exp $ */
+/* $Cambridge: exim/src/src/globals.c,v 1.79 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -523,6 +523,12 @@ uschar *dk_signing_selector    = NULL;
 int     dk_do_verify           = 0;
 #endif
 
+#ifdef EXPERIMENTAL_DKIM
+uschar *dkim_signing_domain      = NULL;
+uschar *dkim_signing_selector    = NULL;
+int     dkim_do_verify           = 0;
+#endif
+
 uschar *dns_again_means_nonexist = NULL;
 int     dns_csa_search_limit   = 5;
 BOOL    dns_csa_use_reverse    = TRUE;
index 5b9733c3870765061bcbd6ecc6e8538e41dd3e16..d25965e380ae533e27300a056c87444769f39fa7 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.59 2007/08/23 11:01:49 ph10 Exp $ */
+/* $Cambridge: exim/src/src/globals.h,v 1.60 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -292,6 +292,12 @@ extern uschar *dk_signing_selector;    /* Selector used for signing a message. *
 extern int     dk_do_verify;           /* DK verification switch. Set with ACL control statement. */
 #endif
 
+#ifdef EXPERIMENTAL_DKIM
+extern uschar *dkim_signing_domain;      /* Domain used for signing a message. */
+extern uschar *dkim_signing_selector;    /* Selector used for signing a message. */
+extern int     dkim_do_verify;           /* DKIM verification switch. Set with ACL control statement. */
+#endif
+
 extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */
 extern int     dns_csa_search_limit;   /* How deep to search for CSA SRV records */
 extern BOOL    dns_csa_use_reverse;    /* Check CSA in reverse DNS? (non-standard) */
index fce944589338932d0d2aafcc77173e76389aba01..f19679a06c914feae3cee64c18215f8738158491 100644 (file)
@@ -1,11 +1,11 @@
-# $Cambridge: exim/src/src/lookups/Makefile,v 1.6 2007/08/23 10:16:51 ph10 Exp $
+# $Cambridge: exim/src/src/lookups/Makefile,v 1.7 2007/09/28 12:21:57 tom Exp $
 
 # Make file for building a library containing all the available lookups and
 # calling it lookups.a. This is called from the main make file, after cd'ing
 # to the lookups subdirectory. When the relevant LOOKUP_ macros are not
 # defined, dummy modules get compiled.
 
-OBJ = cdb.o dbmdb.o dnsdb.o dsearch.o ibase.o ldap.o lsearch.o mysql.o nis.o \
+OBJ = cdb.o dbmdb.o dkim.o dnsdb.o dsearch.o ibase.o ldap.o lsearch.o mysql.o nis.o \
       nisplus.o oracle.o passwd.o pgsql.o spf.o sqlite.o testdb.o whoson.o \
       lf_check_file.o lf_quote.o lf_sqlperform.o
 
@@ -26,6 +26,7 @@ lf_sqlperform.o: $(HDRS) lf_sqlperform.c  lf_functions.h
 
 cdb.o:           $(HDRS) cdb.c       cdb.h
 dbmdb.o:         $(HDRS) dbmdb.c     dbmdb.h
+dkim.o:                 $(HDRS) dkim.c      dkim.h
 dnsdb.o:         $(HDRS) dnsdb.c     dnsdb.h
 dsearch.o:       $(HDRS) dsearch.c   dsearch.h
 ibase.o:         $(HDRS) ibase.c     ibase.h
diff --git a/src/src/lookups/dkim.c b/src/src/lookups/dkim.c
new file mode 100755 (executable)
index 0000000..f90283e
--- /dev/null
@@ -0,0 +1,52 @@
+/* $Cambridge: exim/src/src/lookups/dkim.c,v 1.1 2007/09/28 12:21:57 tom Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+#include "../exim.h"
+#include "dkim.h"
+
+
+
+/*************************************************
+*              Open entry point                  *
+*************************************************/
+
+/* See local README for interface description */
+
+void *
+dkim_open(uschar *filename, uschar **errmsg)
+{
+filename = filename;     /* Keep picky compilers happy */
+errmsg = errmsg;
+return (void *)(-1);     /* Just return something non-null */
+}
+
+
+
+
+/*************************************************
+*         Find entry point for passwd           *
+*************************************************/
+
+/* See local README for interface description */
+
+int
+dkim_find(void *handle, uschar *filename, uschar *keystring, int length,
+  uschar **result, uschar **errmsg, BOOL *do_cache)
+{
+#ifdef EXPERIMENTAL_DKIM
+  dkim_exim_verify_result(keystring,result,errmsg);
+  return OK;
+#else
+  *errmsg = US"DKIM support not compiled.";
+  *result = US"unverified";
+  return FAIL;
+#endif
+}
+
+/* End of lookups/dkim.c */
diff --git a/src/src/lookups/dkim.h b/src/src/lookups/dkim.h
new file mode 100755 (executable)
index 0000000..6e07142
--- /dev/null
@@ -0,0 +1,16 @@
+/* $Cambridge: exim/src/src/lookups/dkim.h,v 1.1 2007/09/28 12:21:57 tom Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Header for the DKIM lookup */
+
+extern void *dkim_open(uschar *, uschar **);
+extern int   dkim_find(void *, uschar *, uschar *, int, uschar **, uschar **,
+               BOOL *);
+
+/* End of lookups/dkim.h */
index 651121956b0326f71e57f8b10f9050794cf9f145..62db50f96afca9a6a1791723a62cf0b916c0b733 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/receive.c,v 1.41 2007/08/22 14:20:28 ph10 Exp $ */
+/* $Cambridge: exim/src/src/receive.c,v 1.42 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 #include "exim.h"
 
+#if (defined EXPERIMENTAL_DOMAINKEYS) && (defined EXPERIMENTAL_DKIM)
+
+#warning Chaining Domainkeys via DKIM receive functions
+#define RECEIVE_GETC dkim_receive_getc
+#define RECEIVE_UNGETC dkim_receive_ungetc
+
+#else
+
+#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
+
 #ifdef EXPERIMENTAL_DOMAINKEYS
+#warning Using Domainkeys receive functions
 #define RECEIVE_GETC dk_receive_getc
 #define RECEIVE_UNGETC dk_receive_ungetc
+#endif
+#ifdef EXPERIMENTAL_DKIM
+#warning Using DKIM receive functions
+#define RECEIVE_GETC dkim_receive_getc
+#define RECEIVE_UNGETC dkim_receive_ungetc
+#endif
+
 #else
+
+/* Normal operation */
 #define RECEIVE_GETC receive_getc
 #define RECEIVE_UNGETC receive_ungetc
+
+#endif
+
 #endif
 
+
 /*************************************************
 *                Local static variables          *
 *************************************************/
@@ -1393,6 +1417,12 @@ message_linecount = body_linecount = body_zerocount =
    inside dk_exim_verify_init(). */
 dk_exim_verify_init();
 #endif
+#ifdef EXPERIMENTAL_DKIM
+/* Call into DKIM to set up the context. Check if DKIM is to be run are carried out
+   inside dk_exim_verify_init(). */
+dkim_exim_verify_init();
+#endif
+
 
 /* Remember the time of reception. Exim uses time+pid for uniqueness of message
 ids, and fractions of a second are required. See the comments that precede the
@@ -2975,6 +3005,9 @@ else
 #ifdef EXPERIMENTAL_DOMAINKEYS
     dk_exim_verify_finish();
 #endif
+#ifdef EXPERIMENTAL_DKIM
+    dkim_exim_verify_finish();
+#endif
 
 #ifdef WITH_CONTENT_SCAN
     if (acl_smtp_mime != NULL &&
index 36353b7c80fc9eb3e23874055835b5f21b336344..656b972477626fa95c8690684abb8fa774179002 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/sieve.c,v 1.30 2007/09/24 11:52:16 michael Exp $ */
+/* $Cambridge: exim/src/src/sieve.c,v 1.31 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -1942,14 +1942,14 @@ Returns:      1                success
               -1               syntax or execution error
 */
 
-static int parse_testlist(struct Sieve *filter, int *n, int *true, int exec)
+static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec)
 {
 if (parse_white(filter)==-1) return -1;
 if (*filter->pc=='(')
   {
   ++filter->pc;
   *n=0;
-   *true=0;
+   *num_true=0;
   for (;;)
     {
     int cond;
@@ -1958,7 +1958,7 @@ if (*filter->pc=='(')
       {
       case -1: return -1;
       case 0: filter->errmsg=CUS "missing test"; return -1;
-      default: ++*n; if (cond) ++*true; break;
+      default: ++*n; if (cond) ++*num_true; break;
       }
     if (parse_white(filter)==-1) return -1;
     if (*filter->pc==',') ++filter->pc;
@@ -2146,13 +2146,13 @@ else if (parse_identifier(filter,CUS "allof"))
   allof-test   = "allof" <tests: test-list>
   */
 
-  int n,true;
+  int n,num_true;
 
-  switch (parse_testlist(filter,&n,&true,exec))
+  switch (parse_testlist(filter,&n,&num_true,exec))
     {
     case -1: return -1;
     case 0: filter->errmsg=CUS "missing test list"; return -1;
-    default: *cond=(n==true); return 1;
+    default: *cond=(n==num_true); return 1;
     }
   }
 else if (parse_identifier(filter,CUS "anyof"))
@@ -2161,13 +2161,13 @@ else if (parse_identifier(filter,CUS "anyof"))
   anyof-test   = "anyof" <tests: test-list>
   */
 
-  int n,true;
+  int n,num_true;
 
-  switch (parse_testlist(filter,&n,&true,exec))
+  switch (parse_testlist(filter,&n,&num_true,exec))
     {
     case -1: return -1;
     case 0: filter->errmsg=CUS "missing test list"; return -1;
-    default: *cond=(true>0); return 1;
+    default: *cond=(num_true>0); return 1;
     }
   }
 else if (parse_identifier(filter,CUS "exists"))
index 6c66a5634240c2e7ab718673f0f44c14011211fb..de7663b1a03dfb33910f4cb3ff06638bfbafc8ad 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.61 2007/08/22 14:20:28 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.62 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -1025,6 +1025,9 @@ bmi_verdicts = NULL;
 #ifdef EXPERIMENTAL_DOMAINKEYS
 dk_do_verify = 0;
 #endif
+#ifdef EXPERIMENTAL_DKIM
+dkim_do_verify = 0;
+#endif
 #ifdef EXPERIMENTAL_SPF
 spf_header_comment = NULL;
 spf_received = NULL;
index a0fdcf96c294a902f203d3303cbee2c012a124c2..a13d9d4cfd5ca5aeb48046c0ad7f6cd35a4cd76b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/spool_in.c,v 1.20 2007/06/22 14:38:58 ph10 Exp $ */
+/* $Cambridge: exim/src/src/spool_in.c,v 1.21 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -282,6 +282,10 @@ bmi_verdicts = NULL;
 dk_do_verify = 0;
 #endif
 
+#ifdef EXPERIMENTAL_DKIM
+dkim_do_verify = 0;
+#endif
+
 #ifdef SUPPORT_TLS
 tls_certificate_verified = FALSE;
 tls_cipher = NULL;
index 3e63052e15d582447e67e5d76d10fec9cb309ae9..4843d5250d9d6607ae6bdbccf37b6e8a0999c62e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transport.c,v 1.19 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transport.c,v 1.20 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -1127,6 +1127,193 @@ dk_transport_write_message(address_item *addr, int fd, int options,
 #endif
 
 
+
+#ifdef EXPERIMENTAL_DKIM
+
+/**********************************************************************************
+*    External interface to write the message, while signing it with DKIM          *
+**********************************************************************************/
+
+/* This function is a wrapper around transport_write_message(). It is only called
+   from the smtp transport if
+   (1) DKIM support is compiled in.
+   (2) The dkim_private_key and dkim_domain option on the smtp transport is set.
+   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
+   very last thing). When we are done signing the file, send the
+   signed message down the original fd (or TLS fd).
+
+Arguments:     as for internal_transport_write_message() above, with additional
+               arguments:
+               uschar *dkim_private_key         The private key to use (filename or plain data)
+               uschar *dkim_domain              The domain to use
+               uschar *dkim_selector            The selector to use.
+               uschar *dkim_canon               The canonalization scheme to use, "simple" or "relaxed"
+               uschar *dkim_strict              What to do if signing fails: 1/true  => throw error
+                                                                             0/false => send anyway
+
+Returns:       TRUE on success; FALSE (with errno) for any failure
+*/
+
+BOOL
+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 *dkim_private_key, uschar *dkim_domain,
+  uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers)
+{
+  int dkim_fd;
+  int save_errno = 0;
+  BOOL rc;
+  uschar dkim_spool_name[256];
+  char sbuf[2048];
+  int sread = 0;
+  int wwritten = 0;
+  uschar *dkim_signature = NULL;
+  off_t size = 0;
+
+  (void)string_format(dkim_spool_name, 256, "%s/input/%s/%s-%d-K",
+          spool_directory, message_subdir, message_id, (int)getpid());
+  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;
+    save_errno = errno;
+    goto CLEANUP;
+    }
+
+  /* Call original function */
+  rc = transport_write_message(addr, dkim_fd, options,
+    size_limit, add_headers, remove_headers,
+    check_string, escape_string, rewrite_rules,
+    rewrite_existflags);
+
+  /* Save error state. We must clean up before returning. */
+  if (!rc)
+    {
+    save_errno = errno;
+    goto CLEANUP;
+    }
+
+  /* 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)
+    {
+    /* Send the signature first */
+    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;
+      }
+    }
+  else 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) )
+        {
+        save_errno = errno;
+        rc = FALSE;
+        goto CLEANUP;
+        }
+      }
+    }
+
+  /* Fetch file positition (the size) */
+  size = lseek(dkim_fd,0,SEEK_CUR);
+
+  /* Rewind file */
+  lseek(dkim_fd, 0, SEEK_SET);
+
+#ifdef HAVE_LINUX_SENDFILE
+  /* We can use sendfile() to shove the file contents
+     to the socket. However only if we don't use TLS,
+     in which case theres another layer of indirection
+     before the data finally hits the socket. */
+  if (tls_active != fd)
+    {
+    ssize_t copied = 0;
+    off_t offset = 0;
+    while((copied >= 0) && (offset<size))
+      {
+      copied = sendfile(fd, dkim_fd, &offset, (size - offset));
+      }
+    if (copied < 0)
+      {
+      save_errno = errno;
+      rc = FALSE;
+      }
+    goto CLEANUP;
+    }
+#endif
+
+  /* Send file down the original fd */
+  while((sread = read(dkim_fd,sbuf,2048)) > 0)
+    {
+    char *p = sbuf;
+    /* write the chunk */
+    DKIM_WRITE:
+    #ifdef SUPPORT_TLS
+    if (tls_active == fd) wwritten = tls_write(US p, sread); else
+    #endif
+    wwritten = write(fd,p,sread);
+    if (wwritten == -1)
+      {
+      /* error, bail out */
+      save_errno = errno;
+      rc = FALSE;
+      goto CLEANUP;
+      }
+    if (wwritten < sread)
+      {
+      /* short write, try again */
+      p += wwritten;
+      sread -= wwritten;
+      goto DKIM_WRITE;
+      }
+    }
+
+  if (sread == -1)
+    {
+    save_errno = errno;
+    rc = FALSE;
+    goto CLEANUP;
+    }
+
+  CLEANUP:
+  /* unlink -K file */
+  (void)close(dkim_fd);
+  Uunlink(dkim_spool_name);
+  errno = save_errno;
+  return rc;
+}
+#endif
+
+
+
 /*************************************************
 *    External interface to write the message     *
 *************************************************/
index 537f32aa512373ea78366b0d1e5e3e55479e6fb7..3c09c91816aa6e9aa26d8f4f6989850068dd42d5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.37 2007/06/18 13:57:50 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.38 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -52,6 +52,20 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, dk_selector) },
   { "dk_strict", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dk_strict) },
+#endif
+#ifdef EXPERIMENTAL_DKIM
+  { "dkim_canon", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_canon) },
+  { "dkim_domain", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_domain) },
+  { "dkim_private_key", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_private_key) },
+  { "dkim_selector", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_selector) },
+  { "dkim_sign_headers", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_sign_headers) },
+  { "dkim_strict", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim_strict) },
 #endif
   { "dns_qualify_single",   opt_bool,
       (void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
@@ -203,6 +217,14 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* dk_selector */
   NULL                 /* dk_strict */
   #endif
+  #ifdef EXPERIMENTAL_DKIM
+ ,NULL,                /* dkim_canon */
+  NULL,                /* dkim_domain */
+  NULL,                /* dkim_private_key */
+  NULL,                /* dkim_selector */
+  NULL,                /* dkim_sign_headers */
+  NULL                 /* dkim_strict */
+  #endif
 };
 
 
@@ -1589,6 +1611,23 @@ if (!ok) ok = TRUE; else
       ob->dk_private_key, ob->dk_domain, ob->dk_selector,
       ob->dk_canon, ob->dk_headers, ob->dk_strict);
   else
+#endif
+#ifdef EXPERIMENTAL_DKIM
+  if ( (ob->dkim_private_key != NULL) && (ob->dkim_domain != NULL) && (ob->dkim_selector != NULL) )
+    ok = dkim_transport_write_message(addrlist, inblock.sock,
+      topt_use_crlf | topt_end_dot | topt_escape_headers |
+        (tblock->body_only? topt_no_headers : 0) |
+        (tblock->headers_only? topt_no_body : 0) |
+        (tblock->return_path_add? topt_add_return_path : 0) |
+        (tblock->delivery_date_add? topt_add_delivery_date : 0) |
+        (tblock->envelope_to_add? topt_add_envelope_to : 0),
+      0,            /* No size limit */
+      tblock->add_headers, tblock->remove_headers,
+      US".", US"..",    /* Escaping strings */
+      tblock->rewrite_rules, tblock->rewrite_existflags,
+      ob->dkim_private_key, ob->dkim_domain, ob->dkim_selector,
+      ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers);
+  else
 #endif
   ok = transport_write_message(addrlist, inblock.sock,
     topt_use_crlf | topt_end_dot | topt_escape_headers |
index 63e76eb938a15a585568ed7b48e2263ca81c4b49..a731c039d4270c2a87317e672909d27f05ca1cd6 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.h,v 1.12 2007/02/06 14:49:13 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.h,v 1.13 2007/09/28 12:21:57 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -65,6 +65,14 @@ typedef struct {
   uschar *dk_headers;
   uschar *dk_strict;
   #endif
+  #ifdef EXPERIMENTAL_DKIM
+  uschar *dkim_domain;
+  uschar *dkim_private_key;
+  uschar *dkim_selector;
+  uschar *dkim_canon;
+  uschar *dkim_sign_headers;
+  uschar *dkim_strict;
+  #endif
 } smtp_transport_options_block;
 
 /* Data for reading the private options. */