From: Tom Kistner Date: Fri, 28 Sep 2007 12:21:57 +0000 (+0000) Subject: [Buzilla 376] Preliminary DKIM support X-Git-Tag: exim-4_69~13 X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=commitdiff_plain;h=f7572e5a358cd3d9581140b87e590d58b6c278f0 [Buzilla 376] Preliminary DKIM support --- diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 698a2b207..ac5a4d822 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -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 diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index c016e50c7..e30a8f9ba 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -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 diff --git a/src/src/acl.c b/src/src/acl.c index bc374b461..04e785821 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -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<next) break; #endif + #ifdef EXPERIMENTAL_DKIM + case CONTROL_DKIM_VERIFY: + dkim_do_verify = 1; + break; + #endif + case CONTROL_ERROR: return ERROR; diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 1949cb7f4..db752dad1 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -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 index 000000000..3b6e4e637 --- /dev/null +++ b/src/src/dkim-exim.c @@ -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= 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 index 000000000..b974d9522 --- /dev/null +++ b/src/src/dkim-exim.h @@ -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 + +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 diff --git a/src/src/drtables.c b/src/src/drtables.c index 38fce1bb0..b95d4fc95 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -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 */ { diff --git a/src/src/exim.c b/src/src/exim.c index 77e5fa4ac..2da43ff88 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -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:"); diff --git a/src/src/exim.h b/src/src/exim.h index 8fa6959c8..9118e4d89 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -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. */ diff --git a/src/src/functions.h b/src/src/functions.h index 40281bd8d..296aa25a6 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -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); diff --git a/src/src/globals.c b/src/src/globals.c index 74e87e6d3..c46199da6 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -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; diff --git a/src/src/globals.h b/src/src/globals.h index 5b9733c38..d25965e38 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -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) */ diff --git a/src/src/lookups/Makefile b/src/src/lookups/Makefile index fce944589..f19679a06 100644 --- a/src/src/lookups/Makefile +++ b/src/src/lookups/Makefile @@ -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 index 000000000..f90283ee5 --- /dev/null +++ b/src/src/lookups/dkim.c @@ -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 index 000000000..6e07142cf --- /dev/null +++ b/src/src/lookups/dkim.h @@ -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 */ diff --git a/src/src/receive.c b/src/src/receive.c index 651121956..62db50f96 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -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 * @@ -11,14 +11,38 @@ #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 && diff --git a/src/src/sieve.c b/src/src/sieve.c index 36353b7c8..656b97247 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -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" */ - 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" */ - 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")) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 6c66a5634..de7663b1a 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -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; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index a0fdcf96c..a13d9d4cf 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -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; diff --git a/src/src/transport.c b/src/src/transport.c index 3e63052e1..4843d5250 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -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 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 * *************************************************/ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 537f32aa5..3c09c9181 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -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 | diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 63e76eb93..a731c039d 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -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. */