summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2016-02-01 18:18:56 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2016-02-01 19:07:32 +0000
commit2592e6c0eda522da0f6a33f4d32e33598288eb6e (patch)
tree2f6a0dfb118ea9fd715a4edfbe5d2ac86c17914a
parent939c3e349db9662100c7cafa8bc1298dfd8d3798 (diff)
DKIM: replace SHA and RSA routines from gnutls, under earlier library
versions, using libgcrypt and libtasn1 directly. Bug 1772
-rwxr-xr-xsrc/scripts/MakeLinks11
-rw-r--r--src/src/dkim.c13
-rw-r--r--src/src/dkim.h1
-rw-r--r--src/src/exim.c7
-rw-r--r--src/src/pdkim/Makefile9
-rw-r--r--src/src/pdkim/README6
-rw-r--r--src/src/pdkim/blob.h17
-rw-r--r--src/src/pdkim/crypt_ver.h12
-rw-r--r--src/src/pdkim/hash.c181
-rw-r--r--src/src/pdkim/hash.h79
-rw-r--r--src/src/pdkim/part-x509parse.c153
-rw-r--r--src/src/pdkim/pdkim.c534
-rw-r--r--src/src/pdkim/pdkim.h31
-rw-r--r--src/src/pdkim/rsa.c679
-rw-r--r--src/src/pdkim/rsa.h81
-rw-r--r--src/src/pdkim/sha1.c323
-rw-r--r--src/src/pdkim/sha2.c453
-rw-r--r--test/aux-fixed/dkim/sign.pl4
-rw-r--r--test/confs/45031
-rw-r--r--test/log/45003
-rw-r--r--test/log/45032
-rw-r--r--test/scripts/4500-Domain-Keys-Identified-Mail/450035
-rw-r--r--test/scripts/4500-Domain-Keys-Identified-Mail/45034
23 files changed, 1258 insertions, 1381 deletions
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks
index 4f6747f7c..886214030 100755
--- a/src/scripts/MakeLinks
+++ b/src/scripts/MakeLinks
@@ -82,17 +82,12 @@ cd ..
# Likewise for the code for the PDKIM library
mkdir pdkim
cd pdkim
-for f in README Makefile crypt_ver.h part-x509parse.c pdkim.c \
- pdkim.h sha1.c sha2.c
+for f in README Makefile crypt_ver.h pdkim.c \
+ pdkim.h hash.c hash.h rsa.c rsa.h blob.h
do
ln -s ../../src/pdkim/$f $f
done
-mkdir polarssl
-cd polarssl
-for i in `ls ../../../src/pdkim/polarssl/` ; do
- ln -s ../../../src/pdkim/polarssl/$i $i
-done
-cd ../..
+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
diff --git a/src/src/dkim.c b/src/src/dkim.c
index 36b103e8f..349947ab1 100644
--- a/src/src/dkim.c
+++ b/src/src/dkim.c
@@ -60,6 +60,13 @@ return PDKIM_FAIL;
void
+dkim_exim_init(void)
+{
+pdkim_init();
+}
+
+
+void
dkim_exim_verify_init(void)
{
/* Free previous context if there is one */
@@ -129,7 +136,7 @@ for (sig = dkim_signatures; sig; sig = sig->next)
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->sigdata_len * 8
+ sig->sigdata.len * 8
),
sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
@@ -255,7 +262,7 @@ for (sig = dkim_signatures; sig; sig = sig->next)
dkim_signing_domain = US sig->domain;
dkim_signing_selector = US sig->selector;
- dkim_key_length = sig->sigdata_len * 8;
+ dkim_key_length = sig->sigdata.len * 8;
return;
}
}
@@ -340,7 +347,7 @@ switch (what)
case DKIM_HEADERNAMES:
return dkim_cur_sig->headernames
- ? US dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
+ ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
case DKIM_IDENTITY:
return dkim_cur_sig->identity
diff --git a/src/src/dkim.h b/src/src/dkim.h
index 39a0408a1..1655f8e45 100644
--- a/src/src/dkim.h
+++ b/src/src/dkim.h
@@ -5,6 +5,7 @@
/* Copyright (c) University of Cambridge, 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
+void dkim_exim_init(void);
uschar *dkim_exim_sign(int, uschar *, const uschar *, uschar *, uschar *, uschar *);
void dkim_exim_verify_init(void);
void dkim_exim_verify_feed(uschar *, int);
diff --git a/src/src/exim.c b/src/src/exim.c
index 28617a510..ebc71dd37 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -1760,7 +1760,6 @@ regex_whitelisted_macro =
for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
-
/* If the program is called as "mailq" treat it as equivalent to "exim -bp";
this seems to be a generally accepted convention, since one finds symbolic
links called "mailq" in standard OS configurations. */
@@ -4556,6 +4555,12 @@ if (list_config)
}
+/* Initialise subsystems as required */
+#ifndef DISABLE_DKIM
+dkim_exim_init();
+#endif
+
+
/* Handle a request to deliver one or more messages that are already on the
queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
above. MSG_LOAD is handled with -be (which is the only time it applies) below.
diff --git a/src/src/pdkim/Makefile b/src/src/pdkim/Makefile
index ec8bb8305..c72a9426b 100644
--- a/src/src/pdkim/Makefile
+++ b/src/src/pdkim/Makefile
@@ -1,6 +1,6 @@
# Make file for building the pdkim library.
-OBJ = pdkim.o
+OBJ = pdkim.o hash.o rsa.o
pdkim.a: $(OBJ)
@$(RM_COMMAND) -f pdkim.a
@@ -12,9 +12,8 @@ pdkim.a: $(OBJ)
.c.o:; @echo "$(CC) $*.c"
$(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -I. $*.c
-part-x509parse.o: $(HDRS) crypt_ver.h part-x509parse.c
-pdkim.o: $(HDRS) crypt_ver.h pdkim.h pdkim.c
-sha1.o: $(HDRS) crypt_ver.h sha1.c
-sha2.o: $(HDRS) crypt_ver.h sha2.c
+pdkim.o: $(HDRS) crypt_ver.h hash.h blob.h pdkim.h pdkim.c
+hash.o: $(HDRS) crypt_ver.h hash.h blob.h pdkim.h hash.c
+rsa.o: $(HDRS) crypt_ver.h rsa.h blob.h rsa.c
# End
diff --git a/src/src/pdkim/README b/src/src/pdkim/README
index de04cff2d..953e86eae 100644
--- a/src/src/pdkim/README
+++ b/src/src/pdkim/README
@@ -2,10 +2,8 @@ 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
+No longer includes code from the PolarSSL project.
+Copyright (C) 2016 Jeremy Harris <jgh@exim.org>
This copy of PDKIM is included with Exim. For a standalone distribution,
visit http://duncanthrax.net/pdkim/.
diff --git a/src/src/pdkim/blob.h b/src/src/pdkim/blob.h
new file mode 100644
index 000000000..e1481c9f4
--- /dev/null
+++ b/src/src/pdkim/blob.h
@@ -0,0 +1,17 @@
+/*
+ * PDKIM - a RFC4871 (DKIM) implementation
+ *
+ * Copyright (C) 2016 Exim maintainers
+ *
+ * RSA signing/verification interface
+ */
+
+#ifndef BLOB_H /* entire file */
+#define BLOB_H
+
+typedef struct {
+ uschar * data;
+ size_t len;
+} blob;
+
+#endif
diff --git a/src/src/pdkim/crypt_ver.h b/src/src/pdkim/crypt_ver.h
index 602d137a3..2a9dde952 100644
--- a/src/src/pdkim/crypt_ver.h
+++ b/src/src/pdkim/crypt_ver.h
@@ -11,14 +11,18 @@
#ifdef USE_GNUTLS
-# define RSA_GNUTLS
-
# include <gnutls/gnutls.h>
+
+# if GNUTLS_VERSION_NUMBER > 0x020c00
+# define RSA_GNUTLS
+# else
+# define RSA_GCRYPT
+# endif
+
# if GNUTLS_VERSION_NUMBER >= 0x020a00
# define SHA_GNUTLS
-
# else
-# define SHA_POLARSSL
+# define SHA_GCRYPT
# endif
#else
diff --git a/src/src/pdkim/hash.c b/src/src/pdkim/hash.c
new file mode 100644
index 000000000..0751683e4
--- /dev/null
+++ b/src/src/pdkim/hash.c
@@ -0,0 +1,181 @@
+/*
+ * PDKIM - a RFC4871 (DKIM) implementation
+ *
+ * Copyright (C) 2016 Exim maintainers
+ *
+ * Hash interface functions
+ */
+
+#include "../exim.h"
+
+#ifndef DISABLE_DKIM /* entire file */
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+# ifdef RSA_VERIFY_GNUTLS
+# include <gnutls/abstract.h>
+# endif
+#endif
+
+#ifdef SHA_GNUTLS
+# include <gnutls/crypto.h>
+#endif
+
+#include "hash.h"
+
+
+/******************************************************************************/
+#ifdef SHA_OPENSSL
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+if (h->sha1)
+ SHA1_Init (&h->u.sha1);
+else
+ SHA256_Init(&h->u.sha2);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+if (h->sha1)
+ SHA1_Update (&h->u.sha1, data, len);
+else
+ SHA256_Update(&h->u.sha2, data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+
+if (h->sha1)
+ SHA1_Final (b->data, &h->u.sha1);
+else
+ SHA256_Final(b->data, &h->u.sha2);
+}
+
+
+
+#elif defined(SHA_GNUTLS)
+/******************************************************************************/
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+gnutls_hash_init(&h->sha, sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+gnutls_hash(h->sha, data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+gnutls_hash_output(h->sha, b->data);
+}
+
+
+
+#elif defined(SHA_GCRYPT)
+/******************************************************************************/
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+gcry_md_open(&h->sha, sha1 ? GCRY_MD_SHA1 : GCRY_MD_SHA256, 0);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+gcry_md_write(h->sha, data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen);
+}
+
+
+
+
+#elif defined(SHA_POLARSSL)
+/******************************************************************************/
+
+void
+exim_sha_init(hctx * h, BOOL sha1)
+{
+h->sha1 = sha1;
+h->hashlen = sha1 ? 20 : 32;
+if (h->sha1)
+ sha1_starts(&h->u.sha1);
+else
+ sha2_starts(&h->u.sha2, 0);
+}
+
+
+void
+exim_sha_update(hctx * h, const char * data, int len)
+{
+if (h->sha1)
+ sha1_update(h->u.sha1, US data, len);
+else
+ sha2_update(h->u.sha2, US data, len);
+}
+
+
+void
+exim_sha_finish(hctx * h, blob * b)
+{
+b->data = store_get(b->len = h->hashlen);
+
+if (h->sha1)
+ sha1_finish(h->u.sha1, b->data);
+else
+ sha2_finish(h->u.sha2, b->data);
+}
+
+#endif
+/******************************************************************************/
+
+/* Common to all library versions */
+int
+exim_sha_hashlen(hctx * h)
+{
+return h->sha1 ? 20 : 32;
+}
+
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/hash.h b/src/src/pdkim/hash.h
new file mode 100644
index 000000000..afd7ea6a6
--- /dev/null
+++ b/src/src/pdkim/hash.h
@@ -0,0 +1,79 @@
+/*
+ * PDKIM - a RFC4871 (DKIM) implementation
+ *
+ * Copyright (C) 2016 Exim maintainers
+ *
+ * Hash interface functions
+ */
+
+#include "../exim.h"
+
+#if !defined(DISABLE_DKIM) && !defined(PDKIM_HASH_H) /* entire file */
+#define PDKIM_HASH_H
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+#include "blob.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+#endif
+
+#ifdef SHA_GNUTLS
+# include <gnutls/crypto.h>
+#elif defined(SHA_GCRYPT)
+# include <gcrypt.h>
+#elif defined(SHA_POLARSSL)
+# include "pdkim.h"
+# include "polarssl/sha1.h"
+# include "polarssl/sha2.h"
+#endif
+
+/* Hash context */
+typedef struct {
+ int sha1;
+ int hashlen;
+
+#ifdef SHA_OPENSSL
+ union {
+ SHA_CTX sha1; /* SHA1 block */
+ SHA256_CTX sha2; /* SHA256 block */
+ } u;
+
+#elif defined(SHA_GNUTLS)
+ gnutls_hash_hd_t sha; /* Either SHA1 or SHA256 block */
+
+#elif defined(SHA_GCRYPT)
+ gcry_md_hd_t sha; /* Either SHA1 or SHA256 block */
+
+#elif defined(SHA_POLARSSL)
+ union {
+ sha1_context sha1; /* SHA1 block */
+ sha2_context sha2; /* SHA256 block */
+ } u;
+#endif
+
+} hctx;
+
+#if defined(SHA_OPENSSL)
+# include "pdkim.h"
+#elif defined(SHA_GCRYPT)
+# include "pdkim.h"
+#endif
+
+
+extern void exim_sha_init(hctx *, BOOL);
+extern void exim_sha_update(hctx *, const char *a, int);
+extern void exim_sha_finish(hctx *, blob *);
+extern int exim_sha_hashlen(hctx *);
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/part-x509parse.c b/src/src/pdkim/part-x509parse.c
deleted file mode 100644
index 5788777f2..000000000
--- a/src/src/pdkim/part-x509parse.c
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "crypt_ver.h"
-
-#ifdef SHA_POLARSSL /* remainder of file */
-
-#include "polarssl/bignum.h"
-#include "polarssl/part-x509.h"
-#include "polarssl/private-x509parse_c.h"
-
-/* all calls are from src/pdkim/pdkim-rsa.c */
-
-/* *************** begin copy from x509parse.c ********************/
-/*
- * X.509 certificate and private key decoding
- *
- * Copyright (C) 2006-2010, Brainspark B.V.
- *
- * This file is part of PolarSSL (http://www.polarssl.org)
- * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
- *
- * All rights reserved.
- *
- * 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 ITU-T X.509 standard defines a certificat format for PKI.
- *
- * http://www.ietf.org/rfc/rfc2459.txt
- * http://www.ietf.org/rfc/rfc3279.txt
- *
- * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
- *
- * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
- * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
- */
-
-
-/*
- * ASN.1 DER decoding routines
- */
-static int asn1_get_len( unsigned char **p,
- const 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 );
-}
-
-/* This function is not exported by PolarSSL 0.14.2
- * static */
-int asn1_get_tag( unsigned char **p,
- const 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 ) );
-}
-
-/* This function is not exported by PolarSSL 0.14.2
- * static */
-int asn1_get_int( unsigned char **p,
- const 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 );
-}
-
-/* This function is not exported by PolarSSL 0.14.2
- * static */
-int asn1_get_mpi( unsigned char **p,
- const 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 );
-}
-/* *************** end copy from x509parse.c ********************/
-#endif
diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c
index 6e471a614..789d650e6 100644
--- a/src/src/pdkim/pdkim.c
+++ b/src/src/pdkim/pdkim.c
@@ -39,17 +39,10 @@
#elif defined(RSA_GNUTLS)
# include <gnutls/gnutls.h>
# include <gnutls/x509.h>
-# include <gnutls/abstract.h>
-#endif
-
-#ifdef SHA_GNUTLS
-# include <gnutls/crypto.h>
-#elif defined(SHA_POLARSSL)
-# include "polarssl/sha1.h"
-# include "polarssl/sha2.h"
#endif
#include "pdkim.h"
+#include "rsa.h"
#define PDKIM_SIGNATURE_VERSION "1"
#define PDKIM_PUB_RECORD_VERSION "DKIM1"
@@ -155,8 +148,8 @@ pdkim_verify_ext_status_str(int ext_status)
/* -------------------------------------------------------------------------- */
/* Print debugging functions */
-void
-pdkim_quoteprint(const char *data, int len, int lf)
+static void
+pdkim_quoteprint(const char *data, int len)
{
int i;
const unsigned char *p = (const unsigned char *)data;
@@ -180,20 +173,18 @@ for (i = 0; i < len; i++)
break;
}
}
-if (lf)
- debug_printf("\n");
+debug_printf("\n");
}
-void
-pdkim_hexprint(const char *data, int len, int lf)
+static void
+pdkim_hexprint(const char *data, int len)
{
int i;
const unsigned char *p = (const unsigned char *)data;
for (i = 0 ; i < len; i++)
debug_printf("%02x", p[i]);
-if (lf)
- debug_printf("\n");
+debug_printf("\n");
}
@@ -318,7 +309,6 @@ if (pub)
if (pub->keytype ) free(pub->keytype);
if (pub->srvtype ) free(pub->srvtype);
if (pub->notes ) free(pub->notes);
-/* if (pub->key ) free(pub->key); */
free(pub);
}
}
@@ -345,7 +335,6 @@ if (sig)
if (sig->selector ) free(sig->selector);
if (sig->domain ) free(sig->domain);
if (sig->identity ) free(sig->identity);
- if (sig->headernames ) free(sig->headernames);
if (sig->copiedheaders ) free(sig->copiedheaders);
if (sig->rsa_privkey ) free(sig->rsa_privkey);
if (sig->sign_headers ) free(sig->sign_headers);
@@ -560,37 +549,26 @@ return n;
/* -------------------------------------------------------------------------- */
-static char *
-pdkim_decode_base64(char *str, int *num_decoded)
+static void
+pdkim_decode_base64(uschar *str, blob * b)
{
-int dlen = 0;
+int dlen;
char *res;
-int old_pool = store_pool;
-
-/* There is a store-reset between header & body reception
-so cannot use the main pool */
-
-store_pool = POOL_PERM;
-dlen = b64decode(US str, USS &res);
-store_pool = old_pool;
-
-if (dlen < 0) return NULL;
-
-if (num_decoded) *num_decoded = dlen;
-return res;
+dlen = b64decode(str, &b->data);
+if (dlen < 0) b->data = NULL;
+b->len = dlen;
}
-
/* -------------------------------------------------------------------------- */
static char *
-pdkim_encode_base64(char *str, int num)
+pdkim_encode_base64(blob * b)
{
char * ret;
int old_pool = store_pool;
store_pool = POOL_PERM;
-ret = CS b64encode(US str, num);
+ret = CS b64encode(b->data, b->len);
store_pool = old_pool;
return ret;
}
@@ -612,6 +590,13 @@ BOOL past_hname = FALSE;
BOOL in_b_val = FALSE;
int where = PDKIM_HDR_LIMBO;
int i;
+int old_pool = store_pool;
+
+/* There is a store-reset between header & body reception
+so cannot use the main pool. Any allocs done by Exim
+memory-handling must use the perm pool. */
+
+store_pool = POOL_PERM;
if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL;
memset(sig, 0, sizeof(pdkim_signature));
@@ -689,11 +674,9 @@ for (p = raw_hdr; ; p++)
{
case 'b':
if (cur_tag->str[1] == 'h')
- sig->bodyhash = pdkim_decode_base64(cur_val->str,
- &sig->bodyhash_len);
+ pdkim_decode_base64(US cur_val->str, &sig->bodyhash);
else
- sig->sigdata = pdkim_decode_base64(cur_val->str,
- &sig->sigdata_len);
+ pdkim_decode_base64(US cur_val->str, &sig->sigdata);
break;
case 'v':
/* We only support version 1, and that is currently the
@@ -739,7 +722,7 @@ for (p = raw_hdr; ; p++)
case 'l':
sig->bodylength = strtol(cur_val->str, NULL, 10); break;
case 'h':
- sig->headernames = strdup(cur_val->str); break;
+ sig->headernames = string_copy(cur_val->str); break;
case 'z':
sig->copiedheaders = pdkim_decode_qp(cur_val->str); break;
default:
@@ -764,12 +747,12 @@ NEXT_CHAR:
*q++ = c;
}
+store_pool = old_pool;
+
/* 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);
@@ -786,38 +769,14 @@ DEBUG(D_acl)
{
debug_printf(
"PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
- pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val), 1);
+ pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val));
debug_printf(
- "PDKIM >> Sig size: %4d bits\n", sig->sigdata_len*8);
+ "PDKIM >> Sig size: %4d bits\n", sig->sigdata.len*8);
debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
-#ifdef SHA_OPENSSL
-
-SHA1_Init (&sig->sha1_body);
-SHA256_Init(&sig->sha2_body);
-
-#elif defined(SHA_GNUTLS)
-
-gnutls_hash_init(&sig->sha_body,
- sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
-
-#elif defined(SHA_POLARSSL)
-
-if ( !(sig->sha1_body = malloc(sizeof(sha1_context)))
- || !(sig->sha2_body = malloc(sizeof(sha2_context)))
- )
- {
- pdkim_free_sig(sig);
- return NULL;
- }
-
-sha1_starts(sig->sha1_body);
-sha2_starts(sig->sha2_body, 0);
-
-#endif /* SHA impl */
-
+exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1);
return sig;
}
@@ -898,7 +857,8 @@ for (p = raw_record; ; p++)
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;
+ pdkim_decode_base64(US cur_val->str, &pub->key);
+ break;
case 'k':
pub->hashes = strdup(cur_val->str); break;
case 's':
@@ -931,7 +891,7 @@ if (!pub->keytype ) pub->keytype = strdup("rsa");
if (!pub->srvtype ) pub->srvtype = strdup("*");
/* p= is required */
-if (pub->key)
+if (pub->key.data)
return pub;
pdkim_free_pubkey(pub);
@@ -1002,23 +962,9 @@ while (sig)
if (canon_len > 0)
{
-#ifdef SHA_GNUTLS
- gnutls_hash(sig->sha_body, canon_data, canon_len);
-#else
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
- SHA1_Update (&sig->sha1_body, canon_data, canon_len);
- else
- SHA256_Update(&sig->sha2_body, canon_data, canon_len);
-# elif defined(SHA_POLARSSL)
- sha1_update(sig->sha1_body, US canon_data, canon_len);
- else
- sha2_update(sig->sha2_body, US canon_data, canon_len);
-# endif
-#endif
-
+ exim_sha_update(&sig->body_hash, canon_data, canon_len);
sig->signed_body_bytes += canon_len;
- DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len, 1);
+ DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
}
sig = sig->next;
@@ -1039,36 +985,22 @@ pdkim_signature *sig;
/* Traverse all signatures */
for (sig = ctx->sig; sig; sig = sig->next)
{ /* Finish hashes */
- uschar bh[32]; /* SHA-256 = 32 Bytes, SHA-1 = 20 Bytes */
-
-#ifdef SHA_GNUTLS
- gnutls_hash_output(sig->sha_body, bh);
-#else
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
- SHA1_Final (bh, &sig->sha1_body);
- else
- SHA256_Final(bh, &sig->sha2_body);
-# elif defined(SHA_POLARSSL)
- sha1_finish(sig->sha1_body, bh);
- else
- sha2_finish(sig->sha2_body, bh);
-# endif
-#endif
+ blob bh;
+
+ exim_sha_finish(&sig->body_hash, &bh);
DEBUG(D_acl)
{
debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
"PDKIM [%s] bh computed: ",
sig->domain, sig->signed_body_bytes, sig->domain);
- pdkim_hexprint((char *)bh, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1);
+ pdkim_hexprint(CS bh.data, bh.len);
}
/* SIGNING -------------------------------------------------------------- */
if (ctx->mode == PDKIM_MODE_SIGN)
{
- sig->bodyhash_len = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32;
- sig->bodyhash = CS string_copyn(US bh, sig->bodyhash_len);
+ sig->bodyhash = bh;
/* If bodylength limit is set, and we have received less bytes
than the requested amount, effectively remove the limit tag. */
@@ -1080,8 +1012,7 @@ for (sig = ctx->sig; sig; sig = sig->next)
else
{
/* Compare bodyhash */
- if (memcmp(bh, sig->bodyhash,
- (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0)
+ if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
{
DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
}
@@ -1090,8 +1021,8 @@ for (sig = ctx->sig; sig; sig = sig->next)
DEBUG(D_acl)
{
debug_printf("PDKIM [%s] bh signature: ", sig->domain);
- pdkim_hexprint(sig->bodyhash,
- sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1);
+ pdkim_hexprint(sig->bodyhash.data,
+ exim_sha_hashlen(&sig->body_hash));
debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
}
sig->verify_status = PDKIM_VERIFY_FAIL;
@@ -1466,7 +1397,7 @@ return str->str;
/* -------------------------------------------------------------------------- */
static char *
-pdkim_create_header(pdkim_signature *sig, int final)
+pdkim_create_header(pdkim_signature *sig, BOOL final)
{
char *rc = NULL;
char *base64_bh = NULL;
@@ -1481,7 +1412,7 @@ if (!(hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION)))
if (!(canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers])))
goto BAIL;
-if (!(base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len)))
+if (!(base64_bh = pdkim_encode_base64(&sig->bodyhash)))
goto BAIL;
col = strlen(hdr->str);
@@ -1496,9 +1427,9 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
&& pdkim_headcat(&col, hdr, ";", "s=", sig->selector)
)
{
- /* list of eader names can be split between items. */
+ /* list of header names can be split between items. */
{
- char *n = strdup(sig->headernames);
+ char *n = CS string_copy(sig->headernames);
char *f = n;
char *i = "h=";
char *s = ";";
@@ -1513,13 +1444,11 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
if (!i)
if (!pdkim_headcat(&col, hdr, NULL, NULL, ":"))
{
- free(f);
goto BAIL;
}
if (!pdkim_headcat(&col, hdr, s, i, n))
{
- free(f);
goto BAIL;
}
@@ -1530,7 +1459,6 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
s = NULL;
i = NULL;
}
- free(f);
}
if(!pdkim_headcat(&col, hdr, ";", "bh=", base64_bh))
@@ -1571,7 +1499,7 @@ if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
/* Preliminary or final version? */
if (final)
{
- if (!(base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len)))
+ if (!(base64_b = pdkim_encode_base64(&sig->sigdata)))
goto BAIL;
if (!pdkim_headcat(&col, hdr, ";", "b=", base64_b))
goto BAIL;
@@ -1627,40 +1555,17 @@ if (ctx->mode == PDKIM_MODE_SIGN)
while (sig)
{
-#ifdef SHA_OPENSSL
- SHA_CTX sha1_headers;
- SHA256_CTX sha2_headers;
-#elif defined(SHA_GNUTLS)
- gnutls_hash_hd_t sha_headers;
-#elif defined(SHA_POLARSSL)
- sha1_context sha1_headers;
- sha2_context sha2_headers;
-#endif
-
- char *sig_hdr;
- char headerhash[32];
-
-#ifdef RSA_GNUTLS
- uschar * hdata = NULL;
+ BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
+ hctx hhash_ctx;
+ char * sig_hdr;
+ blob hhash;
+ blob hdata;
int hdata_alloc = 0;
- int hdata_size = 0;
-#endif
-#ifdef SHA_GNUTLS
- gnutls_hash_init(&sha_headers,
- sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
-#else
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
- SHA1_Init(&sha1_headers);
- else
- SHA256_Init(&sha2_headers);
-# elif defined(SHA_POLARSSL)
- sha1_starts(&sha1_headers);
- else
- sha2_starts(&sha2_headers, 0);
-# endif
-#endif
+ hdata.data = NULL;
+ hdata.len = 0;
+
+ exim_sha_init(&hhash_ctx, is_sha1);
DEBUG(D_acl) debug_printf(
"PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
@@ -1675,43 +1580,27 @@ while (sig)
for (p = sig->headers; p; p = p->next)
{
- char *rh = NULL;
+ uschar * rh;
/* Collect header names (Note: colon presence is guaranteed here) */
- char *q = strchr(p->value, ':');
+ uschar * q = Ustrchr(p->value, ':');
if (!(pdkim_strncat(headernames, p->value,
- (q-(p->value)) + (p->next ? 1 : 0))))
+ (q-US (p->value)) + (p->next ? 1 : 0))))
return PDKIM_ERR_OOM;
rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
- : strdup(p->value); /* just copy it for simple canon */
+ ? US pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
+ : string_copy(p->value); /* just copy it for simple canon */
if (!rh)
return PDKIM_ERR_OOM;
/* Feed header to the hash algorithm */
-#ifdef SHA_GNUTLS
- gnutls_hash(sha_headers, rh, strlen(rh));
-#else
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
- SHA1_Update (&sha1_headers, rh, strlen(rh));
- else
- SHA256_Update(&sha2_headers, rh, strlen(rh));
-# elif defined(SHA_POLARSSL)
- sha1_update(&sha1_headers, US rh, strlen(rh));
- else
- sha2_update(&sha2_headers, US rh, strlen(rh));
-# endif
-#endif
+ exim_sha_update(&hhash_ctx, rh, strlen(rh));
-#ifdef RSA_GNUTLS
- /* Remember headers block for signing */
- hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, rh);
-#endif
+ /* Remember headers block for signing (when the library cannot do incremental) */
+ (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
- DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1);
- free(rh);
+ DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
}
}
@@ -1720,10 +1609,10 @@ while (sig)
add the headers to the hash in that order. */
else
{
- char *b = strdup(sig->headernames);
- char *p = b;
- char *q = NULL;
- pdkim_stringlist *hdrs;
+ uschar * b = string_copy(sig->headernames);
+ uschar * p = b;
+ uschar * q;
+ pdkim_stringlist * hdrs;
if (!b) return PDKIM_ERR_OOM;
@@ -1733,41 +1622,25 @@ while (sig)
while(1)
{
- if ((q = strchr(p, ':')))
+ if ((q = Ustrchr(p, ':')))
*q = '\0';
for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
if ( hdrs->tag == 0
- && strncasecmp(hdrs->value, p, strlen(p)) == 0
- && (hdrs->value)[strlen(p)] == ':'
+ && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
+ && (hdrs->value)[Ustrlen(p)] == ':'
)
{
- char *rh;
-
- rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
- : strdup(hdrs->value); /* just copy it for simple canon */
+ uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
+ ? US pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
+ : string_copy(hdrs->value); /* just copy it for simple canon */
if (!rh)
return PDKIM_ERR_OOM;
/* Feed header to the hash algorithm */
-#ifdef SHA_GNUTLS
- gnutls_hash(sha_headers, rh, strlen(rh));
-#else
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
- SHA1_Update (&sha1_headers, rh, strlen(rh));
- else
- SHA256_Update(&sha2_headers, rh, strlen(rh));
-# elif defined(SHA_POLARSSL)
- sha1_update(&sha1_headers, US rh, strlen(rh));
- else
- sha2_update(&sha2_headers, US rh, strlen(rh));
-# endif
-#endif
+ exim_sha_update(&hhash_ctx, rh, strlen(rh));
- DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1);
- free(rh);
+ DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
hdrs->tag = 1;
break;
}
@@ -1775,7 +1648,6 @@ while (sig)
if (!q) break;
p = q+1;
}
- free(b);
}
DEBUG(D_acl) debug_printf(
@@ -1785,11 +1657,11 @@ while (sig)
if (ctx->mode == PDKIM_MODE_SIGN)
{
/* Copy headernames to signature struct */
- sig->headernames = strdup(headernames->str);
+ sig->headernames = string_copy(US headernames->str);
pdkim_strfree(headernames);
/* Create signature header with b= omitted */
- sig_hdr = pdkim_create_header(ctx->sig, 0);
+ sig_hdr = pdkim_create_header(ctx->sig, FALSE);
}
/* VERIFICATION ----------------------------------------------------------- */
@@ -1815,172 +1687,71 @@ while (sig)
{
debug_printf(
"PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
- pdkim_quoteprint(sig_hdr, strlen(sig_hdr), 1);
+ pdkim_quoteprint(sig_hdr, strlen(sig_hdr));
debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
/* Finalize header hash */
-#ifdef SHA_GNUTLS
- gnutls_hash(sha_headers, sig_hdr, strlen(sig_hdr));
- gnutls_hash_output(sha_headers, headerhash);
-#else
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-# ifdef SHA_OPENSSL
- {
- SHA1_Update(&sha1_headers, sig_hdr, strlen(sig_hdr));
- SHA1_Final(US headerhash, &sha1_headers);
- }
- else
- {
- SHA256_Update(&sha2_headers, sig_hdr, strlen(sig_hdr));
- SHA256_Final(US headerhash, &sha2_headers);
- }
-# elif defined(SHA_POLARSSL)
- {
- sha1_update(&sha1_headers, US sig_hdr, strlen(sig_hdr));
- sha1_finish(&sha1_headers, US headerhash);
- }
- else
- {
- sha2_update(&sha2_headers, US sig_hdr, strlen(sig_hdr));
- sha2_finish(&sha2_headers, US headerhash);
- }
-# endif
-#endif
+ exim_sha_update(&hhash_ctx, sig_hdr, strlen(sig_hdr));
+ exim_sha_finish(&hhash_ctx, &hhash);
DEBUG(D_acl)
{
debug_printf("PDKIM [%s] hh computed: ", sig->domain);
- pdkim_hexprint(headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32, 1);
+ pdkim_hexprint(hhash.data, hhash.len);
}
-#ifdef RSA_GNUTLS
+ /* Remember headers block for signing (when the library cannot do incremental) */
if (ctx->mode == PDKIM_MODE_SIGN)
- hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, sig_hdr);
-#endif
+ (void) exim_rsa_data_append(&hdata, &hdata_alloc, sig_hdr);
free(sig_hdr);
/* SIGNING ---------------------------------------------------------------- */
if (ctx->mode == PDKIM_MODE_SIGN)
{
-#ifdef RSA_OPENSSL
- RSA * rsa;
- uschar * p, * q;
- int len;
-#elif defined(RSA_GNUTLS)
- gnutls_x509_privkey_t rsa;
- gnutls_datum_t k;
- int rc;
- size_t sigsize;
-#endif
+ es_ctx sctx;
+ const uschar * errstr;
/* Import private key */
-#ifdef RSA_OPENSSL
-
- if ( !(p = Ustrstr(sig->rsa_privkey, "-----BEGIN RSA PRIVATE KEY-----"))
- || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----"))
- )
- return PDKIM_SIGN_PRIVKEY_WRAP;
- *q = '\0';
- if ((len = b64decode(p, &p)) < 0)
- {
- DEBUG(D_acl) debug_printf("b64decode failed\n");
- return PDKIM_SIGN_PRIVKEY_B64D;
- }
- if (!(rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len)))
- {
- DEBUG(D_acl)
- {
- char ssl_errstring[256];
- ERR_error_string(ERR_get_error(), ssl_errstring);
- debug_printf("d2i_RSAPrivateKey: %s\n", ssl_errstring);
- }
- return PDKIM_ERR_RSA_PRIVKEY;
- }
-
-#elif defined(RSA_GNUTLS)
-
- k.data = sig->rsa_privkey;
- k.size = strlen(sig->rsa_privkey);
- if ( (rc = gnutls_x509_privkey_init(&rsa)) != GNUTLS_E_SUCCESS
- || (rc = gnutls_x509_privkey_import2(rsa, &k,
- GNUTLS_X509_FMT_PEM, NULL, GNUTLS_PKCS_PLAIN)) != GNUTLS_E_SUCCESS
- )
+ if ((errstr = exim_rsa_signing_init(sig->rsa_privkey, &sctx)))
{
- DEBUG(D_acl) debug_printf("gnutls_x509_privkey_import2: %s\n",
- gnutls_strerror(rc));
+ DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
return PDKIM_ERR_RSA_PRIVKEY;
}
-#endif
-
+ /* Do signing. With OpenSSL we are signing the hash of headers just
+ calculated, with GnuTLS we have to sign an entire block of headers
+ (due to available interfaces) and it recalculates the hash internally. */
- /* Allocate mem for signature */
-#ifdef RSA_OPENSSL
- sig->sigdata = store_get(RSA_size(rsa));
-#elif defined(RSA_GNUTLS)
- k.data = hdata;
- k.size = hdata_size;
- (void) gnutls_x509_privkey_sign_data(rsa,
- sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
- 0, &k, NULL, &sigsize);
- sig->sigdata = store_get(sig->sigdata_len = sigsize);
+#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
+ hdata = hhash;
#endif
- /* Do signing */
-#ifdef RSA_OPENSSL
-
- if (RSA_sign(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256,
- CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32,
- US sig->sigdata, (unsigned int *)&sig->sigdata_len,
- rsa) != 1)
- return PDKIM_ERR_RSA_SIGNING;
- RSA_free(rsa);
-
-#elif defined(RSA_GNUTLS)
-
- if ((rc = gnutls_x509_privkey_sign_data(rsa,
- sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
- 0, &k, sig->sigdata, &sigsize)) != GNUTLS_E_SUCCESS
- )
+ if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
{
- DEBUG(D_acl) debug_printf("gnutls_x509_privkey_sign_data: %s\n",
- gnutls_strerror(rc));
+ DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
return PDKIM_ERR_RSA_SIGNING;
}
- gnutls_x509_privkey_deinit(rsa);
-
-#endif
-
DEBUG(D_acl)
{
debug_printf( "PDKIM [%s] b computed: ", sig->domain);
- pdkim_hexprint(sig->sigdata, sig->sigdata_len, 1);
+ pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
}
- if (!(sig->signature_header = pdkim_create_header(ctx->sig, 1)))
+ if (!(sig->signature_header = pdkim_create_header(ctx->sig, TRUE)))
return PDKIM_ERR_OOM;
}
/* VERIFICATION ----------------------------------------------------------- */
else
{
-#ifdef RSA_OPENSSL
- RSA * rsa;
- const unsigned char * p;
-#elif defined(RSA_GNUTLS)
- gnutls_pubkey_t rsa;
- gnutls_datum_t k, s;
- int rc;
-#endif
- char *dns_txt_name, *dns_txt_reply;
+ ev_ctx vctx;
+ const uschar * errstr;
-#ifdef RSA_GNUTLS
- gnutls_pubkey_init(&rsa);
-#endif
+ char *dns_txt_name, *dns_txt_reply;
/* Fetch public key for signing domain, from DNS */
@@ -2016,9 +1787,9 @@ while (sig)
DEBUG(D_acl)
{
debug_printf(
- "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
- " Raw record: ");
- pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply), 1);
+ "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
+ " Raw record: ");
+ pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply));
}
if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply)))
@@ -2033,69 +1804,27 @@ while (sig)
}
DEBUG(D_acl) debug_printf(
- "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
/* Import public key */
-#ifdef RSA_OPENSSL
-
- p = CUS sig->pubkey->key;
- if (!(rsa = d2i_RSA_PUBKEY(NULL, &p, (long) sig->pubkey->key_len)))
-
-#elif defined(RSA_GNUTLS)
-
- k.data = sig->pubkey->key;
- k.size = sig->pubkey->key_len;
- if ((rc = gnutls_pubkey_import(rsa, &k, GNUTLS_X509_FMT_DER))
- != GNUTLS_E_SUCCESS)
-
-#endif
+ if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx)))
{
- DEBUG(D_acl)
- {
-#ifdef RSA_OPENSSL
- long e;
- ERR_load_crypto_strings(); /*XXX move to a startup routine */
- while ((e = ERR_get_error()))
- debug_printf("Az: %.120s\n", ERR_error_string(e, NULL));
-#elif defined(RSA_GNUTLS)
- debug_printf("gnutls_pubkey_import: %s\n", gnutls_strerror(rc));
-#endif
- }
-
+ DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
sig->verify_status = PDKIM_VERIFY_INVALID;
sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
goto NEXT_VERIFY;
}
/* Check the signature */
-#ifdef RSA_OPENSSL
-
- if (RSA_verify(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256,
- CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32,
- US sig->sigdata, (unsigned int)sig->sigdata_len,
- rsa) != 1)
-
-#elif defined(RSA_GNUTLS)
-
- k.data = headerhash;
- k.size = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32;
- s.data = sig->sigdata;
- s.size = sig->sigdata_len;
- if ((rc = gnutls_pubkey_verify_hash2(rsa,
- sig->algo == PDKIM_ALGO_RSA_SHA1
- ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
- 0, &k, &s)) < 0)
-
-#endif
+ if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
{
-#if defined(RSA_GNUTLS)
- debug_printf("gnutls_pubkey_verify_hash2: %s\n", gnutls_strerror(rc));
-#endif
+ DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
sig->verify_status = PDKIM_VERIFY_FAIL;
sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
goto NEXT_VERIFY;
}
+
/* We have a winner! (if bodydhash was correct earlier) */
if (sig->verify_status == PDKIM_VERIFY_NONE)
sig->verify_status = PDKIM_VERIFY_PASS;
@@ -2113,9 +1842,6 @@ NEXT_VERIFY:
debug_printf("\n");
}
-#ifdef RSA_GNUTLS
- gnutls_pubkey_deinit(rsa);
-#endif
free(dns_txt_name);
free(dns_txt_reply);
}
@@ -2189,33 +1915,15 @@ sig->bodylength = -1;
ctx->mode = PDKIM_MODE_SIGN;
ctx->sig = sig;
-ctx->sig->domain = strdup(domain);
-ctx->sig->selector = strdup(selector);
-ctx->sig->rsa_privkey = strdup(rsa_privkey);
-ctx->sig->algo = algo;
-
-if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey)
- goto BAIL;
-
-#ifdef SHA_OPENSSL
-SHA1_Init (&ctx->sig->sha1_body);
-SHA256_Init(&ctx->sig->sha2_body);
-
-#elif defined(SHA_GNUTLS)
-gnutls_hash_init(&ctx->sig->sha_body,
- algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
-
-#elif defined(SHA_POLARSSL)
-if (!(ctx->sig->sha1_body = malloc(sizeof(sha1_context))))
- goto BAIL;
-sha1_starts(ctx->sig->sha1_body);
+sig->domain = strdup(domain);
+sig->selector = strdup(selector);
+sig->rsa_privkey = strdup(rsa_privkey);
+sig->algo = algo;
-if (!(ctx->sig->sha2_body = malloc(sizeof(sha2_context))))
+if (!sig->domain || !sig->selector || !sig->rsa_privkey)
goto BAIL;
-sha2_starts(ctx->sig->sha2_body, 0);
-
-#endif
+exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1);
return ctx;
BAIL:
@@ -2254,4 +1962,12 @@ return PDKIM_OK;
}
+void
+pdkim_init(void)
+{
+exim_rsa_init();
+}
+
+
+
#endif /*DISABLE_DKIM*/
diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h
index 313afd503..4de0a7ad6 100644
--- a/src/src/pdkim/pdkim.h
+++ b/src/src/pdkim/pdkim.h
@@ -19,6 +19,11 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#ifndef PDKIM_H
+#define PDKIM_H
+
+#include "blob.h"
+#include "hash.h"
/* -------------------------------------------------------------------------- */
/* Length of the preallocated buffer for the "answer" from the dns/txt
@@ -98,8 +103,7 @@ typedef struct pdkim_pubkey {
char *srvtype; /* s= */
char *notes; /* n= */
- char *key; /* p= */
- int key_len;
+ blob key; /* p= */
int testing; /* t=y */
int no_subdomaining; /* t=s */
@@ -151,18 +155,16 @@ typedef struct pdkim_signature {
/* (h=) Colon-separated list of header names that are included in the
signature */
- char *headernames;
+ uschar *headernames;
/* (z=) */
char *copiedheaders;
/* (b=) Raw signature data, along with its length in bytes */
- char *sigdata;
- int sigdata_len;
+ blob sigdata;
/* (bh=) Raw body hash data, along with its length in bytes */
- char *bodyhash;
- int bodyhash_len;
+ blob bodyhash;
/* Folded DKIM-Signature: header. Singing only, NULL for verifying.
Ready for insertion into the message. Note: Folded using CRLFTB,
@@ -228,15 +230,8 @@ typedef struct pdkim_signature {
/* Properties below this point are used internally only ------------- */
/* Per-signature helper variables ----------------------------------- */
-#ifdef SHA_OPENSSL
- SHA_CTX sha1_body; /* SHA1 block */
- SHA256_CTX sha2_body; /* SHA256 block */
-#elif defined(SHA_GNUTLS)
- gnutls_hash_hd_t sha_body; /* Either SHA1 or SHA256 block */
-#elif defined(SHA_POLARSSL)
- sha1_context *sha1_body; /* SHA1 block */
- sha2_context *sha2_body; /* SHA256 block */
-#endif
+ hctx body_hash;
+
unsigned long signed_body_bytes; /* How many body bytes we hashed */
pdkim_stringlist *headers; /* Raw headers included in the sig */
/* Signing specific ------------------------------------------------- */
@@ -283,6 +278,8 @@ typedef struct pdkim_ctx {
extern "C" {
#endif
+void pdkim_init (void);
+
DLLEXPORT
pdkim_ctx *pdkim_init_sign (char *, char *, char *, int);
@@ -306,3 +303,5 @@ void pdkim_free_ctx (pdkim_ctx *);
#ifdef __cplusplus
}
#endif
+
+#endif
diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c
new file mode 100644
index 000000000..c5d4c2efa
--- /dev/null
+++ b/src/src/pdkim/rsa.c
@@ -0,0 +1,679 @@
+/*
+ * PDKIM - a RFC4871 (DKIM) implementation
+ *
+ * Copyright (C) 2016 Exim maintainers
+ *
+ * RSA signing/verification interface
+ */
+
+#include "../exim.h"
+
+#ifndef DISABLE_DKIM /* entire file */
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+#include "rsa.h"
+
+
+/******************************************************************************/
+#ifdef RSA_GNUTLS
+
+void
+exim_rsa_init(void)
+{
+}
+
+
+/* accumulate data (gnutls-only). String to be appended must be nul-terminated. */
+blob *
+exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+{
+int len = b->len;
+b->data = string_append(b->data, alloc, &len, 1, s);
+b->len = len;
+return b;
+}
+
+
+
+/* import private key from PEM string in memory.
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+{
+gnutls_datum_t k;
+int rc;
+
+k.data = privkey_pem;
+k.size = strlen(privkey_pem);
+
+if ( (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
+ /*|| (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
+ GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS */
+ )
+ return gnutls_strerror(rc);
+
+if ( /* (rc = gnutls_x509_privkey_init(&sign_ctx->rsa)) != GNUTLS_E_SUCCESS
+ ||*/ (rc = gnutls_x509_privkey_import(sign_ctx->rsa, &k,
+ GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
+ )
+ return gnutls_strerror(rc);
+
+return NULL;
+}
+
+
+
+/* allocate mem for signature (when signing) */
+/* sign data (gnutls_only)
+OR
+sign hash.
+
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+{
+gnutls_datum_t k;
+size_t sigsize = 0;
+int rc;
+const uschar * ret = NULL;
+
+/* Allocate mem for signature */
+k.data = data->data;
+k.size = data->len;
+(void) gnutls_x509_privkey_sign_data(sign_ctx->rsa,
+ is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+ 0, &k, NULL, &sigsize);
+
+sig->data = store_get(sigsize);
+sig->len = sigsize;
+
+/* Do signing */
+if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->rsa,
+ is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+ 0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
+ )
+ ret = gnutls_strerror(rc);
+
+gnutls_x509_privkey_deinit(sign_ctx->rsa);
+return ret;
+}
+
+
+
+/* import public key (from DER in memory)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+{
+gnutls_datum_t k;
+int rc;
+const uschar * ret = NULL;
+
+gnutls_pubkey_init(&verify_ctx->rsa);
+
+k.data = pubkey_der->data;
+k.size = pubkey_der->len;
+
+if ((rc = gnutls_pubkey_import(verify_ctx->rsa, &k, GNUTLS_X509_FMT_DER))
+ != GNUTLS_E_SUCCESS)
+ ret = gnutls_strerror(rc);
+return ret;
+}
+
+
+/* verify signature (of hash) (given pubkey & alleged sig)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+{
+gnutls_datum_t k, s;
+int rc;
+const uschar * ret = NULL;
+
+k.data = data_hash->data;
+k.size = data_hash->len;
+s.data = sig->data;
+s.size = sig->len;
+if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->rsa,
+ is_sha1 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
+ 0, &k, &s)) < 0)
+ ret = gnutls_strerror(rc);
+
+gnutls_pubkey_deinit(verify_ctx->rsa);
+return ret;
+}
+
+
+
+
+#elif defined(RSA_GCRYPT)
+/******************************************************************************/
+
+
+/* Internal service routine:
+Read and move past an asn.1 header, checking class & tag,
+optionally returning the data-length */
+
+static int
+as_tag(blob * der, uschar req_cls, long req_tag, long * alen)
+{
+int rc;
+uschar tag_class;
+int taglen;
+long tag, len;
+
+/* debug_printf("as_tag: %02x %02x %02x %02x\n",
+ der->data[0], der->data[1], der->data[2], der->data[3]); */
+
+if ((rc = asn1_get_tag_der(der->data++, der->len--, &tag_class, &taglen, &tag))
+ != ASN1_SUCCESS)
+ return rc;
+
+if (tag_class != req_cls || tag != req_tag) return ASN1_ELEMENT_NOT_FOUND;
+
+if ((len = asn1_get_length_der(der->data, der->len, &taglen)) < 0)
+ return ASN1_DER_ERROR;
+if (alen) *alen = len;
+
+/* debug_printf("as_tag: tlen %d dlen %d\n", taglen, (int)len); */
+
+der->data += taglen;
+der->len -= taglen;
+return rc;
+}
+
+/* Internal service routine:
+Read and move over an asn.1 integer, setting an MPI to the value
+*/
+
+static uschar *
+as_mpi(blob * der, gcry_mpi_t * mpi)
+{
+long alen;
+int rc;
+gcry_error_t gerr;
+
+/* integer; move past the header */
+if ((rc = as_tag(der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
+ return US asn1_strerror(rc);
+
+/* read to an MPI */
+if ((gerr = gcry_mpi_scan(mpi, GCRYMPI_FMT_STD, der->data, alen, NULL)))
+ return US gcry_strerror(gerr);
+
+/* move over the data */
+der->data += alen; der->len -= alen;
+return NULL;
+}
+
+
+
+void
+exim_rsa_init(void)
+{
+/* Version check should be the very first call because it
+makes sure that important subsystems are initialized. */
+if (!gcry_check_version (GCRYPT_VERSION))
+ {
+ fputs ("libgcrypt version mismatch\n", stderr);
+ exit (2);
+ }
+
+/* We don't want to see any warnings, e.g. because we have not yet
+parsed program options which might be used to suppress such
+warnings. */
+gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+
+/* ... If required, other initialization goes here. Note that the
+process might still be running with increased privileges and that
+the secure memory has not been initialized. */
+
+/* Allocate a pool of 16k secure memory. This make the secure memory
+available and also drops privileges where needed. */
+gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+
+/* It is now okay to let Libgcrypt complain when there was/is
+a problem with the secure memory. */
+gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+
+/* ... If required, other initialization goes here. */
+
+/* Tell Libgcrypt that initialization has completed. */
+gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+return;
+}
+
+
+
+
+/* Accumulate data (gnutls-only).
+String to be appended must be nul-terminated. */
+
+blob *
+exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+{
+return b; /*dummy*/
+}
+
+
+
+/* import private key from PEM string in memory.
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+{
+uschar * s1, * s2;
+blob der;
+long alen;
+int rc;
+
+/*
+ * 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 ( !(s1 = Ustrstr(CS privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
+ || !(s2 = Ustrstr(CS (s1+=31), "-----END RSA PRIVATE KEY-----" ))
+ )
+ return US"Bad PEM wrapper";
+
+*s2 = '\0';
+
+if ((der.len = b64decode(s1, &der.data)) < 0)
+ return US"Bad PEM-DER b64 decode";
+
+/* untangle asn.1 */
+
+/* sequence; just move past the header */
+if ((rc = as_tag(&der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
+ != ASN1_SUCCESS) goto asn_err;
+
+/* integer version; move past the header, check is zero */
+if ((rc = as_tag(&der, 0, ASN1_TAG_INTEGER, &alen)) != ASN1_SUCCESS)
+ goto asn_err;
+if (alen != 1 || *der.data != 0)
+ return US"Bad version number";
+der.data++; der.len--;
+
+if ( (s1 = as_mpi(&der, &sign_ctx->n))
+ || (s1 = as_mpi(&der, &sign_ctx->e))
+ || (s1 = as_mpi(&der, &sign_ctx->d))
+ || (s1 = as_mpi(&der, &sign_ctx->p))
+ || (s1 = as_mpi(&der, &sign_ctx->q))
+ || (s1 = as_mpi(&der, &sign_ctx->dp))
+ || (s1 = as_mpi(&der, &sign_ctx->dq))
+ || (s1 = as_mpi(&der, &sign_ctx->qp))
+ )
+ return s1;
+
+DEBUG(D_acl) debug_printf("rsa_signing_init:\n");
+ {
+ uschar * s;
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->n);
+ debug_printf(" N : %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->e);
+ debug_printf(" E : %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->d);
+ debug_printf(" D : %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->p);
+ debug_printf(" P : %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->q);
+ debug_printf(" Q : %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dp);
+ debug_printf(" DP: %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->dq);
+ debug_printf(" DQ: %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, sign_ctx->qp);
+ debug_printf(" QP: %s\n", s);
+ }
+return NULL;
+
+asn_err: return US asn1_strerror(rc);
+}
+
+
+
+/* allocate mem for signature (when signing) */
+/* sign data (gnutls_only)
+OR
+sign hash.
+
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+{
+gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
+gcry_mpi_t m_sig;
+uschar * errstr;
+gcry_error_t gerr;
+
+#define SIGSPACE 128
+sig->data = store_get(SIGSPACE);
+
+if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0)
+ {
+ gcry_mpi_swap (sign_ctx->p, sign_ctx->q);
+ gcry_mpi_invm (sign_ctx->qp, sign_ctx->p, sign_ctx->q);
+ }
+
+if ( (gerr = gcry_sexp_build (&s_key, NULL,
+ "(private-key (rsa (n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+ sign_ctx->n, sign_ctx->e,
+ sign_ctx->d, sign_ctx->p,
+ sign_ctx->q, sign_ctx->qp))
+ || (gerr = gcry_sexp_build (&s_hash, NULL,
+ is_sha1
+ ? "(data(flags pkcs1)(hash sha1 %b))"
+ : "(data(flags pkcs1)(hash sha256 %b))",
+ (int) data->len, CS data->data))
+ || (gerr = gcry_pk_sign (&s_sig, s_hash, s_key))
+ )
+ return US gcry_strerror(gerr);
+
+/* gcry_sexp_dump(s_sig); */
+
+if ( !(s_sig = gcry_sexp_find_token(s_sig, "s", 0))
+ )
+ return US"no sig result";
+
+m_sig = gcry_sexp_nth_mpi(s_sig, 1, GCRYMPI_FMT_USG);
+
+DEBUG(D_acl)
+ {
+ uschar * s;
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, m_sig);
+ debug_printf(" SG: %s\n", s);
+ }
+
+gerr = gcry_mpi_print(GCRYMPI_FMT_USG, sig->data, SIGSPACE, &sig->len, m_sig);
+if (gerr)
+ {
+ debug_printf("signature conversion from MPI to buffer failed\n");
+ return US gcry_strerror(gerr);
+ }
+#undef SIGSPACE
+
+return NULL;
+}
+
+
+/* import public key (from DER in memory)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+{
+/*
+in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi()
+*/
+uschar tag_class;
+int taglen;
+long alen;
+int rc;
+uschar * errstr;
+gcry_error_t gerr;
+uschar * stage = US"S1";
+
+/*
+sequence
+ sequence
+ OBJECT:rsaEncryption
+ NULL
+ BIT STRING:RSAPublicKey
+ sequence
+ INTEGER:Public modulus
+ INTEGER:Public exponent
+
+openssl rsa -in aux-fixed/dkim/dkim.private -pubout -outform DER | od -t x1 | head;
+openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump;
+openssl rsa -in aux-fixed/dkim/dkim.private -pubout | openssl asn1parse -dump -offset 22;
+*/
+
+/* sequence; just move past the header */
+if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
+ != ASN1_SUCCESS) goto asn_err;
+
+/* sequence; skip the entire thing */
+DEBUG(D_acl) stage = US"S2";
+if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, &alen))
+ != ASN1_SUCCESS) goto asn_err;
+pubkey_der->data += alen; pubkey_der->len -= alen;
+
+
+/* bitstring: limit range to size of bitstring;
+move over header + content wrapper */
+DEBUG(D_acl) stage = US"BS";
+if ((rc = as_tag(pubkey_der, 0, ASN1_TAG_BIT_STRING, &alen)) != ASN1_SUCCESS)
+ goto asn_err;
+pubkey_der->len = alen;
+pubkey_der->data++; pubkey_der->len--;
+
+/* sequence; just move past the header */
+DEBUG(D_acl) stage = US"S3";
+if ((rc = as_tag(pubkey_der, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL))
+ != ASN1_SUCCESS) goto asn_err;
+
+/* read two integers */
+DEBUG(D_acl) stage = US"MPI";
+if ( (errstr = as_mpi(pubkey_der, &verify_ctx->n))
+ || (errstr = as_mpi(pubkey_der, &verify_ctx->e))
+ )
+ return errstr;
+
+DEBUG(D_acl) debug_printf("rsa_verify_init:\n");
+ {
+ uschar * s;
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->n);
+ debug_printf(" N : %s\n", s);
+ gcry_mpi_aprint (GCRYMPI_FMT_HEX, &s, NULL, verify_ctx->e);
+ debug_printf(" E : %s\n", s);
+ }
+
+return NULL;
+
+asn_err:
+DEBUG(D_acl) return string_sprintf("%s: %s", stage, asn1_strerror(rc));
+ return US asn1_strerror(rc);
+}
+
+
+/* verify signature (of hash) (given pubkey & alleged sig)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+{
+/*
+cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
+*/
+gcry_mpi_t m_sig;
+gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL;
+gcry_error_t gerr;
+uschar * stage;
+
+if ( (stage = US"pkey sexp build",
+ gerr = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))",
+ verify_ctx->n, verify_ctx->e))
+ || (stage = US"data sexp build",
+ gerr = gcry_sexp_build (&s_hash, NULL,
+ is_sha1
+ ? "(data(flags pkcs1)(hash sha1 %b))"
+ : "(data(flags pkcs1)(hash sha256 %b))",
+ (int) data_hash->len, CS data_hash->data))
+ || (stage = US"sig mpi scan",
+ gerr = gcry_mpi_scan(&m_sig, GCRYMPI_FMT_USG, sig->data, sig->len, NULL))
+ || (stage = US"sig sexp build",
+ gerr = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", m_sig))
+ || (stage = US"verify",
+ gerr = gcry_pk_verify (s_sig, s_hash, s_pkey))
+ )
+ {
+ DEBUG(D_acl) debug_printf("verify: error in stage '%s'\n", stage);
+ return US gcry_strerror(gerr);
+ }
+
+if (s_sig) gcry_sexp_release (s_sig);
+if (s_hash) gcry_sexp_release (s_hash);
+if (s_pkey) gcry_sexp_release (s_pkey);
+gcry_mpi_release (m_sig);
+gcry_mpi_release (verify_ctx->n);
+gcry_mpi_release (verify_ctx->e);
+
+return NULL;
+}
+
+
+
+
+#elif defined(RSA_OPENSSL)
+/******************************************************************************/
+
+void
+exim_rsa_init(void)
+{
+}
+
+
+/* accumulate data (gnutls-only) */
+blob *
+exim_rsa_data_append(blob * b, int * alloc, uschar * s)
+{
+return b; /*dummy*/
+}
+
+
+/* import private key from PEM string in memory.
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
+{
+uschar * p, * q;
+int len;
+
+/* Convert PEM to DER */
+if ( !(p = Ustrstr(privkey_pem, "-----BEGIN RSA PRIVATE KEY-----"))
+ || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----"))
+ )
+ return US"Bad PEM wrapping";
+
+*q = '\0';
+if ((len = b64decode(p, &p)) < 0)
+ return US"b64decode failed";
+
+if (!(sign_ctx->rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len)))
+ {
+ char ssl_errstring[256];
+ ERR_load_crypto_strings(); /*XXX move to a startup routine */
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ return string_copy(ssl_errstring);
+ }
+
+return NULL;
+}
+
+
+
+/* allocate mem for signature (when signing) */
+/* sign data (gnutls_only)
+OR
+sign hash.
+
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+{
+uint len;
+const uschar * ret = NULL;
+
+/* Allocate mem for signature */
+len = RSA_size(sign_ctx->rsa);
+sig->data = store_get(len);
+sig->len = len;
+
+/* Do signing */
+if (RSA_sign(is_sha1 ? NID_sha1 : NID_sha256,
+ CUS data->data, data->len,
+ US sig->data, &len, sign_ctx->rsa) != 1)
+ {
+ char ssl_errstring[256];
+ ERR_load_crypto_strings(); /*XXX move to a startup routine */
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ ret = string_copy(ssl_errstring);
+ }
+
+RSA_free(sign_ctx->rsa);
+return ret;;
+}
+
+
+
+/* import public key (from DER in memory)
+Return: nULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
+{
+const uschar * p = CUS pubkey_der->data;
+const uschar * ret = NULL;
+
+if (!(verify_ctx->rsa = d2i_RSA_PUBKEY(NULL, &p, (long) pubkey_der->len)))
+ {
+ char ssl_errstring[256];
+ ERR_load_crypto_strings(); /*XXX move to a startup routine */
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ ret = string_copy(ssl_errstring);
+ }
+return ret;
+}
+
+
+
+
+/* verify signature (of hash) (given pubkey & alleged sig)
+Return: NULL for success, or an error string */
+
+const uschar *
+exim_rsa_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+{
+const uschar * ret = NULL;
+
+if (RSA_verify(is_sha1 ? NID_sha1 : NID_sha256,
+ CUS data_hash->data, data_hash->len,
+ US sig->data, (uint) sig->len, verify_ctx->rsa) != 1)
+ {
+ char ssl_errstring[256];
+ ERR_load_crypto_strings(); /*XXX move to a startup routine */
+ ERR_error_string(ERR_get_error(), ssl_errstring);
+ ret = string_copy(ssl_errstring);
+ }
+return ret;
+}
+
+
+#endif
+/******************************************************************************/
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/rsa.h
new file mode 100644
index 000000000..32631fdac
--- /dev/null
+++ b/src/src/pdkim/rsa.h
@@ -0,0 +1,81 @@
+/*
+ * PDKIM - a RFC4871 (DKIM) implementation
+ *
+ * Copyright (C) 2016 Exim maintainers
+ *
+ * RSA signing/verification interface
+ */
+
+#include "../exim.h"
+
+#ifndef DISABLE_DKIM /* entire file */
+
+#include "crypt_ver.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+# include <gnutls/abstract.h>
+#elif defined(RSA_GCRYPT)
+# include <gcrypt.h>
+# include <libtasn1.h>
+#endif
+
+#include "blob.h"
+
+
+#ifdef RSA_OPENSSL
+
+typedef struct {
+ RSA * rsa;
+} es_ctx;
+
+typedef struct {
+ RSA * rsa;
+} ev_ctx;
+
+#elif defined(RSA_GNUTLS)
+
+typedef struct {
+ gnutls_x509_privkey_t rsa;
+} es_ctx;
+
+typedef struct {
+ gnutls_pubkey_t rsa;
+} ev_ctx;
+
+#elif defined(RSA_GCRYPT)
+
+typedef struct {
+ gcry_mpi_t n;
+ gcry_mpi_t e;
+ gcry_mpi_t d;
+ gcry_mpi_t p;
+ gcry_mpi_t q;
+ gcry_mpi_t dp;
+ gcry_mpi_t dq;
+ gcry_mpi_t qp;
+} es_ctx;
+
+typedef struct {
+ gcry_mpi_t n;
+ gcry_mpi_t e;
+} ev_ctx;
+
+#endif
+
+
+extern void exim_rsa_init(void);
+extern blob * exim_rsa_data_append(blob *, int *, uschar *);
+
+extern const uschar * exim_rsa_signing_init(uschar *, es_ctx *);
+extern const uschar * exim_rsa_sign(es_ctx *, BOOL, blob *, blob *);
+extern const uschar * exim_rsa_verify_init(blob *, ev_ctx *);
+extern const uschar * exim_rsa_verify(ev_ctx *, BOOL, blob *, blob *);
+
+#endif /*DISABLE_DKIM*/
+/* End of File */
diff --git a/src/src/pdkim/sha1.c b/src/src/pdkim/sha1.c
deleted file mode 100644
index 96f4e7b57..000000000
--- a/src/src/pdkim/sha1.c
+++ /dev/null
@@ -1,323 +0,0 @@
-#include "crypt_ver.h"
-
-#ifdef SHA_POLARSSL /* remainder of file */
-
-/*
- * FIPS-180-1 compliant SHA-1 implementation
- *
- * Copyright (C) 2006-2010, Brainspark B.V.
- *
- * This file is part of PolarSSL (http://www.polarssl.org)
- * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
- *
- * All rights reserved.
- *
- * 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
- */
-
-#include "polarssl/config.h"
-
-#if defined(POLARSSL_SHA1_C)
-
-#include "polarssl/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
- * Called from pdkim_parse_sig_header() pdkim_feed_finish() pdkim_init_sign()
- */
-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, const 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
- * Called from pdkim_feed_finish() & pdkim_finish_bodyhash()
- */
-void sha1_update( sha1_context *ctx, const 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
- * Called from pdkim_feed_finish() & pdkim_finish_bodyhash()
- */
-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 );
-}
-
-#endif
-#endif
diff --git a/src/src/pdkim/sha2.c b/src/src/pdkim/sha2.c
deleted file mode 100644
index 6ab6cb906..000000000
--- a/src/src/pdkim/sha2.c
+++ /dev/null
@@ -1,453 +0,0 @@
-#include "crypt_ver.h"
-
-#ifdef SHA_POLARSSL /* remainder of file */
-
-/*
- * FIPS-180-2 compliant SHA-256 implementation
- *
- * Copyright (C) 2006-2010, Brainspark B.V.
- *
- * This file is part of PolarSSL (http://www.polarssl.org)
- * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
- *
- * All rights reserved.
- *
- * 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
- */
-
-#include "polarssl/config.h"
-
-#if defined(POLARSSL_SHA2_C)
-
-#include "polarssl/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, const 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, const 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( const 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( const 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, const 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, const 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 ) );
-}
-
-/*
- * SHA-256 HMAC context reset
- */
-void sha2_hmac_reset( sha2_context *ctx )
-{
- sha2_starts( ctx, ctx->is224 );
- sha2_update( ctx, ctx->ipad, 64 );
-}
-
-/*
- * output = HMAC-SHA-256( hmac key, input buffer )
- */
-void sha2_hmac( const unsigned char *key, int keylen,
- const 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 ) );
-}
-
-
-#endif
-#endif
diff --git a/test/aux-fixed/dkim/sign.pl b/test/aux-fixed/dkim/sign.pl
index 6220015ae..a08f38f56 100644
--- a/test/aux-fixed/dkim/sign.pl
+++ b/test/aux-fixed/dkim/sign.pl
@@ -6,16 +6,18 @@ use Getopt::Long;
my $method = "simple/simple";
my $selector = "sel";
my $keyfile = "aux-fixed/dkim/dkim.private";
+my $algorithm = "rsa-sha1";
GetOptions(
"method=s" => \$method,
"selector=s" => \$selector,
"keyfile=s" => \$keyfile,
+ "algorithm=s" => \$algorithm,
);
# create a signer object
my $dkim = Mail::DKIM::Signer->new(
- Algorithm => "rsa-sha1",
+ Algorithm => $algorithm,
Method => $method,
Domain => "test.ex",
Selector => $selector,
diff --git a/test/confs/4503 b/test/confs/4503
index e9f2d5d47..ddd87d0fa 100644
--- a/test/confs/4503
+++ b/test/confs/4503
@@ -43,5 +43,6 @@ send_to_server:
dkim_domain = test.ex
dkim_selector = sel
dkim_private_key = DIR/aux-fixed/dkim/dkim.private
+ dkim_sign_headers = From
# End
diff --git a/test/log/4500 b/test/log/4500
index 4787e6423..0e0f8400d 100644
--- a/test/log/4500
+++ b/test/log/4500
@@ -7,3 +7,6 @@
1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=ses c=simple/simple a=rsa-sha1 b=512 [verification succeeded]
1999-03-02 09:44:33 10HmaY-0005vi-00 signer: test.ex bits: 512
1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
+1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=test.ex s=sel c=simple/simple a=rsa-sha256 b=1024 [verification succeeded]
+1999-03-02 09:44:33 10HmaZ-0005vi-00 signer: test.ex bits: 1024
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
diff --git a/test/log/4503 b/test/log/4503
index 056b52946..39b918a2c 100644
--- a/test/log/4503
+++ b/test/log/4503
@@ -1,4 +1,6 @@
1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]:1225 C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
******** SERVER ********
1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
diff --git a/test/scripts/4500-Domain-Keys-Identified-Mail/4500 b/test/scripts/4500-Domain-Keys-Identified-Mail/4500
index b352893e3..56283992b 100644
--- a/test/scripts/4500-Domain-Keys-Identified-Mail/4500
+++ b/test/scripts/4500-Domain-Keys-Identified-Mail/4500
@@ -4,6 +4,7 @@ exim -DSERVER=server -bd -oX PORT_D
****
#
# This should pass.
+# - sha1, 1024b
# Mail original in aux-fixed/4500.msg1.txt
# Sig generated by: perl aux-fixed/dkim/sign.pl --method=simple/simple < aux-fixed/4500.msg1.txt
client 127.0.0.1 PORT_D
@@ -35,6 +36,7 @@ QUIT
****
#
# This should pass.
+# - sha1, 512b
# Mail original in aux-fixed/4500.msg1.txt
# Sig generated by: perl aux-fixed/dkim/sign.pl --method=simple/simple --selector=ses \
# --keyfile=aux-fixed/dkim/dkim512.private < aux-fixed/4500.msg1.txt
@@ -65,6 +67,39 @@ QUIT
??? 221
****
#
+# This should pass.
+# - sha256, 1024b
+# Mail original in aux-fixed/4500.msg1.txt
+# Sig generated by: perl aux-fixed/dkim/sign.pl --algorithm=rsa-sha256 \
+# --method=simple/simple < aux-fixed/4500.msg1.txt
+client 127.0.0.1 PORT_D
+??? 220
+HELO xxx
+??? 250
+MAIL FROM:<CALLER@bloggs.com>
+??? 250
+RCPT TO:<a@test.ex>
+??? 250
+DATA
+??? 354
+DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=test.ex; h=from:to
+ :date:message-id:subject; s=sel; bh=3UbbJTudPxmejzh7U1Zg33U3QT+1
+ 6kfV2eOTvMeiEis=; b=xQSD/JMqz0C+xKf0A1NTkPTbkDuDdJbpBuyjjT9iYvyP
+ Zez+xl0TkoPobFGVa6EN8+ZeYV18zjifhtWYLSsNmPinUtcpKQLG1zxAKmmS0JEh
+ +qihlWbeGJ5+tK588ugUzXHPj+4JBW0H6kxHvdH0l2SlQE5xs/cdggnx5QX5USY=
+From: mrgus@text.ex
+To: bakawolf@yahoo.com
+Date: Thu, 19 Nov 2015 17:00:07 -0700
+Message-ID: <qwerty1234@disco-zombie.net>
+Subject: simple test
+
+This is a simple test.
+.
+??? 250
+QUIT
+??? 221
+****
+#
#
killdaemon
no_stdout_check
diff --git a/test/scripts/4500-Domain-Keys-Identified-Mail/4503 b/test/scripts/4500-Domain-Keys-Identified-Mail/4503
index 7ca338275..cebc62dc3 100644
--- a/test/scripts/4500-Domain-Keys-Identified-Mail/4503
+++ b/test/scripts/4500-Domain-Keys-Identified-Mail/4503
@@ -3,8 +3,10 @@
exim -bd -DSERVER=server -oX PORT_D
****
exim a@test.ex
+From: nobody@example.com
+
content
****
-millisleep 200
+millisleep 500
killdaemon
no_msglog_check