summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile4
-rw-r--r--src/OS/Makefile-Base26
-rw-r--r--src/OS/os.h-Linux10
-rwxr-xr-xsrc/scripts/MakeLinks29
-rw-r--r--src/src/acl.c236
-rw-r--r--src/src/config.h.defaults6
-rw-r--r--src/src/dk.c440
-rw-r--r--src/src/dk.h51
-rwxr-xr-xsrc/src/dkim-exim.c510
-rwxr-xr-xsrc/src/dkim-exim.h35
-rw-r--r--src/src/dkim.c500
-rw-r--r--src/src/dkim.h33
-rw-r--r--src/src/dns.c9
-rw-r--r--src/src/drtables.c24
-rw-r--r--src/src/exim.c11
-rw-r--r--src/src/exim.h9
-rw-r--r--src/src/expand.c91
-rw-r--r--src/src/functions.h8
-rw-r--r--src/src/globals.c22
-rw-r--r--src/src/globals.h22
-rw-r--r--src/src/lookups/Makefile5
-rwxr-xr-xsrc/src/lookups/dkim.c52
-rwxr-xr-xsrc/src/lookups/dkim.h16
-rw-r--r--src/src/lookups/dnsdb.c12
-rw-r--r--src/src/macros.h3
-rw-r--r--src/src/pdkim/Makefile22
-rw-r--r--src/src/pdkim/README13
-rw-r--r--src/src/pdkim/base64.c180
-rw-r--r--src/src/pdkim/base64.h76
-rw-r--r--src/src/pdkim/bignum.c1813
-rw-r--r--src/src/pdkim/bignum.h395
-rw-r--r--src/src/pdkim/bn_mul.h719
-rw-r--r--src/src/pdkim/pdkim.c1714
-rw-r--r--src/src/pdkim/pdkim.h325
-rw-r--r--src/src/pdkim/rsa.c822
-rw-r--r--src/src/pdkim/rsa.h356
-rw-r--r--src/src/pdkim/sha1.c424
-rw-r--r--src/src/pdkim/sha1.h137
-rw-r--r--src/src/pdkim/sha2.c431
-rw-r--r--src/src/pdkim/sha2.h145
-rw-r--r--src/src/readconf.c8
-rw-r--r--src/src/receive.c139
-rw-r--r--src/src/smtp_in.c14
-rw-r--r--src/src/spool_in.c12
-rw-r--r--src/src/tls-gnu.c6
-rw-r--r--src/src/tls-openssl.c6
-rw-r--r--src/src/transport.c68
-rw-r--r--src/src/transports/smtp.c58
-rw-r--r--src/src/transports/smtp.h10
49 files changed, 8411 insertions, 1646 deletions
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<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* demime */
#endif
- #ifdef EXPERIMENTAL_DOMAINKEYS
- (1<<ACL_WHERE_AUTH)| /* dk_domain_source */
- (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
- (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
- (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
- (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
- (1<<ACL_WHERE_AUTH)| /* dk_policy */
- (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
- (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
- (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
- (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
- (1<<ACL_WHERE_AUTH)| /* dk_sender_domains */
- (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
- (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
- (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
- (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
- (1<<ACL_WHERE_AUTH)| /* dk_sender_local_parts */
- (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
- (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
- (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
- (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
-
- (1<<ACL_WHERE_AUTH)| /* dk_senders */
- (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
- (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
- (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
- (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
+ #ifndef DISABLE_DKIM
+ (unsigned int)
+ ~(1<<ACL_WHERE_DKIM), /* dkim_signers */
- (1<<ACL_WHERE_AUTH)| /* dk_status */
- (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
- (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
- (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
- (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
- (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
- (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_NOTSMTP_START),
+ (unsigned int)
+ ~(1<<ACL_WHERE_DKIM), /* dkim_status */
#endif
(1<<ACL_WHERE_NOTSMTP)| /* dnslists */
@@ -580,13 +516,8 @@ static unsigned int control_forbids[] = {
0, /* bmi_run */
#endif
- #ifdef EXPERIMENTAL_DOMAINKEYS
- (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dk_verify */
- (1<<ACL_WHERE_NOTSMTP_START),
- #endif
-
- #ifdef EXPERIMENTAL_DKIM
- (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_verify */
+ #ifndef DISABLE_DKIM
+ (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */
(1<<ACL_WHERE_NOTSMTP_START),
#endif
@@ -666,11 +597,8 @@ static control_def controls_list[] = {
#ifdef EXPERIMENTAL_BRIGHTMAIL
{ US"bmi_run", CONTROL_BMI_RUN, FALSE },
#endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
- { US"dk_verify", CONTROL_DK_VERIFY, FALSE },
-#endif
-#ifdef EXPERIMENTAL_DKIM
- { US"dkim_verify", CONTROL_DKIM_VERIFY, FALSE },
+#ifndef DISABLE_DKIM
+ { US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE },
#endif
{ US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
{ US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE },
@@ -2650,15 +2578,9 @@ for (; cb != NULL; cb = cb->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 <domainkeys.h>
-
-#define DK_EXIM_ADDRESS_NONE 0
-#define DK_EXIM_ADDRESS_FROM_FROM 1
-#define DK_EXIM_ADDRESS_FROM_SENDER 2
-
-#define DK_EXIM_RESULT_ERR 0
-#define DK_EXIM_RESULT_BAD_FORMAT 1
-#define DK_EXIM_RESULT_NO_KEY 2
-#define DK_EXIM_RESULT_NO_SIGNATURE 3
-#define DK_EXIM_RESULT_REVOKED 4
-#define DK_EXIM_RESULT_NON_PARTICIPANT 5
-#define DK_EXIM_RESULT_GOOD 6
-#define DK_EXIM_RESULT_BAD 7
-
-typedef struct dk_exim_verify_block {
- int result;
- int address_source;
- uschar *result_string;
- uschar *address;
- uschar *domain;
- uschar *local_part;
- BOOL is_signed;
- BOOL signsall;
- BOOL testing;
-} dk_exim_verify_block;
-
-int dk_receive_getc(void);
-int dk_receive_ungetc(int);
-void dk_exim_verify_init(void);
-void dk_exim_verify_finish(void);
-int dk_exim_verify_result(uschar **);
-uschar *dk_exim_sign(int, uschar *, uschar *, uschar *, uschar *);
-
-extern dk_exim_verify_block *dk_verify_block;
-
-#endif
diff --git a/src/src/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<sig_count;i++) {
- debug_printf( "DKIM: [%d] ", i + 1 );
- if (!dkim_verify_details[i].Domain) {
- debug_printf("parse error (no domain)\n");
- continue;
- }
-
- if (dkim_verify_details[i].nResult >= 0) {
- debug_printf( "GOOD d=%s i=%s\n",
- dkim_verify_details[i].Domain,
- dkim_verify_details[i].IdentityDomain );
- }
- else {
- debug_printf( "FAIL d=%s i=%s c=%d\n",
- dkim_verify_details[i].Domain,
- dkim_verify_details[i].IdentityDomain,
- dkim_verify_details[i].nResult
- );
-
- }
-
- if ( (strcmpic(domain,dkim_verify_details[i].Domain) == 0) ||
- (strcmpic(domain,dkim_verify_details[i].IdentityDomain) == 0) ) {
- if (dkim_verify_details[i].nResult >= 0) {
- rc = DKIM_EXIM_GOOD;
- /* TODO: Add From: domain check */
- }
- else {
- /* Return DEFER for temp. error types */
- if (dkim_verify_details[i].nResult == DKIM_SELECTOR_DNS_TEMP_FAILURE) {
- rc = DKIM_EXIM_DEFER;
- }
- else {
- rc = DKIM_EXIM_FAIL;
- }
- }
- }
- }
-
- YIELD:
- switch (rc) {
- case DKIM_EXIM_FAIL:
- *result = "bad";
- break;
- case DKIM_EXIM_DEFER:
- *result = "defer";
- break;
- case DKIM_EXIM_UNVERIFIED:
- *result = "unverified";
- break;
- case DKIM_EXIM_UNSIGNED:
- *result = "unsigned";
- break;
- case DKIM_EXIM_GOOD:
- *result = "good";
- break;
- }
-
- return rc;
-}
-
-
-
-uschar *dkim_exim_sign_headers = NULL;
-int dkim_exim_header_callback(const char* header) {
- int sep = 0;
- uschar *hdr_ptr = dkim_exim_sign_headers;
- uschar *hdr_itr = NULL;
- uschar hdr_buf[512];
- uschar *hdr_name = string_copy(US header);
- char *colon_pos = strchr(hdr_name,':');
-
- if (colon_pos == NULL) return 0;
- *colon_pos = '\0';
-
- debug_printf("DKIM: header '%s' ",hdr_name);
- while ((hdr_itr = string_nextinlist(&hdr_ptr, &sep,
- hdr_buf,
- sizeof(hdr_buf))) != NULL) {
- if (strcmpic((uschar *)hdr_name,hdr_itr) == 0) {
- debug_printf("included in signature.\n");
- return 1;
- }
- }
- debug_printf("NOT included in signature.\n");
- return 0;
-}
-
-uschar *dkim_exim_sign(int dkim_fd,
- uschar *dkim_private_key,
- uschar *dkim_domain,
- uschar *dkim_selector,
- uschar *dkim_canon,
- uschar *dkim_sign_headers) {
-
- uschar *rc = NULL;
- char buf[4096];
- int seen_lf = 0;
- int seen_lfdot = 0;
- int save_errno = 0;
- int sread;
- char *signature;
- int old_pool = store_pool;
- store_pool = POOL_PERM;
-
- dkim_context = NULL;
- dkim_sign_options = NULL;
-
- dkim_context = store_get(sizeof(DKIMContext));
- dkim_sign_options = store_get(sizeof(DKIMSignOptions));
-
- 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 <dkim.h>
-
-int dkim_exim_verify_result(uschar *,uschar **,uschar **);
-
-/* Internal prototypes */
-int dkim_receive_getc(void);
-int dkim_receive_ungetc(int);
-void dkim_exim_verify_init(void);
-void dkim_exim_verify_finish(void);
-uschar *dkim_exim_sign(int, uschar *, uschar *, uschar *, uschar *, uschar *);
-unsigned int dkim_status_wrap(int, uschar *);
-
-#endif
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 <tom@duncanthrax.net>
+
+Includes code from the PolarSSL project.
+http://polarssl.org
+Copyright (C) 2009 Paul Bakker <polarssl_maintainer@polarssl.org>
+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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <stdio.h>
+
+#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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <tom@duncanthrax.net>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#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<len;i++) {
+ int c = p[i];
+ switch (c) {
+ case ' ' : fprintf(stream,"{SP}"); break;
+ case '\t': fprintf(stream,"{TB}"); break;
+ case '\r': fprintf(stream,"{CR}"); break;
+ case '\n': fprintf(stream,"{LF}"); break;
+ case '{' : fprintf(stream,"{BO}"); break;
+ case '}' : fprintf(stream,"{BC}"); break;
+ default:
+ if ( (c < 32) || (c > 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;i<len;i++) {
+ int c = p[i];
+ fprintf(stream,"%02x",c);
+ }
+ if (lf)
+ fputc('\n',stream);
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+/* Simple string list implementation for convinience */
+pdkim_stringlist *pdkim_append_stringlist(pdkim_stringlist *base, char *str) {
+ pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist));
+ if (new_entry == NULL) return NULL;
+ memset(new_entry,0,sizeof(pdkim_stringlist));
+ new_entry->value = 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;p<len;p++) {
+ char c = data[p];
+ if (ctx->past_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 '<CR><LF>' */
+ 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 <tom@duncanthrax.net>
+ *
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+/*
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+
+/*
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+
+/*
+ * 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 <polarssl_maintainer at polarssl dot org>
+ *
+ * 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;