-# $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
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.
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
#!/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.
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
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
-/* $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 *
#ifdef EXPERIMENTAL_DOMAINKEYS
CONTROL_DK_VERIFY,
#endif
+ #ifdef EXPERIMENTAL_DKIM
+ CONTROL_DKIM_VERIFY,
+ #endif
CONTROL_ERROR,
CONTROL_CASEFUL_LOCAL_PART,
CONTROL_CASELOWER_LOCAL_PART,
#ifdef EXPERIMENTAL_DOMAINKEYS
US"dk_verify",
#endif
+ #ifdef EXPERIMENTAL_DKIM
+ US"dkim_verify",
+ #endif
US"error",
US"caseful_local_part",
US"caselower_local_part",
(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)
#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 },
break;
#endif
+ #ifdef EXPERIMENTAL_DKIM
+ case CONTROL_DKIM_VERIFY:
+ dkim_do_verify = 1;
+ break;
+ #endif
+
case CONTROL_ERROR:
return ERROR;
-/* $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 *
#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
--- /dev/null
+/* $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
--- /dev/null
+/* $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
-/* $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 *
#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,
#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 */
{
-/* $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 *
#ifdef EXPERIMENTAL_DOMAINKEYS
fprintf(f, " Experimental_DomainKeys");
#endif
+#ifdef EXPERIMENTAL_DKIM
+ fprintf(f, " Experimental_DKIM");
+#endif
fprintf(f, "\n");
fprintf(f, "Lookups:");
-/* $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 *
#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. */
-/* $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 *
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);
-/* $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 *
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;
-/* $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 *
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) */
-# $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
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
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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 */
-/* $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 *
*************************************************/
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
#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 &&
-/* $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 *
-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;
{
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;
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"))
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"))
-/* $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 *
#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;
-/* $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 *
dk_do_verify = 0;
#endif
+#ifdef EXPERIMENTAL_DKIM
+dkim_do_verify = 0;
+#endif
+
#ifdef SUPPORT_TLS
tls_certificate_verified = FALSE;
tls_cipher = NULL;
-/* $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 *
#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 *
*************************************************/
-/* $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 *
(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) },
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
};
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 |
-/* $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 *
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. */