From 80a47a2c9633437d4ceebd214cd44abfbd4f4543 Mon Sep 17 00:00:00 2001 From: Tom Kistner Date: Wed, 10 Jun 2009 07:34:04 +0000 Subject: [PATCH] Merge native DKIM support (from DEVEL_PDKIM) --- src/Makefile | 4 +- src/OS/Makefile-Base | 26 +- src/OS/os.h-Linux | 10 +- src/scripts/MakeLinks | 29 +- src/src/acl.c | 236 +---- src/src/config.h.defaults | 6 +- src/src/dk.c | 440 --------- src/src/dk.h | 51 -- src/src/dkim-exim.c | 510 ----------- src/src/dkim-exim.h | 35 - src/src/dkim.c | 500 ++++++++++ src/src/dkim.h | 33 + src/src/dns.c | 9 +- src/src/drtables.c | 24 +- src/src/exim.c | 11 +- src/src/exim.h | 9 +- src/src/expand.c | 91 +- src/src/functions.h | 8 +- src/src/globals.c | 22 +- src/src/globals.h | 22 +- src/src/lookups/Makefile | 5 +- src/src/lookups/dkim.c | 52 -- src/src/lookups/dkim.h | 16 - src/src/lookups/dnsdb.c | 12 +- src/src/macros.h | 3 +- src/src/pdkim/Makefile | 22 + src/src/pdkim/README | 13 + src/src/pdkim/base64.c | 180 ++++ src/src/pdkim/base64.h | 76 ++ src/src/pdkim/bignum.c | 1813 +++++++++++++++++++++++++++++++++++++ src/src/pdkim/bignum.h | 395 ++++++++ src/src/pdkim/bn_mul.h | 719 +++++++++++++++ src/src/pdkim/pdkim.c | 1714 +++++++++++++++++++++++++++++++++++ src/src/pdkim/pdkim.h | 325 +++++++ src/src/pdkim/rsa.c | 822 +++++++++++++++++ src/src/pdkim/rsa.h | 356 ++++++++ src/src/pdkim/sha1.c | 424 +++++++++ src/src/pdkim/sha1.h | 137 +++ src/src/pdkim/sha2.c | 431 +++++++++ src/src/pdkim/sha2.h | 145 +++ src/src/readconf.c | 8 +- src/src/receive.c | 139 +-- src/src/smtp_in.c | 14 +- src/src/spool_in.c | 12 +- src/src/tls-gnu.c | 6 +- src/src/tls-openssl.c | 6 +- src/src/transport.c | 68 +- src/src/transports/smtp.c | 58 +- src/src/transports/smtp.h | 10 +- 49 files changed, 8411 insertions(+), 1646 deletions(-) delete mode 100644 src/src/dk.c delete mode 100644 src/src/dk.h delete mode 100755 src/src/dkim-exim.c delete mode 100755 src/src/dkim-exim.h create mode 100644 src/src/dkim.c create mode 100644 src/src/dkim.h delete mode 100755 src/src/lookups/dkim.c delete mode 100755 src/src/lookups/dkim.h create mode 100644 src/src/pdkim/Makefile create mode 100644 src/src/pdkim/README create mode 100644 src/src/pdkim/base64.c create mode 100644 src/src/pdkim/base64.h create mode 100644 src/src/pdkim/bignum.c create mode 100644 src/src/pdkim/bignum.h create mode 100644 src/src/pdkim/bn_mul.h create mode 100644 src/src/pdkim/pdkim.c create mode 100644 src/src/pdkim/pdkim.h create mode 100644 src/src/pdkim/rsa.c create mode 100644 src/src/pdkim/rsa.h create mode 100644 src/src/pdkim/sha1.c create mode 100644 src/src/pdkim/sha1.h create mode 100644 src/src/pdkim/sha2.c create mode 100644 src/src/pdkim/sha2.h diff --git a/src/Makefile b/src/Makefile index cf7968db0..ec2856e0e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/Makefile,v 1.5 2008/02/14 13:49:35 fanf2 Exp $ +# $Cambridge: exim/src/Makefile,v 1.6 2009/06/10 07:34:04 tom Exp $ # Top-level makefile for Exim; handles creating a build directory with # appropriate links, and then creating and running the main makefile in that @@ -77,7 +77,7 @@ clean:; @echo ""; echo '*** "make clean" just removes all .o and .a files' cd build-$(buildname); \ $(RM_COMMAND) -f *.o lookups/*.o lookups/*.a auths/*.o auths/*.a \ routers/*.o routers/*.a transports/*.o transports/*.a \ - pcre/*.o pcre/*.a + pdkim/*.o pdkim/*.a clean_exim:; cd build-$(buildname); \ $(RM_COMMAND) -f *.o lookups/*.o lookups/*.a auths/*.o auths/*.a \ diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index e62f64ad8..451fcd245 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-Base,v 1.16 2009/01/20 16:06:14 fanf2 Exp $ +# $Cambridge: exim/src/OS/Makefile-Base,v 1.17 2009/06/10 07:34:04 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 @@ -96,7 +96,7 @@ config.h: Makefile buildconfig ../src/config.h.defaults $(EDITME) # therefore always be run, even if the files exist. This shouldn't in fact be a # problem, but it does no harm. Other make programs will just ignore this. -.PHONY: all allexim buildauths buildlookups buildrouters \ +.PHONY: all allexim buildauths buildlookups buildpdkim buildrouters \ buildtransports checklocalmake clean @@ -109,7 +109,7 @@ allexim: config.h $(EXIM_MONITOR) exicyclog exinext exiwhat \ exim_checkaccess \ exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock \ buildlookups buildrouters buildtransports \ - buildauths exim + buildauths buildpdkim exim # Targets for special-purpose configuration header builders @@ -300,14 +300,14 @@ 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 dkim-exim.o dcc.o +OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dcc.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. OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \ directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \ - filtertest.o globals.o \ + filtertest.o globals.o dkim.o \ header.o host.o ip.o log.o lss.o match.o moan.o \ os.o parse.o queue.o \ rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \ @@ -316,7 +316,7 @@ OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \ local_scan.o $(EXIM_PERL) $(OBJ_WITH_CONTENT_SCAN) \ $(OBJ_WITH_OLD_DEMIME) $(OBJ_EXPERIMENTAL) -exim: lookups/lookups.a auths/auths.a \ +exim: lookups/lookups.a auths/auths.a pdkim/pdkim.a \ routers/routers.a transports/transports.a \ $(OBJ_EXIM) version.c @echo " " @@ -328,7 +328,7 @@ exim: lookups/lookups.a auths/auths.a \ @echo "$(LNCC) -o exim" $(FE)$(PURIFY) $(LNCC) -o exim $(LFLAGS) $(OBJ_EXIM) version.o \ routers/routers.a transports/transports.a lookups/lookups.a \ - auths/auths.a \ + auths/auths.a pdkim/pdkim.a \ $(LIBRESOLV) $(LIBS) $(LIBS_EXIM) $(IPV6_LIBS) $(EXTRALIBS) \ $(EXTRALIBS_EXIM) $(DBMLIB) $(LOOKUP_LIBS) $(AUTH_LIBS) \ $(PERL_LIBS) $(TLS_LIBS) $(PCRE_LIBS) $(LDFLAGS) @@ -578,7 +578,7 @@ tod.o: $(HDRS) tod.c transport.o: $(HDRS) transport.c tree.o: $(HDRS) tree.c verify.o: $(HDRS) verify.c - +dkim.o: $(HDRS) dkim.c # Dependencies for WITH_CONTENT_SCAN modules @@ -599,8 +599,6 @@ demime.o: $(HDRS) demime.c 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 dcc.o: $(HDRS) dcc.h dcc.c # The module containing tables of available lookups, routers, auths, and @@ -670,6 +668,14 @@ buildauths auths/auths.a: config.h INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"; \ echo " " +# The PDKIM library + +buildpdkim pdkim/pdkim.a: config.h + @cd pdkim; $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \ + FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" HDRS="$(PHDRS)" \ + INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"; \ + echo " " + # The "clean", "install", and "makefile" targets just pass themselves back to # the main Exim makefile. These targets will be obeyed only if "make" is obeyed # for them in the build directory. diff --git a/src/OS/os.h-Linux b/src/OS/os.h-Linux index bde5dd710..c9f417be5 100644 --- a/src/OS/os.h-Linux +++ b/src/OS/os.h-Linux @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/OS/os.h-Linux,v 1.7 2007/10/04 13:28:06 tom Exp $ */ +/* $Cambridge: exim/src/OS/os.h-Linux,v 1.8 2009/06/10 07:34:04 tom Exp $ */ /* Exim: OS-specific C header file for Linux */ @@ -10,10 +10,10 @@ #define NO_IP_VAR_H #define SIG_IGN_WORKS -/* When using the experimental Domainkeys/DKIM extensions, setting -HAVE_LINUX_SENDFILE can increase performance on outgoing mail a bit. -Note: With older glibc versions this setting will conflict with the -_FILE_OFFSET_BITS=64 setting defined as part of the Linux CFLAGS. */ +/* When using the DKIM, setting HAVE_LINUX_SENDFILE can increase +performance on outgoing mail a bit. Note: With older glibc versions +this setting will conflict with the _FILE_OFFSET_BITS=64 setting +defined as part of the Linux CFLAGS. */ /* #define HAVE_LINUX_SENDFILE */ diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 74d7adc32..15c648c44 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -1,5 +1,5 @@ #!/bin/sh -# $Cambridge: exim/src/scripts/MakeLinks,v 1.14 2008/01/17 13:03:35 tom Exp $ +# $Cambridge: exim/src/scripts/MakeLinks,v 1.15 2009/06/10 07:34:04 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. @@ -189,6 +189,27 @@ ln -s ../../src/auths/spa.c spa.c ln -s ../../src/auths/spa.h spa.h cd .. +# Likewise for the code for the PDKIM library +mkdir pdkim +cd pdkim +ln -s ../../src/pdkim/README README +ln -s ../../src/pdkim/Makefile Makefile +ln -s ../../src/pdkim/base64.c base64.c +ln -s ../../src/pdkim/base64.h base64.h +ln -s ../../src/pdkim/bignum.c bignum.c +ln -s ../../src/pdkim/bignum.h bignum.h +ln -s ../../src/pdkim/bn_mul.h bn_mul.h +ln -s ../../src/pdkim/pdkim.c pdkim.c +ln -s ../../src/pdkim/pdkim.h pdkim.h +ln -s ../../src/pdkim/pdkim-api.h pdkim-api.h +ln -s ../../src/pdkim/rsa.c rsa.c +ln -s ../../src/pdkim/rsa.h rsa.h +ln -s ../../src/pdkim/sha1.c sha1.c +ln -s ../../src/pdkim/sha1.h sha1.h +ln -s ../../src/pdkim/sha2.c sha2.c +ln -s ../../src/pdkim/sha2.h sha2.h +cd .. + # The basic source files for Exim and utilities. NB local_scan.h gets linked, # but local_scan.c does not, because its location is taken from the build-time # configuration. Likewise for the os.c file, which gets build dynamically. @@ -259,6 +280,8 @@ ln -s ../src/transport.c transport.c ln -s ../src/tree.c tree.c ln -s ../src/verify.c verify.c ln -s ../src/version.c version.c +ln -s ../src/dkim.c dkim.c +ln -s ../src/dkim.h dkim.h # WITH_CONTENT_SCAN ln -s ../src/spam.c spam.c @@ -280,10 +303,6 @@ 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 -ln -s ../src/dkim-exim.c dkim-exim.c -ln -s ../src/dkim-exim.h dkim-exim.h ln -s ../src/dcc.c dcc.c ln -s ../src/dcc.h dcc.h diff --git a/src/src/acl.c b/src/src/acl.c index fcafc6b58..a3e79b13d 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.82 2008/02/12 12:52:51 nm4 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.83 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -65,13 +65,9 @@ enum { ACLC_ACL, #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, +#ifndef DISABLE_DKIM + ACLC_DKIM_SIGNER, + ACLC_DKIM_STATUS, #endif ACLC_DNSLISTS, ACLC_DOMAINS, @@ -131,13 +127,9 @@ static uschar *conditions[] = { #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", +#ifndef DISABLE_DKIM + US"dkim_signers", + US"dkim_status", #endif US"dnslists", US"domains", @@ -179,10 +171,7 @@ enum { #ifdef EXPERIMENTAL_BRIGHTMAIL CONTROL_BMI_RUN, #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - CONTROL_DK_VERIFY, - #endif - #ifdef EXPERIMENTAL_DKIM + #ifndef DISABLE_DKIM CONTROL_DKIM_VERIFY, #endif CONTROL_ERROR, @@ -215,11 +204,8 @@ static uschar *controls[] = { #ifdef EXPERIMENTAL_BRIGHTMAIL US"bmi_run", #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - US"dk_verify", - #endif - #ifdef EXPERIMENTAL_DKIM - US"dkim_verify", + #ifndef DISABLE_DKIM + US"dkim_disable_verify", #endif US"error", US"caseful_local_part", @@ -265,13 +251,9 @@ static uschar cond_expand_at_top[] = { #ifdef WITH_OLD_DEMIME TRUE, /* demime */ #endif -#ifdef EXPERIMENTAL_DOMAINKEYS - TRUE, /* dk_domain_source */ - TRUE, /* dk_policy */ - TRUE, /* dk_sender_domains */ - TRUE, /* dk_sender_local_parts */ - TRUE, /* dk_senders */ - TRUE, /* dk_status */ +#ifndef DISABLE_DKIM + TRUE, /* dkim_signers */ + TRUE, /* dkim_status */ #endif TRUE, /* dnslists */ FALSE, /* domains */ @@ -329,13 +311,9 @@ static uschar cond_modifiers[] = { #ifdef WITH_OLD_DEMIME FALSE, /* demime */ #endif -#ifdef EXPERIMENTAL_DOMAINKEYS - FALSE, /* dk_domain_source */ - FALSE, /* dk_policy */ - FALSE, /* dk_sender_domains */ - FALSE, /* dk_sender_local_parts */ - FALSE, /* dk_senders */ - FALSE, /* dk_status */ +#ifndef DISABLE_DKIM + FALSE, /* dkim_signers */ + FALSE, /* dkim_status */ #endif FALSE, /* dnslists */ FALSE, /* domains */ @@ -426,54 +404,12 @@ static unsigned int cond_forbids[] = { ~((1<next) break; #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - case CONTROL_DK_VERIFY: - dk_do_verify = 1; - break; - #endif - - #ifdef EXPERIMENTAL_DKIM + #ifndef DISABLE_DKIM case CONTROL_DKIM_VERIFY: - dkim_do_verify = 1; + dkim_disable_verify = TRUE; break; #endif @@ -2862,95 +2784,27 @@ 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; + #ifndef DISABLE_DKIM + case ACLC_DKIM_SIGNER: + if (dkim_signing_domain != NULL) + { + rc = match_isinlist(dkim_signing_domain, + &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); + if (rc == FAIL) + { + rc = match_isinlist(dkim_exim_expand_query(DKIM_IDENTITY), + &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); + } + } + else + { + rc = FAIL; } 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; - } - } + case ACLC_DKIM_STATUS: + rc = match_isinlist(dkim_exim_expand_query(DKIM_VERIFY_STATUS), + &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); break; #endif diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 9df56e08c..298f2f37f 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.16 2008/01/17 13:03:35 tom Exp $ */ +/* $Cambridge: exim/src/src/config.h.defaults,v 1.17 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -150,15 +150,13 @@ it's a default value. */ /* EXPERIMENTAL features */ #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS -#define EXPERIMENTAL_DOMAINKEYS -#define EXPERIMENTAL_DKIM #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DCC /* Things that are not routinely changed but are nevertheless configurable just in case. */ -#define DNS_MAXNAME 256 +#define DNS_MAXNAME 1024 #define EXPAND_MAXN 20 #define ROOT_UID 0 diff --git a/src/src/dk.c b/src/src/dk.c deleted file mode 100644 index 713684b2a..000000000 --- a/src/src/dk.c +++ /dev/null @@ -1,440 +0,0 @@ -/* $Cambridge: exim/src/src/dk.c,v 1.12 2007/01/08 10:50:18 ph10 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 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, CUS &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, CUS &dkbuff[i], 1); - - /* Flag end-of-message. */ - dk_internal_status = dk_end(dk_context, &dk_flags); - - /* dk_flags now has the selector flags (if there was one). - It seems that currently only the "t=" flag is supported - in selectors. */ - if (dk_flags & DK_FLAG_SET) - if (dk_flags & DK_FLAG_TESTING) - dk_verify_block->testing = TRUE; - - /* 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); - *q = '@'; - } - } - } - - /* Now grab the domain-wide DK policy */ - dk_flags = dk_policy(dk_context); - - if (dk_flags & DK_FLAG_SET) { - /* Selector "t=" flag has precedence, don't overwrite it if - the selector has set it above. */ - if ((dk_flags & DK_FLAG_TESTING) && !dk_verify_block->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,1); - 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; - uschar *headers = NULL; - int headers_len; - int dk_canon_int = DK_CANON_SIMPLE; - char buf[4096]; - 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 = US "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,&buf,4096)) > 0) { - int pos = 0; - char c; - - while (pos < sread) { - c = buf[pos++]; - - if ((c == '.') && seen_lfdot) { - /* escaped dot, write "\n.", continue */ - dk_message(dk_context, CUS "\n.", 2); - seen_lf = 0; - seen_lfdot = 0; - continue; - } - - if (seen_lfdot) { - /* EOM, write "\n" and break */ - dk_message(dk_context, CUS "\n", 1); - break; - } - - if ((c == '.') && seen_lf) { - seen_lfdot = 1; - continue; - } - - if (seen_lf) { - /* normal lf, just send it */ - dk_message(dk_context, CUS "\n", 1); - seen_lf = 0; - } - - if (c == '\n') { - seen_lf = 1; - continue; - } - - /* write the char */ - dk_message(dk_context, CUS &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 = US dk_address(dk_context); - switch(dk_domain[0]) { - case 'N': dk_domain = NULL; break; - case 'F': - case 'S': - dk_domain++; - dk_domain = Ustrrchr(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 = US""; - 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 = US""; - 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(CS dk_private_key,O_RDONLY); - (void)read(privkey_fd,big_buffer,16383); - (void)close(privkey_fd); - dk_private_key = big_buffer; - } - - /* Get the signature. */ - dk_internal_status = dk_getsig(dk_context, dk_private_key, sig, 1024); - - /* 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; - } - - headers_len = dk_headers(dk_context, NULL); - rc = store_get(1024+256+headers_len); - headers = store_malloc(headers_len); - dk_headers(dk_context, CS headers); - /* Build DomainKey-Signature header to return. */ - (void)string_format(rc, 1024+256+headers_len, "DomainKey-Signature: a=rsa-sha1; q=dns; c=%s; s=%s; d=%s;\r\n" - "\th=%s;\r\n" - "\tb=%s;\r\n", dk_canon, dk_selector, dk_domain, headers, sig); - - log_write(0, LOG_MAIN, "DK: message signed using a=rsa-sha1; q=dns; c=%s; s=%s; d=%s; h=%s;", dk_canon, dk_selector, dk_domain, headers); - store_free(headers); - - CLEANUP: - if (dk_context != NULL) { - dk_free(dk_context,1); - 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 deleted file mode 100644 index 85e1dd7cd..000000000 --- a/src/src/dk.h +++ /dev/null @@ -1,51 +0,0 @@ -/* $Cambridge: exim/src/src/dk.h,v 1.3 2007/01/08 10:50:18 ph10 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 DomainKeys support. Other DK relevant code is in - receive.c, transport.c and transports/smtp.c */ - -#ifdef EXPERIMENTAL_DOMAINKEYS - -#include - -#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/dkim-exim.c b/src/src/dkim-exim.c deleted file mode 100755 index 35c6fcfd9..000000000 --- a/src/src/dkim-exim.c +++ /dev/null @@ -1,510 +0,0 @@ -/* $Cambridge: exim/src/src/dkim-exim.c,v 1.4 2008/09/30 10:03:55 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)); - - memset(dkim_sign_options,0,sizeof(DKIMSignOptions)); - memset(dkim_context,0,sizeof(DKIMContext)); - - 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 deleted file mode 100755 index b974d9522..000000000 --- a/src/src/dkim-exim.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $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/dkim.c b/src/src/dkim.c new file mode 100644 index 000000000..2a53f154f --- /dev/null +++ b/src/src/dkim.c @@ -0,0 +1,500 @@ +/* $Cambridge: exim/src/src/dkim.c,v 1.2 2009/06/10 07:34:04 tom Exp $ */ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 2009 */ +/* 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" + +#ifndef DISABLE_DKIM + +#include "pdkim/pdkim.h" + +pdkim_ctx *dkim_verify_ctx = NULL; +pdkim_signature *dkim_signatures = NULL; +pdkim_signature *dkim_cur_sig = NULL; + +int dkim_exim_query_dns_txt(char *name, char *answer) { + dns_answer dnsa; + dns_scan dnss; + dns_record *rr; + + if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL; + + /* Search for TXT record */ + for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); + rr != NULL; + rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) + if (rr->type == T_TXT) break; + + /* Copy record content to the answer buffer */ + if (rr != NULL) { + int rr_offset = 0; + int answer_offset = 0; + while (rr_offset < rr->size) { + uschar len = (rr->data)[rr_offset++]; + snprintf(answer+(answer_offset), + PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset), + "%.*s", (int)len, (char *)((rr->data)+rr_offset)); + rr_offset+=len; + answer_offset+=len; + } + } + else return PDKIM_FAIL; + + return PDKIM_OK; +} + + +void dkim_exim_verify_init(void) { + + /* Free previous context if there is one */ + if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx); + + /* Create new context */ + dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP, + &dkim_exim_query_dns_txt + ); + + if (dkim_verify_ctx != NULL) { + dkim_collect_input = TRUE; + pdkim_set_debug_stream(dkim_verify_ctx,debug_file); + } + else dkim_collect_input = FALSE; + +} + + +void dkim_exim_verify_feed(uschar *data, int len) { + if (dkim_collect_input && + pdkim_feed(dkim_verify_ctx, + (char *)data, + len) != PDKIM_OK) dkim_collect_input = FALSE; +} + + +void dkim_exim_verify_finish(void) { + pdkim_signature *sig = NULL; + int dkim_signing_domains_size = 0; + int dkim_signing_domains_ptr = 0; + dkim_signing_domains = NULL; + + /* Delete eventual previous signature chain */ + dkim_signatures = NULL; + + /* If we have arrived here with dkim_collect_input == FALSE, it + means there was a processing error somewhere along the way. + Log the incident and disable futher verification. */ + if (!dkim_collect_input) { + log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: Error while running this message through validation, disabling signature verification."); + dkim_disable_verify = TRUE; + return; + } + dkim_collect_input = FALSE; + + /* Finish DKIM operation and fetch link to signatures chain */ + if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return; + + sig = dkim_signatures; + while (sig != NULL) { + int size = 0; + int ptr = 0; + /* Log a line for each signature */ + uschar *logmsg = string_append(NULL, &size, &ptr, 5, + + string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ", + sig->domain, + sig->selector, + (sig->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed", + (sig->canon_body == PDKIM_CANON_SIMPLE)?"simple":"relaxed", + (sig->algo == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1" + ), + ((sig->identity != NULL)? + string_sprintf("i=%s ", sig->identity) + : + US"" + ), + ((sig->created > 0)? + string_sprintf("t=%lu ", sig->created) + : + US"" + ), + ((sig->expires > 0)? + string_sprintf("x=%lu ", sig->expires) + : + US"" + ), + ((sig->bodylength > -1)? + string_sprintf("l=%lu ", sig->bodylength) + : + US"" + ) + ); + + switch(sig->verify_status) { + case PDKIM_VERIFY_NONE: + logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]"); + break; + case PDKIM_VERIFY_INVALID: + logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - "); + switch (sig->verify_ext_status) { + case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: + logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]"); + break; + case PDKIM_VERIFY_INVALID_BUFFER_SIZE: + logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]"); + break; + case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: + logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]"); + break; + default: + logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]"); + } + break; + case PDKIM_VERIFY_FAIL: + logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - "); + switch (sig->verify_ext_status) { + case PDKIM_VERIFY_FAIL_BODY: + logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]"); + break; + case PDKIM_VERIFY_FAIL_MESSAGE: + logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]"); + break; + default: + logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]"); + } + break; + case PDKIM_VERIFY_PASS: + logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]"); + break; + } + + logmsg[ptr] = '\0'; + log_write(0, LOG_MAIN, (char *)logmsg); + + /* Build a colon-separated list of signing domains in dkim_signing_domains */ + dkim_signing_domains = string_append(dkim_signing_domains, + &dkim_signing_domains_size, + &dkim_signing_domains_ptr, + 2, + sig->domain, + ":" + ); + + /* Process next signature */ + sig = sig->next; + } + + /* Chop the last colon from the domain list */ + if ((dkim_signing_domains != NULL) && + (Ustrlen(dkim_signing_domains) > 0)) + dkim_signing_domains[Ustrlen(dkim_signing_domains)-1] = '\0'; +} + + +void dkim_exim_acl_setup(uschar *id) { + pdkim_signature *sig = dkim_signatures; + dkim_cur_sig = NULL; + if (dkim_disable_verify || + !id || !sig || + !dkim_verify_ctx) return; + /* Find signature to run ACL on */ + while (sig != NULL) { + uschar *cmp_val = NULL; + if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity; + else cmp_val = (uschar *)sig->domain; + if (cmp_val && (strcmpic(cmp_val,id) == 0)) { + dkim_cur_sig = sig; + /* The "dkim_domain" and "dkim_selector" expansion variables have + related globals, since they are used in the signing code too. + Instead of inventing separate names for verification, we set + them here. This is easy since a domain and selector is guaranteed + to be in a signature. The other dkim_* expansion items are + dynamically fetched from dkim_cur_sig at expansion time (see + function below). */ + dkim_signing_domain = (uschar *)sig->domain; + dkim_signing_selector = (uschar *)sig->selector; + return; + } + sig = sig->next; + } +} + + +uschar *dkim_exim_expand_query(int what) { + + if (!dkim_verify_ctx || + dkim_disable_verify || + !dkim_cur_sig) return dkim_exim_expand_defaults(what); + + switch(what) { + case DKIM_ALGO: + return dkim_cur_sig->algo? + (uschar *)(dkim_cur_sig->algo) + :dkim_exim_expand_defaults(what); + case DKIM_BODYLENGTH: + return (dkim_cur_sig->bodylength >= 0)? + (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength) + :dkim_exim_expand_defaults(what); + case DKIM_CANON_BODY: + return dkim_cur_sig->canon_body? + (uschar *)(dkim_cur_sig->canon_body) + :dkim_exim_expand_defaults(what); + case DKIM_CANON_HEADERS: + return dkim_cur_sig->canon_headers? + (uschar *)(dkim_cur_sig->canon_headers) + :dkim_exim_expand_defaults(what); + case DKIM_COPIEDHEADERS: + return dkim_cur_sig->copiedheaders? + (uschar *)(dkim_cur_sig->copiedheaders) + :dkim_exim_expand_defaults(what); + case DKIM_CREATED: + return (dkim_cur_sig->created > 0)? + (uschar *)string_sprintf("%llu",dkim_cur_sig->created) + :dkim_exim_expand_defaults(what); + case DKIM_EXPIRES: + return (dkim_cur_sig->expires > 0)? + (uschar *)string_sprintf("%llu",dkim_cur_sig->expires) + :dkim_exim_expand_defaults(what); + case DKIM_HEADERNAMES: + return dkim_cur_sig->headernames? + (uschar *)(dkim_cur_sig->headernames) + :dkim_exim_expand_defaults(what); + case DKIM_IDENTITY: + return dkim_cur_sig->identity? + (uschar *)(dkim_cur_sig->identity) + :dkim_exim_expand_defaults(what); + case DKIM_KEY_GRANULARITY: + return dkim_cur_sig->pubkey? + (dkim_cur_sig->pubkey->granularity? + (uschar *)(dkim_cur_sig->pubkey->granularity) + :dkim_exim_expand_defaults(what) + ) + :dkim_exim_expand_defaults(what); + case DKIM_KEY_SRVTYPE: + return dkim_cur_sig->pubkey? + (dkim_cur_sig->pubkey->srvtype? + (uschar *)(dkim_cur_sig->pubkey->srvtype) + :dkim_exim_expand_defaults(what) + ) + :dkim_exim_expand_defaults(what); + case DKIM_KEY_NOTES: + return dkim_cur_sig->pubkey? + (dkim_cur_sig->pubkey->notes? + (uschar *)(dkim_cur_sig->pubkey->notes) + :dkim_exim_expand_defaults(what) + ) + :dkim_exim_expand_defaults(what); + case DKIM_KEY_TESTING: + return dkim_cur_sig->pubkey? + (dkim_cur_sig->pubkey->testing? + US"1" + :dkim_exim_expand_defaults(what) + ) + :dkim_exim_expand_defaults(what); + case DKIM_NOSUBDOMAINS: + return dkim_cur_sig->pubkey? + (dkim_cur_sig->pubkey->no_subdomaining? + US"1" + :dkim_exim_expand_defaults(what) + ) + :dkim_exim_expand_defaults(what); + case DKIM_VERIFY_STATUS: + switch(dkim_cur_sig->verify_status) { + case PDKIM_VERIFY_INVALID: + return US"invalid"; + case PDKIM_VERIFY_FAIL: + return US"fail"; + case PDKIM_VERIFY_PASS: + return US"pass"; + case PDKIM_VERIFY_NONE: + default: + return US"none"; + } + case DKIM_VERIFY_REASON: + switch (dkim_cur_sig->verify_ext_status) { + case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: + return US"pubkey_unavailable"; + case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: + return US"pubkey_syntax"; + case PDKIM_VERIFY_FAIL_BODY: + return US"bodyhash_mismatch"; + case PDKIM_VERIFY_FAIL_MESSAGE: + return US"signature_incorrect"; + } + default: + return US""; + } +} + + +uschar *dkim_exim_expand_defaults(int what) { + switch(what) { + case DKIM_ALGO: return US""; + case DKIM_BODYLENGTH: return US"9999999999999"; + case DKIM_CANON_BODY: return US""; + case DKIM_CANON_HEADERS: return US""; + case DKIM_COPIEDHEADERS: return US""; + case DKIM_CREATED: return US"0"; + case DKIM_EXPIRES: return US"9999999999999"; + case DKIM_HEADERNAMES: return US""; + case DKIM_IDENTITY: return US""; + case DKIM_KEY_GRANULARITY: return US"*"; + case DKIM_KEY_SRVTYPE: return US"*"; + case DKIM_KEY_NOTES: return US""; + case DKIM_KEY_TESTING: return US"0"; + case DKIM_NOSUBDOMAINS: return US"0"; + case DKIM_VERIFY_STATUS: return US"none"; + case DKIM_VERIFY_REASON: return US""; + default: return US""; + } +} + + +uschar *dkim_exim_sign(int dkim_fd, + uschar *dkim_private_key, + uschar *dkim_domain, + uschar *dkim_selector, + uschar *dkim_canon, + uschar *dkim_sign_headers) { + pdkim_ctx *ctx = NULL; + uschar *rc = NULL; + pdkim_signature *signature; + int pdkim_canon; + int sread; + char buf[4096]; + int save_errno = 0; + int old_pool = store_pool; + + 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; + + /* 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; + + /* Get canonicalization to use */ + 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) + pdkim_canon = PDKIM_CANON_RELAXED; + else if (Ustrcmp(dkim_canon, "simple") == 0) + pdkim_canon = PDKIM_CANON_RELAXED; + else { + log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon); + pdkim_canon = PDKIM_CANON_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; + } + } + + /* 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; + } + + ctx = pdkim_init_sign(PDKIM_INPUT_SMTP, + (char *)dkim_signing_domain, + (char *)dkim_signing_selector, + (char *)dkim_private_key + ); + + pdkim_set_debug_stream(ctx,debug_file); + + pdkim_set_optional(ctx, + (char *)dkim_sign_headers, + NULL, + pdkim_canon, + pdkim_canon, + -1, + PDKIM_ALGO_RSA_SHA256, + 0, + 0); + + while((sread = read(dkim_fd,&buf,4096)) > 0) { + if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) { + rc = NULL; + goto CLEANUP; + } + } + /* Handle failed read above. */ + if (sread == -1) { + debug_printf("DKIM: Error reading -K file.\n"); + save_errno = errno; + rc = NULL; + goto CLEANUP; + } + + if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK) + goto CLEANUP; + + rc = store_get(strlen(signature->signature_header)+3); + Ustrcpy(rc,US signature->signature_header); + Ustrcat(rc,US"\r\n"); + + CLEANUP: + if (ctx != NULL) { + pdkim_free_ctx(ctx); + } + store_pool = old_pool; + errno = save_errno; + return rc; +}; + +#endif diff --git a/src/src/dkim.h b/src/src/dkim.h new file mode 100644 index 000000000..28459c58a --- /dev/null +++ b/src/src/dkim.h @@ -0,0 +1,33 @@ +/* $Cambridge: exim/src/src/dkim.h,v 1.2 2009/06/10 07:34:04 tom Exp $ */ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 2009 */ +/* See the file NOTICE for conditions of use and distribution. */ + +uschar *dkim_exim_sign(int,uschar *,uschar *,uschar *,uschar *,uschar *); +void dkim_exim_verify_init(void); +void dkim_exim_verify_feed(uschar *, int); +void dkim_exim_verify_finish(void); +void dkim_exim_acl_setup(uschar *); +uschar *dkim_exim_expand_query(int); +uschar *dkim_exim_expand_defaults(int); + +#define DKIM_ALGO 1 +#define DKIM_BODYLENGTH 2 +#define DKIM_CANON_BODY 3 +#define DKIM_CANON_HEADERS 4 +#define DKIM_COPIEDHEADERS 5 +#define DKIM_CREATED 6 +#define DKIM_EXPIRES 7 +#define DKIM_HEADERNAMES 8 +#define DKIM_IDENTITY 9 +#define DKIM_KEY_GRANULARITY 10 +#define DKIM_KEY_SRVTYPE 11 +#define DKIM_KEY_NOTES 12 +#define DKIM_KEY_TESTING 13 +#define DKIM_NOSUBDOMAINS 14 +#define DKIM_VERIFY_STATUS 15 +#define DKIM_VERIFY_REASON 16 diff --git a/src/src/dns.c b/src/src/dns.c index 0b1d59d20..deafedc7b 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/dns.c,v 1.17 2007/01/08 10:50:18 ph10 Exp $ */ +/* $Cambridge: exim/src/src/dns.c,v 1.18 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -562,7 +562,12 @@ if (running_in_test_harness) else dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET); -if (dnsa->answerlen > MAXPACKET) dnsa->answerlen = MAXPACKET; +if (dnsa->answerlen > MAXPACKET) + { + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n", + name, dns_text_type(type), dnsa->answerlen, MAXPACKET); + dnsa->answerlen = MAXPACKET; + } if (dnsa->answerlen < 0) switch (h_errno) { diff --git a/src/src/drtables.c b/src/src/drtables.c index b95d4fc95..b4419073d 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/drtables.c,v 1.9 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/drtables.c,v 1.10 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -105,11 +105,6 @@ 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, @@ -176,23 +171,6 @@ 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 b078c6000..77d27ab53 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim.c,v 1.61 2008/10/12 09:58:13 nm4 Exp $ */ +/* $Cambridge: exim/src/src/exim.c,v 1.62 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -905,6 +905,9 @@ fprintf(f, "Support for:"); #ifdef WITH_CONTENT_SCAN fprintf(f, " Content_Scanning"); #endif +#ifndef DISABLE_DKIM + fprintf(f, " DKIM"); +#endif #ifdef WITH_OLD_DEMIME fprintf(f, " Old_Demime"); #endif @@ -917,12 +920,6 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_BRIGHTMAIL fprintf(f, " Experimental_Brightmail"); #endif -#ifdef EXPERIMENTAL_DOMAINKEYS - fprintf(f, " Experimental_DomainKeys"); -#endif -#ifdef EXPERIMENTAL_DKIM - fprintf(f, " Experimental_DKIM"); -#endif #ifdef EXPERIMENTAL_DCC fprintf(f, " Experimental_DCC"); #endif diff --git a/src/src/exim.h b/src/src/exim.h index a2f422c11..34fb118e0 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim.h,v 1.24 2008/01/16 13:44:45 nm4 Exp $ */ +/* $Cambridge: exim/src/src/exim.h,v 1.25 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -446,11 +446,8 @@ config.h, 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 -#ifdef EXPERIMENTAL_DKIM -#include "dkim-exim.h" +#ifndef DISABLE_DKIM +#include "dkim.h" #endif /* The following stuff must follow the inclusion of config.h because it diff --git a/src/src/expand.c b/src/src/expand.c index 599dd9c0d..d01d0d11e 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.97 2008/12/12 14:51:47 nm4 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.98 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -364,9 +364,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 + #ifndef DISABLE_DKIM + ,vtype_dkim /* Lookup of value in DKIM signature */ + #endif }; /* This table must be kept in alphabetical order. */ @@ -404,22 +404,26 @@ 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 -#ifdef EXPERIMENTAL_DKIM +#ifndef DISABLE_DKIM + { "dkim_algo", vtype_dkim, (void *)DKIM_ALGO }, + { "dkim_bodylength", vtype_dkim, (void *)DKIM_BODYLENGTH }, + { "dkim_canon_body", vtype_dkim, (void *)DKIM_CANON_BODY }, + { "dkim_canon_headers", vtype_dkim, (void *)DKIM_CANON_HEADERS }, + { "dkim_copiedheaders", vtype_dkim, (void *)DKIM_COPIEDHEADERS }, + { "dkim_created", vtype_dkim, (void *)DKIM_CREATED }, { "dkim_domain", vtype_stringptr, &dkim_signing_domain }, + { "dkim_expires", vtype_dkim, (void *)DKIM_EXPIRES }, + { "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES }, + { "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY }, + { "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY }, + { "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS }, + { "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES }, + { "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE }, + { "dkim_key_testing", vtype_dkim, (void *)DKIM_KEY_TESTING }, { "dkim_selector", vtype_stringptr, &dkim_signing_selector }, + { "dkim_signing_domains",vtype_stringptr, &dkim_signing_domains }, + { "dkim_verify_reason", vtype_dkim, (void *)DKIM_VERIFY_REASON }, + { "dkim_verify_status", vtype_dkim, (void *)DKIM_VERIFY_STATUS}, #endif { "dnslist_domain", vtype_stringptr, &dnslist_domain }, { "dnslist_matched", vtype_stringptr, &dnslist_matched }, @@ -1382,51 +1386,6 @@ while (last > first) switch (var_table[middle].type) { -#ifdef EXPERIMENTAL_DOMAINKEYS - - case vtype_dk_verify: - if (dk_verify_block == NULL) return US""; - 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 = US"0"; break; - case DK_EXIM_ADDRESS_FROM_FROM: s = US"from"; break; - case DK_EXIM_ADDRESS_FROM_SENDER: s = US"sender"; break; - } - - if (Ustrcmp(var_table[middle].name, "dk_status") == 0) - switch(dk_verify_block->result) { - case DK_EXIM_RESULT_ERR: s = US"error"; break; - case DK_EXIM_RESULT_BAD_FORMAT: s = US"bad format"; break; - case DK_EXIM_RESULT_NO_KEY: s = US"no key"; break; - case DK_EXIM_RESULT_NO_SIGNATURE: s = US"no signature"; break; - case DK_EXIM_RESULT_REVOKED: s = US"revoked"; break; - case DK_EXIM_RESULT_NON_PARTICIPANT: s = US"non-participant"; break; - case DK_EXIM_RESULT_GOOD: s = US"good"; break; - case DK_EXIM_RESULT_BAD: s = US"bad"; break; - } - - if (Ustrcmp(var_table[middle].name, "dk_signsall") == 0) - s = (dk_verify_block->signsall)? US"1" : US"0"; - - if (Ustrcmp(var_table[middle].name, "dk_testing") == 0) - s = (dk_verify_block->testing)? US"1" : US"0"; - - if (Ustrcmp(var_table[middle].name, "dk_is_signed") == 0) - s = (dk_verify_block->is_signed)? US"1" : US"0"; - - return (s == NULL)? US"" : s; -#endif - case vtype_filter_int: if (!filter_running) return NULL; /* Fall through */ @@ -1605,6 +1564,12 @@ while (last > first) sprintf(CS var_buffer, "%d", inodes); } return var_buffer; + + #ifndef DKIM_DISABLE + case vtype_dkim: + return dkim_exim_expand_query((int)var_table[middle].value); + #endif + } } diff --git a/src/src/functions.h b/src/src/functions.h index 2c77c17fd..691ff7af7 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.43 2008/12/18 13:13:54 michael Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.44 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -83,12 +83,10 @@ extern void deliver_succeeded(address_item *); extern int demime(uschar **); #endif extern BOOL directory_make(uschar *, uschar *, int, BOOL); -#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) +#ifndef DISABLE_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 *, - uschar *, uschar *, uschar *, uschar *, uschar *, uschar * - ); + 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 *); diff --git a/src/src/globals.c b/src/src/globals.c index 93f74910c..dcb6bece0 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.c,v 1.81 2008/02/12 12:52:51 nm4 Exp $ */ +/* $Cambridge: exim/src/src/globals.c,v 1.82 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -182,6 +182,9 @@ uschar *acl_not_smtp_start = NULL; uschar *acl_smtp_auth = NULL; uschar *acl_smtp_connect = NULL; uschar *acl_smtp_data = NULL; +#ifndef DISABLE_DKIM +uschar *acl_smtp_dkim = NULL; +#endif uschar *acl_smtp_etrn = NULL; uschar *acl_smtp_expn = NULL; uschar *acl_smtp_helo = NULL; @@ -210,6 +213,7 @@ uschar *acl_wherenames[] = { US"RCPT", US"MAIL", US"PREDATA", US"MIME", + US"DKIM", US"DATA", US"non-SMTP", US"AUTH", @@ -229,6 +233,7 @@ uschar *acl_wherecodes[] = { US"550", /* RCPT */ US"550", /* MAIL */ US"550", /* PREDATA */ US"550", /* MIME */ + US"550", /* DKIM */ US"550", /* DATA */ US"0", /* not SMTP; not relevant */ US"503", /* AUTH */ @@ -391,7 +396,7 @@ int callout_cache_domain_negative_expire = 3*60*60; int callout_cache_positive_expire = 24*60*60; int callout_cache_negative_expire = 2*60*60; uschar *callout_random_local_part = US"$primary_hostname-$tod_epoch-testing"; -uschar *check_dns_names_pattern= US"(?i)^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9/-]*[^\\W_])?)+$"; +uschar *check_dns_names_pattern= US"(?i)^(?>(?(1)\\.|())[^\\W](?>[a-z0-9/_-]*[^\\W])?)+(\\.?)$"; int check_log_inodes = 0; int check_log_space = 0; BOOL check_rfc2047_length = TRUE; @@ -526,16 +531,13 @@ BOOL disable_fsync = FALSE; BOOL disable_ipv6 = FALSE; BOOL disable_logging = FALSE; -#ifdef EXPERIMENTAL_DOMAINKEYS -uschar *dk_signing_domain = NULL; -uschar *dk_signing_selector = NULL; -int dk_do_verify = 0; -#endif - -#ifdef EXPERIMENTAL_DKIM +#ifndef DISABLE_DKIM +uschar *dkim_signing_domains = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; -int dkim_do_verify = 0; +uschar *dkim_verify_signers = US"$dkim_signing_domains"; +BOOL dkim_collect_input = FALSE; +BOOL dkim_disable_verify = FALSE; #endif uschar *dns_again_means_nonexist = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index ac425ed98..ff087dfbc 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.h,v 1.62 2008/02/12 12:52:51 nm4 Exp $ */ +/* $Cambridge: exim/src/src/globals.h,v 1.63 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -118,6 +118,9 @@ extern uschar *acl_not_smtp_start; /* ACL run at the beginning of a non-SMTP extern uschar *acl_smtp_auth; /* ACL run for AUTH */ extern uschar *acl_smtp_connect; /* ACL run on SMTP connection */ extern uschar *acl_smtp_data; /* ACL run after DATA received */ +#ifndef DISABLE_DKIM +extern uschar *acl_smtp_dkim; /* ACL run for DKIM signatures / domains */ +#endif extern uschar *acl_smtp_etrn; /* ACL run for ETRN */ extern uschar *acl_smtp_expn; /* ACL run for EXPN */ extern uschar *acl_smtp_helo; /* ACL run for HELO/EHLO */ @@ -295,16 +298,13 @@ extern BOOL disable_fsync; /* Not for normal use */ extern BOOL disable_ipv6; /* Don't do any IPv6 things */ 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 - -#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. */ +#ifndef DISABLE_DKIM +extern uschar *dkim_signing_domains; /* Expansion variable, holds colon-separated list of domains that have signed a message */ +extern uschar *dkim_signing_domain; /* Expansion variable, domain used for signing a message. */ +extern uschar *dkim_signing_selector; /* Expansion variable, selector used for signing a message. */ +extern uschar *dkim_verify_signers; /* Colon-separated list of domains for each of which we call the DKIM ACL */ +extern BOOL dkim_collect_input; /* Runtime flag that tracks wether SMTP input is fed to DKIM validation */ +extern BOOL dkim_disable_verify; /* Set via ACL control statement. When set, DKIM verification is disabled for the current message */ #endif extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */ diff --git a/src/src/lookups/Makefile b/src/src/lookups/Makefile index 48d208d02..2c7cb8721 100644 --- a/src/src/lookups/Makefile +++ b/src/src/lookups/Makefile @@ -1,11 +1,11 @@ -# $Cambridge: exim/src/src/lookups/Makefile,v 1.8 2008/02/14 13:49:35 fanf2 Exp $ +# $Cambridge: exim/src/src/lookups/Makefile,v 1.9 2009/06/10 07:34:05 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 dkim.o dnsdb.o dsearch.o ibase.o ldap.o lsearch.o mysql.o nis.o \ +OBJ = cdb.o dbmdb.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 @@ -25,7 +25,6 @@ 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 deleted file mode 100755 index f90283ee5..000000000 --- a/src/src/lookups/dkim.c +++ /dev/null @@ -1,52 +0,0 @@ -/* $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 deleted file mode 100755 index 6e07142cf..000000000 --- a/src/src/lookups/dkim.h +++ /dev/null @@ -1,16 +0,0 @@ -/* $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/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index ab3bc430d..c81671aa0 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.17 2007/01/08 10:50:19 ph10 Exp $ */ +/* $Cambridge: exim/src/src/lookups/dnsdb.c,v 1.18 2009/06/10 07:34:05 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -303,8 +303,14 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) if (type == T_TXT) { - yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1), - (rr->data)[0]); + int data_offset = 0; + while (data_offset < rr->size) + { + uschar chunk_len = (rr->data)[data_offset++]; + yield = string_cat(yield, &size, &ptr, + (uschar *)((rr->data)+data_offset), chunk_len); + data_offset += chunk_len; + } } else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */ { diff --git a/src/src/macros.h b/src/src/macros.h index aa4acd1c8..73944a333 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/macros.h,v 1.37 2008/09/29 11:41:07 nm4 Exp $ */ +/* $Cambridge: exim/src/src/macros.h,v 1.38 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -799,6 +799,7 @@ enum { ACL_WHERE_RCPT, /* Some controls are for RCPT only */ ACL_WHERE_MAIL, /* ) */ ACL_WHERE_PREDATA, /* ) There are several tests for "in message", */ ACL_WHERE_MIME, /* ) implemented by <= WHERE_NOTSMTP */ + ACL_WHERE_DKIM, /* ) */ ACL_WHERE_DATA, /* ) */ ACL_WHERE_NOTSMTP, /* ) */ diff --git a/src/src/pdkim/Makefile b/src/src/pdkim/Makefile new file mode 100644 index 000000000..a2bf6b005 --- /dev/null +++ b/src/src/pdkim/Makefile @@ -0,0 +1,22 @@ +# $Cambridge: exim/src/src/pdkim/Makefile,v 1.2 2009/06/10 07:34:05 tom Exp $ + +OBJ = base64.o bignum.o pdkim.o rsa.o sha1.o sha2.o + +pdkim.a: $(OBJ) + @$(RM_COMMAND) -f pdkim.a + @echo "$(AR) pdkim.a" + $(FE)$(AR) pdkim.a $(OBJ) + $(RANLIB) $@ + +.SUFFIXES: .o .c +.c.o:; @echo "$(CC) $*.c" + $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c + +base64.o: $(HDRS) base64.c +bignum.o: $(HDRS) bignum.c +pdkim.o: $(HDRS) pdkim.c +rsa.o: $(HDRS) rsa.c +sha1.o: $(HDRS) sha1.c +sha2.o: $(HDRS) sha2.c + +# End diff --git a/src/src/pdkim/README b/src/src/pdkim/README new file mode 100644 index 000000000..cd0106559 --- /dev/null +++ b/src/src/pdkim/README @@ -0,0 +1,13 @@ +$Cambridge: exim/src/src/pdkim/README,v 1.2 2009/06/10 07:34:05 tom Exp $ + +PDKIM - a RFC4871 (DKIM) implementation +http://duncanthrax.net/pdkim/ +Copyright (C) 2009 Tom Kistner + +Includes code from the PolarSSL project. +http://polarssl.org +Copyright (C) 2009 Paul Bakker +Copyright (C) 2006-2008 Christophe Devine + +This copy of PDKIM is included with Exim. For a standalone distribution, +visit http://duncanthrax.net/pdkim/. diff --git a/src/src/pdkim/base64.c b/src/src/pdkim/base64.c new file mode 100644 index 000000000..385d8776c --- /dev/null +++ b/src/src/pdkim/base64.c @@ -0,0 +1,180 @@ +/* + * RFC 1521 base64 encoding/decoding + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/base64.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#include "base64.h" + +static const unsigned char base64_enc_map[64] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/' +}; + +static const unsigned char base64_dec_map[128] = +{ + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, + 127, 64, 127, 127, 127, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 127, 127, 127, 127, 127 +}; + +/* + * Encode a buffer into base64 format + */ +int base64_encode( unsigned char *dst, int *dlen, + unsigned char *src, int slen ) +{ + int i, n; + int C1, C2, C3; + unsigned char *p; + + if( slen == 0 ) + return( 0 ); + + n = (slen << 3) / 6; + + switch( (slen << 3) - (n * 6) ) + { + case 2: n += 3; break; + case 4: n += 2; break; + default: break; + } + + if( *dlen < n + 1 ) + { + *dlen = n + 1; + return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); + } + + n = (slen / 3) * 3; + + for( i = 0, p = dst; i < n; i += 3 ) + { + C1 = *src++; + C2 = *src++; + C3 = *src++; + + *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; + *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; + *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F]; + *p++ = base64_enc_map[C3 & 0x3F]; + } + + if( i < slen ) + { + C1 = *src++; + C2 = ((i + 1) < slen) ? *src++ : 0; + + *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; + *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; + + if( (i + 1) < slen ) + *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F]; + else *p++ = '='; + + *p++ = '='; + } + + *dlen = p - dst; + *p = 0; + + return( 0 ); +} + +/* + * Decode a base64-formatted buffer + */ +int base64_decode( unsigned char *dst, int *dlen, + unsigned char *src, int slen ) +{ + int i, j, n; + unsigned long x; + unsigned char *p; + + for( i = j = n = 0; i < slen; i++ ) + { + if( ( slen - i ) >= 2 && + src[i] == '\r' && src[i + 1] == '\n' ) + continue; + + if( src[i] == '\n' ) + continue; + + if( src[i] == '=' && ++j > 2 ) + return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); + + if( src[i] > 127 || base64_dec_map[src[i]] == 127 ) + return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); + + if( base64_dec_map[src[i]] < 64 && j != 0 ) + return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); + + n++; + } + + if( n == 0 ) + return( 0 ); + + n = ((n * 6) + 7) >> 3; + + if( *dlen < n ) + { + *dlen = n; + return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); + } + + for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) + { + if( *src == '\r' || *src == '\n' ) + continue; + + j -= ( base64_dec_map[*src] == 64 ); + x = (x << 6) | ( base64_dec_map[*src] & 0x3F ); + + if( ++n == 4 ) + { + n = 0; + if( j > 0 ) *p++ = (unsigned char)( x >> 16 ); + if( j > 1 ) *p++ = (unsigned char)( x >> 8 ); + if( j > 2 ) *p++ = (unsigned char)( x ); + } + } + + *dlen = p - dst; + + return( 0 ); +} diff --git a/src/src/pdkim/base64.h b/src/src/pdkim/base64.h new file mode 100644 index 000000000..b5ac98caf --- /dev/null +++ b/src/src/pdkim/base64.h @@ -0,0 +1,76 @@ +/** + * \file base64.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/base64.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#ifndef POLARSSL_BASE64_H +#define POLARSSL_BASE64_H + +#define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL -0x0010 +#define POLARSSL_ERR_BASE64_INVALID_CHARACTER -0x0012 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Encode a buffer into base64 format + * + * \param dst destination buffer + * \param dlen size of the buffer + * \param src source buffer + * \param slen amount of data to be encoded + * + * \return 0 if successful, or POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL. + * *dlen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *dlen = 0 to obtain the + * required buffer size in *dlen + */ +int base64_encode( unsigned char *dst, int *dlen, + unsigned char *src, int slen ); + +/** + * \brief Decode a base64-formatted buffer + * + * \param dst destination buffer + * \param dlen size of the buffer + * \param src source buffer + * \param slen amount of data to be decoded + * + * \return 0 if successful, POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL, or + * POLARSSL_ERR_BASE64_INVALID_DATA if the input data is not + * correct. *dlen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *dlen = 0 to obtain the + * required buffer size in *dlen + */ +int base64_decode( unsigned char *dst, int *dlen, + unsigned char *src, int slen ); + +#ifdef __cplusplus +} +#endif + +#endif /* base64.h */ diff --git a/src/src/pdkim/bignum.c b/src/src/pdkim/bignum.c new file mode 100644 index 000000000..23d79968c --- /dev/null +++ b/src/src/pdkim/bignum.c @@ -0,0 +1,1813 @@ +/* + * Multi-precision integer library + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +/* $Cambridge: exim/src/src/pdkim/bignum.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#include "bignum.h" +#include "bn_mul.h" + +#include +#include +#include + +#define ciL ((int) sizeof(t_int)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one or more mpi + */ +void mpi_init( mpi *X, ... ) +{ + va_list args; + + va_start( args, X ); + + while( X != NULL ) + { + X->s = 1; + X->n = 0; + X->p = NULL; + + X = va_arg( args, mpi* ); + } + + va_end( args ); +} + +/* + * Unallocate one or more mpi + */ +void mpi_free( mpi *X, ... ) +{ + va_list args; + + va_start( args, X ); + + while( X != NULL ) + { + if( X->p != NULL ) + { + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; + + X = va_arg( args, mpi* ); + } + + va_end( args ); +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, int nblimbs ) +{ + t_int *p; + + if( X->n < nblimbs ) + { + if( ( p = (t_int *) malloc( nblimbs * ciL ) ) == NULL ) + return( 1 ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, mpi *Y ) +{ + int ret, i; + + if( X == Y ) + return( 0 ); + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, int z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +int mpi_lsb( mpi *X ) +{ + int i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < (int) biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +/* + * Return the number of most significant bits + */ +int mpi_msb( mpi *X ) +{ + int i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL - 1; j >= 0; j-- ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j + 1 ); +} + +/* + * Return the total size in bytes + */ +int mpi_size( mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_int *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_int) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, char *s ) +{ + int ret, i, j, n; + t_int d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T, NULL ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( strlen( s ) << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = strlen( s ) - 1, j = 0; i >= 0; i--, j++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < (int) strlen( s ); i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + } + +cleanup: + + mpi_free( &T, NULL ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_int r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( mpi *X, int radix, char *s, int *slen ) +{ + int ret = 0, n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T, NULL ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c, i, j, k; + + for( i = X->n - 1, k = 0; i >= 0; i-- ) + { + for( j = ciL - 1; j >= 0; j-- ) + { + c = ( X->p[i] >> (j << 3) ) & 0xFF; + + if( c == 0 && k == 0 && (i + j) != 0 ) + continue; + + p += sprintf( p, "%02X", c ); + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T, NULL ); + + return( ret ); +} + +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_int d; + int slen; + char *p; + char s[1024]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( char *p, mpi *X, int radix, FILE *fout ) +{ + int n, ret; + size_t slen; + size_t plen; + char s[1024]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (int *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, unsigned char *buf, int buflen ) +{ + int ret, i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen - 1, j = 0; i >= n; i--, j++ ) + X->p[j / ciL] |= ((t_int) buf[i]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( mpi *X, unsigned char *buf, int buflen ) +{ + int i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, int count ) +{ + int ret, i, v0, t1; + t_int r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * (int) biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n - 1; i >= v0; i-- ) + X->p[i] = X->p[i - v0]; + + for( ; i >= 0; i-- ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, int count ) +{ + int i, v0, v1; + t_int r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n - 1; i >= 0; i-- ) + { + r1 = X->p[i] << (biL - v1); + X->p[i] >>= v1; + X->p[i] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( mpi *X, mpi *Y ) +{ + int i, j; + + for( i = X->n - 1; i >= 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = Y->n - 1; j >= 0; j-- ) + if( Y->p[j] != 0 ) + break; + + if( i < 0 && j < 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i >= 0; i-- ) + { + if( X->p[i] > Y->p[i] ) return( 1 ); + if( X->p[i] < Y->p[i] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( mpi *X, mpi *Y ) +{ + int i, j; + + for( i = X->n - 1; i >= 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = Y->n - 1; j >= 0; j-- ) + if( Y->p[j] != 0 ) + break; + + if( i < 0 && j < 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -X->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i >= 0; i-- ) + { + if( X->p[i] > Y->p[i] ) return( X->s ); + if( X->p[i] < Y->p[i] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( mpi *X, int z ) +{ + mpi Y; + t_int p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, mpi *A, mpi *B ) +{ + int ret, i, j; + t_int *o, *p, c; + + if( X == B ) + { + mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + for( j = B->n - 1; j >= 0; j-- ) + if( B->p[j] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j + 1 ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i <= j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi substraction + */ +static void mpi_sub_hlp( int n, t_int *s, t_int *d ) +{ + int i; + t_int c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; i++; d++; + } +} + +/* + * Unsigned substraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, mpi *A, mpi *B ) +{ + mpi TB; + int ret, n; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB, NULL ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + ret = 0; + + for( n = B->n - 1; n >= 0; n-- ) + if( B->p[n] != 0 ) + break; + + mpi_sub_hlp( n + 1, B->p, X->p ); + +cleanup: + + mpi_free( &TB, NULL ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, mpi *A, mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed substraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, mpi *A, mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, mpi *A, int b ) +{ + mpi _B; + t_int p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed substraction: X = A - b + */ +int mpi_sub_int( mpi *X, mpi *A, int b ) +{ + mpi _B; + t_int p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static void mpi_mul_hlp( int i, t_int *s, t_int *d, t_int b ) +{ + t_int c = 0, t = 0; + +#if defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif + + t++; + + do { + *d += c; c = ( *d < c ); d++; + } + while( c != 0 ); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, mpi *A, mpi *B ) +{ + int ret, i, j; + mpi TA, TB; + + mpi_init( &TA, &TB, NULL ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n - 1; i >= 0; i-- ) + if( A->p[i] != 0 ) + break; + + for( j = B->n - 1; j >= 0; j-- ) + if( B->p[j] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j + 2 ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i++; j >= 0; j-- ) + mpi_mul_hlp( i, A->p, X->p + j, B->p[j] ); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB, &TA, NULL ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, mpi *A, t_int b ) +{ + mpi _B; + t_int p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, mpi *A, mpi *B ) +{ + int ret, i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X, &Y, &Z, &T1, &T2, NULL ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < (int) biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + mpi_shift_l( &Y, biL * (n - t) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + mpi_sub_mpi( &X, &X, &Y ); + } + mpi_shift_r( &Y, biL * (n - t) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0; + else + { +#if defined(POLARSSL_HAVE_LONGLONG) + t_dbl r; + + r = (t_dbl) X.p[i] << biL; + r |= (t_dbl) X.p[i - 1]; + r /= Y.p[t]; + if( r > ((t_dbl) 1 << biL) - 1) + r = ((t_dbl) 1 << biL) - 1; + + Z.p[i - t - 1] = (t_int) r; +#else + /* + * __udiv_qrnnd_c, from gmp/longlong.h + */ + t_int q0, q1, r0, r1; + t_int d0, d1, d, m; + + d = Y.p[t]; + d0 = ( d << biH ) >> biH; + d1 = ( d >> biH ); + + q1 = X.p[i] / d1; + r1 = X.p[i] - d1 * q1; + r1 <<= biH; + r1 |= ( X.p[i - 1] >> biH ); + + m = q1 * d0; + if( r1 < m ) + { + q1--, r1 += d; + while( r1 >= d && r1 < m ) + q1--, r1 += d; + } + r1 -= m; + + q0 = r1 / d1; + r0 = r1 - d1 * q0; + r0 <<= biH; + r0 |= ( X.p[i - 1] << biH ) >> biH; + + m = q0 * d0; + if( r0 < m ) + { + q0--, r0 += d; + while( r0 >= d && r0 < m ) + q0--, r0 += d; + } + r0 -= m; + + Z.p[i - t - 1] = ( q1 << biH ) | q0; +#endif + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + if( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + mpi_copy( Q, &Z ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + mpi_shift_r( &X, k ); + mpi_copy( R, &X ); + + R->s = A->s; + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X, &Y, &Z, &T1, &T2, NULL ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + * + * Returns 0 if successful + * 1 if memory allocation failed + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + */ +int mpi_div_int( mpi *Q, mpi *R, mpi *A, int b ) +{ + mpi _B; + t_int p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, mpi *A, mpi *B ) +{ + int ret; + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_int *r, mpi *A, int b ) +{ + int i; + t_int x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + b = -b; + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n - 1, y = 0; i >= 0; i-- ) + { + x = A->p[i]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_int *mm, mpi *N ) +{ + t_int x, m0 = N->p[0]; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + x *= ( 2 - ( m0 * x ) ); + + if( biL >= 16 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 32 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 64 ) x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + */ +static void mpi_montmul( mpi *A, mpi *B, mpi *N, t_int mm, mpi *T ) +{ + int i, n, m; + t_int u0, u1, *d; + + memset( T->p, 0, T->n * ciL ); + + d = T->p; + n = N->n; + m = ( B->n < n ) ? B->n : n; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = A->p[i]; + u1 = ( d[0] + u0 * B->p[0] ) * mm; + + mpi_mul_hlp( m, B->p, d, u0 ); + mpi_mul_hlp( n, N->p, d, u1 ); + + *d++ = u0; d[n + 1] = 0; + } + + memcpy( A->p, d, (n + 1) * ciL ); + + if( mpi_cmp_abs( A, N ) >= 0 ) + mpi_sub_hlp( n, N->p, A->p ); + else + /* prevent timing attacks */ + mpi_sub_hlp( n, A->p, T->p ); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + */ +static void mpi_montred( mpi *A, mpi *N, t_int mm, mpi *T ) +{ + t_int z = 1; + mpi U; + + U.n = U.s = z; + U.p = &z; + + mpi_montmul( A, &U, N, mm, T ); +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mpi_exp_mod( mpi *X, mpi *A, mpi *E, mpi *N, mpi *_RR ) +{ + int ret, i, j, wsize, wbits; + int bufsize, nblimbs, nbits; + t_int ei, mm, state; + mpi RR, T, W[64]; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + mpi_init( &RR, &T, NULL ); + memset( W, 0, sizeof( W ) ); + + i = mpi_msb( E ); + + wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + + j = N->n + 1; + MPI_CHK( mpi_grow( X, j ) ); + MPI_CHK( mpi_grow( &W[1], j ) ); + MPI_CHK( mpi_grow( &T, j * 2 ) ); + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + MPI_CHK( mpi_lset( &RR, 1 ) ); + MPI_CHK( mpi_shift_l( &RR, N->n * 2 * biL ) ); + MPI_CHK( mpi_mod_mpi( &RR, &RR, N ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + } + else + memcpy( &RR, _RR, sizeof( mpi ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) + mpi_mod_mpi( &W[1], A, N ); + else mpi_copy( &W[1], A ); + + mpi_montmul( &W[1], &RR, N, mm, &T ); + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + MPI_CHK( mpi_copy( X, &RR ) ); + mpi_montred( X, N, mm, &T ); + + if( wsize > 1 ) + { + /* + * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + */ + j = 1 << (wsize - 1); + + MPI_CHK( mpi_grow( &W[j], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[j], &W[1] ) ); + + for( i = 0; i < wsize - 1; i++ ) + mpi_montmul( &W[j], &W[j], N, mm, &T ); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = j + 1; i < (1 << wsize); i++ ) + { + MPI_CHK( mpi_grow( &W[i], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[i], &W[i - 1] ) ); + + mpi_montmul( &W[i], &W[1], N, mm, &T ); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs-- == 0 ) + break; + + bufsize = sizeof( t_int ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montmul( X, X, N, mm, &T ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= (ei << (wsize - nbits)); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montmul( X, X, N, mm, &T ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( X, &W[wbits], N, mm, &T ); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montmul( X, X, N, mm, &T ); + + wbits <<= 1; + + if( (wbits & (1 << wsize)) != 0 ) + mpi_montmul( X, &W[1], N, mm, &T ); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( X, N, mm, &T ); + +cleanup: + + for( i = (1 << (wsize - 1)); i < (1 << wsize); i++ ) + mpi_free( &W[i], NULL ); + + if( _RR != NULL ) + mpi_free( &W[1], &T, NULL ); + else mpi_free( &W[1], &T, &RR, NULL ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, mpi *A, mpi *B ) +{ + int ret; + mpi TG, TA, TB; + + mpi_init( &TG, &TA, &TB, NULL ); + + MPI_CHK( mpi_lset( &TG, 1 ) ); + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + while( ( TA.p[0] & 1 ) == 0 ) MPI_CHK( mpi_shift_r( &TA, 1 ) ); + while( ( TB.p[0] & 1 ) == 0 ) MPI_CHK( mpi_shift_r( &TB, 1 ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_mul_mpi( G, &TG, &TB ) ); + +cleanup: + + mpi_free( &TB, &TA, &TG, NULL ); + + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, mpi *A, mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA, &TU, &U1, &U2, &G, + &TB, &TV, &V1, &V2, NULL ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &V2, &V1, &TV, &TB, &G, + &U2, &U1, &TU, &TA, NULL ); + + return( ret ); +} + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Miller-Rabin primality test (HAC 4.24) + */ +int mpi_is_prime( mpi *X, int (*f_rng)(void *), void *p_rng ) +{ + int ret, i, j, n, s, xs; + mpi W, R, T, A, RR; + unsigned char *p; + + if( mpi_cmp_int( X, 0 ) == 0 ) + return( 0 ); + + mpi_init( &W, &R, &T, &A, &RR, NULL ); + + xs = X->s; X->s = 1; + + /* + * test trivial factors first + */ + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + for( i = 0; small_prime[i] > 0; i++ ) + { + t_int r; + + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 0 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + s = mpi_lsb( &W ); + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + + i = mpi_msb( X ); + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_grow( &A, X->n ) ); + + p = (unsigned char *) A.p; + for( j = 0; j < A.n * ciL; j++ ) + *p++ = (unsigned char) f_rng( p_rng ); + + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + + X->s = xs; + + mpi_free( &RR, &A, &T, &R, &W, NULL ); + + return( ret ); +} + +/* + * Prime number generation + */ +int mpi_gen_prime( mpi *X, int nbits, int dh_flag, + int (*f_rng)(void *), void *p_rng ) +{ + int ret, k, n; + unsigned char *p; + mpi Y; + + if( nbits < 3 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &Y, NULL ); + + n = BITS_TO_LIMBS( nbits ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + p = (unsigned char *) X->p; + for( k = 0; k < X->n * ciL; k++ ) + *p++ = (unsigned char) f_rng( p_rng ); + + k = mpi_msb( X ); + if( k < nbits ) MPI_CHK( mpi_shift_l( X, nbits - k ) ); + if( k > nbits ) MPI_CHK( mpi_shift_r( X, k - nbits ) ); + + X->p[0] |= 3; + + if( dh_flag == 0 ) + { + while( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) != 0 ) + { + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( X, X, 2 ) ); + } + } + else + { + MPI_CHK( mpi_sub_int( &Y, X, 1 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + + while( 1 ) + { + if( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) == 0 ) + { + if( ( ret = mpi_is_prime( &Y, f_rng, p_rng ) ) == 0 ) + break; + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + } + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( &Y, X, 1 ) ); + MPI_CHK( mpi_add_int( X, X, 2 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + } + } + +cleanup: + + mpi_free( &Y, NULL ); + + return( ret ); +} diff --git a/src/src/pdkim/bignum.h b/src/src/pdkim/bignum.h new file mode 100644 index 000000000..28e746d4d --- /dev/null +++ b/src/src/pdkim/bignum.h @@ -0,0 +1,395 @@ +/** + * \file bignum.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/bignum.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR -0x0002 +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA -0x0004 +#define POLARSSL_ERR_MPI_INVALID_CHARACTER -0x0006 +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL -0x0008 +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE -0x000A +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO -0x000C +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE -0x000E + +#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef unsigned char t_int; +typedef unsigned short t_dbl; +#else +#if defined(POLARSSL_HAVE_INT16) +typedef unsigned short t_int; +typedef unsigned long t_dbl; +#else + typedef unsigned long t_int; + #if defined(_MSC_VER) && defined(_M_IX86) + typedef unsigned __int64 t_dbl; + #else + #if defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) + typedef unsigned int t_dbl __attribute__((mode(TI))); + #else + typedef unsigned long long t_dbl; + #endif + #endif +#endif +#endif + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + int n; /*!< total # of limbs */ + t_int *p; /*!< pointer to limbs */ +} +mpi; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize one or more mpi + */ +void mpi_init( mpi *X, ... ); + +/** + * \brief Unallocate one or more mpi + */ +void mpi_free( mpi *X, ... ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_grow( mpi *X, int nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_copy( mpi *X, mpi *Y ); + +/** + * \brief Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Set value from integer + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_lset( mpi *X, int z ); + +/** + * \brief Return the number of least significant bits + */ +int mpi_lsb( mpi *X ); + +/** + * \brief Return the number of most significant bits + */ +int mpi_msb( mpi *X ); + +/** + * \brief Return the total size in bytes + */ +int mpi_size( mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X destination mpi + * \param radix input numeric base + * \param s null-terminated string buffer + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X source mpi + * \param radix output numeric base + * \param s string buffer + * \param slen string buffer size + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( mpi *X, int radix, char *s, int *slen ); + +/** + * \brief Read X from an opened file + * + * \param X destination mpi + * \param radix input numeric base + * \param fin input file handle + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout + * + * \param p prefix, can be NULL + * \param X source mpi + * \param radix output numeric base + * \param fout output file handle + * + * \return 0 if successful, or an POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( char *p, mpi *X, int radix, FILE *fout ); + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X destination mpi + * \param buf input buffer + * \param buflen input buffer size + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_read_binary( mpi *X, unsigned char *buf, int buflen ); + +/** + * \brief Export X into unsigned binary data, big endian + * + * \param X source mpi + * \param buf output buffer + * \param buflen output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + * + * \note Call this function with *buflen = 0 to obtain the + * minimum required buffer size in *buflen. + */ +int mpi_write_binary( mpi *X, unsigned char *buf, int buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_shift_l( mpi *X, int count ); + +/** + * \brief Right-shift: X >>= count + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_shift_r( mpi *X, int count ); + +/** + * \brief Compare unsigned values + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( mpi *X, mpi *Y ); + +/** + * \brief Compare signed values + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( mpi *X, mpi *Y ); + +/** + * \brief Compare signed values + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( mpi *X, int z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_add_abs( mpi *X, mpi *A, mpi *B ); + +/** + * \brief Unsigned substraction: X = |A| - |B| + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, mpi *A, mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_add_mpi( mpi *X, mpi *A, mpi *B ); + +/** + * \brief Signed substraction: X = A - B + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, mpi *A, mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_add_int( mpi *X, mpi *A, int b ); + +/** + * \brief Signed substraction: X = A - b + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_sub_int( mpi *X, mpi *A, int b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, mpi *A, mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_mul_int( mpi *X, mpi *A, t_int b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, mpi *A, mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, mpi *A, int b ); + +/** + * \brief Modulo: R = A mod B + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + */ +int mpi_mod_mpi( mpi *R, mpi *A, mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + */ +int mpi_mod_int( t_int *r, mpi *A, int b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, mpi *A, mpi *E, mpi *N, mpi *_RR ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \return 0 if successful, + * 1 if memory allocation failed + */ +int mpi_gcd( mpi *G, mpi *A, mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \return 0 if successful, + * 1 if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, mpi *A, mpi *N ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/src/src/pdkim/bn_mul.h b/src/src/pdkim/bn_mul.h new file mode 100644 index 000000000..8f8355bee --- /dev/null +++ b/src/src/pdkim/bn_mul.h @@ -0,0 +1,719 @@ +/** + * \file bn_mul.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ + +/* $Cambridge: exim/src/src/pdkim/bn_mul.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( "movl %%ebx, %0 " : "=m" (t)); \ + asm( "movl %0, %%esi " :: "m" (s)); \ + asm( "movl %0, %%edi " :: "m" (d)); \ + asm( "movl %0, %%ecx " :: "m" (c)); \ + asm( "movl %0, %%ebx " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lodsl " ); \ + asm( "mull %ebx " ); \ + asm( "addl %ecx, %eax " ); \ + asm( "adcl $0, %edx " ); \ + asm( "addl (%edi), %eax " ); \ + asm( "adcl $0, %edx " ); \ + asm( "movl %edx, %ecx " ); \ + asm( "stosl " ); + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + asm( "movd %ecx, %mm1 " ); \ + asm( "movd %ebx, %mm0 " ); \ + asm( "movd (%edi), %mm3 " ); \ + asm( "paddq %mm3, %mm1 " ); \ + asm( "movd (%esi), %mm2 " ); \ + asm( "pmuludq %mm0, %mm2 " ); \ + asm( "movd 4(%esi), %mm4 " ); \ + asm( "pmuludq %mm0, %mm4 " ); \ + asm( "movd 8(%esi), %mm6 " ); \ + asm( "pmuludq %mm0, %mm6 " ); \ + asm( "movd 12(%esi), %mm7 " ); \ + asm( "pmuludq %mm0, %mm7 " ); \ + asm( "paddq %mm2, %mm1 " ); \ + asm( "movd 4(%edi), %mm3 " ); \ + asm( "paddq %mm4, %mm3 " ); \ + asm( "movd 8(%edi), %mm5 " ); \ + asm( "paddq %mm6, %mm5 " ); \ + asm( "movd 12(%edi), %mm4 " ); \ + asm( "paddq %mm4, %mm7 " ); \ + asm( "movd %mm1, (%edi) " ); \ + asm( "movd 16(%esi), %mm2 " ); \ + asm( "pmuludq %mm0, %mm2 " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "movd 20(%esi), %mm4 " ); \ + asm( "pmuludq %mm0, %mm4 " ); \ + asm( "paddq %mm3, %mm1 " ); \ + asm( "movd 24(%esi), %mm6 " ); \ + asm( "pmuludq %mm0, %mm6 " ); \ + asm( "movd %mm1, 4(%edi) " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "movd 28(%esi), %mm3 " ); \ + asm( "pmuludq %mm0, %mm3 " ); \ + asm( "paddq %mm5, %mm1 " ); \ + asm( "movd 16(%edi), %mm5 " ); \ + asm( "paddq %mm5, %mm2 " ); \ + asm( "movd %mm1, 8(%edi) " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "paddq %mm7, %mm1 " ); \ + asm( "movd 20(%edi), %mm5 " ); \ + asm( "paddq %mm5, %mm4 " ); \ + asm( "movd %mm1, 12(%edi) " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "paddq %mm2, %mm1 " ); \ + asm( "movd 24(%edi), %mm5 " ); \ + asm( "paddq %mm5, %mm6 " ); \ + asm( "movd %mm1, 16(%edi) " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "paddq %mm4, %mm1 " ); \ + asm( "movd 28(%edi), %mm5 " ); \ + asm( "paddq %mm5, %mm3 " ); \ + asm( "movd %mm1, 20(%edi) " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "paddq %mm6, %mm1 " ); \ + asm( "movd %mm1, 24(%edi) " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "paddq %mm3, %mm1 " ); \ + asm( "movd %mm1, 28(%edi) " ); \ + asm( "addl $32, %edi " ); \ + asm( "addl $32, %esi " ); \ + asm( "psrlq $32, %mm1 " ); \ + asm( "movd %mm1, %ecx " ); + +#define MULADDC_STOP \ + asm( "emms " ); \ + asm( "movl %0, %%ebx " :: "m" (t)); \ + asm( "movl %%ecx, %0 " : "=m" (c)); \ + asm( "movl %%edi, %0 " : "=m" (d)); \ + asm( "movl %%esi, %0 " : "=m" (s) :: \ + "eax", "ecx", "edx", "esi", "edi" ); + +#else + +#define MULADDC_STOP \ + asm( "movl %0, %%ebx " :: "m" (t)); \ + asm( "movl %%ecx, %0 " : "=m" (c)); \ + asm( "movl %%edi, %0 " : "=m" (d)); \ + asm( "movl %%esi, %0 " : "=m" (s) :: \ + "eax", "ecx", "edx", "esi", "edi" ); + +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) + +#define MULADDC_INIT \ + asm( "ld %0, %%o0 " :: "m" (s)); \ + asm( "ld %0, %%o1 " :: "m" (d)); \ + asm( "ld %0, %%o2 " :: "m" (c)); \ + asm( "ld %0, %%o3 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ld [%o0], %o4 " ); \ + asm( "inc 4, %o0 " ); \ + asm( "ld [%o1], %o5 " ); \ + asm( "umul %o3, %o4, %o4 " ); \ + asm( "addcc %o4, %o2, %o4 " ); \ + asm( "rd %y, %g1 " ); \ + asm( "addx %g1, 0, %g1 " ); \ + asm( "addcc %o4, %o5, %o4 " ); \ + asm( "st %o4, [%o1] " ); \ + asm( "addx %g1, 0, %o2 " ); \ + asm( "inc 4, %o1 " ); + +#define MULADDC_STOP \ + asm( "st %%o2, %0 " : "=m" (c)); \ + asm( "st %%o1, %0 " : "=m" (d)); \ + asm( "st %%o0, %0 " : "=m" (s) :: \ + "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#define MULADDC_INIT \ + asm( "ldr r0, %0 " :: "m" (s)); \ + asm( "ldr r1, %0 " :: "m" (d)); \ + asm( "ldr r2, %0 " :: "m" (c)); \ + asm( "ldr r3, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldr r4, [r0], #4 " ); \ + asm( "mov r5, #0 " ); \ + asm( "ldr r6, [r1] " ); \ + asm( "umlal r2, r5, r3, r4 " ); \ + asm( "adds r7, r6, r2 " ); \ + asm( "adc r2, r5, #0 " ); \ + asm( "str r7, [r1], #4 " ); + +#define MULADDC_STOP \ + asm( "str r2, %0 " : "=m" (c)); \ + asm( "str r1, %0 " : "=m" (d)); \ + asm( "str r0, %0 " : "=m" (s) :: \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" ); + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_LONGLONG) + +#define MULADDC_INIT \ +{ \ + t_dbl r; \ + t_int r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_dbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_int s0, s1, b0, b1; \ + t_int r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c new file mode 100644 index 000000000..e269f7796 --- /dev/null +++ b/src/src/pdkim/pdkim.c @@ -0,0 +1,1714 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2009 Tom Kistner + * + * http://duncanthrax.net/pdkim/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#include +#include +#include +#include + +#include "pdkim.h" + +#include "sha1.h" +#include "sha2.h" +#include "rsa.h" +#include "base64.h" + +#define PDKIM_SIGNATURE_VERSION "1" +#define PDKIM_PUB_RECORD_VERSION "DKIM1" + +#define PDKIM_MAX_HEADER_LEN 65536 +#define PDKIM_MAX_HEADERS 512 +#define PDKIM_MAX_BODY_LINE_LEN 16384 +#define PDKIM_DNS_TXT_MAX_NAMELEN 1024 +#define PDKIM_DEFAULT_SIGN_HEADERS "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" + +/* -------------------------------------------------------------------------- */ +struct pdkim_stringlist { + char *value; + void *next; +}; + +#define PDKIM_STR_ALLOC_FRAG 256 +struct pdkim_str { + char *str; + unsigned int len; + unsigned int allocated; +}; + +/* -------------------------------------------------------------------------- */ +/* A bunch of list constants */ +char *pdkim_querymethods[] = { + "dns/txt", + NULL +}; +char *pdkim_algos[] = { + "rsa-sha256", + "rsa-sha1", + NULL +}; +char *pdkim_canons[] = { + "simple", + "relaxed", + NULL +}; +char *pdkim_hashes[] = { + "sha256", + "sha1", + NULL +}; +char *pdkim_keytypes[] = { + "rsa", + NULL +}; + +typedef struct pdkim_combined_canon_entry { + char *str; + int canon_headers; + int canon_body; +} pdkim_combined_canon_entry; +pdkim_combined_canon_entry pdkim_combined_canons[] = { + { "simple/simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE }, + { "simple/relaxed", PDKIM_CANON_SIMPLE, PDKIM_CANON_RELAXED }, + { "relaxed/simple", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE }, + { "relaxed/relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_RELAXED }, + { "simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE }, + { "relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE }, + { NULL, 0, 0 } +}; + + +/* -------------------------------------------------------------------------- */ +/* Print debugging functions */ +#ifdef PDKIM_DEBUG +void pdkim_quoteprint(FILE *stream, char *data, int len, int lf) { + int i; + unsigned char *p = (unsigned char *)data; + + for (i=0;i 127) ) + fprintf(stream,"{%02x}",c); + else + fputc(c,stream); + break; + } + } + if (lf) + fputc('\n',stream); +} +void pdkim_hexprint(FILE *stream, char *data, int len, int lf) { + int i; + unsigned char *p = (unsigned char *)data; + + for (i=0;ivalue = strdup(str); + if (new_entry->value == NULL) return NULL; + if (base != NULL) { + pdkim_stringlist *last = base; + while (last->next != NULL) { last = last->next; }; + last->next = new_entry; + return base; + } + else return new_entry; +}; + + +/* -------------------------------------------------------------------------- */ +/* A small "growing string" implementation to escape malloc/realloc hell */ +pdkim_str *pdkim_strnew (char *cstr) { + unsigned int len = cstr?strlen(cstr):0; + pdkim_str *p = malloc(sizeof(pdkim_str)); + if (p == NULL) return NULL; + memset(p,0,sizeof(pdkim_str)); + p->str = malloc(len+1); + if (p->str == NULL) { + free(p); + return NULL; + } + p->allocated=(len+1); + p->len=len; + if (cstr) strcpy(p->str,cstr); + return p; +}; +char *pdkim_strncat(pdkim_str *str, char *data, int len) { + if ((str->allocated - str->len) < (len+1)) { + /* Extend the buffer */ + int num_frags = ((len+1)/PDKIM_STR_ALLOC_FRAG)+1; + char *n = realloc(str->str, + (str->allocated+(num_frags*PDKIM_STR_ALLOC_FRAG))); + if (n == NULL) return NULL; + str->str = n; + str->allocated += (num_frags*PDKIM_STR_ALLOC_FRAG); + } + strncpy(&(str->str[str->len]),data,len); + str->len+=len; + str->str[str->len] = '\0'; + return str->str; +}; +char *pdkim_strcat(pdkim_str *str, char *cstr) { + return pdkim_strncat(str, cstr, strlen(cstr)); +}; +char *pdkim_numcat(pdkim_str *str, unsigned long num) { + char minibuf[20]; + snprintf(minibuf,20,"%lu",num); + return pdkim_strcat(str,minibuf); +}; +char *pdkim_strtrim(pdkim_str *str) { + char *p = str->str; + char *q = str->str; + while ( (*p != '\0') && ((*p == '\t') || (*p == ' ')) ) p++; + while (*p != '\0') {*q = *p; q++; p++;}; + *q = '\0'; + while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) ) { + *q = '\0'; + q--; + } + str->len = strlen(str->str); + return str->str; +}; +char *pdkim_strclear(pdkim_str *str) { + str->str[0] = '\0'; + str->len = 0; + return str->str; +}; +void pdkim_strfree(pdkim_str *str) { + if (str == NULL) return; + if (str->str != NULL) free(str->str); + free(str); +}; + + + +/* -------------------------------------------------------------------------- */ +void pdkim_free_pubkey(pdkim_pubkey *pub) { + if (pub) { + if (pub->version != NULL) free(pub->version); + if (pub->granularity != NULL) free(pub->granularity); + if (pub->hashes != NULL) free(pub->hashes); + if (pub->keytype != NULL) free(pub->keytype); + if (pub->srvtype != NULL) free(pub->srvtype); + if (pub->notes != NULL) free(pub->notes); + if (pub->key != NULL) free(pub->key); + free(pub); + } +} + + +/* -------------------------------------------------------------------------- */ +void pdkim_free_sig(pdkim_signature *sig) { + if (sig) { + pdkim_signature *next = (pdkim_signature *)sig->next; + + pdkim_stringlist *e = sig->headers; + while(e != NULL) { + pdkim_stringlist *c = e; + if (e->value != NULL) free(e->value); + e = e->next; + free(c); + } + + if (sig->sigdata != NULL) free(sig->sigdata); + if (sig->bodyhash != NULL) free(sig->bodyhash); + if (sig->selector != NULL) free(sig->selector); + if (sig->domain != NULL) free(sig->domain); + if (sig->identity != NULL) free(sig->identity); + if (sig->headernames != NULL) free(sig->headernames); + if (sig->copiedheaders != NULL) free(sig->copiedheaders); + if (sig->rsa_privkey != NULL) free(sig->rsa_privkey); + if (sig->sign_headers != NULL) free(sig->sign_headers); + if (sig->signature_header != NULL) free(sig->signature_header); + if (sig->sha1_body != NULL) free(sig->sha1_body); + if (sig->sha2_body != NULL) free(sig->sha2_body); + if (sig->hnames_check != NULL) free(sig->hnames_check); + + if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey); + + free(sig); + if (next != NULL) pdkim_free_sig(next); + } +}; + + +/* -------------------------------------------------------------------------- */ +DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) { + if (ctx) { + pdkim_free_sig(ctx->sig); + pdkim_strfree(ctx->cur_header); + free(ctx); + } +}; + + +/* -------------------------------------------------------------------------- */ +/* Matches the name of the passed raw "header" against + the passed colon-separated "list", starting at entry + "start". Returns the position of the header name in + the list. */ +int header_name_match(char *header, + char *tick, + int do_tick) { + char *hname; + char *lcopy; + char *p; + char *q; + int rc = PDKIM_FAIL; + + /* Get header name */ + char *hcolon = strchr(header,':'); + if (hcolon == NULL) return rc; /* This isn't a header */ + hname = malloc((hcolon-header)+1); + if (hname == NULL) return PDKIM_ERR_OOM; + memset(hname,0,(hcolon-header)+1); + strncpy(hname,header,(hcolon-header)); + + /* Copy tick-off list locally, so we can punch zeroes into it */ + lcopy = strdup(tick); + if (lcopy == NULL) { + free(hname); + return PDKIM_ERR_OOM; + } + p = lcopy; + q = strchr(p,':'); + while (q != NULL) { + *q = '\0'; + + if (strcasecmp(p,hname) == 0) { + rc = PDKIM_OK; + /* Invalidate header name instance in tick-off list */ + if (do_tick) tick[p-lcopy] = '_'; + goto BAIL; + } + + p = q+1; + q = strchr(p,':'); + } + + if (strcasecmp(p,hname) == 0) { + rc = PDKIM_OK; + /* Invalidate header name instance in tick-off list */ + if (do_tick) tick[p-lcopy] = '_'; + } + + BAIL: + free(hname); + free(lcopy); + return rc; +} + + +/* -------------------------------------------------------------------------- */ +/* Performs "relaxed" canonicalization of a header. The returned pointer needs + to be free()d. */ +char *pdkim_relax_header (char *header, int crlf) { + int past_field_name = 0; + int seen_wsp = 0; + char *p = header; + char *q; + char *relaxed = malloc(strlen(header)); + if (relaxed == NULL) return NULL; + q = relaxed; + while (*p != '\0') { + int c = *p; + /* Ignore CR & LF */ + if ( (c == '\r') || (c == '\n') ) { + p++; + continue; + } + if ( (c == '\t') || (c == ' ') ) { + c = ' '; /* Turns WSP into SP */ + if (seen_wsp) { + p++; + continue; + } + else seen_wsp = 1; + } + else { + if ( (!past_field_name) && (c == ':') ) { + if (seen_wsp) q--; /* This removes WSP before the colon */ + seen_wsp = 1; /* This removes WSP after the colon */ + past_field_name = 1; + } + else seen_wsp = 0; + } + /* Lowercase header name */ + if (!past_field_name) c = tolower(c); + *q = c; + p++; + q++; + } + *q = '\0'; + if (crlf) strcat(relaxed,"\r\n"); + return relaxed; +}; + + +/* -------------------------------------------------------------------------- */ +#define PDKIM_QP_ERROR_DECODE -1 +char *pdkim_decode_qp_char(char *qp_p, int *c) { + char *initial_pos = qp_p; + + /* Advance one char */ + qp_p++; + + /* Check for two hex digits and decode them */ + if (isxdigit(*qp_p) && isxdigit(qp_p[1])) { + /* Do hex conversion */ + if (isdigit(*qp_p)) {*c = *qp_p - '0';} + else {*c = toupper(*qp_p) - 'A' + 10;}; + *c <<= 4; + if (isdigit(qp_p[1])) {*c |= qp_p[1] - '0';} + else {*c |= toupper(qp_p[1]) - 'A' + 10;}; + return qp_p + 2; + }; + + /* Illegal char here */ + *c = PDKIM_QP_ERROR_DECODE; + return initial_pos; +} + + +/* -------------------------------------------------------------------------- */ +char *pdkim_decode_qp(char *str) { + int nchar = 0; + char *q; + char *p = str; + char *n = malloc(strlen(p)+1); + if (n == NULL) return NULL; + *n = '\0'; + q = n; + while (*p != '\0') { + if (*p == '=') { + p = pdkim_decode_qp_char(p,&nchar); + if (nchar >= 0) { + *q = nchar; + q++; + continue; + } + } + else { + *q = *p; + q++; + } + p++; + } + *q = '\0'; + return n; +} + + +/* -------------------------------------------------------------------------- */ +char *pdkim_decode_base64(char *str, int *num_decoded) { + int dlen = 0; + char *res; + + base64_decode(NULL, &dlen, (unsigned char *)str, strlen(str)); + res = malloc(dlen+1); + if (res == NULL) return NULL; + if (base64_decode((unsigned char *)res,&dlen,(unsigned char *)str,strlen(str)) != 0) { + free(res); + return NULL; + } + if (num_decoded != NULL) *num_decoded = dlen; + return res; +} + +/* -------------------------------------------------------------------------- */ +char *pdkim_encode_base64(char *str, int num) { + int dlen = 0; + char *res; + + base64_encode(NULL, &dlen, (unsigned char *)str, num); + res = malloc(dlen+1); + if (res == NULL) return NULL; + if (base64_encode((unsigned char *)res,&dlen,(unsigned char *)str,num) != 0) { + free(res); + return NULL; + } + return res; +} + + +/* -------------------------------------------------------------------------- */ +#define PDKIM_HDR_LIMBO 0 +#define PDKIM_HDR_TAG 1 +#define PDKIM_HDR_VALUE 2 +pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) { + pdkim_signature *sig ; + char *p,*q; + pdkim_str *cur_tag = NULL; + pdkim_str *cur_val = NULL; + int past_hname = 0; + int in_b_val = 0; + int where = PDKIM_HDR_LIMBO; + int i; + + sig = malloc(sizeof(pdkim_signature)); + if (sig == NULL) return NULL; + memset(sig,0,sizeof(pdkim_signature)); + sig->bodylength = -1; + + sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1); + if (sig->rawsig_no_b_val == NULL) { + free(sig); + return NULL; + } + + p = raw_hdr; + q = sig->rawsig_no_b_val; + + while (1) { + + /* Ignore FWS */ + if ( (*p == '\r') || (*p == '\n') ) + goto NEXT_CHAR; + + /* Fast-forward through header name */ + if (!past_hname) { + if (*p == ':') past_hname = 1; + goto NEXT_CHAR; + } + + if (where == PDKIM_HDR_LIMBO) { + /* In limbo, just wait for a tag-char to appear */ + if (!((*p >= 'a') && (*p <= 'z'))) + goto NEXT_CHAR; + + where = PDKIM_HDR_TAG; + } + + if (where == PDKIM_HDR_TAG) { + if (cur_tag == NULL) + cur_tag = pdkim_strnew(NULL); + + if ((*p >= 'a') && (*p <= 'z')) + pdkim_strncat(cur_tag,p,1); + + if (*p == '=') { + if (strcmp(cur_tag->str,"b") == 0) { + *q = '='; q++; + in_b_val = 1; + } + where = PDKIM_HDR_VALUE; + goto NEXT_CHAR; + } + } + + if (where == PDKIM_HDR_VALUE) { + if (cur_val == NULL) + cur_val = pdkim_strnew(NULL); + + if ( (*p == '\r') || (*p == '\n') || (*p == ' ') || (*p == '\t') ) + goto NEXT_CHAR; + + if ( (*p == ';') || (*p == '\0') ) { + if (cur_tag->len > 0) { + pdkim_strtrim(cur_val); + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str); + #endif + switch (cur_tag->str[0]) { + case 'b': + switch (cur_tag->str[1]) { + case 'h': + sig->bodyhash = pdkim_decode_base64(cur_val->str,&(sig->bodyhash_len)); + break; + default: + sig->sigdata = pdkim_decode_base64(cur_val->str,&(sig->sigdata_len)); + break; + } + break; + case 'v': + if (strcmp(cur_val->str,PDKIM_SIGNATURE_VERSION) == 0) { + /* We only support version 1, and that is currently the + only version there is. */ + sig->version = 1; + } + break; + case 'a': + i = 0; + while (pdkim_algos[i] != NULL) { + if (strcmp(cur_val->str,pdkim_algos[i]) == 0 ) { + sig->algo = i; + break; + } + i++; + } + break; + case 'c': + i = 0; + while (pdkim_combined_canons[i].str != NULL) { + if (strcmp(cur_val->str,pdkim_combined_canons[i].str) == 0 ) { + sig->canon_headers = pdkim_combined_canons[i].canon_headers; + sig->canon_body = pdkim_combined_canons[i].canon_body; + break; + } + i++; + } + break; + case 'q': + i = 0; + while (pdkim_querymethods[i] != NULL) { + if (strcmp(cur_val->str,pdkim_querymethods[i]) == 0 ) { + sig->querymethod = i; + break; + } + i++; + } + break; + case 's': + sig->selector = strdup(cur_val->str); + break; + case 'd': + sig->domain = strdup(cur_val->str); + break; + case 'i': + sig->identity = pdkim_decode_qp(cur_val->str); + break; + case 't': + sig->created = strtoul(cur_val->str,NULL,10); + break; + case 'x': + sig->expires = strtoul(cur_val->str,NULL,10); + break; + case 'l': + sig->bodylength = strtol(cur_val->str,NULL,10); + break; + case 'h': + sig->headernames = strdup(cur_val->str); + break; + case 'z': + sig->copiedheaders = pdkim_decode_qp(cur_val->str); + break; + default: + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, "Unknown tag encountered\n"); + #endif + break; + } + } + pdkim_strclear(cur_tag); + pdkim_strclear(cur_val); + in_b_val = 0; + where = PDKIM_HDR_LIMBO; + goto NEXT_CHAR; + } + else pdkim_strncat(cur_val,p,1); + } + + NEXT_CHAR: + if (*p == '\0') break; + + if (!in_b_val) { + *q = *p; + q++; + } + p++; + } + + /* Make sure the most important bits are there. */ + if (!(sig->domain && (*(sig->domain) != '\0') && + sig->selector && (*(sig->selector) != '\0') && + sig->headernames && (*(sig->headernames) != '\0') && + sig->bodyhash && + sig->sigdata && + sig->version)) { + pdkim_free_sig(sig); + return NULL; + } + + /* Copy header list to 'tick-off' header list */ + sig->hnames_check = strdup(sig->headernames); + + *q = '\0'; + /* Chomp raw header. The final newline must not be added to the signature. */ + q--; + while( (q > sig->rawsig_no_b_val) && ((*q == '\r') || (*q == '\n')) ) { + *q = '\0'; q--; + } + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, + "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + pdkim_quoteprint(ctx->debug_stream, + sig->rawsig_no_b_val, + strlen(sig->rawsig_no_b_val), 1); + fprintf(ctx->debug_stream, + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + #endif + + sig->sha1_body = malloc(sizeof(sha1_context)); + if (sig->sha1_body == NULL) { + pdkim_free_sig(sig); + return NULL; + } + sig->sha2_body = malloc(sizeof(sha2_context)); + if (sig->sha2_body == NULL) { + pdkim_free_sig(sig); + return NULL; + } + + sha1_starts(sig->sha1_body); + sha2_starts(sig->sha2_body,0); + + return sig; +} + + +/* -------------------------------------------------------------------------- */ +pdkim_pubkey *pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) { + pdkim_pubkey *pub ; + char *p; + pdkim_str *cur_tag = NULL; + pdkim_str *cur_val = NULL; + int where = PDKIM_HDR_LIMBO; + + pub = malloc(sizeof(pdkim_pubkey)); + if (pub == NULL) return NULL; + memset(pub,0,sizeof(pdkim_pubkey)); + + p = raw_record; + + while (1) { + + /* Ignore FWS */ + if ( (*p == '\r') || (*p == '\n') ) + goto NEXT_CHAR; + + if (where == PDKIM_HDR_LIMBO) { + /* In limbo, just wait for a tag-char to appear */ + if (!((*p >= 'a') && (*p <= 'z'))) + goto NEXT_CHAR; + + where = PDKIM_HDR_TAG; + } + + if (where == PDKIM_HDR_TAG) { + if (cur_tag == NULL) + cur_tag = pdkim_strnew(NULL); + + if ((*p >= 'a') && (*p <= 'z')) + pdkim_strncat(cur_tag,p,1); + + if (*p == '=') { + where = PDKIM_HDR_VALUE; + goto NEXT_CHAR; + } + } + + if (where == PDKIM_HDR_VALUE) { + if (cur_val == NULL) + cur_val = pdkim_strnew(NULL); + + if ( (*p == '\r') || (*p == '\n') ) + goto NEXT_CHAR; + + if ( (*p == ';') || (*p == '\0') ) { + if (cur_tag->len > 0) { + pdkim_strtrim(cur_val); + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str); + #endif + switch (cur_tag->str[0]) { + case 'v': + /* This tag isn't evaluated because: + - We only support version DKIM1. + - Which is the default for this value (set below) + - Other versions are currently not specified. */ + break; + case 'h': + pub->hashes = strdup(cur_val->str); + break; + case 'g': + pub->granularity = strdup(cur_val->str); + break; + case 'n': + pub->notes = pdkim_decode_qp(cur_val->str); + break; + case 'p': + pub->key = pdkim_decode_base64(cur_val->str,&(pub->key_len)); + break; + case 'k': + pub->hashes = strdup(cur_val->str); + break; + case 's': + pub->srvtype = strdup(cur_val->str); + break; + case 't': + if (strchr(cur_val->str,'t') != NULL) pub->testing = 1; + if (strchr(cur_val->str,'s') != NULL) pub->no_subdomaining = 1; + break; + default: + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, "Unknown tag encountered\n"); + #endif + break; + } + } + pdkim_strclear(cur_tag); + pdkim_strclear(cur_val); + where = PDKIM_HDR_LIMBO; + goto NEXT_CHAR; + } + else pdkim_strncat(cur_val,p,1); + } + + NEXT_CHAR: + if (*p == '\0') break; + p++; + } + + /* Set fallback defaults */ + if (pub->version == NULL) pub->version = strdup(PDKIM_PUB_RECORD_VERSION); + if (pub->granularity == NULL) pub->granularity = strdup("*"); + if (pub->keytype == NULL) pub->keytype = strdup("rsa"); + if (pub->srvtype == NULL) pub->srvtype = strdup("*"); + + /* p= is required */ + if (pub->key == NULL) { + pdkim_free_pubkey(pub); + return NULL; + } + + return pub; +} + + +/* -------------------------------------------------------------------------- */ +int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) { + pdkim_signature *sig = ctx->sig; + /* Cache relaxed version of data */ + char *relaxed_data = NULL; + int relaxed_len = 0; + + /* Traverse all signatures, updating their hashes. */ + while (sig != NULL) { + /* Defaults to simple canon (no further treatment necessary) */ + char *canon_data = data; + int canon_len = len; + + if (sig->canon_body == PDKIM_CANON_RELAXED) { + /* Relax the line if not done already */ + if (relaxed_data == NULL) { + int seen_wsp = 0; + char *p = data; + int q = 0; + relaxed_data = malloc(len+1); + if (relaxed_data == NULL) return PDKIM_ERR_OOM; + while (*p != '\0') { + char c = *p; + if ( (c == '\t') || (c == ' ') ) { + c = ' '; /* Turns WSP into SP */ + if (seen_wsp) { + p++; + continue; + } + else seen_wsp = 1; + } + else seen_wsp = 0; + relaxed_data[q++] = c; + p++; + } + relaxed_data[q] = '\0'; + relaxed_len = q; + } + canon_data = relaxed_data; + canon_len = relaxed_len; + } + + /* Make sure we don't exceed the to-be-signed body length */ + if ((sig->bodylength >= 0) && + ((sig->signed_body_bytes+(unsigned long)canon_len) > sig->bodylength)) + canon_len = (sig->bodylength - sig->signed_body_bytes); + + if (canon_len > 0) { + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_update(sig->sha1_body,(unsigned char *)canon_data,canon_len); + else + sha2_update(sig->sha2_body,(unsigned char *)canon_data,canon_len); + sig->signed_body_bytes += canon_len; +#ifdef PDKIM_DEBUG + if (ctx->debug_stream!=NULL) + pdkim_quoteprint(ctx->debug_stream,canon_data,canon_len,0); +#endif + } + + sig = sig->next; + } + + if (relaxed_data != NULL) free(relaxed_data); + return PDKIM_OK; +}; + + +/* -------------------------------------------------------------------------- */ +int pdkim_finish_bodyhash(pdkim_ctx *ctx) { + pdkim_signature *sig = ctx->sig; + + /* Traverse all signatures */ + while (sig != NULL) { + + /* Finish hashes */ + unsigned char bh[32]; /* SHA-256 = 32 Bytes, SHA-1 = 20 Bytes */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_finish(sig->sha1_body,bh); + else + sha2_finish(sig->sha2_body,bh); + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] Body bytes hashed: %lu\n", + sig->domain, sig->signed_body_bytes); + fprintf(ctx->debug_stream, "PDKIM [%s] bh computed: ", sig->domain); + pdkim_hexprint(ctx->debug_stream, (char *)bh, + (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32,1); + } + #endif + + /* SIGNING -------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) { + sig->bodyhash_len = (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32; + sig->bodyhash = malloc(sig->bodyhash_len); + if (sig->bodyhash == NULL) return PDKIM_ERR_OOM; + memcpy(sig->bodyhash,bh,sig->bodyhash_len); + + /* If bodylength limit is set, and we have received less bytes + than the requested amount, effectively remove the limit tag. */ + if (sig->signed_body_bytes < sig->bodylength) sig->bodylength = -1; + } + /* VERIFICATION --------------------------------------------------------- */ + else { + /* Compare bodyhash */ + if (memcmp(bh,sig->bodyhash, + (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0) { + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, "PDKIM [%s] Body hash verified OK\n", + sig->domain); + #endif + } + else { + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] Body hash did NOT verify\n", + sig->domain); + fprintf(ctx->debug_stream, "PDKIM [%s] bh signature: ", sig->domain); + pdkim_hexprint(ctx->debug_stream, sig->bodyhash, + (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32,1); + } + #endif + sig->verify_status = PDKIM_VERIFY_FAIL; + sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY; + } + } + + sig = sig->next; + } + + return PDKIM_OK; +}; + + + +/* -------------------------------------------------------------------------- */ +/* Callback from pdkim_feed below for processing complete body lines */ +int pdkim_bodyline_complete(pdkim_ctx *ctx) { + char *p = ctx->linebuf; + int n = ctx->linebuf_offset; + + /* Ignore extra data if we've seen the end-of-data marker */ + if (ctx->seen_eod) goto BAIL; + + /* We've always got one extra byte to stuff a zero ... */ + ctx->linebuf[(ctx->linebuf_offset)] = '\0'; + + if (ctx->input_mode == PDKIM_INPUT_SMTP) { + /* Terminate on EOD marker */ + if (memcmp(p,".\r\n",3) == 0) { + ctx->seen_eod = 1; + goto BAIL; + } + /* Unstuff dots */ + if (memcmp(p,"..",2) == 0) { + p++; + n--; + } + } + + /* Empty lines need to be buffered until we find a non-empty line */ + if (memcmp(p,"\r\n",2) == 0) { + ctx->num_buffered_crlf++; + goto BAIL; + } + + /* At this point, we have a non-empty line, so release the buffered ones. */ + while (ctx->num_buffered_crlf) { + pdkim_update_bodyhash(ctx,"\r\n",2); + ctx->num_buffered_crlf--; + } + + pdkim_update_bodyhash(ctx,p,n); + + BAIL: + ctx->linebuf_offset = 0; + return PDKIM_OK; +} + + +/* -------------------------------------------------------------------------- */ +/* Callback from pdkim_feed below for processing complete headers */ +#define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:" +int pdkim_header_complete(pdkim_ctx *ctx) { + pdkim_signature *sig = ctx->sig; + + /* Special case: The last header can have an extra \r appended */ + if ( (ctx->cur_header->len > 1) && + (ctx->cur_header->str[(ctx->cur_header->len)-1] == '\r') ) { + ctx->cur_header->str[(ctx->cur_header->len)-1] = '\0'; + ctx->cur_header->len--; + } + + ctx->num_headers++; + if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL; + + /* Traverse all signatures */ + while (sig != NULL) { + pdkim_stringlist *list; + + /* SIGNING -------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) { + if (header_name_match(ctx->cur_header->str, + sig->sign_headers? + sig->sign_headers: + PDKIM_DEFAULT_SIGN_HEADERS, 0) != PDKIM_OK) goto NEXT_SIG; + } + /* VERIFICATION --------------------------------------------------------- */ + else { + /* Header is not included or all instances were already 'ticked off' */ + if (header_name_match(ctx->cur_header->str, + sig->hnames_check, 1) != PDKIM_OK) goto NEXT_SIG; + } + + /* Add header to the signed headers list */ + list = pdkim_append_stringlist(sig->headers, + ctx->cur_header->str); + if (list == NULL) return PDKIM_ERR_OOM; + sig->headers = list; + + NEXT_SIG: + sig = sig->next; + } + + /* DKIM-Signature: headers are added to the verification list */ + if ( (ctx->mode == PDKIM_MODE_VERIFY) && + (strncasecmp(ctx->cur_header->str, + DKIM_SIGNATURE_HEADERNAME, + strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) ) { + pdkim_signature *new_sig; + /* Create and chain new signature block */ + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, + "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + #endif + new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str); + if (new_sig != NULL) { + pdkim_signature *last_sig = ctx->sig; + if (last_sig == NULL) { + ctx->sig = new_sig; + } + else { + while (last_sig->next != NULL) { last_sig = last_sig->next; }; + last_sig->next = new_sig; + } + } + else { + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream,"Error while parsing signature header\n"); + fprintf(ctx->debug_stream, + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + #endif + } + } + + BAIL: + pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */ + return PDKIM_OK; +}; + + + +/* -------------------------------------------------------------------------- */ +#define HEADER_BUFFER_FRAG_SIZE 256 +DLLEXPORT int pdkim_feed (pdkim_ctx *ctx, + char *data, + int len) { + int p; + for (p=0;ppast_headers) { + /* Processing body byte */ + ctx->linebuf[(ctx->linebuf_offset)++] = c; + if (c == '\n') { + int rc = pdkim_bodyline_complete(ctx); /* End of line */ + if (rc != PDKIM_OK) return rc; + } + if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1)) + return PDKIM_ERR_LONG_LINE; + } + else { + /* Processing header byte */ + if (c != '\r') { + if (c == '\n') { + if (ctx->seen_lf) { + int rc = pdkim_header_complete(ctx); /* Seen last header line */ + if (rc != PDKIM_OK) return rc; + ctx->past_headers = 1; + ctx->seen_lf = 0; +#ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, + "PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); +#endif + continue; + } + else ctx->seen_lf = 1; + } + else if (ctx->seen_lf) { + if (! ((c == '\t') || (c == ' '))) { + int rc = pdkim_header_complete(ctx); /* End of header */ + if (rc != PDKIM_OK) return rc; + } + ctx->seen_lf = 0; + } + } + if (ctx->cur_header == NULL) { + ctx->cur_header = pdkim_strnew(NULL); + if (ctx->cur_header == NULL) return PDKIM_ERR_OOM; + } + if (ctx->cur_header->len < PDKIM_MAX_HEADER_LEN) + if (pdkim_strncat(ctx->cur_header,&data[p],1) == NULL) + return PDKIM_ERR_OOM; + } + } + return PDKIM_OK; +}; + + +/* -------------------------------------------------------------------------- */ +char *pdkim_create_header(pdkim_signature *sig, int final) { + char *rc = NULL; + char *base64_bh = NULL; + char *base64_b = NULL; + pdkim_str *hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION); + if (hdr == NULL) return NULL; + + base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len); + if (base64_bh == NULL) goto BAIL; + + /* Required and static bits */ + if ( + pdkim_strcat(hdr,"; a=") && + pdkim_strcat(hdr,pdkim_algos[sig->algo]) && + pdkim_strcat(hdr,"; q=") && + pdkim_strcat(hdr,pdkim_querymethods[sig->querymethod]) && + pdkim_strcat(hdr,"; c=") && + pdkim_strcat(hdr,pdkim_canons[sig->canon_headers]) && + pdkim_strcat(hdr,"/") && + pdkim_strcat(hdr,pdkim_canons[sig->canon_body]) && + pdkim_strcat(hdr,"; d=") && + pdkim_strcat(hdr,sig->domain) && + pdkim_strcat(hdr,"; s=") && + pdkim_strcat(hdr,sig->selector) && + pdkim_strcat(hdr,";\r\n\th=") && + pdkim_strcat(hdr,sig->headernames) && + pdkim_strcat(hdr,"; bh=") && + pdkim_strcat(hdr,base64_bh) && + pdkim_strcat(hdr,";\r\n\t") + ) { + /* Optional bits */ + if (sig->identity != NULL) { + if (!( pdkim_strcat(hdr,"i=") && + pdkim_strcat(hdr,sig->identity) && + pdkim_strcat(hdr,";") ) ) { + goto BAIL; + } + } + if (sig->created > 0) { + if (!( pdkim_strcat(hdr,"t=") && + pdkim_numcat(hdr,sig->created) && + pdkim_strcat(hdr,";") ) ) { + goto BAIL; + } + } + if (sig->expires > 0) { + if (!( pdkim_strcat(hdr,"x=") && + pdkim_numcat(hdr,sig->expires) && + pdkim_strcat(hdr,";") ) ) { + goto BAIL; + } + } + if (sig->bodylength >= 0) { + if (!( pdkim_strcat(hdr,"l=") && + pdkim_numcat(hdr,sig->bodylength) && + pdkim_strcat(hdr,";") ) ) { + goto BAIL; + } + } + /* Extra linebreak */ + if (hdr->str[(hdr->len)-1] == ';') { + if (!pdkim_strcat(hdr," \r\n\t")) goto BAIL; + } + /* Preliminary or final version? */ + if (final) { + base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len); + if (base64_b == NULL) goto BAIL; + if ( + pdkim_strcat(hdr,"b=") && + pdkim_strcat(hdr,base64_b) && + pdkim_strcat(hdr,";") + ) goto DONE; + } + else { + if (pdkim_strcat(hdr,"b=;")) goto DONE; + } + + goto BAIL; + } + + DONE: + rc = strdup(hdr->str); + + BAIL: + pdkim_strfree(hdr); + if (base64_bh != NULL) free(base64_bh); + if (base64_b != NULL) free(base64_b); + return rc; +} + + +/* -------------------------------------------------------------------------- */ +DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures) { + pdkim_signature *sig = ctx->sig; + pdkim_str *headernames = NULL; /* Collected signed header names */ + + /* Check if we must still flush a (partial) header. If that is the + case, the message has no body, and we must compute a body hash + out of '' */ + if (ctx->cur_header->len) { + int rc = pdkim_header_complete(ctx); + if (rc != PDKIM_OK) return rc; + pdkim_update_bodyhash(ctx,"\r\n",2); + } + else { + /* For non-smtp input, check if there's an unfinished line in the + body line buffer. If that is the case, we must add a CRLF to the + hash to properly terminate the message. */ + if ((ctx->input_mode == PDKIM_INPUT_NORMAL) && ctx->linebuf_offset) { + pdkim_update_bodyhash(ctx, ctx->linebuf, ctx->linebuf_offset); + pdkim_update_bodyhash(ctx,"\r\n",2); + } + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, + "\nPDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + #endif + } + + /* Build (and/or evaluate) body hash */ + if (pdkim_finish_bodyhash(ctx) != PDKIM_OK) return PDKIM_ERR_OOM; + + /* SIGNING -------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) { + headernames = pdkim_strnew(NULL); + if (headernames == NULL) return PDKIM_ERR_OOM; + } + /* ---------------------------------------------------------------------- */ + + while (sig != NULL) { + sha1_context sha1_headers; + sha2_context sha2_headers; + char *sig_hdr; + char headerhash[32]; + + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_starts(&sha1_headers); + else + sha2_starts(&sha2_headers,0); + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, + "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n"); + #endif + + /* SIGNING ---------------------------------------------------------------- */ + /* When signing, walk through our header list and add them to the hash. As we + go, construct a list of the header's names to use for the h= parameter. */ + if (ctx->mode == PDKIM_MODE_SIGN) { + pdkim_stringlist *p = sig->headers; + while (p != NULL) { + char *rh = NULL; + /* Collect header names (Note: colon presence is guaranteed here) */ + char *q = strchr(p->value,':'); + if (pdkim_strncat(headernames, p->value, + (q-(p->value))+((p->next==NULL)?0:1)) == NULL) + return PDKIM_ERR_OOM; + + if (sig->canon_headers == PDKIM_CANON_RELAXED) + rh = pdkim_relax_header(p->value,1); /* cook header for relaxed canon */ + else + rh = strdup(p->value); /* just copy it for simple canon */ + + if (rh == NULL) return PDKIM_ERR_OOM; + + /* Feed header to the hash algorithm */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_update(&(sha1_headers),(unsigned char *)rh,strlen(rh)); + else + sha2_update(&(sha2_headers),(unsigned char *)rh,strlen(rh)); + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1); + #endif + free(rh); + p = p->next; + } + } + /* VERIFICATION ----------------------------------------------------------- */ + /* When verifying, walk through the header name list in the h= parameter and + add the headers to the hash in that order. */ + else { + char *b = strdup(sig->headernames); + char *p = b; + char *q = NULL; + if (b == NULL) return PDKIM_ERR_OOM; + + while(1) { + pdkim_stringlist *hdrs = sig->headers; + q = strchr(p,':'); + if (q != NULL) *q = '\0'; + while (hdrs != NULL) { + if (strncasecmp(hdrs->value,p,strlen(p)) == 0) { + char *rh = NULL; + if (sig->canon_headers == PDKIM_CANON_RELAXED) + rh = pdkim_relax_header(hdrs->value,1); /* cook header for relaxed canon */ + else + rh = strdup(hdrs->value); /* just copy it for simple canon */ + if (rh == NULL) return PDKIM_ERR_OOM; + /* Feed header to the hash algorithm */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_update(&(sha1_headers),(unsigned char *)rh,strlen(rh)); + else + sha2_update(&(sha2_headers),(unsigned char *)rh,strlen(rh)); + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1); + #endif + free(rh); + } + hdrs = hdrs->next; + } + if (q == NULL) break; + p = q+1; + } + free(b); + } + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) + fprintf(ctx->debug_stream, + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + #endif + + /* SIGNING ---------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) { + /* Copy headernames to signature struct */ + sig->headernames = strdup(headernames->str); + pdkim_strfree(headernames); + + /* Create signature header with b= omitted */ + sig_hdr = pdkim_create_header(ctx->sig,0); + } + /* VERIFICATION ----------------------------------------------------------- */ + else { + sig_hdr = strdup(sig->rawsig_no_b_val); + } + /* ------------------------------------------------------------------------ */ + + if (sig_hdr == NULL) return PDKIM_ERR_OOM; + + /* Relax header if necessary */ + if (sig->canon_headers == PDKIM_CANON_RELAXED) { + char *relaxed_hdr = pdkim_relax_header(sig_hdr,0); + free(sig_hdr); + if (relaxed_hdr == NULL) return PDKIM_ERR_OOM; + sig_hdr = relaxed_hdr; + } + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, + "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n"); + pdkim_quoteprint(ctx->debug_stream, sig_hdr, strlen(sig_hdr), 1); + fprintf(ctx->debug_stream, + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + #endif + + /* Finalize header hash */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) { + sha1_update(&(sha1_headers),(unsigned char *)sig_hdr,strlen(sig_hdr)); + sha1_finish(&(sha1_headers),(unsigned char *)headerhash); + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain); + pdkim_hexprint(ctx->debug_stream, headerhash, 20, 1); + } + #endif + } + else { + sha2_update(&(sha2_headers),(unsigned char *)sig_hdr,strlen(sig_hdr)); + sha2_finish(&(sha2_headers),(unsigned char *)headerhash); + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain); + pdkim_hexprint(ctx->debug_stream, headerhash, 32, 1); + } + #endif + } + + free(sig_hdr); + + /* SIGNING ---------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) { + rsa_context rsa; + + rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL); + + /* Perform private key operation */ + if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey, + strlen(sig->rsa_privkey), NULL, 0) != 0) { + return PDKIM_ERR_RSA_PRIVKEY; + } + + sig->sigdata_len = mpi_size(&(rsa.N)); + sig->sigdata = malloc(sig->sigdata_len); + if (sig->sigdata == NULL) return PDKIM_ERR_OOM; + + if (rsa_pkcs1_sign( &rsa, RSA_PRIVATE, + ((sig->algo == PDKIM_ALGO_RSA_SHA1)? + RSA_SHA1:RSA_SHA256), + 0, + (unsigned char *)headerhash, + (unsigned char *)sig->sigdata ) != 0) { + return PDKIM_ERR_RSA_SIGNING; + } + + rsa_free(&rsa); + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] b computed: ", + sig->domain); + pdkim_hexprint(ctx->debug_stream, sig->sigdata, sig->sigdata_len, 1); + } + #endif + + sig->signature_header = pdkim_create_header(ctx->sig,1); + if (sig->signature_header == NULL) return PDKIM_ERR_OOM; + } + /* VERIFICATION ----------------------------------------------------------- */ + else { + rsa_context rsa; + char *dns_txt_name, *dns_txt_reply; + + rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL); + + dns_txt_name = malloc(PDKIM_DNS_TXT_MAX_NAMELEN); + if (dns_txt_name == NULL) return PDKIM_ERR_OOM; + dns_txt_reply = malloc(PDKIM_DNS_TXT_MAX_RECLEN); + if (dns_txt_reply == NULL) { + free(dns_txt_name); + return PDKIM_ERR_OOM; + } + memset(dns_txt_reply,0,PDKIM_DNS_TXT_MAX_RECLEN); + memset(dns_txt_name ,0,PDKIM_DNS_TXT_MAX_NAMELEN); + + if (snprintf(dns_txt_name,PDKIM_DNS_TXT_MAX_NAMELEN, + "%s._domainkey.%s.", + sig->selector,sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN) { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_BUFFER_SIZE; + goto NEXT_VERIFY; + }; + + if ((ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK) || + (dns_txt_reply[0] == '\0')) { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE; + goto NEXT_VERIFY; + } + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, + "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + fprintf(ctx->debug_stream,"Raw record: "); + pdkim_quoteprint(ctx->debug_stream, dns_txt_reply, strlen(dns_txt_reply), 1); + } + #endif + + sig->pubkey = pdkim_parse_pubkey_record(ctx,dns_txt_reply); + if (sig->pubkey == NULL) { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING; + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream,"Error while parsing public key record\n"); + fprintf(ctx->debug_stream, + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + #endif + goto NEXT_VERIFY; + } + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + #endif + + if (rsa_parse_public_key(&rsa, + (unsigned char *)sig->pubkey->key, + sig->pubkey->key_len) != 0) { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING; + goto NEXT_VERIFY; + } + + /* Check the signature */ + if (rsa_pkcs1_verify(&rsa, + RSA_PUBLIC, + ((sig->algo == PDKIM_ALGO_RSA_SHA1)? + RSA_SHA1:RSA_SHA256), + 0, + (unsigned char *)headerhash, + (unsigned char *)sig->sigdata) != 0) { + sig->verify_status = PDKIM_VERIFY_FAIL; + sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE; + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] signature did NOT verify OK\n", + sig->domain); + } + #endif + goto NEXT_VERIFY; + } + + /* We have a winner! */ + sig->verify_status = PDKIM_VERIFY_PASS; + + #ifdef PDKIM_DEBUG + if (ctx->debug_stream) { + fprintf(ctx->debug_stream, "PDKIM [%s] signature verified OK\n", + sig->domain); + } + #endif + + NEXT_VERIFY: + rsa_free(&rsa); + free(dns_txt_name); + free(dns_txt_reply); + } + + sig = sig->next; + } + + /* If requested, set return pointer to signature(s) */ + if (return_signatures != NULL) { + *return_signatures = ctx->sig; + } + + return PDKIM_OK; +} + + +/* -------------------------------------------------------------------------- */ +DLLEXPORT pdkim_ctx *pdkim_init_verify(int input_mode, + int(*dns_txt_callback)(char *, char *) + ) { + pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx)); + if (ctx == NULL) return NULL; + memset(ctx,0,sizeof(pdkim_ctx)); + + ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN); + if (ctx->linebuf == NULL) { + free(ctx); + return NULL; + } + + ctx->mode = PDKIM_MODE_VERIFY; + ctx->input_mode = input_mode; + ctx->dns_txt_callback = dns_txt_callback; + + return ctx; +} + + +/* -------------------------------------------------------------------------- */ +DLLEXPORT pdkim_ctx *pdkim_init_sign(int input_mode, + char *domain, + char *selector, + char *rsa_privkey) { + pdkim_ctx *ctx; + pdkim_signature *sig; + + if (!domain || !selector || !rsa_privkey) return NULL; + + ctx = malloc(sizeof(pdkim_ctx)); + if (ctx == NULL) return NULL; + memset(ctx,0,sizeof(pdkim_ctx)); + + ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN); + if (ctx->linebuf == NULL) { + free(ctx); + return NULL; + } + + sig = malloc(sizeof(pdkim_signature)); + if (sig == NULL) { + free(ctx->linebuf); + free(ctx); + return NULL; + } + memset(sig,0,sizeof(pdkim_signature)); + sig->bodylength = -1; + + ctx->mode = PDKIM_MODE_SIGN; + ctx->input_mode = input_mode; + ctx->sig = sig; + + ctx->sig->domain = strdup(domain); + ctx->sig->selector = strdup(selector); + ctx->sig->rsa_privkey = strdup(rsa_privkey); + + if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey) { + pdkim_free_ctx(ctx); + return NULL; + } + + ctx->sig->sha1_body = malloc(sizeof(sha1_context)); + if (ctx->sig->sha1_body == NULL) { + pdkim_free_ctx(ctx); + return NULL; + } + sha1_starts(ctx->sig->sha1_body); + + ctx->sig->sha2_body = malloc(sizeof(sha2_context)); + if (ctx->sig->sha2_body == NULL) { + pdkim_free_ctx(ctx); + return NULL; + } + sha2_starts(ctx->sig->sha2_body,0); + + return ctx; +}; + +#ifdef PDKIM_DEBUG +/* -------------------------------------------------------------------------- */ +DLLEXPORT void pdkim_set_debug_stream(pdkim_ctx *ctx, + FILE *debug_stream) { + ctx->debug_stream = debug_stream; +}; +#endif + +/* -------------------------------------------------------------------------- */ +DLLEXPORT int pdkim_set_optional(pdkim_ctx *ctx, + char *sign_headers, + char *identity, + int canon_headers, + int canon_body, + long bodylength, + int algo, + unsigned long created, + unsigned long expires) { + + if (identity != NULL) { + ctx->sig->identity = strdup(identity); + if (ctx->sig->identity == NULL) return PDKIM_ERR_OOM; + } + + if (sign_headers != NULL) { + ctx->sig->sign_headers = strdup(sign_headers); + if (ctx->sig->sign_headers == NULL) return PDKIM_ERR_OOM; + } + + ctx->sig->canon_headers = canon_headers; + ctx->sig->canon_body = canon_body; + ctx->sig->bodylength = bodylength; + ctx->sig->algo = algo; + ctx->sig->created = created; + ctx->sig->expires = expires; + + return PDKIM_OK; +}; diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h new file mode 100644 index 000000000..a6a2f760a --- /dev/null +++ b/src/src/pdkim/pdkim.h @@ -0,0 +1,325 @@ +/* + * PDKIM - a RFC4871 (DKIM) implementation + * + * Copyright (C) 2009 Tom Kistner + * + * http://duncanthrax.net/pdkim/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/pdkim.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +/* -------------------------------------------------------------------------- */ +/* Debugging. This can also be enabled/disabled at run-time. I recommend to + leave it defined. */ +#define PDKIM_DEBUG + +/* -------------------------------------------------------------------------- */ +/* Length of the preallocated buffer for the "answer" from the dns/txt + callback function. */ +#define PDKIM_DNS_TXT_MAX_RECLEN 4096 + +/* -------------------------------------------------------------------------- */ +/* Function success / error codes */ +#define PDKIM_OK 0 +#define PDKIM_FAIL -1 +#define PDKIM_ERR_OOM -100 +#define PDKIM_ERR_RSA_PRIVKEY -101 +#define PDKIM_ERR_RSA_SIGNING -102 +#define PDKIM_ERR_LONG_LINE -103 +#define PDKIM_ERR_BUFFER_TOO_SMALL -104 + +/* -------------------------------------------------------------------------- */ +/* Main/Extended verification status */ +#define PDKIM_VERIFY_NONE 0 +#define PDKIM_VERIFY_INVALID 1 +#define PDKIM_VERIFY_FAIL 2 +#define PDKIM_VERIFY_PASS 3 + +#define PDKIM_VERIFY_FAIL_BODY 1 +#define PDKIM_VERIFY_FAIL_MESSAGE 2 +#define PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE 3 +#define PDKIM_VERIFY_INVALID_BUFFER_SIZE 4 +#define PDKIM_VERIFY_INVALID_PUBKEY_PARSING 5 + +/* -------------------------------------------------------------------------- */ +/* Some parameter values */ +#define PDKIM_QUERYMETHOD_DNS_TXT 0 + +#define PDKIM_ALGO_RSA_SHA256 0 +#define PDKIM_ALGO_RSA_SHA1 1 + +#define PDKIM_CANON_SIMPLE 0 +#define PDKIM_CANON_RELAXED 1 + +#define PDKIM_HASH_SHA256 0 +#define PDKIM_HASH_SHA1 1 + +#define PDKIM_KEYTYPE_RSA 0 + +/* -------------------------------------------------------------------------- */ +/* Some required forward declarations, please ignore */ +typedef struct pdkim_stringlist pdkim_stringlist; +typedef struct pdkim_str pdkim_str; +typedef struct sha1_context sha1_context; +typedef struct sha2_context sha2_context; +#define HAVE_SHA1_CONTEXT +#define HAVE_SHA2_CONTEXT + +/* -------------------------------------------------------------------------- */ +/* Some concessions towards Redmond */ +#ifdef WINDOWS +#define snprintf _snprintf +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + + +/* -------------------------------------------------------------------------- */ +/* Public key as (usually) fetched from DNS */ +typedef struct pdkim_pubkey { + char *version; /* v= */ + char *granularity; /* g= */ + + char *hashes; /* h= */ + char *keytype; /* k= */ + char *srvtype; /* s= */ + char *notes; /* n= */ + + char *key; /* p= */ + int key_len; + + int testing; /* t=y */ + int no_subdomaining; /* t=s */ +} pdkim_pubkey; + +/* -------------------------------------------------------------------------- */ +/* Signature as it appears in a DKIM-Signature header */ +typedef struct pdkim_signature { + + /* Bits stored in a DKIM signature header --------------------------- */ + + /* (v=) The version, as an integer. Currently, always "1" */ + int version; + + /* (a=) The signature algorithm. Either PDKIM_ALGO_RSA_SHA256 + or PDKIM_ALGO_RSA_SHA1 */ + int algo; + + /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE + or PDKIM_CANON_RELAXED */ + int canon_headers; + + /* (c=/x) Body canonicalization method. Either PDKIM_CANON_SIMPLE + or PDKIM_CANON_RELAXED */ + int canon_body; + + /* (q=) Query Method. Currently, only PDKIM_QUERYMETHOD_DNS_TXT + is specified */ + int querymethod; + + /* (s=) The selector string as given in the signature */ + char *selector; + + /* (d=) The domain as given in the signature */ + char *domain; + + /* (i=) The identity as given in the signature */ + char *identity; + + /* (t=) Timestamp of signature creation */ + unsigned long created; + + /* (x=) Timestamp of expiry of signature */ + unsigned long expires; + + /* (l=) Amount of hashed body bytes (after canonicalization). Default + is -1. Note: a value of 0 means that the body is unsigned! */ + long bodylength; + + /* (h=) Colon-separated list of header names that are included in the + signature */ + char *headernames; + + /* (z=) */ + char *copiedheaders; + + /* (b=) Raw signature data, along with its length in bytes */ + char *sigdata; + int sigdata_len; + + /* (bh=) Raw body hash data, along with its length in bytes */ + char *bodyhash; + int bodyhash_len; + + /* Folded DKIM-Signature: header. Singing only, NULL for verifying. + Ready for insertion into the message. Note: Folded using CRLFTB, + but final line terminator is NOT included. Note2: This buffer is + free()d when you call pdkim_free_ctx(). */ + char *signature_header; + + /* The main verification status. Verification only. One of: + + PDKIM_VERIFY_NONE Verification was not attempted. This status + should not appear. + + PDKIM_VERIFY_INVALID There was an error while trying to verify + the signature. A more precise description + is available in verify_ext_status. + + PDKIM_VERIFY_FAIL Verification failed because either the body + hash did not match, or the signature verification + failed. This means the message was modified. + Check verify_ext_status for the exact reason. + + PDKIM_VERIFY_PASS Verification succeeded. + */ + int verify_status; + + /* Extended verification status. Verification only. Depending on the value + of verify_status, it can contain: + + For verify_status == PDKIM_VERIFY_INVALID: + + PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE + Unable to retrieve a public key container. + + PDKIM_VERIFY_INVALID_BUFFER_SIZE + Either the DNS name constructed to retrieve the public key record + does not fit into PDKIM_DNS_TXT_MAX_NAMELEN bytes, or the retrieved + record is longer than PDKIM_DNS_TXT_MAX_RECLEN bytes. + + PDKIM_VERIFY_INVALID_PUBKEY_PARSING + (Syntax) error while parsing the retrieved public key record. + + + For verify_status == PDKIM_VERIFY_FAIL: + + PDKIM_VERIFY_FAIL_BODY + The calculated body hash does not match the advertised body hash + from the bh= tag of the signature. + + PDKIM_VERIFY_FAIL_MESSAGE + RSA verification of the signature (b= tag) failed. + */ + int verify_ext_status; + + /* Pointer to a public key record that was used to verify the signature. + See pdkim_pubkey declaration above for more information. + Caution: is NULL if signing or if no record was retrieved. */ + pdkim_pubkey *pubkey; + + /* Pointer to the next pdkim_signature signature. NULL if signing or if + this is the last signature. */ + void *next; + + /* Properties below this point are used internally only ------------- */ + + /* Per-signature helper variables ----------------------------------- */ + sha1_context *sha1_body; /* SHA1 block */ + sha2_context *sha2_body; /* SHA256 block */ + unsigned long signed_body_bytes; /* How many body bytes we hashed */ + pdkim_stringlist *headers; /* Raw headers included in the sig */ + /* Signing specific ------------------------------------------------- */ + char *rsa_privkey; /* Private RSA key */ + char *sign_headers; /* To-be-signed header names */ + /* Verification specific -------------------------------------------- */ + char *hnames_check; /* Tick-off header list that we use to keep + track of header names that we have already + added to the signature candidates. */ + char *rawsig_no_b_val; /* Original signature header w/o b= tag value. */ +} pdkim_signature; + + +/* -------------------------------------------------------------------------- */ +/* Context to keep state between all operations. */ +#define PDKIM_MODE_SIGN 0 +#define PDKIM_MODE_VERIFY 1 +#define PDKIM_INPUT_NORMAL 0 +#define PDKIM_INPUT_SMTP 1 +typedef struct pdkim_ctx { + + /* PDKIM_MODE_VERIFY or PDKIM_MODE_SIGN */ + int mode; + + /* PDKIM_INPUT_SMTP or PDKIM_INPUT_NORMAL */ + int input_mode; + + /* One (signing) or several chained (verification) signatures */ + pdkim_signature *sig; + + /* Callback for dns/txt query method (verification only) */ + int(*dns_txt_callback)(char *, char *); + + /* Coder's little helpers */ + pdkim_str *cur_header; + char *linebuf; + int linebuf_offset; + int seen_lf; + int seen_eod; + int past_headers; + int num_buffered_crlf; + int num_headers; + +#ifdef PDKIM_DEBUG + /* A FILE pointer. When not NULL, debug output will be generated + and sent to this stream */ + FILE *debug_stream; +#endif + +} pdkim_ctx; + + +/* -------------------------------------------------------------------------- */ +/* API functions. Please see the sample code in sample/test_sign.c and + sample/test_verify.c for documentation. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +DLLEXPORT +pdkim_ctx *pdkim_init_sign (int, char *, char *, char *); + +DLLEXPORT +pdkim_ctx *pdkim_init_verify (int, int(*)(char *, char *)); + +DLLEXPORT +int pdkim_set_optional (pdkim_ctx *, char *, char *,int, int, + long, int, + unsigned long, + unsigned long); + +DLLEXPORT +int pdkim_feed (pdkim_ctx *, char *, int); +DLLEXPORT +int pdkim_feed_finish (pdkim_ctx *, pdkim_signature **); + +DLLEXPORT +void pdkim_free_ctx (pdkim_ctx *); + +#ifdef PDKIM_DEBUG +DLLEXPORT +void pdkim_set_debug_stream(pdkim_ctx *, FILE *); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c new file mode 100644 index 000000000..992a396aa --- /dev/null +++ b/src/src/pdkim/rsa.c @@ -0,0 +1,822 @@ +/* + * The RSA public-key cryptosystem + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +/* $Cambridge: exim/src/src/pdkim/rsa.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#include "rsa.h" +#include "base64.h" + +#include +#include +#include + + +/* + * ASN.1 DER decoding routines + */ +static int asn1_get_len( unsigned char **p, + unsigned char *end, + int *len ) +{ + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( ( **p & 0x80 ) == 0 ) + *len = *(*p)++; + else + { + switch( **p & 0x7F ) + { + case 1: + if( ( end - *p ) < 2 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = (*p)[1]; + (*p) += 2; + break; + + case 2: + if( ( end - *p ) < 3 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (*p)[1] << 8 ) | (*p)[2]; + (*p) += 3; + break; + + default: + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + break; + } + } + + if( *len > (int) ( end - *p ) ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + return( 0 ); +} + +static int asn1_get_tag( unsigned char **p, + unsigned char *end, + int *len, int tag ) +{ + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != tag ) + return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + (*p)++; + + return( asn1_get_len( p, end, len ) ); +} + +static int asn1_get_int( unsigned char **p, + unsigned char *end, + int *val ) +{ + int ret, len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) + return( ret ); + + if( len > (int) sizeof( int ) || ( **p & 0x80 ) != 0 ) + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + *val = 0; + + while( len-- > 0 ) + { + *val = ( *val << 8 ) | **p; + (*p)++; + } + + return( 0 ); +} + +static int asn1_get_mpi( unsigned char **p, + unsigned char *end, + mpi *X ) +{ + int ret, len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) + return( ret ); + + ret = mpi_read_binary( X, *p, len ); + + *p += len; + + return( ret ); +} + + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id, + int (*f_rng)(void *), + void *p_rng ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + ctx->padding = padding; + ctx->hash_id = hash_id; + + ctx->f_rng = f_rng; + ctx->p_rng = p_rng; +} + + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( rsa_context *ctx ) +{ + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > 4096 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_msb( &ctx->E ) > 64 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + mpi_init( &PQ, &DE, &P1, &Q1, &H, &I, &G, NULL ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &H ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + if( mpi_cmp_mpi( &PQ, &ctx->N ) == 0 && + mpi_cmp_int( &I, 1 ) == 0 && + mpi_cmp_int( &G, 1 ) == 0 ) + { + mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, NULL ); + return( 0 ); + } + +cleanup: + + mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, NULL ); + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED | ret ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + unsigned char *input, + unsigned char *output ) +{ + int ret, olen; + mpi T; + + mpi_init( &T, NULL ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T, NULL ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T, NULL ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED | ret ); + + return( 0 ); +} + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + unsigned char *input, + unsigned char *output ) +{ + int ret, olen; + mpi T, T1, T2; + + mpi_init( &T, &T1, &T2, NULL ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T, NULL ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + +#if 0 + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * output = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); +#endif + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T, &T1, &T2, NULL ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED | ret ); + + return( 0 ); +} + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int mode, int ilen, + unsigned char *input, + unsigned char *output ) +{ + int nb_pad, olen; + unsigned char *p = output; + + olen = ctx->len; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + if( ilen < 0 || olen < ilen + 11 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + do { + *p = (unsigned char) rand(); + } while( *p == 0 ); + p++; + } + *p++ = 0; + memcpy( p, input, ilen ); + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, output, output ) ); +} + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, int *olen, + unsigned char *input, + unsigned char *output, + int output_max_len) +{ + int ret, ilen; + unsigned char *p; + unsigned char buf[512]; + + ilen = ctx->len; + + if( ilen < 16 || ilen > (int) sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + if( *p++ != 0 || *p++ != RSA_CRYPT ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + ilen - 1 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + if (ilen - (int)(p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TO_LARGE ); + + *olen = ilen - (int)(p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + unsigned char *hash, + unsigned char *sig ) +{ + int nb_pad, olen; + unsigned char *p = sig; + + olen = ctx->len; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + switch( hash_id ) + { + case RSA_RAW: + nb_pad = olen - 3 - hashlen; + break; + + case RSA_MD2: + case RSA_MD4: + case RSA_MD5: + nb_pad = olen - 3 - 16 - 18; + break; + + case RSA_SHA1: + nb_pad = olen - 3 - 20 - 15; + break; + + case RSA_SHA256: + nb_pad = olen - 3 - 32 - 19; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( nb_pad < 8 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + switch( hash_id ) + { + case RSA_RAW: + memcpy( p, hash, hashlen ); + break; + + case RSA_MD2: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 2; break; + + case RSA_MD4: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 4; break; + + case RSA_MD5: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 5; break; + + case RSA_SHA1: + memcpy( p, ASN1_HASH_SHA1, 15 ); + memcpy( p + 15, hash, 20 ); + break; + + case RSA_SHA256: + memcpy( p, ASN1_HASH_SHA256, 19 ); + memcpy( p + 19, hash, 32 ); + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, sig, sig ) ); +} + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + unsigned char *hash, + unsigned char *sig ) +{ + int ret, len, siglen; + unsigned char *p, c; + unsigned char buf[512]; + + siglen = ctx->len; + + if( siglen < 16 || siglen > (int) sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + switch( ctx->padding ) + { + case RSA_PKCS_V15: + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + break; + + default: + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + len = siglen - (int)( p - buf ); + + if( len == 34 ) + { + c = p[13]; + p[13] = 0; + + if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( c == 2 && hash_id == RSA_MD2 ) || + ( c == 4 && hash_id == RSA_MD4 ) || + ( c == 5 && hash_id == RSA_MD5 ) ) + { + if( memcmp( p + 18, hash, 16 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + } + + if( len == 35 && hash_id == RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 && + memcmp( p + 15, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == 51 && hash_id == RSA_SHA256 ) + { + if( memcmp( p, ASN1_HASH_SHA256, 19 ) == 0 && + memcmp( p + 19, hash, 32 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == hashlen && hash_id == RSA_RAW ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ + mpi_free( &ctx->RQ, &ctx->RP, &ctx->RN, + &ctx->QP, &ctx->DQ, &ctx->DP, + &ctx->Q, &ctx->P, &ctx->D, + &ctx->E, &ctx->N, NULL ); +} + + +/* + * Parse a public RSA key + +OpenSSL RSA public key ASN1 container + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT:rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING:RSAPublicKey (below) + +RSAPublicKey ASN1 container + 0:d=0 hl=3 l= 137 cons: SEQUENCE + 3:d=1 hl=3 l= 129 prim: INTEGER:Public modulus +135:d=1 hl=2 l= 3 prim: INTEGER:Public exponent +*/ + +int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen ) +{ + unsigned char *p, *end; + int ret, len; + + p = buf; + end = buf+buflen; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) == 0 ) { + /* Skip over embedded rsaEncryption Object */ + p+=len; + + /* The RSAPublicKey ASN1 container is wrapped in a BIT STRING */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_BIT_STRING ) ) != 0 ) { + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + /* Limit range to that BIT STRING */ + end = p + len; + p++; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + } + + if ( ( ( ret = asn1_get_mpi( &p, end, &(rsa->N) ) ) == 0 ) && + ( ( ret = asn1_get_mpi( &p, end, &(rsa->E) ) ) == 0 ) ) { + rsa->len = mpi_size( &rsa->N ); + return 0; + } + + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); +} + +/* + * Parse a private RSA key + */ +int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, + unsigned char *pwd, int pwdlen ) +{ + int ret, len, enc; + unsigned char *s1, *s2; + unsigned char *p, *end; + + s1 = (unsigned char *) strstr( (char *) buf, + "-----BEGIN RSA PRIVATE KEY-----" ); + + if( s1 != NULL ) + { + s2 = (unsigned char *) strstr( (char *) buf, + "-----END RSA PRIVATE KEY-----" ); + + if( s2 == NULL || s2 <= s1 ) + return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); + + s1 += 31; + if( *s1 == '\r' ) s1++; + if( *s1 == '\n' ) s1++; + else return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); + + enc = 0; + + if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) + { + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); + } + + len = 0; + ret = base64_decode( NULL, &len, s1, s2 - s1 ); + + if( ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER ) + return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM ); + + if( ( buf = (unsigned char *) malloc( len ) ) == NULL ) + return( 1 ); + + if( ( ret = base64_decode( buf, &len, s1, s2 - s1 ) ) != 0 ) + { + free( buf ); + return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM ); + } + + buflen = len; + + if( enc != 0 ) + { + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); + } + } + + memset( rsa, 0, sizeof( rsa_context ) ); + + p = buf; + end = buf + buflen; + + /* + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + if( rsa->ver != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( ret | POLARSSL_ERR_X509_KEY_INVALID_VERSION ); + } + + if( ( ret = asn1_get_mpi( &p, end, &rsa->N ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->E ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->D ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->P ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->Q ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( ret | POLARSSL_ERR_X509_KEY_INVALID_FORMAT ); + } + + rsa->len = mpi_size( &rsa->N ); + + if( p != end ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( ret ); + } + + if( s1 != NULL ) + free( buf ); + + return( 0 ); +} diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/rsa.h new file mode 100644 index 000000000..57a7b2438 --- /dev/null +++ b/src/src/pdkim/rsa.h @@ -0,0 +1,356 @@ +/** + * \file rsa.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/rsa.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#include "bignum.h" + +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x0400 +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x0410 +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x0420 +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x0430 +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x0440 +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x0450 +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x0460 +#define POLARSSL_ERR_RSA_OUTPUT_TO_LARGE -0x0470 + +#define POLARSSL_ERR_ASN1_OUT_OF_DATA -0x0014 +#define POLARSSL_ERR_ASN1_UNEXPECTED_TAG -0x0016 +#define POLARSSL_ERR_ASN1_INVALID_LENGTH -0x0018 +#define POLARSSL_ERR_ASN1_LENGTH_MISMATCH -0x001A +#define POLARSSL_ERR_ASN1_INVALID_DATA -0x001C + +#define POLARSSL_ERR_X509_FEATURE_UNAVAILABLE -0x0020 +#define POLARSSL_ERR_X509_CERT_INVALID_PEM -0x0040 +#define POLARSSL_ERR_X509_CERT_INVALID_FORMAT -0x0060 +#define POLARSSL_ERR_X509_CERT_INVALID_VERSION -0x0080 +#define POLARSSL_ERR_X509_CERT_INVALID_SERIAL -0x00A0 +#define POLARSSL_ERR_X509_CERT_INVALID_ALG -0x00C0 +#define POLARSSL_ERR_X509_CERT_INVALID_NAME -0x00E0 +#define POLARSSL_ERR_X509_CERT_INVALID_DATE -0x0100 +#define POLARSSL_ERR_X509_CERT_INVALID_PUBKEY -0x0120 +#define POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE -0x0140 +#define POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS -0x0160 +#define POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION -0x0180 +#define POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG -0x01A0 +#define POLARSSL_ERR_X509_CERT_UNKNOWN_PK_ALG -0x01C0 +#define POLARSSL_ERR_X509_CERT_SIG_MISMATCH -0x01E0 +#define POLARSSL_ERR_X509_CERT_VERIFY_FAILED -0x0200 +#define POLARSSL_ERR_X509_KEY_INVALID_PEM -0x0220 +#define POLARSSL_ERR_X509_KEY_INVALID_VERSION -0x0240 +#define POLARSSL_ERR_X509_KEY_INVALID_FORMAT -0x0260 +#define POLARSSL_ERR_X509_KEY_INVALID_ENC_IV -0x0280 +#define POLARSSL_ERR_X509_KEY_UNKNOWN_ENC_ALG -0x02A0 +#define POLARSSL_ERR_X509_KEY_PASSWORD_REQUIRED -0x02C0 +#define POLARSSL_ERR_X509_KEY_PASSWORD_MISMATCH -0x02E0 +#define POLARSSL_ERR_X509_POINT_ERROR -0x0300 +#define POLARSSL_ERR_X509_VALUE_TO_LENGTH -0x0320 + +/* + * DER constants + */ +#define ASN1_BOOLEAN 0x01 +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_UTF8_STRING 0x0C +#define ASN1_SEQUENCE 0x10 +#define ASN1_SET 0x11 +#define ASN1_PRINTABLE_STRING 0x13 +#define ASN1_T61_STRING 0x14 +#define ASN1_IA5_STRING 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_UNIVERSAL_STRING 0x1C +#define ASN1_BMP_STRING 0x1E +#define ASN1_PRIMITIVE 0x00 +#define ASN1_CONSTRUCTED 0x20 +#define ASN1_CONTEXT_SPECIFIC 0x80 + +/* + * PKCS#1 constants + */ +#define RSA_RAW 0 +#define RSA_MD2 2 +#define RSA_MD4 3 +#define RSA_MD5 4 +#define RSA_SHA1 5 +#define RSA_SHA256 6 + +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ +#define ASN1_HASH_MDX \ + "\x30\x20\x30\x0C\x06\x08\x2A\x86\x48" \ + "\x86\xF7\x0D\x02\x00\x05\x00\x04\x10" + +#define ASN1_HASH_SHA1 \ + "\x30\x21\x30\x09\x06\x05\x2B\x0E\x03" \ + "\x02\x1A\x05\x00\x04\x14" + +#define ASN1_HASH_SHA256 \ + "\x30\x31\x30\x0d\x06\x09\x60\x86\x48" \ + "\x01\x65\x03\x04\x02\x01\x05\x00\x04" \ + "\x20" + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + int len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + + int padding; /*!< 1.5 or OAEP/PSS */ + int hash_id; /*!< hash identifier */ + int (*f_rng)(void *); /*!< RNG function */ + void *p_rng; /*!< RNG parameter */ +} +rsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize an RSA context + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + * + * \note Currently (xyssl-0.8), RSA_PKCS_V21 padding + * is not supported. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id, + int (*f_rng)(void *), + void *p_rng ); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context (especially f_rng and p_rng). + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + unsigned char *input, + unsigned char *output ); + +/** + * \brief Add the message padding, then do an RSA operation + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int mode, int ilen, + unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA operation, then remove the message padding + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param olen will contain the plaintext length + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, int *olen, + unsigned char *input, + unsigned char *output, + int output_max_len); + +/** + * \brief Do a private RSA to sign a message digest + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id RSA_RAW, RSA_MD{2,4,5} or RSA_SHA{1,256} + * \param hashlen message digest length (for RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Do a public RSA and check the message digest + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id RSA_RAW, RSA_MD{2,4,5} or RSA_SHA{1,256} + * \param hashlen message digest length (for RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + int hashlen, + unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ); + +int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen ); + +int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, + unsigned char *pwd, int pwdlen ); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/src/src/pdkim/sha1.c b/src/src/pdkim/sha1.c new file mode 100644 index 000000000..f118f4eb1 --- /dev/null +++ b/src/src/pdkim/sha1.c @@ -0,0 +1,424 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +/* $Cambridge: exim/src/src/pdkim/sha1.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#include "sha1.h" + +#include +#include + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * SHA-1 context setup + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +static void sha1_process( sha1_context *ctx, unsigned char data[64] ) +{ + unsigned long temp, W[16], A, B, C, D, E; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-1 final digest + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, (unsigned char *) sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); +} + +/* + * output = SHA-1( input buffer ) + */ +void sha1_oneshot( unsigned char *input, int ilen, unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, input, ilen ); + sha1_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha1_context ) ); +} + +/* + * output = SHA-1( file contents ) + */ +int sha1_file( char *path, unsigned char output[20] ) +{ + FILE *f; + size_t n; + sha1_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( 1 ); + + sha1_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha1_update( &ctx, buf, (int) n ); + + sha1_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha1_context ) ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( 2 ); + } + + fclose( f ); + return( 0 ); +} + +/* + * SHA-1 HMAC context setup + */ +void sha1_hmac_starts( sha1_context *ctx, unsigned char *key, int keylen ) +{ + int i; + unsigned char sum[20]; + + if( keylen > 64 ) + { + sha1_oneshot( key, keylen, sum ); + keylen = 20; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha1_starts( ctx ); + sha1_update( ctx, ctx->ipad, 64 ); + + memset( sum, 0, sizeof( sum ) ); +} + +/* + * SHA-1 HMAC process buffer + */ +void sha1_hmac_update( sha1_context *ctx, unsigned char *input, int ilen ) +{ + sha1_update( ctx, input, ilen ); +} + +/* + * SHA-1 HMAC final digest + */ +void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned char tmpbuf[20]; + + sha1_finish( ctx, tmpbuf ); + sha1_starts( ctx ); + sha1_update( ctx, ctx->opad, 64 ); + sha1_update( ctx, tmpbuf, 20 ); + sha1_finish( ctx, output ); + + memset( tmpbuf, 0, sizeof( tmpbuf ) ); +} + +/* + * output = HMAC-SHA-1( hmac key, input buffer ) + */ +void sha1_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_hmac_starts( &ctx, key, keylen ); + sha1_hmac_update( &ctx, input, ilen ); + sha1_hmac_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha1_context ) ); +} diff --git a/src/src/pdkim/sha1.h b/src/src/pdkim/sha1.h new file mode 100644 index 000000000..0349be5d2 --- /dev/null +++ b/src/src/pdkim/sha1.h @@ -0,0 +1,137 @@ +/** + * \file sha1.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/sha1.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#ifndef POLARSSL_SHA1_H +#define POLARSSL_SHA1_H + +/** + * \brief SHA-1 context structure + */ +#ifndef HAVE_SHA1_CONTEXT +#define HAVE_SHA1_CONTEXT +typedef struct sha1_context sha1_context; +#endif + +struct sha1_context +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-1 context setup + * + * \param ctx context to be initialized + */ +void sha1_starts( sha1_context *ctx ); + +/** + * \brief SHA-1 process buffer + * + * \param ctx SHA-1 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-1 final digest + * + * \param ctx SHA-1 context + * \param output SHA-1 checksum result + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief Output = SHA-1( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-1 checksum result + */ +void sha1_oneshot( unsigned char *input, int ilen, unsigned char output[20] ); + +/** + * \brief Output = SHA-1( file contents ) + * + * \param path input file name + * \param output SHA-1 checksum result + * + * \return 0 if successful, 1 if fopen failed, + * or 2 if fread failed + */ +int sha1_file( char *path, unsigned char output[20] ); + +/** + * \brief SHA-1 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void sha1_hmac_starts( sha1_context *ctx, unsigned char *key, int keylen ); + +/** + * \brief SHA-1 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_hmac_update( sha1_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-1 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-1 HMAC checksum result + */ +void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief Output = HMAC-SHA-1( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-1 result + */ +void sha1_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[20] ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha1.h */ diff --git a/src/src/pdkim/sha2.c b/src/src/pdkim/sha2.c new file mode 100644 index 000000000..acc07c78c --- /dev/null +++ b/src/src/pdkim/sha2.c @@ -0,0 +1,431 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +/* $Cambridge: exim/src/src/pdkim/sha2.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#include "sha2.h" + +#include +#include + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * SHA-256 context setup + */ +void sha2_starts( sha2_context *ctx, int is224 ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + if( is224 == 0 ) + { + /* SHA-256 */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; + } + else + { + /* SHA-224 */ + ctx->state[0] = 0xC1059ED8; + ctx->state[1] = 0x367CD507; + ctx->state[2] = 0x3070DD17; + ctx->state[3] = 0xF70E5939; + ctx->state[4] = 0xFFC00B31; + ctx->state[5] = 0x68581511; + ctx->state[6] = 0x64F98FA7; + ctx->state[7] = 0xBEFA4FA4; + } + + ctx->is224 = is224; +} + +static void sha2_process( sha2_context *ctx, unsigned char data[64] ) +{ + unsigned long temp1, temp2, W[64]; + unsigned long A, B, C, D, E, F, G, H; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + + P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); + P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); + P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); + P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); + P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); + P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); + P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); + P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); + P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); + P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); + P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); + P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); + P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); + P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); + P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); + P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); + P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); + P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); + P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); + P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); + P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); + P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); + P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); + P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); + P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); + P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); + P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); + P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); + P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); + P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); + P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); + P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); + P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); + P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); + P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); + P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); + P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); + P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); + P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); + P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); + P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); + P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); + P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); + P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); + P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); + P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); + P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); + P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); + P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); + P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); + P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); + P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); + P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); + P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); + P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); + P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); + P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); + P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); + P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); + P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); + P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); + P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); + P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); + P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +/* + * SHA-256 process buffer + */ +void sha2_update( sha2_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha2_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha2_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char sha2_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-256 final digest + */ +void sha2_finish( sha2_context *ctx, unsigned char output[32] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha2_update( ctx, (unsigned char *) sha2_padding, padn ); + sha2_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); + PUT_ULONG_BE( ctx->state[5], output, 20 ); + PUT_ULONG_BE( ctx->state[6], output, 24 ); + + if( ctx->is224 == 0 ) + PUT_ULONG_BE( ctx->state[7], output, 28 ); +} + +/* + * output = SHA-256( input buffer ) + */ +void sha2( unsigned char *input, int ilen, + unsigned char output[32], int is224 ) +{ + sha2_context ctx; + + sha2_starts( &ctx, is224 ); + sha2_update( &ctx, input, ilen ); + sha2_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha2_context ) ); +} + +/* + * output = SHA-256( file contents ) + */ +int sha2_file( char *path, unsigned char output[32], int is224 ) +{ + FILE *f; + size_t n; + sha2_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( 1 ); + + sha2_starts( &ctx, is224 ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha2_update( &ctx, buf, (int) n ); + + sha2_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha2_context ) ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( 2 ); + } + + fclose( f ); + return( 0 ); +} + +/* + * SHA-256 HMAC context setup + */ +void sha2_hmac_starts( sha2_context *ctx, unsigned char *key, int keylen, + int is224 ) +{ + int i; + unsigned char sum[32]; + + if( keylen > 64 ) + { + sha2( key, keylen, sum, is224 ); + keylen = ( is224 ) ? 28 : 32; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha2_starts( ctx, is224 ); + sha2_update( ctx, ctx->ipad, 64 ); + + memset( sum, 0, sizeof( sum ) ); +} + +/* + * SHA-256 HMAC process buffer + */ +void sha2_hmac_update( sha2_context *ctx, unsigned char *input, int ilen ) +{ + sha2_update( ctx, input, ilen ); +} + +/* + * SHA-256 HMAC final digest + */ +void sha2_hmac_finish( sha2_context *ctx, unsigned char output[32] ) +{ + int is224, hlen; + unsigned char tmpbuf[32]; + + is224 = ctx->is224; + hlen = ( is224 == 0 ) ? 32 : 28; + + sha2_finish( ctx, tmpbuf ); + sha2_starts( ctx, is224 ); + sha2_update( ctx, ctx->opad, 64 ); + sha2_update( ctx, tmpbuf, hlen ); + sha2_finish( ctx, output ); + + memset( tmpbuf, 0, sizeof( tmpbuf ) ); +} + +/* + * output = HMAC-SHA-256( hmac key, input buffer ) + */ +void sha2_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[32], int is224 ) +{ + sha2_context ctx; + + sha2_hmac_starts( &ctx, key, keylen, is224 ); + sha2_hmac_update( &ctx, input, ilen ); + sha2_hmac_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha2_context ) ); +} diff --git a/src/src/pdkim/sha2.h b/src/src/pdkim/sha2.h new file mode 100644 index 000000000..c1ec2c705 --- /dev/null +++ b/src/src/pdkim/sha2.h @@ -0,0 +1,145 @@ +/** + * \file sha2.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* $Cambridge: exim/src/src/pdkim/sha2.h,v 1.2 2009/06/10 07:34:05 tom Exp $ */ + +#ifndef POLARSSL_SHA2_H +#define POLARSSL_SHA2_H + +/** + * \brief SHA-256 context structure + */ +#ifndef HAVE_SHA2_CONTEXT +#define HAVE_SHA2_CONTEXT +typedef struct sha2_context sha2_context; +#endif + +struct sha2_context +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[8]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ + int is224; /*!< 0 => SHA-256, else SHA-224 */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-256 context setup + * + * \param ctx context to be initialized + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2_starts( sha2_context *ctx, int is224 ); + +/** + * \brief SHA-256 process buffer + * + * \param ctx SHA-256 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha2_update( sha2_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-256 final digest + * + * \param ctx SHA-256 context + * \param output SHA-224/256 checksum result + */ +void sha2_finish( sha2_context *ctx, unsigned char output[32] ); + +/** + * \brief Output = SHA-256( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2( unsigned char *input, int ilen, + unsigned char output[32], int is224 ); + +/** + * \brief Output = SHA-256( file contents ) + * + * \param path input file name + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + * + * \return 0 if successful, 1 if fopen failed, + * or 2 if fread failed + */ +int sha2_file( char *path, unsigned char output[32], int is224 ); + +/** + * \brief SHA-256 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2_hmac_starts( sha2_context *ctx, unsigned char *key, int keylen, + int is224 ); + +/** + * \brief SHA-256 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha2_hmac_update( sha2_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-256 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-224/256 HMAC checksum result + */ +void sha2_hmac_finish( sha2_context *ctx, unsigned char output[32] ); + +/** + * \brief Output = HMAC-SHA-256( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-224/256 result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha2_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[32], int is224 ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha2.h */ diff --git a/src/src/readconf.c b/src/src/readconf.c index 2e65cd970..2b02ddf64 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.35 2008/02/12 12:52:51 nm4 Exp $ */ +/* $Cambridge: exim/src/src/readconf.c,v 1.36 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -142,6 +142,9 @@ static optionlist optionlist_config[] = { { "acl_smtp_auth", opt_stringptr, &acl_smtp_auth }, { "acl_smtp_connect", opt_stringptr, &acl_smtp_connect }, { "acl_smtp_data", opt_stringptr, &acl_smtp_data }, +#ifndef DISABLE_DKIM + { "acl_smtp_dkim", opt_stringptr, &acl_smtp_dkim }, +#endif { "acl_smtp_etrn", opt_stringptr, &acl_smtp_etrn }, { "acl_smtp_expn", opt_stringptr, &acl_smtp_expn }, { "acl_smtp_helo", opt_stringptr, &acl_smtp_helo }, @@ -205,6 +208,9 @@ static optionlist optionlist_config[] = { { "disable_fsync", opt_bool, &disable_fsync }, #endif { "disable_ipv6", opt_bool, &disable_ipv6 }, +#ifndef DISABLE_DKIM + { "dkim_verify_signers", opt_stringptr, &dkim_verify_signers }, +#endif { "dns_again_means_nonexist", opt_stringptr, &dns_again_means_nonexist }, { "dns_check_names_pattern", opt_stringptr, &check_dns_names_pattern }, { "dns_csa_search_limit", opt_int, &dns_csa_search_limit }, diff --git a/src/src/receive.c b/src/src/receive.c index f0df716df..734ca7737 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/receive.c,v 1.45 2009/01/02 17:12:03 nm4 Exp $ */ +/* $Cambridge: exim/src/src/receive.c,v 1.46 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -11,38 +11,6 @@ #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 - - #ifdef EXPERIMENTAL_DCC extern int dcc_ok; #endif @@ -600,7 +568,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') @@ -642,7 +610,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) @@ -758,7 +726,7 @@ int ch_state = 0; register int ch; register int linelength = 0; -while ((ch = (RECEIVE_GETC)()) != EOF) +while ((ch = (receive_getc)()) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -1416,17 +1384,10 @@ if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX; message_linecount = body_linecount = body_zerocount = max_received_linelength = 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(); +#ifndef DISABLE_DKIM +/* Call into DKIM to set up the context. */ +if (smtp_input && !smtp_batched_input && !dkim_disable_verify) dkim_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 @@ -1476,7 +1437,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. */ @@ -1540,7 +1501,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; } @@ -1555,13 +1516,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 */ } } @@ -1589,7 +1550,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; @@ -1599,7 +1560,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 = ' '; @@ -1684,14 +1645,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 */ } @@ -3007,15 +2968,65 @@ else if (smtp_input && !smtp_batched_input) { -#ifdef EXPERIMENTAL_DOMAINKEYS - dk_exim_verify_finish(); -#endif -#ifdef EXPERIMENTAL_DKIM - dkim_exim_verify_finish(); -#endif +#ifndef DISABLE_DKIM + if (!dkim_disable_verify) + { + /* Finish verification, this will log individual signature results to + the mainlog */ + dkim_exim_verify_finish(); + + /* Check if we must run the DKIM ACL */ + if ((acl_smtp_dkim != NULL) && + (dkim_verify_signers != NULL) && + (dkim_verify_signers[0] != '\0')) + { + uschar *dkim_verify_signers_expanded = + expand_string(dkim_verify_signers); + if (dkim_verify_signers_expanded == NULL) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "expansion of dkim_verify_signers option failed: %s", + expand_string_message); + } + else + { + int sep = 0; + uschar *ptr = dkim_verify_signers_expanded; + uschar *item = NULL; + uschar itembuf[256]; + while ((item = string_nextinlist(&ptr, &sep, + itembuf, + sizeof(itembuf))) != NULL) + { + dkim_exim_acl_setup(item); + rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg); + if (rc != OK) break; + } + add_acl_headers(US"DKIM"); + if (rc == DISCARD) + { + recipients_count = 0; + blackholed_by = US"DKIM ACL"; + if (log_msg != NULL) + blackhole_log_msg = string_sprintf(": %s", log_msg); + } + else if (rc != OK) + { + Uunlink(spool_name); + if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0) + smtp_yield = FALSE; /* No more messsages after dropped connection */ + smtp_reply = US""; /* Indicate reply already sent */ + message_id[0] = 0; /* Indicate no message accepted */ + goto TIDYUP; /* Skip to end of function */ + } + } + } + } +#endif /* DISABLE_DKIM */ #ifdef WITH_CONTENT_SCAN - if (acl_smtp_mime != NULL && + if (recipients_count > 0 && + acl_smtp_mime != NULL && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by)) goto TIDYUP; #endif /* WITH_CONTENT_SCAN */ @@ -3554,8 +3565,8 @@ if (smtp_input && sender_host_address != NULL && !sender_host_notsocket && if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0) { - int c = (RECEIVE_GETC)(); - if (c != EOF) (RECEIVE_UNGETC)(c); else + int c = (receive_getc)(); + if (c != EOF) (receive_ungetc)(c); else { uschar *msg = US"SMTP connection lost after final dot"; smtp_reply = US""; /* No attempt to send a response */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b710c89ce..b9d92d631 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.63 2008/09/29 11:41:07 nm4 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.64 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -264,6 +264,9 @@ if (smtp_inptr >= smtp_inend) else smtp_had_eof = 1; return EOF; } +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(smtp_inbuffer, rc); +#endif smtp_inend = smtp_inbuffer + rc; smtp_inptr = smtp_inbuffer; } @@ -1037,11 +1040,10 @@ authenticated_sender = NULL; bmi_run = 0; bmi_verdicts = NULL; #endif -#ifdef EXPERIMENTAL_DOMAINKEYS -dk_do_verify = 0; -#endif -#ifdef EXPERIMENTAL_DKIM -dkim_do_verify = 0; +#ifndef DISABLE_DKIM +dkim_signing_domains = NULL; +dkim_disable_verify = FALSE; +dkim_collect_input = FALSE; #endif #ifdef EXPERIMENTAL_SPF spf_header_comment = NULL; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 311d1833c..1674e78f0 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.23 2009/01/20 16:09:20 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/spool_in.c,v 1.24 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -278,12 +278,10 @@ bmi_run = 0; bmi_verdicts = NULL; #endif -#ifdef EXPERIMENTAL_DOMAINKEYS -dk_do_verify = 0; -#endif - -#ifdef EXPERIMENTAL_DKIM -dkim_do_verify = 0; +#ifndef DISABLE_DKIM +dkim_signing_domains = NULL; +dkim_disable_verify = FALSE; +dkim_collect_input = FALSE; #endif #ifdef SUPPORT_TLS diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 2a81c8b8a..a73d8b893 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/tls-gnu.c,v 1.20 2008/09/03 18:53:29 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/tls-gnu.c,v 1.21 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -1172,7 +1172,9 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) ssl_xfer_error = 1; return EOF; } - +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); +#endif ssl_xfer_buffer_hwm = inbytes; ssl_xfer_buffer_lwm = 0; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index d7f9efdad..703612d0d 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/tls-openssl.c,v 1.13 2008/09/03 18:53:29 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/tls-openssl.c,v 1.14 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -887,7 +887,9 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) ssl_xfer_error = 1; return EOF; } - +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); +#endif ssl_xfer_buffer_hwm = inbytes; ssl_xfer_buffer_lwm = 0; } diff --git a/src/src/transport.c b/src/src/transport.c index 3c8c3f5f2..aeb172932 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/transport.c,v 1.23 2008/09/30 10:03:55 tom Exp $ */ +/* $Cambridge: exim/src/src/transport.c,v 1.24 2009/06/10 07:34:04 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -941,7 +941,7 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 || } -#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) +#ifndef DISABLE_DKIM /*************************************************************************************************** * External interface to write the message, while signing it with DKIM and/or Domainkeys * @@ -965,14 +965,6 @@ Arguments: as for internal_transport_write_message() above, with additional 0/false => send anyway uschar *dkim_sign_headers DKIM: List of headers that should be included in signature generation - uschar *dk_private_key Domainkeys: The private key to use (filename or plain data) - uschar *dk_domain Domainkeys: Override domain (normally NULL) - uschar *dk_selector Domainkeys: The selector to use. - uschar *dk_canon Domainkeys: The canonalization scheme to use, "simple" or "nofws" - uschar *dk_headers Domainkeys: Colon-separated header list to include in the signing - process. - uschar *dk_strict Domainkeys: What to do if signing fails: 1/true => throw error - 0/false => send anyway Returns: TRUE on success; FALSE (with errno) for any failure */ @@ -982,9 +974,7 @@ 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, - uschar *dk_private_key, uschar *dk_domain, uschar *dk_selector, uschar *dk_canon, - uschar *dk_headers, uschar *dk_strict + uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers ) { int dkim_fd; @@ -995,12 +985,10 @@ dkim_transport_write_message(address_item *addr, int fd, int options, int sread = 0; int wwritten = 0; uschar *dkim_signature = NULL; - uschar *dk_signature = NULL; off_t size = 0; - if ( !( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) || - ((dk_private_key != NULL) && (dk_selector != NULL)) ) ) { - /* If we can sign with neither method, just call the original function. */ + if (!( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) )) { + /* If we can't sign, just call the original function. */ return transport_write_message(addr, fd, options, size_limit, add_headers, remove_headers, check_string, escape_string, rewrite_rules, @@ -1031,8 +1019,6 @@ dkim_transport_write_message(address_item *addr, int fd, int options, goto CLEANUP; } - - #ifdef EXPERIMENTAL_DKIM if ( (dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL) ) { /* Rewind file and feed it to the goats^W DKIM lib */ lseek(dkim_fd, 0, SEEK_SET); @@ -1073,49 +1059,6 @@ dkim_transport_write_message(address_item *addr, int fd, int options, } } } - #endif - - #ifdef EXPERIMENTAL_DOMAINKEYS - if ( (dk_private_key != NULL) && (dk_selector != NULL) ) { - /* Rewind file and feed it to the goats^W DK lib */ - lseek(dkim_fd, 0, SEEK_SET); - dk_signature = dk_exim_sign(dkim_fd, - dk_private_key, - dk_domain, - dk_selector, - dk_canon); - if (dk_signature == NULL) { - if (dk_strict != NULL) { - uschar *dk_strict_result = expand_string(dk_strict); - if (dk_strict_result != NULL) { - if ( (strcmpic(dk_strict,US"1") == 0) || - (strcmpic(dk_strict,US"true") == 0) ) { - save_errno = errno; - rc = FALSE; - goto CLEANUP; - } - } - } - } - else { - int siglen = Ustrlen(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; - } - } - } - #endif /* Fetch file positition (the size) */ size = lseek(dkim_fd,0,SEEK_CUR); @@ -1185,6 +1128,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options, errno = save_errno; return rc; } + #endif diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index e56093f4d..e17ad7319 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.41 2009/01/02 17:22:12 nm4 Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.c,v 1.42 2009/06/10 07:34:05 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -39,19 +39,7 @@ 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) }, - #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) - { "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) }, +#ifndef DISABLE_DKIM { "dkim_canon", opt_stringptr, (void *)offsetof(smtp_transport_options_block, dkim_canon) }, { "dkim_domain", opt_stringptr, @@ -64,7 +52,7 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, dkim_sign_headers) }, { "dkim_strict", opt_stringptr, (void *)offsetof(smtp_transport_options_block, dkim_strict) }, - #endif +#endif { "dns_qualify_single", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, { "dns_search_parents", opt_bool, @@ -75,14 +63,14 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, final_timeout) }, { "gethostbyname", opt_bool, (void *)offsetof(smtp_transport_options_block, gethostbyname) }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "gnutls_require_kx", opt_stringptr, (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, { "gnutls_require_mac", opt_stringptr, (void *)offsetof(smtp_transport_options_block, gnutls_require_mac) }, { "gnutls_require_protocols", opt_stringptr, (void *)offsetof(smtp_transport_options_block, gnutls_require_proto) }, - #endif +#endif { "helo_data", opt_stringptr, (void *)offsetof(smtp_transport_options_block, helo_data) }, { "hosts", opt_stringptr, @@ -91,28 +79,28 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) }, { "hosts_avoid_pipelining", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "hosts_avoid_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) }, - #endif +#endif { "hosts_max_try", opt_int, (void *)offsetof(smtp_transport_options_block, hosts_max_try) }, { "hosts_max_try_hardlimit", opt_int, (void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "hosts_nopass_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) }, - #endif +#endif { "hosts_override", opt_bool, (void *)offsetof(smtp_transport_options_block, hosts_override) }, { "hosts_randomize", opt_bool, (void *)offsetof(smtp_transport_options_block, hosts_randomize) }, { "hosts_require_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_auth) }, - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS { "hosts_require_tls", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_tls) }, - #endif +#endif { "hosts_try_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_auth) }, { "interface", opt_stringptr, @@ -135,7 +123,7 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, serialize_hosts) }, { "size_addition", opt_int, (void *)offsetof(smtp_transport_options_block, size_addition) } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS ,{ "tls_certificate", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_certificate) }, { "tls_crl", opt_stringptr, @@ -148,7 +136,7 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) }, { "tls_verify_certificates", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) } - #endif +#endif }; /* Size of the options list. An extern variable has to be used so that its @@ -196,7 +184,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { TRUE, /* keepalive */ FALSE, /* lmtp_ignore_quota */ TRUE /* retry_include_ip_address */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ NULL, /* tls_crl */ NULL, /* tls_privatekey */ @@ -206,21 +194,15 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* gnutls_require_proto */ NULL, /* tls_verify_certificates */ TRUE /* tls_tempfail_tryclear */ - #endif - #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) - ,NULL, /* dk_canon */ - NULL, /* dk_domain */ - NULL, /* dk_headers */ - NULL, /* dk_private_key */ - NULL, /* dk_selector */ - NULL /* dk_strict */ +#endif +#ifndef DISABLE_DKIM ,NULL, /* dkim_canon */ NULL, /* dkim_domain */ NULL, /* dkim_private_key */ NULL, /* dkim_selector */ NULL, /* dkim_sign_headers */ NULL /* dkim_strict */ - #endif +#endif }; @@ -1592,7 +1574,7 @@ if (!ok) ok = TRUE; else DEBUG(D_transport|D_v) debug_printf(" SMTP>> writing message and terminating \".\"\n"); transport_count = 0; -#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) +#ifndef DISABLE_DKIM ok = dkim_transport_write_message(addrlist, inblock.sock, topt_use_crlf | topt_end_dot | topt_escape_headers | (tblock->body_only? topt_no_headers : 0) | @@ -1605,9 +1587,7 @@ if (!ok) ok = TRUE; else 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, - ob->dk_private_key, ob->dk_domain, ob->dk_selector, - ob->dk_canon, ob->dk_headers, ob->dk_strict + ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers ); #else ok = transport_write_message(addrlist, inblock.sock, diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 79239ad39..55ede7c7f 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.14 2008/03/05 21:13:23 tom Exp $ */ +/* $Cambridge: exim/src/src/transports/smtp.h,v 1.15 2009/06/10 07:34:05 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -57,13 +57,7 @@ typedef struct { uschar *tls_verify_certificates; BOOL tls_tempfail_tryclear; #endif - #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) - uschar *dk_domain; - uschar *dk_private_key; - uschar *dk_selector; - uschar *dk_canon; - uschar *dk_headers; - uschar *dk_strict; + #ifndef DISABLE_DKIM uschar *dkim_domain; uschar *dkim_private_key; uschar *dkim_selector; -- 2.25.1