diff options
-rw-r--r-- | src/OS/Makefile-Base | 8 | ||||
-rwxr-xr-x | src/scripts/MakeLinks | 4 | ||||
-rw-r--r-- | src/src/acl.c | 186 | ||||
-rw-r--r-- | src/src/config.h.defaults | 3 | ||||
-rw-r--r-- | src/src/dk.c | 418 | ||||
-rw-r--r-- | src/src/dk.h | 51 | ||||
-rw-r--r-- | src/src/exim.c | 5 | ||||
-rw-r--r-- | src/src/exim.h | 5 | ||||
-rw-r--r-- | src/src/expand.c | 62 | ||||
-rw-r--r-- | src/src/functions.h | 7 | ||||
-rw-r--r-- | src/src/globals.c | 8 | ||||
-rw-r--r-- | src/src/globals.h | 8 | ||||
-rw-r--r-- | src/src/mime.c | 23 | ||||
-rw-r--r-- | src/src/receive.c | 45 | ||||
-rw-r--r-- | src/src/smtp_in.c | 5 | ||||
-rw-r--r-- | src/src/spool_in.c | 6 | ||||
-rw-r--r-- | src/src/transport.c | 160 | ||||
-rw-r--r-- | src/src/transports/smtp.c | 41 | ||||
-rw-r--r-- | src/src/transports/smtp.h | 10 |
19 files changed, 1012 insertions, 43 deletions
diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 23305f84c..e91015cb2 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-Base,v 1.3 2005/02/17 11:58:25 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-Base,v 1.4 2005/03/08 15:32:02 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 @@ -290,7 +290,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 +OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.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. @@ -455,7 +455,7 @@ dummies.o: dummies.c # Compile instructions for perl.o for when EXIM_PERL is set perl.o: $(HDRS) perl.c - $(PERL_CC) $(PERL_CCOPTS) $(INCLUDE) -c perl.c + $(PERL_CC) $(PERL_CCOPTS) $(CFLAGS) $(INCLUDE) -c perl.c # Compile instructions for the database utility modules @@ -575,7 +575,7 @@ demime.o: $(HDRS) demime.c bmi_spam.o: $(HDRS) bmi_spam.c spf.o: $(HDRS) spf.c srs.o: $(HDRS) srs.c - +dk.o: $(HDRS) dk.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 1e6eaf151..fea7c1d06 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -1,5 +1,5 @@ #!/bin/sh -# $Cambridge: exim/src/scripts/MakeLinks,v 1.2 2004/12/16 15:11:47 tom Exp $ +# $Cambridge: exim/src/scripts/MakeLinks,v 1.3 2005/03/08 15:32:02 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. @@ -261,5 +261,7 @@ ln -s ../src/spf.c spf.c ln -s ../src/spf.h spf.h 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 # End of MakeLinks diff --git a/src/src/acl.c b/src/src/acl.c index 7b176b690..439e9d424 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.19 2005/02/17 11:58:25 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.20 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -46,6 +46,14 @@ ACLC_CONDITION, ACLC_CONTROL, #ifdef WITH_OLD_DEMIME ACLC_DEMIME, #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + ACLC_DK_DOMAIN_SOURCE, + ACLC_DK_POLICY, + ACLC_DK_SENDER_DOMAINS, + ACLC_DK_SENDER_LOCAL_PARTS, + ACLC_DK_SENDERS, + ACLC_DK_STATUS, +#endif ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS, ACLC_HOSTS, ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE, #ifdef WITH_CONTENT_SCAN @@ -85,6 +93,14 @@ static uschar *conditions[] = { US"acl", US"authenticated", #ifdef WITH_OLD_DEMIME US"demime", #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + US"dk_domain_source", + US"dk_policy", + US"dk_sender_domains", + US"dk_sender_local_parts", + US"dk_senders", + US"dk_status", +#endif US"dnslists", US"domains", US"encrypted", US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite", #ifdef WITH_CONTENT_SCAN @@ -132,6 +148,14 @@ static uschar cond_expand_at_top[] = { #ifdef WITH_OLD_DEMIME TRUE, /* demime */ #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + TRUE, + TRUE, + TRUE, + TRUE, + TRUE, + TRUE, +#endif TRUE, /* dnslists */ FALSE, /* domains */ FALSE, /* encrypted */ @@ -180,6 +204,14 @@ static uschar cond_modifiers[] = { #ifdef WITH_OLD_DEMIME FALSE, /* demime */ #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, +#endif FALSE, /* dnslists */ FALSE, /* domains */ FALSE, /* encrypted */ @@ -259,6 +291,56 @@ static unsigned int cond_forbids[] = { (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME), #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + (1<<ACL_WHERE_AUTH)| + (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)| + (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)| + (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)| + (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)| + (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)| + (1<<ACL_WHERE_VRFY), + + (1<<ACL_WHERE_AUTH)| + (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)| + (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)| + (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)| + (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)| + (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)| + (1<<ACL_WHERE_VRFY), + + (1<<ACL_WHERE_AUTH)| + (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)| + (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)| + (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)| + (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)| + (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)| + (1<<ACL_WHERE_VRFY), + + (1<<ACL_WHERE_AUTH)| + (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)| + (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)| + (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)| + (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)| + (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)| + (1<<ACL_WHERE_VRFY), + + (1<<ACL_WHERE_AUTH)| + (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)| + (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)| + (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)| + (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)| + (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)| + (1<<ACL_WHERE_VRFY), + + (1<<ACL_WHERE_AUTH)| + (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)| + (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)| + (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)| + (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)| + (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)| + (1<<ACL_WHERE_VRFY), +#endif + (1<<ACL_WHERE_NOTSMTP), /* dnslists */ (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)| /* domains */ @@ -373,6 +455,9 @@ enum { #ifdef EXPERIMENTAL_BRIGHTMAIL CONTROL_BMI_RUN, #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + CONTROL_DK_VERIFY, +#endif CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART, CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE, CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, @@ -389,6 +474,9 @@ static unsigned int control_forbids[] = { #ifdef EXPERIMENTAL_BRIGHTMAIL 0, /* bmi_run */ #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA), /* dk_verify */ +#endif 0, /* error */ @@ -441,6 +529,9 @@ static control_def controls_list[] = { #ifdef EXPERIMENTAL_BRIGHTMAIL { US"bmi_run", CONTROL_BMI_RUN, FALSE}, #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + { US"dk_verify", CONTROL_DK_VERIFY, FALSE}, +#endif { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE}, { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE}, { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE}, @@ -1648,7 +1739,11 @@ for (; cb != NULL; cb = cb->next) bmi_run = 1; break; #endif - +#ifdef EXPERIMENTAL_DOMAINKEYS + case CONTROL_DK_VERIFY: + dk_do_verify = 1; + break; +#endif case CONTROL_ERROR: return ERROR; @@ -1769,6 +1864,93 @@ for (; cb != NULL; cb = cb->next) break; #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + case ACLC_DK_DOMAIN_SOURCE: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + /* check header source of domain against given string */ + switch (dk_verify_block->address_source) { + case DK_EXIM_ADDRESS_FROM_FROM: + rc = match_isinlist(US"from", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_ADDRESS_FROM_SENDER: + rc = match_isinlist(US"sender", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_ADDRESS_NONE: + rc = match_isinlist(US"none", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + } + break; + case ACLC_DK_POLICY: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + /* check policy against given string, default FAIL */ + rc = FAIL; + if (dk_verify_block->signsall) + rc = match_isinlist(US"signsall", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + if (dk_verify_block->testing) + rc = match_isinlist(US"testing", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case ACLC_DK_SENDER_DOMAINS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->domain != NULL) + rc = match_isinlist(dk_verify_block->domain, &arg, 0, &domainlist_anchor, + NULL, MCL_DOMAIN, TRUE, NULL); + else rc = FAIL; + break; + case ACLC_DK_SENDER_LOCAL_PARTS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->local_part != NULL) + rc = match_isinlist(dk_verify_block->local_part, &arg, 0, &localpartlist_anchor, + NULL, MCL_LOCALPART, TRUE, NULL); + else rc = FAIL; + break; + case ACLC_DK_SENDERS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->address != NULL) + rc = match_address_list(dk_verify_block->address, TRUE, TRUE, &arg, NULL, -1, 0, NULL); + else rc = FAIL; + break; + case ACLC_DK_STATUS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->result > 0) { + switch(dk_verify_block->result) { + case DK_EXIM_RESULT_BAD_FORMAT: + rc = match_isinlist(US"bad format", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_NO_KEY: + rc = match_isinlist(US"no key", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_NO_SIGNATURE: + rc = match_isinlist(US"no signature", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_REVOKED: + rc = match_isinlist(US"revoked", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_NON_PARTICIPANT: + rc = match_isinlist(US"non-participant", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_GOOD: + rc = match_isinlist(US"good", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_BAD: + rc = match_isinlist(US"bad", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + } + } + break; +#endif + case ACLC_DNSLISTS: rc = verify_check_dnsbl(&arg); break; diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 5484a0786..ea63c888f 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.4 2005/01/04 10:00:42 ph10 Exp $ */ +/* $Cambridge: exim/src/src/config.h.defaults,v 1.5 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -144,6 +144,7 @@ in config.h unless some value is defined in Local/Makefile. */ /* EXPERIMENTAL features */ #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS +#define EXPERIMENTAL_DOMAINKEYS #define EXPERIMENTAL_BRIGHTMAIL /* Things that are not routinely changed but are nevertheless configurable diff --git a/src/src/dk.c b/src/src/dk.c new file mode 100644 index 000000000..c31a1ddb6 --- /dev/null +++ b/src/src/dk.c @@ -0,0 +1,418 @@ +/* $Cambridge: exim/src/src/dk.c,v 1.1 2005/03/08 15:32:02 tom Exp $ */ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Code for DomainKeys support. Other DK relevant code is in + receive.c, transport.c and transports/smtp.c */ + +#include "exim.h" + +#ifdef EXPERIMENTAL_DOMAINKEYS + +/* Globals related to the DK reference library. */ +DK *dk_context = NULL; +DK_LIB *dk_lib = NULL; +DK_FLAGS dk_flags; +DK_STAT dk_internal_status; + +/* Globals related to Exim DK implementation. */ +dk_exim_verify_block *dk_verify_block = NULL; + +/* 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 dkbuff[6] = {256,256,256,256,256,256}; + +/* receive_getc() wrapper that feeds DK while Exim reads + the message. */ +int dk_receive_getc(void) { + int i; + int c = receive_getc(); + + if (dk_context != NULL) { + /* Send oldest byte */ + if ((dkbuff[0] < 256) && (dk_internal_status == DK_STAT_OK)) { + dk_internal_status = dk_message(dk_context, (char *)&dkbuff[0], 1); + if (dk_internal_status != DK_STAT_OK) + DEBUG(D_receive) debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status)); + } + /* rotate buffer */ + for (i=0;i<5;i++) dkbuff[i]=dkbuff[i+1]; + dkbuff[5]=c; + /* look for our candidate patterns */ + if ( (dkbuff[1] == '\r') && + (dkbuff[2] == '\n') && + (dkbuff[3] == '.') && + (dkbuff[4] == '\r') && + (dkbuff[5] == '\n') ) { + /* End of DATA */ + dkbuff[3] = 256; + dkbuff[4] = 256; + dkbuff[5] = 256; + } + if ( (dkbuff[2] == '\r') && + (dkbuff[3] == '\n') && + (dkbuff[4] == '.') && + (dkbuff[5] == '.') ) { + /* doubled dot, skip this char */ + dkbuff[5] = 256; + } + } +return c; +} + +/* When exim puts a char back in the fd, we + must rotate our buffer back. */ +int dk_receive_ungetc(int c) { + int i; + if (dk_context != NULL) { + /* rotate buffer back */ + for (i=5;i>0;i--) dkbuff[i]=dkbuff[i-1]; + dkbuff[0]=256; + } + return receive_ungetc(c); +} + + +void dk_exim_verify_init(void) { + int old_pool = store_pool; + store_pool = POOL_PERM; + + /* Reset DK state in any case. */ + dk_context = NULL; + dk_lib = NULL; + dk_verify_block = NULL; + + /* Set up DK context if DK was requested and input is SMTP. */ + if (smtp_input && !smtp_batched_input && dk_do_verify) { + /* initialize library */ + dk_lib = dk_init(&dk_internal_status); + if (dk_internal_status != DK_STAT_OK) + debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status)); + else { + /* initialize verification context */ + dk_context = dk_verify(dk_lib, &dk_internal_status); + if (dk_internal_status != DK_STAT_OK) { + debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status)); + dk_context = NULL; + } + else { + /* Reserve some space for the verify block. */ + dk_verify_block = store_get(sizeof(dk_exim_verify_block)); + if (dk_verify_block == NULL) { + debug_printf("DK: Can't allocate %d bytes.\n",sizeof(dk_exim_verify_block)); + dk_context = NULL; + } + else { + memset(dk_verify_block, 0, sizeof(dk_exim_verify_block)); + } + } + } + } + store_pool = old_pool; +} + + +void dk_exim_verify_finish(void) { + char *p,*q; + int i; + int old_pool = store_pool; + + /* Bail out if context could not be set up earlier. */ + if (dk_context == NULL) + return; + + store_pool = POOL_PERM; + + /* Send remaining bytes from input which are still in the buffer. */ + for (i=0;i<6;i++) + if (dkbuff[i] < 256) + dk_internal_status = dk_message(dk_context, (char *)&dkbuff[i], 1); + + /* Flag end-of-message. */ + dk_internal_status = dk_end(dk_context, NULL); + + /* Grab address/domain information. */ + p = dk_address(dk_context); + if (p != NULL) { + switch(p[0]) { + case 'N': + dk_verify_block->address_source = DK_EXIM_ADDRESS_NONE; + break; + case 'S': + dk_verify_block->address_source = DK_EXIM_ADDRESS_FROM_SENDER; + break; + case 'F': + dk_verify_block->address_source = DK_EXIM_ADDRESS_FROM_FROM; + break; + } + p++; + if (*p != '\0') { + dk_verify_block->address = string_copy((uschar *)p); + q = strrchr(p,'@'); + if ((q != NULL) && (*(q+1) != '\0')) { + dk_verify_block->domain = string_copy((uschar *)(q+1)); + *q = '\0'; + dk_verify_block->local_part = string_copy((uschar *)p); + } + } + } + + dk_flags = dk_policy(dk_context); + + /* Grab domain policy */ + if (dk_flags & DK_FLAG_SET) { + if (dk_flags & DK_FLAG_TESTING) + dk_verify_block->testing = TRUE; + if (dk_flags & DK_FLAG_SIGNSALL) + dk_verify_block->signsall = TRUE; + } + + /* Set up main result. */ + switch(dk_internal_status) + { + case DK_STAT_NOSIG: + dk_verify_block->is_signed = FALSE; + dk_verify_block->result = DK_EXIM_RESULT_NO_SIGNATURE; + break; + case DK_STAT_OK: + dk_verify_block->is_signed = TRUE; + dk_verify_block->result = DK_EXIM_RESULT_GOOD; + break; + case DK_STAT_BADSIG: + dk_verify_block->is_signed = TRUE; + dk_verify_block->result = DK_EXIM_RESULT_BAD; + break; + case DK_STAT_REVOKED: + dk_verify_block->is_signed = TRUE; + dk_verify_block->result = DK_EXIM_RESULT_REVOKED; + break; + case DK_STAT_BADKEY: + case DK_STAT_SYNTAX: + dk_verify_block->is_signed = TRUE; + /* Syntax -> Bad format? */ + dk_verify_block->result = DK_EXIM_RESULT_BAD_FORMAT; + break; + case DK_STAT_NOKEY: + dk_verify_block->is_signed = TRUE; + dk_verify_block->result = DK_EXIM_RESULT_NO_KEY; + break; + case DK_STAT_NORESOURCE: + case DK_STAT_INTERNAL: + case DK_STAT_ARGS: + case DK_STAT_CANTVRFY: + dk_verify_block->result = DK_EXIM_RESULT_ERR; + break; + /* This is missing DK_EXIM_RESULT_NON_PARTICIPANT. The lib does not + report such a status. */ + } + + /* Set up human readable result string. */ + dk_verify_block->result_string = string_copy((uschar *)DK_STAT_to_string(dk_internal_status)); + + /* All done, reset dk_context. */ + dk_free(dk_context); + dk_context = NULL; + + store_pool = old_pool; +} + +uschar *dk_exim_sign(int dk_fd, + uschar *dk_private_key, + uschar *dk_domain, + uschar *dk_selector, + uschar *dk_canon) { + uschar *rc = NULL; + int dk_canon_int = DK_CANON_SIMPLE; + char c; + int seen_lf = 0; + int seen_lfdot = 0; + uschar sig[1024]; + int save_errno = 0; + int sread; + int old_pool = store_pool; + store_pool = POOL_PERM; + + dk_lib = dk_init(&dk_internal_status); + if (dk_internal_status != DK_STAT_OK) { + debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status)); + rc = NULL; + goto CLEANUP; + } + + /* Figure out what canonicalization to use. Unfortunately + we must do this BEFORE knowing which domain we sign for. */ + if ((dk_canon != NULL) && (Ustrcmp(dk_canon, "nofws") == 0)) dk_canon_int = DK_CANON_NOFWS; + else dk_canon = "simple"; + + /* Initialize signing context. */ + dk_context = dk_sign(dk_lib, &dk_internal_status, dk_canon_int); + if (dk_internal_status != DK_STAT_OK) { + debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status)); + dk_context = NULL; + goto CLEANUP; + } + + while((sread = read(dk_fd,&c,1)) > 0) { + + if ((c == '.') && seen_lfdot) { + /* escaped dot, write "\n.", continue */ + dk_message(dk_context, "\n.", 2); + seen_lf = 0; + seen_lfdot = 0; + continue; + } + + if (seen_lfdot) { + /* EOM, write "\n" and break */ + dk_message(dk_context, "\n", 1); + break; + } + + if ((c == '.') && seen_lf) { + seen_lfdot = 1; + continue; + } + + if (seen_lf) { + /* normal lf, just send it */ + dk_message(dk_context, "\n", 1); + seen_lf = 0; + } + + if (c == '\n') { + seen_lf = 1; + continue; + } + + /* write the char */ + dk_message(dk_context, &c, 1); + } + + /* Handle failed read above. */ + if (sread == -1) { + debug_printf("DK: Error reading -K file.\n"); + save_errno = errno; + rc = NULL; + goto CLEANUP; + } + + /* Flag end-of-message. */ + dk_internal_status = dk_end(dk_context, NULL); + /* TODO: check status */ + + + /* Get domain to use, unless overridden. */ + if (dk_domain == NULL) { + dk_domain = dk_address(dk_context); + switch(dk_domain[0]) { + case 'N': dk_domain = NULL; break; + case 'F': + case 'S': + dk_domain++; + dk_domain = strrchr(dk_domain,'@'); + if (dk_domain != NULL) { + uschar *p; + dk_domain++; + p = dk_domain; + while (*p != 0) { *p = tolower(*p); p++; } + } + break; + } + if (dk_domain == NULL) { + debug_printf("DK: Could not determine domain to use for signing from message headers.\n"); + /* In this case, we return "OK" by sending up an empty string as the + DomainKey-Signature header. If there is no domain to sign for, we + can send the message anyway since the recipient has no policy to + apply ... */ + rc = ""; + goto CLEANUP; + } + } + else { + dk_domain = expand_string(dk_domain); + if (dk_domain == NULL) { + /* expansion error, do not send message. */ + debug_printf("DK: Error while expanding dk_domain option.\n"); + rc = NULL; + goto CLEANUP; + } + } + + /* Set up $dk_domain expansion variable. */ + dk_signing_domain = dk_domain; + + /* Get selector to use. */ + dk_selector = expand_string(dk_selector); + if (dk_selector == NULL) { + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "dk_selector: %s", expand_string_message); + rc = NULL; + goto CLEANUP; + } + + /* Set up $dk_selector expansion variable. */ + dk_signing_selector = dk_selector; + + /* Get private key to use. */ + dk_private_key = expand_string(dk_private_key); + if (dk_private_key == NULL) { + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "dk_private_key: %s", expand_string_message); + rc = NULL; + goto CLEANUP; + } + + if ( (Ustrlen(dk_private_key) == 0) || + (Ustrcmp(dk_private_key,"0") == 0) || + (Ustrcmp(dk_private_key,"false") == 0) ) { + /* don't sign, but no error */ + rc = ""; + goto CLEANUP; + } + + if (dk_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(dk_private_key,O_RDONLY); + read(privkey_fd,big_buffer,16383); + close(privkey_fd); + dk_private_key = big_buffer; + } + + /* Get the signature. */ + dk_internal_status = dk_getsig(dk_context, dk_private_key, sig, 8192); + + /* Check for unuseable key */ + if (dk_internal_status != DK_STAT_OK) { + debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status)); + rc = NULL; + goto CLEANUP; + } + + rc = store_get(1024); + /* Build DomainKey-Signature header to return. */ + snprintf(rc, 1024, "DomainKey-Signature: a=rsa-sha1; q=dns; c=%s;\r\n" + "\ts=%s; d=%s;\r\n" + "\tb=%s;\r\n", dk_canon, dk_selector, dk_domain, sig); + + log_write(0, LOG_MAIN, "DK: message signed using a=rsa-sha1; q=dns; c=%s; s=%s; d=%s;", dk_canon, dk_selector, dk_domain); + + CLEANUP: + if (dk_context != NULL) { + dk_free(dk_context); + dk_context = NULL; + } + store_pool = old_pool; + errno = save_errno; + return rc; +} + +#endif diff --git a/src/src/dk.h b/src/src/dk.h new file mode 100644 index 000000000..0837ebc5d --- /dev/null +++ b/src/src/dk.h @@ -0,0 +1,51 @@ +/* $Cambridge: exim/src/src/dk.h,v 1.1 2005/03/08 15:32:02 tom Exp $ */ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Code for DomainKeys support. Other DK relevant code is in + receive.c, transport.c and transports/smtp.c */ + +#ifdef EXPERIMENTAL_DOMAINKEYS + +#include <domainkeys.h> + +#define DK_EXIM_ADDRESS_NONE 0 +#define DK_EXIM_ADDRESS_FROM_FROM 1 +#define DK_EXIM_ADDRESS_FROM_SENDER 2 + +#define DK_EXIM_RESULT_ERR 0 +#define DK_EXIM_RESULT_BAD_FORMAT 1 +#define DK_EXIM_RESULT_NO_KEY 2 +#define DK_EXIM_RESULT_NO_SIGNATURE 3 +#define DK_EXIM_RESULT_REVOKED 4 +#define DK_EXIM_RESULT_NON_PARTICIPANT 5 +#define DK_EXIM_RESULT_GOOD 6 +#define DK_EXIM_RESULT_BAD 7 + +typedef struct dk_exim_verify_block { + int result; + int address_source; + uschar *result_string; + uschar *address; + uschar *domain; + uschar *local_part; + BOOL is_signed; + BOOL signsall; + BOOL testing; +} dk_exim_verify_block; + +int dk_receive_getc(void); +int dk_receive_ungetc(int); +void dk_exim_verify_init(void); +void dk_exim_verify_finish(void); +int dk_exim_verify_result(uschar **); +uschar *dk_exim_sign(int, uschar *, uschar *, uschar *, uschar *); + +extern dk_exim_verify_block *dk_verify_block; + +#endif diff --git a/src/src/exim.c b/src/src/exim.c index 214427bf5..040e385f1 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim.c,v 1.14 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/exim.c,v 1.15 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -854,6 +854,9 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_BRIGHTMAIL fprintf(f, " Experimental_Brightmail"); #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + fprintf(f, " Experimental_DomainKeys"); +#endif fprintf(f, "\n"); fprintf(f, "Lookups:"); diff --git a/src/src/exim.h b/src/src/exim.h index d179ca671..ae22894c2 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim.h,v 1.7 2005/01/04 10:00:42 ph10 Exp $ */ +/* $Cambridge: exim/src/src/exim.h,v 1.8 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -402,6 +402,9 @@ mytypes.h and store.h, so we don't need to mention them explicitly. */ #ifdef EXPERIMENTAL_SRS #include "srs.h" #endif +#ifdef EXPERIMENTAL_DOMAINKEYS +#include "dk.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/expand.c b/src/src/expand.c index 714e76b6b..9c9dc22f4 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.13 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.14 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -286,6 +286,9 @@ enum { vtype_load_avg, /* value not used; result is int from os_getloadavg */ vtype_pspace, /* partition space; value is T/F for spool/log */ vtype_pinodes /* partition inodes; value is T/F for spool/log */ +#ifdef EXPERIMENTAL_DOMAINKEYS + ,vtype_dk_verify /* Serve request out of DomainKeys verification structure */ +#endif }; /* This table must be kept in alphabetical order. */ @@ -336,6 +339,19 @@ static var_entry var_table[] = { { "demime_errorlevel", vtype_int, &demime_errorlevel }, { "demime_reason", vtype_stringptr, &demime_reason }, #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + { "dk_domain", vtype_stringptr, &dk_signing_domain }, + { "dk_is_signed", vtype_dk_verify, NULL }, + { "dk_result", vtype_dk_verify, NULL }, + { "dk_selector", vtype_stringptr, &dk_signing_selector }, + { "dk_sender", vtype_dk_verify, NULL }, + { "dk_sender_domain", vtype_dk_verify, NULL }, + { "dk_sender_local_part",vtype_dk_verify, NULL }, + { "dk_sender_source", vtype_dk_verify, NULL }, + { "dk_signsall", vtype_dk_verify, NULL }, + { "dk_status", vtype_dk_verify, NULL }, + { "dk_testing", vtype_dk_verify, NULL }, +#endif { "dnslist_domain", vtype_stringptr, &dnslist_domain }, { "dnslist_text", vtype_stringptr, &dnslist_text }, { "dnslist_value", vtype_stringptr, &dnslist_value }, @@ -1238,6 +1254,50 @@ while (last > first) if (!filter_running) return NULL; /* Fall through */ +#ifdef EXPERIMENTAL_DOMAINKEYS + + case vtype_dk_verify: + s = NULL; + if (Ustrcmp(var_table[middle].name, "dk_result") == 0) + s = dk_verify_block->result_string; + if (Ustrcmp(var_table[middle].name, "dk_sender") == 0) + s = dk_verify_block->address; + if (Ustrcmp(var_table[middle].name, "dk_sender_domain") == 0) + s = dk_verify_block->domain; + if (Ustrcmp(var_table[middle].name, "dk_sender_local_part") == 0) + s = dk_verify_block->local_part; + + if (Ustrcmp(var_table[middle].name, "dk_sender_source") == 0) + switch(dk_verify_block->address_source) { + case DK_EXIM_ADDRESS_NONE: s = "0"; break; + case DK_EXIM_ADDRESS_FROM_FROM: s = "from"; break; + case DK_EXIM_ADDRESS_FROM_SENDER: s = "sender"; break; + } + + if (Ustrcmp(var_table[middle].name, "dk_status") == 0) + switch(dk_verify_block->result) { + case DK_EXIM_RESULT_ERR: s = "error"; break; + case DK_EXIM_RESULT_BAD_FORMAT: s = "bad format"; break; + case DK_EXIM_RESULT_NO_KEY: s = "no key"; break; + case DK_EXIM_RESULT_NO_SIGNATURE: s = "no signature"; break; + case DK_EXIM_RESULT_REVOKED: s = "revoked"; break; + case DK_EXIM_RESULT_NON_PARTICIPANT: s = "non-participant"; break; + case DK_EXIM_RESULT_GOOD: s = "good"; break; + case DK_EXIM_RESULT_BAD: s = "bad"; break; + } + + if (Ustrcmp(var_table[middle].name, "dk_signsall") == 0) + s = (dk_verify_block->signsall)? "1" : "0"; + + if (Ustrcmp(var_table[middle].name, "dk_testing") == 0) + s = (dk_verify_block->testing)? "1" : "0"; + + if (Ustrcmp(var_table[middle].name, "dk_is_signed") == 0) + s = (dk_verify_block->is_signed)? "1" : "0"; + + return (s == NULL)? US"" : s; +#endif + case vtype_int: sprintf(CS var_buffer, "%d", *(int *)(var_table[middle].value)); /* Integer */ return var_buffer; diff --git a/src/src/functions.h b/src/src/functions.h index 95c91cb9a..88bc53d74 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.11 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.12 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -72,6 +72,11 @@ extern void deliver_succeeded(address_item *); extern int demime(uschar **); #endif extern BOOL directory_make(uschar *, uschar *, int, BOOL); +#ifdef EXPERIMENTAL_DOMAINKEYS +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 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 7bef5792f..fdccc0b98 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.c,v 1.18 2005/03/01 10:21:44 ph10 Exp $ */ +/* $Cambridge: exim/src/src/globals.c,v 1.19 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -463,6 +463,12 @@ uschar *demime_reason = NULL; #endif BOOL disable_logging = FALSE; +#ifdef EXPERIMENTAL_DOMAINKEYS +uschar *dk_signing_domain = NULL; +uschar *dk_signing_selector = NULL; +int dk_do_verify = 0; +#endif + uschar *dns_again_means_nonexist = NULL; uschar *dns_ipv4_lookup = NULL; int dns_retrans = 0; diff --git a/src/src/globals.h b/src/src/globals.h index c7be2e0ea..2bb61c145 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.h,v 1.11 2005/01/25 14:16:33 ph10 Exp $ */ +/* $Cambridge: exim/src/src/globals.h,v 1.12 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -257,6 +257,12 @@ extern uschar *demime_reason; /* Reason for broken MIME container */ #endif extern BOOL disable_logging; /* Disables log writing when TRUE */ +#ifdef EXPERIMENTAL_DOMAINKEYS +extern uschar *dk_signing_domain; /* Domain used for signing a message. */ +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 + extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */ extern uschar *dns_ipv4_lookup; /* For these domains, don't look for AAAA (or A6) */ extern int dns_retrans; /* Retransmission time setting */ diff --git a/src/src/mime.c b/src/src/mime.c index 0a6367200..bece458c0 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/mime.c,v 1.4 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/mime.c,v 1.5 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -115,11 +115,8 @@ uschar *mime_decode_qp_char(uschar *qp_p,int *c) { } -uschar *mime_parse_line(uschar *buffer, uschar *encoding, int *num_decoded) { - uschar *data = NULL; - - data = (uschar *)malloc(Ustrlen(buffer)+2); - +uschar *mime_parse_line(uschar *buffer, uschar *data, uschar *encoding, int *num_decoded) { + if (encoding == NULL) { /* no encoding type at all */ NO_DECODING: @@ -285,6 +282,7 @@ int mime_decode(uschar **listptr) { uschar decode_path[1024]; FILE *decode_file = NULL; uschar *buffer = NULL; + uschar *decode_buffer = NULL; long f_pos = 0; unsigned int size_counter = 0; @@ -296,7 +294,7 @@ int mime_decode(uschar **listptr) { /* build default decode path (will exist since MBOX must be spooled up) */ snprintf(CS decode_path,1024,"%s/scan/%s",spool_directory,message_id); - /* reserve a line buffer to work in */ + /* reserve a line and decoder buffer to work in */ buffer = (uschar *)malloc(MIME_MAX_LINE_LENGTH+1); if (buffer == NULL) { log_write(0, LOG_PANIC, @@ -304,6 +302,13 @@ int mime_decode(uschar **listptr) { return DEFER; }; + decode_buffer = (uschar *)malloc(MIME_MAX_LINE_LENGTH+1); + if (decode_buffer == NULL) { + log_write(0, LOG_PANIC, + "decode ACL condition: can't allocate %d bytes of memory.", MIME_MAX_LINE_LENGTH+1); + return DEFER; + }; + /* try to find 1st option */ if ((option = string_nextinlist(&list, &sep, option_buffer, @@ -358,7 +363,8 @@ int mime_decode(uschar **listptr) { }; }; - decoded_line = mime_parse_line(buffer, mime_content_transfer_encoding, &decoded_line_length); + decoded_line = mime_parse_line(buffer, decode_buffer, mime_content_transfer_encoding, &decoded_line_length); + /* write line to decode file */ if (fwrite(decoded_line, 1, decoded_line_length, decode_file) < decoded_line_length) { /* error/short write */ @@ -376,7 +382,6 @@ int mime_decode(uschar **listptr) { size_counter = (size_counter % 1024); }; - free(decoded_line); } fclose(decode_file); diff --git a/src/src/receive.c b/src/src/receive.c index 7f814e64a..e4ce9cb23 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/receive.c,v 1.11 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/receive.c,v 1.12 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -9,10 +9,15 @@ /* Code for receiving a message and setting up spool files. */ - #include "exim.h" - +#ifdef EXPERIMENTAL_DOMAINKEYS +#define RECEIVE_GETC dk_receive_getc +#define RECEIVE_UNGETC dk_receive_ungetc +#else +#define RECEIVE_GETC receive_getc +#define RECEIVE_UNGETC receive_ungetc +#endif /************************************************* * Local static variables * @@ -565,7 +570,7 @@ if (!dot_ends) { register int last_ch = '\n'; - for (; (ch = (receive_getc)()) != EOF; last_ch = ch) + for (; (ch = (RECEIVE_GETC)()) != EOF; last_ch = ch) { if (ch == 0) body_zerocount++; if (last_ch == '\r' && ch != '\n') @@ -595,7 +600,7 @@ if (!dot_ends) ch_state = 1; -while ((ch = (receive_getc)()) != EOF) +while ((ch = (RECEIVE_GETC)()) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -696,7 +701,7 @@ read_message_data_smtp(FILE *fout) int ch_state = 0; register int ch; -while ((ch = (receive_getc)()) != EOF) +while ((ch = (RECEIVE_GETC)()) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -1197,6 +1202,12 @@ from the spool for delivery. */ body_linecount = body_zerocount = 0; +#ifdef EXPERIMENTAL_DOMAINKEYS +/* Call into DK to set up the context. Check if DK is to be run are carried out + inside dk_exim_verify_init(). */ +dk_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 message id creation below. */ @@ -1245,7 +1256,7 @@ next->text. */ for (;;) { - int ch = (receive_getc)(); + int ch = (RECEIVE_GETC)(); /* If we hit EOF on a SMTP connection, it's an error, since incoming SMTP must have a correct "." terminator. */ @@ -1309,7 +1320,7 @@ for (;;) if (ch == '\n') { if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = FALSE; - else if (first_line_ended_crlf) receive_ungetc(' '); + else if (first_line_ended_crlf) RECEIVE_UNGETC(' '); goto EOL; } @@ -1324,13 +1335,13 @@ for (;;) if (ptr == 0 && ch == '.' && (smtp_input || dot_ends)) { - ch = (receive_getc)(); + ch = (RECEIVE_GETC)(); if (ch == '\r') { - ch = (receive_getc)(); + ch = (RECEIVE_GETC)(); if (ch != '\n') { - receive_ungetc(ch); + RECEIVE_UNGETC(ch); ch = '\r'; /* Revert to CR */ } } @@ -1358,7 +1369,7 @@ for (;;) if (ch == '\r') { - ch = (receive_getc)(); + ch = (RECEIVE_GETC)(); if (ch == '\n') { if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE; @@ -1368,7 +1379,7 @@ for (;;) /* Otherwise, put back the character after CR, and turn the bare CR into LF SP. */ - ch = (receive_ungetc)(ch); + ch = (RECEIVE_UNGETC)(ch); next->text[ptr++] = '\n'; message_size++; ch = ' '; @@ -1443,14 +1454,14 @@ for (;;) if (ch != EOF) { - int nextch = (receive_getc)(); + int nextch = (RECEIVE_GETC)(); if (nextch == ' ' || nextch == '\t') { next->text[ptr++] = nextch; message_size++; continue; /* Iterate the loop */ } - else if (nextch != EOF) (receive_ungetc)(nextch); /* For next time */ + else if (nextch != EOF) (RECEIVE_UNGETC)(nextch); /* For next time */ else ch = EOF; /* Cause main loop to exit at end */ } @@ -2738,6 +2749,10 @@ else if (smtp_input && !smtp_batched_input) { +#ifdef EXPERIMENTAL_DOMAINKEYS + dk_exim_verify_finish(); +#endif + #ifdef WITH_CONTENT_SCAN /* MIME ACL hook */ if (acl_smtp_mime != NULL && recipients_count > 0) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 6b4f25522..98a8d6ddf 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.11 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.12 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -823,6 +823,9 @@ authenticated_sender = NULL; bmi_run = 0; bmi_verdicts = NULL; #endif +#ifdef EXPERIMENTAL_DOMAINKEYS +dk_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 0a63887de..fb03d58b0 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.8 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/spool_in.c,v 1.9 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -278,6 +278,10 @@ bmi_run = 0; bmi_verdicts = NULL; #endif +#ifdef EXPERIMENTAL_DOMAINKEYS +dk_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 970e853f0..1bdb67746 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/transport.c,v 1.4 2005/02/17 11:58:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transport.c,v 1.5 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -903,6 +903,164 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 || } +#ifdef EXPERIMENTAL_DOMAINKEYS + +/********************************************************************************** +* External interface to write the message, while signing it with domainkeys * +**********************************************************************************/ + +/* This function is a wrapper around transport_write_message(). It is only called + from the smtp transport if + (1) Domainkeys support is compiled in. + (2) The dk_private_key option on the smtp transport is set. + 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 *dk_private_key The private key to use (filename or plain data) + uschar *dk_domain Override domain (normally NULL) + uschar *dk_selector The selector to use. + uschar *dk_canon The canonalization scheme to use, "simple" or "nofws" + uschar *dk_headers Colon-separated header list to include in the signing + process. + uschar *dk_strict What to do if signing fails: 1/true => throw error + 0/false => send anyway + +Returns: TRUE on success; FALSE (with errno) for any failure +*/ + +BOOL +dk_transport_write_message(address_item *addr, int fd, int options, + int size_limit, uschar *add_headers, uschar *remove_headers, + uschar *check_string, uschar *escape_string, rewrite_rule *rewrite_rules, + int rewrite_existflags, uschar *dk_private_key, uschar *dk_domain, + uschar *dk_selector, uschar *dk_canon, uschar *dk_headers, uschar *dk_strict) +{ + int dk_fd; + int save_errno = 0; + BOOL rc; + uschar dk_spool_name[256]; + char sbuf[2048]; + int sread = 0; + int wwritten = 0; + uschar *dk_signature = NULL; + + snprintf(CS dk_spool_name, 256, "%s/input/%s/%s-K", + spool_directory, message_subdir, message_id); + dk_fd = Uopen(dk_spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE); + if (dk_fd < 0) + { + /* Can't create spool file. Ugh. */ + rc = FALSE; + save_errno = errno; + goto CLEANUP; + } + + /* Call original function */ + rc = transport_write_message(addr, dk_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 DK lib */ + lseek(dk_fd, 0, SEEK_SET); + dk_signature = dk_exim_sign(dk_fd, + dk_private_key, + dk_domain, + dk_selector, + dk_canon); + + if (dk_signature != NULL) + { + /* Send the signature first */ + int siglen = Ustrlen(dk_signature); + while(siglen > 0) + { + #ifdef SUPPORT_TLS + if (tls_active == fd) wwritten = tls_write(dk_signature, siglen); else + #endif + wwritten = write(fd,dk_signature,siglen); + if (wwritten == -1) + { + /* error, bail out */ + save_errno = errno; + rc = FALSE; + goto CLEANUP; + } + siglen -= wwritten; + dk_signature += wwritten; + } + } + else if (dk_strict != NULL) + { + uschar *dk_strict_result = expand_string(dk_strict); + if (dk_strict_result != NULL) + { + if ( (strcmpic(dk_strict,"1") == 0) || + (strcmpic(dk_strict,"true") == 0) ) + { + save_errno = errno; + rc = FALSE; + goto CLEANUP; + } + } + } + + /* Rewind file and send it down the original fd. */ + lseek(dk_fd, 0, SEEK_SET); + + while((sread = read(dk_fd,sbuf,2048)) > 0) + { + char *p = sbuf; + /* write the chunk */ + DK_WRITE: + #ifdef SUPPORT_TLS + if (tls_active == fd) wwritten = tls_write(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 DK_WRITE; + } + } + + if (sread == -1) + { + save_errno = errno; + rc = FALSE; + goto CLEANUP; + } + + + CLEANUP: + /* unlink -K file */ + close(dk_fd); + Uunlink(dk_spool_name); + errno = save_errno; + return rc; +} +#endif /************************************************* diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 962ad6445..be8b15029 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.6 2005/02/17 11:58:27 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.c,v 1.7 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -35,6 +35,20 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, data_timeout) }, { "delay_after_cutoff", opt_bool, (void *)offsetof(smtp_transport_options_block, delay_after_cutoff) }, +#ifdef EXPERIMENTAL_DOMAINKEYS + { "dk_canon", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dk_canon) }, + { "dk_domain", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dk_domain) }, + { "dk_headers", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dk_headers) }, + { "dk_private_key", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dk_private_key) }, + { "dk_selector", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dk_selector) }, + { "dk_strict", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dk_strict) }, +#endif { "dns_qualify_single", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, { "dns_search_parents", opt_bool, @@ -158,6 +172,14 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* tls_verify_certificates */ TRUE /* tls_tempfail_tryclear */ #endif + #ifdef EXPERIMENTAL_DOMAINKEYS + ,NULL, /* dk_canon */ + NULL, /* dk_domain */ + NULL, /* dk_headers */ + NULL, /* dk_private_key */ + NULL, /* dk_selector */ + NULL /* dk_strict */ + #endif }; @@ -1394,6 +1416,23 @@ if (!ok) ok = TRUE; else DEBUG(D_transport|D_v) debug_printf(" SMTP>> writing message and terminating \".\"\n"); transport_count = 0; +#ifdef EXPERIMENTAL_DOMAINKEYS + if ( (ob->dk_private_key != NULL) && (ob->dk_selector != NULL) ) + ok = dk_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->dk_private_key, ob->dk_domain, ob->dk_selector, + ob->dk_canon, ob->dk_headers, ob->dk_strict); + else +#endif ok = transport_write_message(addrlist, inblock.sock, topt_use_crlf | topt_end_dot | topt_escape_headers | (tblock->body_only? topt_no_headers : 0) | diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index b6abc25e1..fe81267ec 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.4 2005/02/17 11:58:27 ph10 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.h,v 1.5 2005/03/08 15:32:02 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -50,6 +50,14 @@ typedef struct { uschar *tls_verify_certificates; BOOL tls_tempfail_tryclear; #endif + #ifdef EXPERIMENTAL_DOMAINKEYS + uschar *dk_domain; + uschar *dk_private_key; + uschar *dk_selector; + uschar *dk_canon; + uschar *dk_headers; + uschar *dk_strict; + #endif } smtp_transport_options_block; /* Data for reading the private options. */ |