DKIM: support multiple hash methods
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 12 Sep 2017 16:37:48 +0000 (17:37 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 12 Sep 2017 19:01:05 +0000 (20:01 +0100)
22 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/dkim.c
src/src/expand.c
src/src/hash.c
src/src/hash.h
src/src/pdkim/crypt_ver.h
src/src/pdkim/pdkim.c
src/src/pdkim/pdkim.h
src/src/pdkim/pdkim_hash.h
src/src/pdkim/signing.c
src/src/pdkim/signing.h
src/src/structs.h
src/src/transports/smtp.c
test/confs/4503 [new symlink]
test/confs/4520
test/confs/4523 [new symlink]
test/log/4503 [new file with mode: 0644]
test/log/4523 [new file with mode: 0644]
test/runtest
test/scripts/4500-DKIM/4503 [new file with mode: 0644]
test/scripts/4500-DKIM/4523 [new file with mode: 0644]

index 862c8f91d901ea0d1fc38665cedbbacd669b3e74..61a6f0e83274d4c2ed6bb0e99aa1e6ca8871ef69 100644 (file)
@@ -23799,6 +23799,7 @@ of the message. Its value must not be zero. See also &%final_timeout%&.
 .option dkim_canon smtp string&!! unset
 .option dkim_strict smtp string&!! unset
 .option dkim_sign_headers smtp string&!! unset
 .option dkim_canon smtp string&!! unset
 .option dkim_strict smtp string&!! unset
 .option dkim_sign_headers smtp string&!! unset
+.option dkim_hash smtp string&!! sha256
 DKIM signing options.  For details see section &<<SECDKIMSIGN>>&.
 
 
 DKIM signing options.  For details see section &<<SECDKIMSIGN>>&.
 
 
@@ -38569,6 +38570,12 @@ list of header names. Headers with these names will be included in the message
 signature.
 When unspecified, the header names recommended in RFC4871 will be used.
 
 signature.
 When unspecified, the header names recommended in RFC4871 will be used.
 
+.new
+.option dkim_hash smtp string&!! sha256
+Can be set alternatively to &"sha1"& to use an alternate hash
+method.  Note that sha1 is now condidered insecure, and deprecated.
+.wen
+
 
 .section "Verifying DKIM signatures in incoming mail" "SECID514"
 .cindex "DKIM" "verification"
 
 .section "Verifying DKIM signatures in incoming mail" "SECID514"
 .cindex "DKIM" "verification"
index 8ea1708bd107a2f9caa60c1e2d24e81b2f6cbdf8..c10649edd904ad94fbb128f1083999e6d22dc76b 100644 (file)
@@ -52,6 +52,8 @@ Version 4.90
     is opened with a TFO cookie.  Support varies between platforms
     (Linux does both. FreeBSD server only, others unknown).
 
     is opened with a TFO cookie.  Support varies between platforms
     (Linux does both. FreeBSD server only, others unknown).
 
+13. DKIM support for multiple hashes.
+
 
 Version 4.89
 ------------
 
 Version 4.89
 ------------
index f7b9ee0d18905d7cf9c8d213cdd69718b2c3eef6..2b7f55ae8596ea680f767a2a65a0a046987991fd 100644 (file)
@@ -20,6 +20,12 @@ pdkim_signature *dkim_signatures = NULL;
 pdkim_signature *dkim_cur_sig = NULL;
 static const uschar * dkim_collect_error = NULL;
 
 pdkim_signature *dkim_cur_sig = NULL;
 static const uschar * dkim_collect_error = NULL;
 
+
+
+/*XXX the caller only uses the first record if we return multiple.
+Could we hand back an allocated string?
+*/
+
 static int
 dkim_exim_query_dns_txt(char *name, char *answer)
 {
 static int
 dkim_exim_query_dns_txt(char *name, char *answer)
 {
@@ -164,9 +170,7 @@ for (sig = dkim_signatures; sig; sig = sig->next)
   logmsg = string_append(logmsg, &size, &ptr, 7, 
        " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
        "/",   sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
   logmsg = string_append(logmsg, &size, &ptr, 7, 
        " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
        "/",   sig->canon_body    == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
-       " a=", sig->algo == PDKIM_ALGO_RSA_SHA256
-               ? "rsa-sha256"
-               : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err",
+       " a=", dkim_sig_to_a_tag(sig),
        string_sprintf(" b=%d",
                        (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
   if ((s= sig->identity)) logmsg = string_append(logmsg, &size, &ptr, 2, " i=", s);
        string_sprintf(" b=%d",
                        (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
   if ((s= sig->identity)) logmsg = string_append(logmsg, &size, &ptr, 2, " i=", s);
@@ -338,12 +342,7 @@ if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
 switch (what)
   {
   case DKIM_ALGO:
 switch (what)
   {
   case DKIM_ALGO:
-    switch (dkim_cur_sig->algo)
-      {
-      case PDKIM_ALGO_RSA_SHA1:        return US"rsa-sha1";
-      case PDKIM_ALGO_RSA_SHA256:
-      default:                 return US"rsa-sha256";
-      }
+    return dkim_sig_to_a_tag(dkim_cur_sig);
 
   case DKIM_BODYLENGTH:
     return dkim_cur_sig->bodylength >= 0
 
   case DKIM_BODYLENGTH:
     return dkim_cur_sig->bodylength >= 0
@@ -466,6 +465,7 @@ int seen_items_offset = 0;
 uschar *dkim_canon_expanded;
 uschar *dkim_sign_headers_expanded;
 uschar *dkim_private_key_expanded;
 uschar *dkim_canon_expanded;
 uschar *dkim_sign_headers_expanded;
 uschar *dkim_private_key_expanded;
+uschar *dkim_hash_expanded;
 pdkim_ctx *ctx = NULL;
 uschar *rc = NULL;
 uschar *sigbuf = NULL;
 pdkim_ctx *ctx = NULL;
 uschar *rc = NULL;
 uschar *sigbuf = NULL;
@@ -608,15 +608,20 @@ while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
     dkim_private_key_expanded = big_buffer;
     }
 
     dkim_private_key_expanded = big_buffer;
     }
 
-/*XXX so we currently nail signing to RSA + SHA256.  Need to extract algo
-from privkey, and provide means for selecting hash-method.
-Check for disallowed combos.
-Will need new dkim_ transport option for hash. */
+  if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
+    {
+    log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
+              "dkim_hash: %s", expand_string_message);
+    goto bad;
+    }
+
+/*XXX so we currently nail signing to RSA + given hash.
+Need to extract algo from privkey and check for disallowed combos. */
 
 
-  if (!(ctx = pdkim_init_sign(CS dkim_signing_domain,
-                       CS dkim_signing_selector,
-                       CS dkim_private_key_expanded,
-                       PDKIM_ALGO_RSA_SHA256,
+  if (!(ctx = pdkim_init_sign(dkim_signing_domain,
+                       dkim_signing_selector,
+                       dkim_private_key_expanded,
+                       dkim_hash_expanded,
                        dkim->dot_stuffed,
                        &dkim_exim_query_dns_txt,
                        errstr
                        dkim->dot_stuffed,
                        &dkim_exim_query_dns_txt,
                        errstr
index 83e519a7df7798ea8a1b1deea7b845608e790cb6..c51c1ff1b289a1ea6179dcc2f6653c650b211d60 100644 (file)
@@ -6461,7 +6461,7 @@ while (*s != 0)
          blob b;
          char st[3];
 
          blob b;
          char st[3];
 
-         if (!exim_sha_init(&h, HASH_SHA256))
+         if (!exim_sha_init(&h, HASH_SHA2_256))
            {
            expand_string_message = US"unrecognised sha256 variant";
            goto EXPAND_FAILED;
            {
            expand_string_message = US"unrecognised sha256 variant";
            goto EXPAND_FAILED;
index e239516e1c380f3c5a5f02502271ebfaf58405d2..19ab1efd09b5782bbfa4fa57256d91ec59e6bfdc 100644 (file)
@@ -36,9 +36,11 @@ exim_sha_init(hctx * h, hashmethod m)
 /*XXX extend for sha512 */
 switch (h->method = m)
   {
 /*XXX extend for sha512 */
 switch (h->method = m)
   {
-  case HASH_SHA1:   h->hashlen = 20; SHA1_Init  (&h->u.sha1); break;
-  case HASH_SHA256: h->hashlen = 32; SHA256_Init(&h->u.sha2); break;
-  default:         h->hashlen = 0; return FALSE;
+  case HASH_SHA1:     h->hashlen = 20; SHA1_Init  (&h->u.sha1);     break;
+  case HASH_SHA2_256: h->hashlen = 32; SHA256_Init(&h->u.sha2_256); break;
+  case HASH_SHA2_384: h->hashlen = 48; SHA384_Init(&h->u.sha2_512); break;
+  case HASH_SHA2_512: h->hashlen = 64; SHA512_Init(&h->u.sha2_512); break;
+  default:           h->hashlen = 0; return FALSE;
   }
 return TRUE;
 }
   }
 return TRUE;
 }
@@ -49,8 +51,10 @@ exim_sha_update(hctx * h, const uschar * data, int len)
 {
 switch (h->method)
   {
 {
 switch (h->method)
   {
-  case HASH_SHA1:   SHA1_Update  (&h->u.sha1, data, len); break;
-  case HASH_SHA256: SHA256_Update(&h->u.sha2, data, len); break;
+  case HASH_SHA1:     SHA1_Update  (&h->u.sha1,     data, len); break;
+  case HASH_SHA2_256: SHA256_Update(&h->u.sha2_256, data, len); break;
+  case HASH_SHA2_384: SHA384_Update(&h->u.sha2_512, data, len); break;
+  case HASH_SHA2_512: SHA512_Update(&h->u.sha2_512, data, len); break;
   /* should be blocked by init not handling these, but be explicit to
   guard against accidents later (and hush up clang -Wswitch) */
   default: assert(0);
   /* should be blocked by init not handling these, but be explicit to
   guard against accidents later (and hush up clang -Wswitch) */
   default: assert(0);
@@ -64,8 +68,10 @@ exim_sha_finish(hctx * h, blob * b)
 b->data = store_get(b->len = h->hashlen);
 switch (h->method)
   {
 b->data = store_get(b->len = h->hashlen);
 switch (h->method)
   {
-  case HASH_SHA1:   SHA1_Final  (b->data, &h->u.sha1); break;
-  case HASH_SHA256: SHA256_Final(b->data, &h->u.sha2); break;
+  case HASH_SHA1:     SHA1_Final  (b->data, &h->u.sha1);     break;
+  case HASH_SHA2_256: SHA256_Final(b->data, &h->u.sha2_256); break;
+  case HASH_SHA2_384: SHA384_Final(b->data, &h->u.sha2_512); break;
+  case HASH_SHA2_512: SHA512_Final(b->data, &h->u.sha2_512); break;
   default: assert(0);
   }
 }
   default: assert(0);
   }
 }
@@ -81,10 +87,14 @@ exim_sha_init(hctx * h, hashmethod m)
 /*XXX extend for sha512 */
 switch (h->method = m)
   {
 /*XXX extend for sha512 */
 switch (h->method = m)
   {
-  case HASH_SHA1:     h->hashlen = 20; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA1); break;
-  case HASH_SHA256:   h->hashlen = 32; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA256); break;
+  case HASH_SHA1:     h->hashlen = 20; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA1);   break;
+  case HASH_SHA2_256: h->hashlen = 32; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA256); break;
+  case HASH_SHA2_384: h->hashlen = 48; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA384); break;
+  case HASH_SHA2_512: h->hashlen = 64; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA512); break;
 #ifdef EXIM_HAVE_SHA3
   case HASH_SHA3_256: h->hashlen = 32; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA3_256); break;
 #ifdef EXIM_HAVE_SHA3
   case HASH_SHA3_256: h->hashlen = 32; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA3_256); break;
+  case HASH_SHA3_384: h->hashlen = 48; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA3_384); break;
+  case HASH_SHA3_512: h->hashlen = 64; gnutls_hash_init(&h->sha, GNUTLS_DIG_SHA3_512); break;
 #endif
   default: h->hashlen = 0; return FALSE;
   }
 #endif
   default: h->hashlen = 0; return FALSE;
   }
@@ -117,8 +127,13 @@ exim_sha_init(hctx * h, hashmethod m)
 /*XXX extend for sha512 */
 switch (h->method = m)
   {
 /*XXX extend for sha512 */
 switch (h->method = m)
   {
-  case HASH_SHA1:   h->hashlen = 20; gcry_md_open(&h->sha, GCRY_MD_SHA1, 0); break;
-  case HASH_SHA256: h->hashlen = 32; gcry_md_open(&h->sha, GCRY_MD_SHA256, 0); break;
+  case HASH_SHA1:     h->hashlen = 20; gcry_md_open(&h->sha, GCRY_MD_SHA1, 0);   break;
+  case HASH_SHA2_256: h->hashlen = 32; gcry_md_open(&h->sha, GCRY_MD_SHA256, 0); break;
+  case HASH_SHA2_384: h->hashlen = 48; gcry_md_open(&h->sha, GCRY_MD_SHA384, 0); break;
+  case HASH_SHA2_512: h->hashlen = 64; gcry_md_open(&h->sha, GCRY_MD_SHA512, 0); break;
+  case HASH_SHA3_256: h->hashlen = 32; gcry_md_open(&h->sha, GCRY_MD_SHA3_256, 0); break;
+  case HASH_SHA3_384: h->hashlen = 48; gcry_md_open(&h->sha, GCRY_MD_SHA3_384, 0); break;
+  case HASH_SHA3_512: h->hashlen = 64; gcry_md_open(&h->sha, GCRY_MD_SHA3_512, 0); break;
   default:         h->hashlen = 0; return FALSE;
   }
 return TRUE;
   default:         h->hashlen = 0; return FALSE;
   }
 return TRUE;
@@ -152,7 +167,7 @@ exim_sha_init(hctx * h, hashmethod m)
 switch (h->method = m)
   {
   case HASH_SHA1:   h->hashlen = 20; sha1_starts(&h->u.sha1);    break;
 switch (h->method = m)
   {
   case HASH_SHA1:   h->hashlen = 20; sha1_starts(&h->u.sha1);    break;
-  case HASH_SHA256: h->hashlen = 32; sha2_starts(&h->u.sha2, 0); break;
+  case HASH_SHA2_256: h->hashlen = 32; sha2_starts(&h->u.sha2, 0); break;
   default:         h->hashlen = 0; return FALSE;
   }
 return TRUE;
   default:         h->hashlen = 0; return FALSE;
   }
 return TRUE;
@@ -165,7 +180,7 @@ exim_sha_update(hctx * h, const uschar * data, int len)
 switch (h->method)
   {
   case HASH_SHA1:   sha1_update(h->u.sha1, US data, len); break;
 switch (h->method)
   {
   case HASH_SHA1:   sha1_update(h->u.sha1, US data, len); break;
-  case HASH_SHA256: sha2_update(h->u.sha2, US data, len); break;
+  case HASH_SHA2_256: sha2_update(h->u.sha2, US data, len); break;
   }
 }
 
   }
 }
 
@@ -177,7 +192,7 @@ b->data = store_get(b->len = h->hashlen);
 switch (h->method)
   {
   case HASH_SHA1:   sha1_finish(h->u.sha1, b->data); break;
 switch (h->method)
   {
   case HASH_SHA1:   sha1_finish(h->u.sha1, b->data); break;
-  case HASH_SHA256: sha2_finish(h->u.sha2, b->data); break;
+  case HASH_SHA2_256: sha2_finish(h->u.sha2, b->data); break;
   }
 }
 
   }
 }
 
@@ -421,16 +436,6 @@ native_sha1_end(&h->sha1, NULL, 0, b->data);
 
 
 #endif
 
 
 #endif
-/******************************************************************************/
-
-/* Common to all library versions */
-int
-exim_sha_hashlen(hctx * h)
-{
-return h->method == HASH_SHA1 ? 20
-     : h->method == HASH_SHA256 ? 32
-     : 0;
-}
 
 
 /******************************************************************************/
 
 
 /******************************************************************************/
index 09b65944d393f3ee6e392edc7b539062d41e1058..b745e6218f86b2c81d7d738a2cbf26cce3684a67 100644 (file)
 typedef enum hashmethod {
   HASH_BADTYPE,
   HASH_SHA1,
 typedef enum hashmethod {
   HASH_BADTYPE,
   HASH_SHA1,
-  HASH_SHA256,
+
+  HASH_SHA2_256,
+  HASH_SHA2_384,
+  HASH_SHA2_512,
+
   HASH_SHA3_224,
   HASH_SHA3_256,
   HASH_SHA3_384,
   HASH_SHA3_224,
   HASH_SHA3_256,
   HASH_SHA3_384,
@@ -46,7 +50,8 @@ typedef struct {
 #ifdef SHA_OPENSSL
   union {
     SHA_CTX      sha1;       /* SHA1 block                                */
 #ifdef SHA_OPENSSL
   union {
     SHA_CTX      sha1;       /* SHA1 block                                */
-    SHA256_CTX   sha2;       /* SHA256 block                              */
+    SHA256_CTX   sha2_256;   /* SHA256 or 224 block                       */
+    SHA512_CTX   sha2_512;   /* SHA512 or 384 block                       */
   } u;
 
 #elif defined(SHA_GNUTLS)
   } u;
 
 #elif defined(SHA_GNUTLS)
@@ -70,7 +75,6 @@ typedef struct {
 extern BOOL     exim_sha_init(hctx *, hashmethod);
 extern void     exim_sha_update(hctx *, const uschar *a, int);
 extern void     exim_sha_finish(hctx *, blob *);
 extern BOOL     exim_sha_init(hctx *, hashmethod);
 extern void     exim_sha_update(hctx *, const uschar *a, int);
 extern void     exim_sha_finish(hctx *, blob *);
-extern int      exim_sha_hashlen(hctx *);
 
 #endif
 /* End of File */
 
 #endif
 /* End of File */
index cd2171c8233fc71f3d32f94c24f5e203cfbcf67e..439d99b3a5fa68a31960633ed7f8f3405d916cc2 100644 (file)
@@ -5,7 +5,7 @@
 /* Copyright (c) Jeremy Harris 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Copyright (c) Jeremy Harris 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
-/* RSA and SHA routine selection for PDKIM */
+/* Signing and hashing routine selection for PDKIM */
 
 #include "../exim.h"
 #include "../sha_ver.h"
 
 #include "../exim.h"
 #include "../sha_ver.h"
 # include <gnutls/gnutls.h>
 
 # if GNUTLS_VERSION_NUMBER >= 0x30000
 # include <gnutls/gnutls.h>
 
 # if GNUTLS_VERSION_NUMBER >= 0x30000
-#  define RSA_GNUTLS
+#  define SIGN_GNUTLS
 # else
 # else
-#  define RSA_GCRYPT
+#  define SIGN_GCRYPT
 # endif
 
 #else
 # endif
 
 #else
-# define RSA_OPENSSL
+# define SIGN_OPENSSL
 #endif
 
 #endif
 
index 441f96cb55b2217cac5c4b54fa8aedfad65e6d23..bef6b6a6999c3db0f065a09e9618d06ce8d9f92d 100644 (file)
 
 #include "crypt_ver.h"
 
 
 #include "crypt_ver.h"
 
-#ifdef RSA_OPENSSL
+#ifdef SIGN_OPENSSL
 # include <openssl/rsa.h>
 # include <openssl/ssl.h>
 # include <openssl/err.h>
 # include <openssl/rsa.h>
 # include <openssl/ssl.h>
 # include <openssl/err.h>
-#elif defined(RSA_GNUTLS)
+#elif defined(SIGN_GNUTLS)
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
 #endif
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
 #endif
@@ -73,26 +73,24 @@ const uschar * pdkim_querymethods[] = {
   US"dns/txt",
   NULL
 };
   US"dns/txt",
   NULL
 };
-const uschar * pdkim_algos[] = {
-  US"rsa-sha256",
-  US"rsa-sha1",
-  NULL
-};
 const uschar * pdkim_canons[] = {
   US"simple",
   US"relaxed",
   NULL
 };
 const uschar * pdkim_canons[] = {
   US"simple",
   US"relaxed",
   NULL
 };
-/*XXX currently unused */
-const uschar * pdkim_hashes[] = {
-  US"sha256",
-  US"sha1",
-  NULL
+
+typedef struct {
+  const uschar * dkim_hashname;
+  hashmethod    exim_hashmethod;
+} pdkim_hashtype;
+static const pdkim_hashtype pdkim_hashes[] = {
+  { US"sha1",   HASH_SHA1 },
+  { US"sha256", HASH_SHA2_256 },
+  { US"sha512", HASH_SHA2_512 }
 };
 };
-/*XXX currently unused */
+
 const uschar * pdkim_keytypes[] = {
 const uschar * pdkim_keytypes[] = {
-  US"rsa",
-  NULL
+  US"rsa"
 };
 
 typedef struct pdkim_combined_canon_entry {
 };
 
 typedef struct pdkim_combined_canon_entry {
@@ -113,6 +111,17 @@ pdkim_combined_canon_entry pdkim_combined_canons[] = {
 
 
 /* -------------------------------------------------------------------------- */
 
 
 /* -------------------------------------------------------------------------- */
+uschar *
+dkim_sig_to_a_tag(pdkim_signature * sig)
+{
+if (  sig->keytype < 0  || sig->keytype > nelem(pdkim_keytypes)
+   || sig->hashtype < 0 || sig->hashtype > nelem(pdkim_hashes))
+  return US"err";
+return string_sprintf("%s-%s",
+  pdkim_keytypes[sig->keytype], pdkim_hashes[sig->hashtype].dkim_hashname);
+}
+
+
 
 const char *
 pdkim_verify_status_str(int status)
 
 const char *
 pdkim_verify_status_str(int status)
@@ -433,8 +442,9 @@ memset(sig, 0, sizeof(pdkim_signature));
 sig->bodylength = -1;
 
 /* Set so invalid/missing data error display is accurate */
 sig->bodylength = -1;
 
 /* Set so invalid/missing data error display is accurate */
-sig->algo = -1;
 sig->version = 0;
 sig->version = 0;
+sig->keytype = -1;
+sig->hashtype = -1;
 
 q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
 
 
 q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
 
@@ -507,14 +517,18 @@ for (p = raw_hdr; ; p++)
              Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
            break;
          case 'a':
              Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
            break;
          case 'a':
-/*XXX this searches a list of combined (algo + hash-method)s */
-           for (i = 0; pdkim_algos[i]; i++)
-             if (Ustrcmp(cur_val, pdkim_algos[i]) == 0)
-               {
-               sig->algo = i;
-               break;
-               }
+           {
+           uschar * s = Ustrchr(cur_val, '-');
+
+           for(i = 0; i < nelem(pdkim_keytypes); i++)
+             if (Ustrncmp(cur_val, pdkim_keytypes[i], s - cur_val) == 0)
+               { sig->keytype = i; break; }
+           for (++s, i = 0; i < nelem(pdkim_hashes); i++)
+             if (Ustrcmp(s, pdkim_hashes[i].dkim_hashname) == 0)
+               { sig->hashtype = i; break; }
            break;
            break;
+           }
+
          case 'c':
            for (i = 0; pdkim_combined_canons[i].str; i++)
              if (Ustrcmp(cur_val, pdkim_combined_canons[i].str) == 0)
          case 'c':
            for (i = 0; pdkim_combined_canons[i].str; i++)
              if (Ustrcmp(cur_val, pdkim_combined_canons[i].str) == 0)
@@ -588,9 +602,10 @@ DEBUG(D_acl)
 
 /*XXX hash method: extend for sha512 */
 if (!exim_sha_init(&sig->body_hash_ctx,
 
 /*XXX hash method: extend for sha512 */
 if (!exim_sha_init(&sig->body_hash_ctx,
-              sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256))
+              pdkim_hashes[sig->hashtype].exim_hashmethod))
   {
   {
-  DEBUG(D_acl) debug_printf("PDKIM: hash init internal error\n");
+  DEBUG(D_acl)
+    debug_printf("PDKIM: hash init error, possibly nonhandled hashtype\n");
   return NULL;
   }
 return sig;
   return NULL;
   }
 return sig;
@@ -1189,8 +1204,7 @@ col = hdr_len;
 
 /* Required and static bits */
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
 
 /* Required and static bits */
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
-/*XXX this is a combo of algo and hash-method */
-                   pdkim_algos[sig->algo]);
+                   dkim_sig_to_a_tag(sig));
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
                    pdkim_querymethods[sig->querymethod]);
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
                    pdkim_querymethods[sig->querymethod]);
 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
@@ -1375,8 +1389,6 @@ pdkim_finish_bodyhash(ctx);
 
 while (sig)
   {
 
 while (sig)
   {
-/*XXX bool probably not enough */
-  BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
   hctx hhash_ctx;
   uschar * sig_hdr = US"";
   blob hhash;
   hctx hhash_ctx;
   uschar * sig_hdr = US"";
   blob hhash;
@@ -1386,9 +1398,10 @@ while (sig)
   hdata.data = NULL;
   hdata.len = 0;
 
   hdata.data = NULL;
   hdata.len = 0;
 
-  if (!exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256))
+  if (!exim_sha_init(&hhash_ctx, pdkim_hashes[sig->hashtype].exim_hashmethod))
     {
     {
-    DEBUG(D_acl) debug_printf("PDKIM: hask setup internal error\n");
+    DEBUG(D_acl)
+      debug_printf("PDKIM: hash setup error, possibly nonhandled hashtype\n");
     break;
     }
 
     break;
     }
 
@@ -1537,9 +1550,9 @@ while (sig)
     {
     es_ctx sctx;
 
     {
     es_ctx sctx;
 
-    /* Import private key */
+    /* Import private key, including the keytype */
 /*XXX extend for non-RSA algos */
 /*XXX extend for non-RSA algos */
-    if ((*err = exim_dkim_signing_init(US sig->rsa_privkey, &sctx)))
+    if ((*err = exim_dkim_signing_init(US sig->privkey, &sctx)))
       {
       DEBUG(D_acl) debug_printf("signing_init: %s\n", *err);
       return PDKIM_ERR_RSA_PRIVKEY;
       {
       DEBUG(D_acl) debug_printf("signing_init: %s\n", *err);
       return PDKIM_ERR_RSA_PRIVKEY;
@@ -1549,15 +1562,14 @@ while (sig)
     calculated, with GnuTLS we have to sign an entire block of headers
     (due to available interfaces) and it recalculates the hash internally. */
 
     calculated, with GnuTLS we have to sign an entire block of headers
     (due to available interfaces) and it recalculates the hash internally. */
 
-#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
+#if defined(SIGN_OPENSSL) || defined(SIGN_GCRYPT)
     hdata = hhash;
 #endif
 
 /*XXX extend for non-RSA algos */
     hdata = hhash;
 #endif
 
 /*XXX extend for non-RSA algos */
-/*XXX oddly the dkim rfc does _not_ say what variant (sha1 or sha256) of
-RSA signing should be done.  We use the same variant as the hash-method. */
-
-    if ((*err = exim_dkim_sign(&sctx, is_sha1, &hdata, &sig->sighash)))
+    if ((*err = exim_dkim_sign(&sctx,
+                 pdkim_hashes[sig->hashtype].exim_hashmethod,
+                 &hdata, &sig->sighash)))
       {
       DEBUG(D_acl) debug_printf("signing: %s\n", *err);
       return PDKIM_ERR_RSA_SIGNING;
       {
       DEBUG(D_acl) debug_printf("signing: %s\n", *err);
       return PDKIM_ERR_RSA_SIGNING;
@@ -1583,7 +1595,8 @@ RSA signing should be done.  We use the same variant as the hash-method. */
         && sig->headernames   && *sig->headernames
         && sig->bodyhash.data
         && sig->sighash.data
         && sig->headernames   && *sig->headernames
         && sig->bodyhash.data
         && sig->sighash.data
-        && sig->algo > -1
+        && sig->keytype >= 0
+        && sig->hashtype >= 0
         && sig->version
        ) )
       {
         && sig->version
        ) )
       {
@@ -1619,11 +1632,13 @@ RSA signing should be done.  We use the same variant as the hash-method. */
       const uschar * list = sig->pubkey->hashes, * ele;
       int sep = ':';
       while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
       const uschar * list = sig->pubkey->hashes, * ele;
       int sep = ':';
       while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
-       if (Ustrcmp(ele, pdkim_algos[sig->algo] + 4) == 0) break;
+       if (Ustrcmp(ele, pdkim_hashes[sig->hashtype].dkim_hashname) == 0) break;
       if (!ele)
        {
       if (!ele)
        {
-       DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%s\n",
-                               sig->pubkey->hashes, pdkim_algos[sig->algo]);
+       DEBUG(D_acl) debug_printf("pubkey h=%s vs. sig a=%s_%s\n",
+         sig->pubkey->hashes,
+         pdkim_keytypes[sig->keytype],
+         pdkim_hashes[sig->hashtype].dkim_hashname);
        sig->verify_status =      PDKIM_VERIFY_FAIL;
        sig->verify_ext_status =  PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH;
        goto NEXT_VERIFY;
        sig->verify_status =      PDKIM_VERIFY_FAIL;
        sig->verify_ext_status =  PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH;
        goto NEXT_VERIFY;
@@ -1632,7 +1647,9 @@ RSA signing should be done.  We use the same variant as the hash-method. */
 
     /* Check the signature */
 /*XXX needs extension for non-RSA */
 
     /* Check the signature */
 /*XXX needs extension for non-RSA */
-    if ((*err = exim_dkim_verify(&vctx, is_sha1, &hhash, &sig->sighash)))
+    if ((*err = exim_dkim_verify(&vctx,
+                 pdkim_hashes[sig->hashtype].exim_hashmethod,
+                 &hhash, &sig->sighash)))
       {
       DEBUG(D_acl) debug_printf("headers verify: %s\n", *err);
       sig->verify_status =      PDKIM_VERIFY_FAIL;
       {
       DEBUG(D_acl) debug_printf("headers verify: %s\n", *err);
       sig->verify_status =      PDKIM_VERIFY_FAIL;
@@ -1690,18 +1707,18 @@ return ctx;
 
 /* -------------------------------------------------------------------------- */
 
 
 /* -------------------------------------------------------------------------- */
 
-/*XXX ? needs extension to cover non-RSA algo?  Currently the "algo" is actually
-the combo of algo and hash-method */
+/*XXX ? needs extension to cover non-RSA algo?  */
 
 DLLEXPORT pdkim_ctx *
 
 DLLEXPORT pdkim_ctx *
-pdkim_init_sign(char * domain, char * selector, char * rsa_privkey, int algo,
-  BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *),
+pdkim_init_sign(uschar * domain, uschar * selector, uschar * privkey,
+  uschar * hashname, BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *),
   const uschar ** errstr)
 {
   const uschar ** errstr)
 {
+int hashtype;
 pdkim_ctx * ctx;
 pdkim_signature * sig;
 
 pdkim_ctx * ctx;
 pdkim_signature * sig;
 
-if (!domain || !selector || !rsa_privkey)
+if (!domain || !selector || !privkey)
   return NULL;
 
 ctx = store_get(sizeof(pdkim_ctx) + PDKIM_MAX_BODY_LINE_LEN + sizeof(pdkim_signature));
   return NULL;
 
 ctx = store_get(sizeof(pdkim_ctx) + PDKIM_MAX_BODY_LINE_LEN + sizeof(pdkim_signature));
@@ -1720,14 +1737,23 @@ ctx->sig = sig;
 
 sig->domain = string_copy(US domain);
 sig->selector = string_copy(US selector);
 
 sig->domain = string_copy(US domain);
 sig->selector = string_copy(US selector);
-sig->rsa_privkey = string_copy(US rsa_privkey);
-sig->algo = algo;
+sig->privkey = string_copy(US privkey);
+/*XXX no keytype yet; comes from privkey */
 
 
-/*XXX extend for sha512 */
-if (!exim_sha_init(&sig->body_hash_ctx,
-              algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256))
+for (hashtype = 0; hashtype < nelem(pdkim_hashes); hashtype++)
+  if (Ustrcmp(hashname, pdkim_hashes[hashtype].dkim_hashname) == 0)
+  { sig->hashtype = hashtype; break; }
+if (hashtype >= nelem(pdkim_hashes))
   {
   {
-  DEBUG(D_acl) debug_printf("PDKIM: hash setup internal error\n");
+  DEBUG(D_acl)
+    debug_printf("PDKIM: unrecognised hashname '%s'\n", hashname);
+  return NULL;
+  }
+
+if (!exim_sha_init(&sig->body_hash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
+  {
+  DEBUG(D_acl)
+    debug_printf("PDKIM: hash setup error, possibly nonhandled hashtype\n");
   return NULL;
   }
 
   return NULL;
   }
 
index 176bc36e0e3c8ac0a974518bfab1c6164b18e542..3c420ae63288b2b03bf884e1287167abf3e8d1f8 100644 (file)
 /* Some parameter values */
 #define PDKIM_QUERYMETHOD_DNS_TXT 0
 
 /* 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_CANON_SIMPLE        0
 #define PDKIM_CANON_RELAXED       1
 
-#define PDKIM_HASH_SHA256         0
-#define PDKIM_HASH_SHA1           1
+/*XXX change to enums */
+#define PDKIM_HASH_SHA256         1
 
 #define PDKIM_KEYTYPE_RSA         0
 
 
 #define PDKIM_KEYTYPE_RSA         0
 
@@ -122,9 +119,8 @@ typedef struct pdkim_signature {
   /* (v=) The version, as an integer. Currently, always "1" */
   int version;
 
   /* (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;
+  int keytype; /* PDKIM_KEYTYPE_RSA */
+  int hashtype;        /* pdkim_hashes index */
 
   /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE
      or PDKIM_CANON_RELAXED */
 
   /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE
      or PDKIM_CANON_RELAXED */
@@ -239,7 +235,7 @@ typedef struct pdkim_signature {
   unsigned long signed_body_bytes; /* How many body bytes we hashed     */
   pdkim_stringlist *headers; /* Raw headers included in the sig         */
   /* Signing specific ------------------------------------------------- */
   unsigned long signed_body_bytes; /* How many body bytes we hashed     */
   pdkim_stringlist *headers; /* Raw headers included in the sig         */
   /* Signing specific ------------------------------------------------- */
-  uschar * rsa_privkey;     /* Private RSA key                             */
+  uschar * privkey;         /* Private key                                 */
   uschar * sign_headers;    /* To-be-signed header names                   */
   uschar * rawsig_no_b_val; /* Original signature header w/o b= tag value. */
 } pdkim_signature;
   uschar * sign_headers;    /* To-be-signed header names                   */
   uschar * rawsig_no_b_val; /* Original signature header w/o b= tag value. */
 } pdkim_signature;
@@ -287,7 +283,7 @@ extern "C" {
 void      pdkim_init         (void);
 
 DLLEXPORT
 void      pdkim_init         (void);
 
 DLLEXPORT
-pdkim_ctx *pdkim_init_sign    (char *, char *, char *, int,
+pdkim_ctx *pdkim_init_sign    (uschar *, uschar *, uschar *, uschar *,
                              BOOL, int(*)(char *, char *), const uschar **);
 
 DLLEXPORT
                              BOOL, int(*)(char *, char *), const uschar **);
 
 DLLEXPORT
@@ -310,6 +306,8 @@ void       pdkim_free_ctx     (pdkim_ctx *);
 
 const uschar * pdkim_errstr(int);
 
 
 const uschar * pdkim_errstr(int);
 
+uschar *       dkim_sig_to_a_tag(pdkim_signature * sig);
+
 #ifdef __cplusplus
 }
 #endif
 #ifdef __cplusplus
 }
 #endif
index 143cd19dfc192d4c3330333edf576e838978f757..008f277b3fd13020e9820d558481008539c8d771 100644 (file)
 #include "../blob.h"
 #include "../hash.h"
 
 #include "../blob.h"
 #include "../hash.h"
 
-#ifdef RSA_OPENSSL
+#ifdef SIGN_OPENSSL
 # include <openssl/rsa.h>
 # include <openssl/ssl.h>
 # include <openssl/err.h>
 # include <openssl/rsa.h>
 # include <openssl/ssl.h>
 # include <openssl/err.h>
-#elif defined(RSA_GNUTLS)
+#elif defined(SIGN_GNUTLS)
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
 #endif
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
 #endif
index bcd64fdc44309fa0c6f9e4491cca35305d7602a9..ec68414c8e83bc5c569904f2a9de1b853e5992c8 100644 (file)
@@ -3,11 +3,7 @@
  *
  *  Copyright (C) 2016  Exim maintainers
  *
  *
  *  Copyright (C) 2016  Exim maintainers
  *
- *  RSA signing/verification interface
-XXX rename interfaces to cover all signature methods.
-the method (algo) needs to be extracted from the supplied private-key
-and not only stashed as needed in the sign- or verify- context, but
-indicated to caller for protocol tag construction.
+ *  signing/verification interface
  */
 
 #include "../exim.h"
  */
 
 #include "../exim.h"
@@ -23,7 +19,7 @@ indicated to caller for protocol tag construction.
 
 
 /******************************************************************************/
 
 
 /******************************************************************************/
-#ifdef RSA_GNUTLS
+#ifdef SIGN_GNUTLS
 
 void
 exim_dkim_init(void)
 
 void
 exim_dkim_init(void)
@@ -55,8 +51,8 @@ int rc;
 k.data = privkey_pem;
 k.size = strlen(privkey_pem);
 
 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,
+if (  (rc = gnutls_x509_privkey_init(&sign_ctx->key)) != GNUTLS_E_SUCCESS
+   || (rc = gnutls_x509_privkey_import(sign_ctx->key, &k,
          GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
    )
   return gnutls_strerror(rc);
          GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS
    )
   return gnutls_strerror(rc);
@@ -74,33 +70,38 @@ sign hash.
 Return: NULL for success, or an error string */
 
 const uschar *
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
 {
 {
+gnutls_digest_algorithm_t dig;
 gnutls_datum_t k;
 size_t sigsize = 0;
 int rc;
 const uschar * ret = NULL;
 
 gnutls_datum_t k;
 size_t sigsize = 0;
 int rc;
 const uschar * ret = NULL;
 
+switch (hash)
+  {
+  case HASH_SHA1:      dig = GNUTLS_DIG_SHA1; break;
+  case HASH_SHA2_256:  dig = GNUTLS_DIG_SHA256; break;
+  case HASH_SHA2_512:  dig = GNUTLS_DIG_SHA512; break;
+  default:             return US"nonhandled hash type";
+  }
+
 /* Allocate mem for signature */
 k.data = data->data;
 k.size = data->len;
 /* 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,
+(void) gnutls_x509_privkey_sign_data(sign_ctx->key, dig,
   0, &k, NULL, &sigsize);
 
 sig->data = store_get(sigsize);
 sig->len = sigsize;
 
 /* Do signing */
   0, &k, NULL, &sigsize);
 
 sig->data = store_get(sigsize);
 sig->len = sigsize;
 
 /* Do signing */
-/*XXX will need extension for hash type; looks ok for non-RSA algos
-so long as the privkey_import stage got them. */
-if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->rsa,
-           is_sha1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+if ((rc = gnutls_x509_privkey_sign_data(sign_ctx->key, dig,
            0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
    )
   ret = gnutls_strerror(rc);
 
            0, &k, sig->data, &sigsize)) != GNUTLS_E_SUCCESS
    )
   ret = gnutls_strerror(rc);
 
-gnutls_x509_privkey_deinit(sign_ctx->rsa);
+gnutls_x509_privkey_deinit(sign_ctx->key);
 return ret;
 }
 
 return ret;
 }
 
@@ -116,12 +117,12 @@ gnutls_datum_t k;
 int rc;
 const uschar * ret = NULL;
 
 int rc;
 const uschar * ret = NULL;
 
-gnutls_pubkey_init(&verify_ctx->rsa);
+gnutls_pubkey_init(&verify_ctx->key);
 
 k.data = pubkey_der->data;
 k.size = pubkey_der->len;
 
 
 k.data = pubkey_der->data;
 k.size = pubkey_der->len;
 
-if ((rc = gnutls_pubkey_import(verify_ctx->rsa, &k, GNUTLS_X509_FMT_DER))
+if ((rc = gnutls_pubkey_import(verify_ctx->key, &k, GNUTLS_X509_FMT_DER))
        != GNUTLS_E_SUCCESS)
   ret = gnutls_strerror(rc);
 return ret;
        != GNUTLS_E_SUCCESS)
   ret = gnutls_strerror(rc);
 return ret;
@@ -132,31 +133,39 @@ return ret;
 Return: NULL for success, or an error string */
 
 const uschar *
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
 {
 {
+gnutls_sign_algorithm_t algo;
 gnutls_datum_t k, s;
 int rc;
 const uschar * ret = NULL;
 
 gnutls_datum_t k, s;
 int rc;
 const uschar * ret = NULL;
 
+/*XXX needs extension for non-rsa */
+switch (hash)
+  {
+  case HASH_SHA1:      algo = GNUTLS_SIGN_RSA_SHA1;   break;
+  case HASH_SHA2_256:  algo = GNUTLS_SIGN_RSA_SHA256; break;
+  case HASH_SHA2_512:  algo = GNUTLS_SIGN_RSA_SHA512; break;
+  default:             return US"nonhandled hash type";
+  }
+
 k.data = data_hash->data;
 k.size = data_hash->len;
 s.data = sig->data;
 s.size = sig->len;
 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,
-/*XXX needs extension for SHA512 */
-           is_sha1 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
-           0, &k, &s)) < 0)
+if ((rc = gnutls_pubkey_verify_hash2(verify_ctx->key, algo, 0, &k, &s)) < 0)
   ret = gnutls_strerror(rc);
 
   ret = gnutls_strerror(rc);
 
-gnutls_pubkey_deinit(verify_ctx->rsa);
+gnutls_pubkey_deinit(verify_ctx->key);
 return ret;
 }
 
 
 
 
 return ret;
 }
 
 
 
 
-#elif defined(RSA_GCRYPT)
+#elif defined(SIGN_GCRYPT)
 /******************************************************************************/
 /******************************************************************************/
+/* This variant is used under pre-3.0.0 GnuTLS.  Only rsa-sha1 and rsa-sha256 */
 
 
 /* Internal service routine:
 
 
 /* Internal service routine:
@@ -364,8 +373,9 @@ sign hash.
 Return: NULL for success, or an error string */
 
 const uschar *
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
 {
 {
+BOOL is_sha1;
 gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
 gcry_mpi_t m_sig;
 uschar * errstr;
 gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
 gcry_mpi_t m_sig;
 uschar * errstr;
@@ -373,11 +383,18 @@ gcry_error_t gerr;
 
 /*XXX will need extension for hash types (though, possibly, should
 be re-specced to not rehash but take an already-hashed value? Actually
 
 /*XXX will need extension for hash types (though, possibly, should
 be re-specced to not rehash but take an already-hashed value? Actually
-current impl looks WRONG - it _is_ given a has so should not be
+current impl looks WRONG - it _is_ given a hash so should not be
 re-hashing.  Has this been tested?
 
 Will need extension for non-RSA sugning algos. */
 
 re-hashing.  Has this been tested?
 
 Will need extension for non-RSA sugning algos. */
 
+switch (hash)
+  {
+  case HASH_SHA1:      is_sha1 = TRUE; break;
+  case HASH_SHA2_256:  is_sha1 = FALSE; break;
+  default:             return US"nonhandled hash type";
+  }
+
 #define SIGSPACE 128
 sig->data = store_get(SIGSPACE);
 
 #define SIGSPACE 128
 sig->data = store_get(SIGSPACE);
 
@@ -512,7 +529,7 @@ DEBUG(D_acl) return string_sprintf("%s: %s", stage, asn1_strerror(rc));
 Return: NULL for success, or an error string */
 
 const uschar *
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
 {
 /*
 cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
 {
 /*
 cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
@@ -522,6 +539,13 @@ gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL;
 gcry_error_t gerr;
 uschar * stage;
 
 gcry_error_t gerr;
 uschar * stage;
 
+switch (hash)
+  {
+  case HASH_SHA1:      is_sha1 = TRUE; break;
+  case HASH_SHA2_256:  is_sha1 = FALSE; break;
+  default:             return US"nonhandled hash type";
+  }
+
 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))
 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))
@@ -557,7 +581,7 @@ return NULL;
 
 
 
 
 
 
-#elif defined(RSA_OPENSSL)
+#elif defined(SIGN_OPENSSL)
 /******************************************************************************/
 
 void
 /******************************************************************************/
 
 void
@@ -580,30 +604,10 @@ Return: NULL for success, or an error string */
 const uschar *
 exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
 {
 const uschar *
 exim_dkim_signing_init(uschar * privkey_pem, es_ctx * sign_ctx)
 {
-uschar * p, * q;
-int len;
-
-/*XXX maybe use PEM_read_bio_PrivateKey() ??? 
-The sign_ctx would need to have an EVP_PKEY* */
-
-/* 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(US ssl_errstring);
-  }
+BIO * bp = BIO_new_mem_buf(privkey_pem, -1);
 
 
+if (!(sign_ctx->key = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL)))
+  return ERR_error_string(ERR_get_error(), NULL);
 return NULL;
 }
 
 return NULL;
 }
 
@@ -617,32 +621,37 @@ sign hash.
 Return: NULL for success, or an error string */
 
 const uschar *
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_dkim_sign(es_ctx * sign_ctx, BOOL is_sha1, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
 {
 {
-uint len;
-const uschar * ret = NULL;
-
-/*XXX will need extension for non-RSA signing algo.  Maybe use
-https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_sign.html  ???  */
+const EVP_MD * md;
+EVP_PKEY_CTX * ctx;
+size_t siglen;
 
 
-/* Allocate mem for signature */
-len = RSA_size(sign_ctx->rsa);
-sig->data = store_get(len);
-sig->len = len;
+switch (hash)
+  {
+  case HASH_SHA1:      md = EVP_sha1();   break;
+  case HASH_SHA2_256:  md = EVP_sha256(); break;
+  case HASH_SHA2_512:  md = EVP_sha512(); break;
+  default:             return US"nonhandled hash type";
+  }
 
 
-/* Do signing */
-if (RSA_sign(is_sha1 ? NID_sha1 : NID_sha256,
-      CUS data->data, data->len,
-      US sig->data, &len, sign_ctx->rsa) != 1)
+if (  (ctx = EVP_PKEY_CTX_new(sign_ctx->key, NULL))
+   && EVP_PKEY_sign_init(ctx) > 0
+   && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) > 0
+   && EVP_PKEY_CTX_set_signature_md(ctx, md) > 0
+   && EVP_PKEY_sign(ctx, NULL, &siglen, data->data, data->len) > 0
+   )
   {
   {
-  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(US ssl_errstring);
+  /* Allocate mem for signature */
+  sig->data = store_get(siglen);
+  sig->len = siglen;
+
+  if (EVP_PKEY_sign(ctx, sig->data, &siglen, data->data, data->len) > 0)
+    { EVP_PKEY_CTX_free(ctx); return NULL; }
   }
 
   }
 
-RSA_free(sign_ctx->rsa);
-return ret;;
+if (ctx) EVP_PKEY_CTX_free(ctx);
+return ERR_error_string(ERR_get_error(), NULL);
 }
 
 
 }
 
 
@@ -653,19 +662,13 @@ Return: NULL for success, or an error string */
 const uschar *
 exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
 {
 const uschar *
 exim_dkim_verify_init(blob * pubkey_der, ev_ctx * verify_ctx)
 {
-const uschar * p = CUS pubkey_der->data;
-const uschar * ret = NULL;
+const uschar * s = pubkey_der->data;
 
 
-/*XXX d2i_X509_PUBKEY, X509_get_pubkey(), and an EVP_PKEY* in verify_ctx. */
+/*XXX hmm, we never free this */
 
 
-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(CUS ssl_errstring);
-  }
-return ret;
+if ((verify_ctx->key = d2i_PUBKEY(NULL, &s, pubkey_der->len)))
+  return NULL;
+return ERR_error_string(ERR_get_error(), NULL);
 }
 
 
 }
 
 
@@ -675,31 +678,34 @@ return ret;
 Return: NULL for success, or an error string */
 
 const uschar *
 Return: NULL for success, or an error string */
 
 const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, BOOL is_sha1, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
 {
 {
-const uschar * ret = NULL;
+const EVP_MD * md;
+EVP_PKEY_CTX * ctx;
 
 
-/*XXX needs extension for SHA512, Possibly EVP_PKEY_verify() is all we need???  */
-/* with EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) 
-- see example code at https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_verify.html
-and maybe also EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) though unclear
-if that only sets a pad/type field byte value, or sets up for an actual hash operation...
-Same on the signing side.
-https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_padding.html says it does what we want. */
-
-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)
+switch (hash)
   {
   {
-  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(US ssl_errstring);
+  case HASH_SHA1:      md = EVP_sha1();   break;
+  case HASH_SHA2_256:  md = EVP_sha256(); break;
+  case HASH_SHA2_512:  md = EVP_sha512(); break;
+  default:             return US"nonhandled hash type";
   }
   }
-return ret;
+
+if (  (ctx = EVP_PKEY_CTX_new(verify_ctx->key, NULL))
+   && EVP_PKEY_verify_init(ctx) > 0
+   && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) > 0
+   && EVP_PKEY_CTX_set_signature_md(ctx, md) > 0
+   && EVP_PKEY_verify(ctx, sig->data, sig->len,
+       data_hash->data, data_hash->len) == 1
+   )
+  { EVP_PKEY_CTX_free(ctx); return NULL; }
+
+if (ctx) EVP_PKEY_CTX_free(ctx);
+return ERR_error_string(ERR_get_error(), NULL);
 }
 
 
 }
 
 
+
 #endif
 /******************************************************************************/
 
 #endif
 /******************************************************************************/
 
index 4e8580859e5ff7635ef6a4fdddd8585d6f330cbf..04288103e0bc585c1e9bb640ea49ee0895a757dd 100644 (file)
 
 #include "crypt_ver.h"
 
 
 #include "crypt_ver.h"
 
-#ifdef RSA_OPENSSL
+#ifdef SIGN_OPENSSL
 # include <openssl/rsa.h>
 # include <openssl/ssl.h>
 # include <openssl/err.h>
 # include <openssl/rsa.h>
 # include <openssl/ssl.h>
 # include <openssl/err.h>
-#elif defined(RSA_GNUTLS)
+#elif defined(SIGN_GNUTLS)
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
 #  include <gnutls/abstract.h>
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
 #  include <gnutls/abstract.h>
-#elif defined(RSA_GCRYPT)
+#elif defined(SIGN_GCRYPT)
 #  include <gcrypt.h>
 #  include <libtasn1.h>
 #endif
 #  include <gcrypt.h>
 #  include <libtasn1.h>
 #endif
 #include "../blob.h"
 
 
 #include "../blob.h"
 
 
-#ifdef RSA_OPENSSL
+#ifdef SIGN_OPENSSL
 
 typedef struct {
 
 typedef struct {
-  RSA * rsa;
+  EVP_PKEY * key;
 } es_ctx;
 
 typedef struct {
 } es_ctx;
 
 typedef struct {
-  RSA * rsa;
+  EVP_PKEY * key;
 } ev_ctx;
 
 } ev_ctx;
 
-#elif defined(RSA_GNUTLS)
+#elif defined(SIGN_GNUTLS)
 
 typedef struct {
 
 typedef struct {
-  gnutls_x509_privkey_t rsa;
+  gnutls_x509_privkey_t key;
 } es_ctx;
 
 typedef struct {
 } es_ctx;
 
 typedef struct {
-  gnutls_pubkey_t rsa;
+  gnutls_pubkey_t key;
 } ev_ctx;
 
 } ev_ctx;
 
-#elif defined(RSA_GCRYPT)
+#elif defined(SIGN_GCRYPT)
 
 typedef struct {
 
 typedef struct {
+  int  keytype;
   gcry_mpi_t n;
   gcry_mpi_t e;
   gcry_mpi_t d;
   gcry_mpi_t n;
   gcry_mpi_t e;
   gcry_mpi_t d;
@@ -62,6 +63,7 @@ typedef struct {
 } es_ctx;
 
 typedef struct {
 } es_ctx;
 
 typedef struct {
+  int  keytype;
   gcry_mpi_t n;
   gcry_mpi_t e;
 } ev_ctx;
   gcry_mpi_t n;
   gcry_mpi_t e;
 } ev_ctx;
index beea57f346ef9b2111a6d0bb3e1c88a2320accec..06fcd4188fcb4225cdaa6e6b1837c895fad91985 100644 (file)
@@ -872,6 +872,7 @@ struct ob_dkim {
   uschar *dkim_canon;
   uschar *dkim_sign_headers;
   uschar *dkim_strict;
   uschar *dkim_canon;
   uschar *dkim_sign_headers;
   uschar *dkim_strict;
+  uschar *dkim_hash;
   BOOL    dot_stuffed;
 };
 
   BOOL    dot_stuffed;
 };
 
index d8bc596fc3f3d4b5f28d38deebd8d7a321465ccb..147dfdeaf45b9a885b90751e3b1e121b719cc096 100644 (file)
@@ -43,6 +43,8 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_canon) },
   { "dkim_domain", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_domain) },
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_canon) },
   { "dkim_domain", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_domain) },
+  { "dkim_hash", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim.dkim_hash) },
   { "dkim_private_key", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_private_key) },
   { "dkim_selector", opt_stringptr,
   { "dkim_private_key", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_private_key) },
   { "dkim_selector", opt_stringptr,
@@ -281,6 +283,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
     .dkim_canon =              NULL,
     .dkim_sign_headers =        NULL,
     .dkim_strict =             NULL,
     .dkim_canon =              NULL,
     .dkim_sign_headers =        NULL,
     .dkim_strict =             NULL,
+    .dkim_hash =               US"sha256",
     .dot_stuffed =             FALSE},
 #endif
 };
     .dot_stuffed =             FALSE},
 #endif
 };
diff --git a/test/confs/4503 b/test/confs/4503
new file mode 120000 (symlink)
index 0000000..c4f73ba
--- /dev/null
@@ -0,0 +1 @@
+4500
\ No newline at end of file
index 70454c33c904acb99962d6feaa80e0abf50fe4d9..4497028553f605ef86b3107fb5348708e7a7be95 100644 (file)
@@ -46,5 +46,8 @@ send_to_server:
 .ifndef HEADERS_MAXSIZE
   dkim_sign_headers =  OPT
 .endif
 .ifndef HEADERS_MAXSIZE
   dkim_sign_headers =  OPT
 .endif
+.ifdef VALUE
+  dkim_hash =          VALUE
+.endif
 
 # End
 
 # End
diff --git a/test/confs/4523 b/test/confs/4523
new file mode 120000 (symlink)
index 0000000..072f5fa
--- /dev/null
@@ -0,0 +1 @@
+4520
\ No newline at end of file
diff --git a/test/log/4503 b/test/log/4503
new file mode 100644 (file)
index 0000000..7ec93a1
--- /dev/null
@@ -0,0 +1,6 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 DKIM: d=test.ex s=sel c=simple/simple a=rsa-sha512 b=1024 [verification failed - signature did not verify (headers probably modified in transit)]
+1999-03-02 09:44:33 10HmaX-0005vi-00 signer: test.ex bits: 1024
+1999-03-02 09:44:33 10HmaX-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/4523 b/test/log/4523
new file mode 100644 (file)
index 0000000..45eb89c
--- /dev/null
@@ -0,0 +1,11 @@
+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] 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
+1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha512 b=1024 [verification succeeded]
+1999-03-02 09:44:33 10HmaY-0005vi-00 signer: test.ex bits: 1024 h=Date:Sender:Message-Id:From:Reply-To:Subject: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
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server_dump
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
index 712ab79bc305355ac5b307a4e100d12736dce76b..35da4931bec293c47f0bbc55e1a8d96fa18ea493 100755 (executable)
@@ -1527,7 +1527,7 @@ $munges =
 
     'optional_config' =>
     { 'stdout' => '/^(
 
     'optional_config' =>
     { 'stdout' => '/^(
-                  dkim_(canon|domain|private_key|selector|sign_headers|strict)
+                  dkim_(canon|domain|private_key|selector|sign_headers|strict|hash)
                   |gnutls_require_(kx|mac|protocols)
                   |hosts_(requ(est|ire)|try)_(dane|ocsp)
                   |hosts_(avoid|nopass|require|verify_avoid)_tls
                   |gnutls_require_(kx|mac|protocols)
                   |hosts_(requ(est|ire)|try)_(dane|ocsp)
                   |hosts_(avoid|nopass|require|verify_avoid)_tls
diff --git a/test/scripts/4500-DKIM/4503 b/test/scripts/4500-DKIM/4503
new file mode 100644 (file)
index 0000000..aca1958
--- /dev/null
@@ -0,0 +1,45 @@
+# DKIM verify, sha512
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+# This should pass, only Mail::DKIM::Signer does not handle rsa-sha512.
+#  - sha512, 1024b
+# Mail original in aux-fixed/4500.msg1.txt
+# Sig generated by: perl aux-fixed/dkim/sign.pl --algorithm=rsa-sha512 \
+#                      --method=simple/simple < aux-fixed/4500.msg1.txt
+#
+# TODO - until we have that we can only test internal consistency,
+# signing vs. verification.
+#
+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-sha512; 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
+no_msglog_check
diff --git a/test/scripts/4500-DKIM/4523 b/test/scripts/4500-DKIM/4523
new file mode 100644 (file)
index 0000000..b897fc2
--- /dev/null
@@ -0,0 +1,15 @@
+# DKIM signing, sha512
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+# default header set
+exim -DHEADERS_MAXSIZE=y -DVALUE=sha512 -odf a@test.ex
+From: nobody@example.com
+
+content
+****
+#
+millisleep 500
+killdaemon
+no_msglog_check