DKIM: Enforce any "h" field present in the DNS publickey record. This can be set...
authorJeremy Harris <jgh146exb@wizmail.org>
Wed, 28 Jun 2017 14:25:12 +0000 (15:25 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Wed, 9 Aug 2017 21:23:58 +0000 (22:23 +0100)
hash types, eg sha256, in signatues.  There is an IETF draft in discussion which deprecates sha1 so this
feature may start to be used.

doc/doc-txt/ChangeLog
src/src/pdkim/pdkim.c
src/src/pdkim/pdkim.h
test/dnszones-src/db.test.ex
test/log/4500
test/log/4506
test/scripts/4500-DKIM/4500
test/scripts/4500-DKIM/4506
test/stderr/4520

index 0b4076c20dfaef1be45c8bc2db8c707a1cab33ee..ecb6b33a975abc406307de166970943a8e0b6b5b 100644 (file)
@@ -138,6 +138,9 @@ JH/21 Bug 2151: Avoid using SIZE on the MAIL for a callout verify, on any but
 JH/22 Retire historical build files to an "unsupported" subdir.  These are
       defined as "ones for which we have no current evidence of testing".
 
+JH/23 DKIM: enforce the DNS pubkey record "h" permitted-hashes optional field,
+      if present.  Previously it was ignored.
+
 
 Exim version 4.89
 -----------------
index d41b60f1361b510a9ddc6ecd8a13a8424cb67821..d289ec7a336e60e9e8322778c34f60f046bf8031 100644 (file)
@@ -132,6 +132,7 @@ switch(ext_status)
   {
   case PDKIM_VERIFY_FAIL_BODY: return "PDKIM_VERIFY_FAIL_BODY";
   case PDKIM_VERIFY_FAIL_MESSAGE: return "PDKIM_VERIFY_FAIL_MESSAGE";
+  case PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH: return "PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH";
   case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: return "PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE";
   case PDKIM_VERIFY_INVALID_BUFFER_SIZE: return "PDKIM_VERIFY_INVALID_BUFFER_SIZE";
   case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD: return "PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD";
@@ -644,12 +645,8 @@ for (p = raw_record; ; p++)
              case 'v':
                pub->version = string_copy(cur_val); break;
              case 'h':
+               pub->hashes = string_copy(cur_val); break;
              case 'k':
-/* This field appears to never be used. Also, unclear why
-a 'k' (key-type_ would go in this field name.  There is a field
-"keytype", also never used.
-               pub->hashes = string_copy(cur_val);
-*/
                break;
              case 'g':
                pub->granularity = string_copy(cur_val); break;
@@ -682,7 +679,11 @@ a 'k' (key-type_ would go in this field name.  There is a field
 
 /* Set fallback defaults */
 if (!pub->version    ) pub->version     = string_copy(PDKIM_PUB_RECORD_VERSION);
-else if (Ustrcmp(pub->version, PDKIM_PUB_RECORD_VERSION) != 0) return NULL;
+else if (Ustrcmp(pub->version, PDKIM_PUB_RECORD_VERSION) != 0)
+  {
+  DEBUG(D_acl) debug_printf(" Bad v= field\n");
+  return NULL;
+  }
 
 if (!pub->granularity) pub->granularity = string_copy(US"*");
 /*
@@ -694,6 +695,7 @@ if (!pub->srvtype    ) pub->srvtype     = string_copy(US"*");
 if (pub->key.data)
   return pub;
 
+DEBUG(D_acl) debug_printf(" Missing p= field\n");
 return NULL;
 }
 
@@ -1631,6 +1633,25 @@ while (sig)
     if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx, err)))
       goto NEXT_VERIFY;
 
+    /* If the pubkey limits to a list of specific hashes, ignore sigs that
+    do not have the hash part of the sig algorithm matching */
+
+    if (sig->pubkey->hashes)
+      {
+      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 (!ele)
+       {
+       DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%s\n",
+                               sig->pubkey->hashes, pdkim_algos[sig->algo]);
+       sig->verify_status =      PDKIM_VERIFY_FAIL;
+       sig->verify_ext_status =  PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH;
+       goto NEXT_VERIFY;
+       }
+      }
+
     /* Check the signature */
     if ((*err = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sighash)))
       {
index 78e3c3c8b148f11b84e6e5e5ac70e4a1c33f39cc..9899356d572ca05ad954791af96e34d7480d907c 100644 (file)
 
 #define PDKIM_VERIFY_FAIL_BODY                    1
 #define PDKIM_VERIFY_FAIL_MESSAGE                 2
-#define PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE   3
-#define PDKIM_VERIFY_INVALID_BUFFER_SIZE          4
-#define PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD     5
-#define PDKIM_VERIFY_INVALID_PUBKEY_IMPORT        6
-#define PDKIM_VERIFY_INVALID_SIGNATURE_ERROR      7
-#define PDKIM_VERIFY_INVALID_DKIM_VERSION         8
+#define PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH      3
+#define PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE   4
+#define PDKIM_VERIFY_INVALID_BUFFER_SIZE          5
+#define PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD     6
+#define PDKIM_VERIFY_INVALID_PUBKEY_IMPORT        7
+#define PDKIM_VERIFY_INVALID_SIGNATURE_ERROR      8
+#define PDKIM_VERIFY_INVALID_DKIM_VERSION         9
 
 /* -------------------------------------------------------------------------- */
 /* Some parameter values */
@@ -100,8 +101,8 @@ typedef struct pdkim_pubkey {
   uschar *version;                /* v=  */
   uschar *granularity;            /* g=  */
 
-#ifdef notdef
   uschar *hashes;                 /* h=  */
+#ifdef notdef
   uschar *keytype;                /* k=  */
 #endif
   uschar *srvtype;                /* s=  */
index f7c9e313bbf7589913dc804b3f44a297f846e256..73db57f9ce60cf8d0649d7e5174a21c1bdfc7301 100644 (file)
@@ -491,13 +491,19 @@ DELAY=1500 delay1500 A HOSTIPV4
 ;  openssl rsa -in aux-fixed/dkim/dkim.private -out /dev/stdout -pubout -outform PEM
 ;
 ; Deliberate bad version, having extra backslashes
+; sha256-hash-only version.... appears to be too long, gets truncated
 ;
 ; Another, 512-bit (with a Notes field)
+; 512 requiring sha1 hash
+; 512 requiring sha256 hash
 ;
 sel._domainkey TXT "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+YdhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB"
 sel_bad._domainkey TXT "v=DKIM1\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+YdhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB"
+sel_sha256._domainkey TXT "v=DKIM1; h=sha256; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+YdhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB"
 
 ses._domainkey TXT "v=DKIM1; n=halfkilo; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL6eAQxd9didJ0/+05iDwJOqT6ly826Vi8aGPecsBiYK5/tAT97fxXk+dPWMZp9kQxtknEzYjYjAydzf+HQ2yJMCAwEAAQ=="
+ses_sha1._domainkey TXT "v=DKIM1; h=sha1; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL6eAQxd9didJ0/+05iDwJOqT6ly826Vi8aGPecsBiYK5/tAT97fxXk+dPWMZp9kQxtknEzYjYjAydzf+HQ2yJMCAwEAAQ=="
+ses_sha256._domainkey TXT "v=DKIM1; h=sha256; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL6eAQxd9didJ0/+05iDwJOqT6ly826Vi8aGPecsBiYK5/tAT97fxXk+dPWMZp9kQxtknEzYjYjAydzf+HQ2yJMCAwEAAQ=="
 
 
 ; End
index 0e0f8400d30afb51e83ebaec385b284ca7e41a88..ec8ef088e148cbd81b72a93b30e226635d773ad4 100644 (file)
@@ -10,3 +10,6 @@
 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
+1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=ses_sha1 c=simple/simple a=rsa-sha1 b=512 [verification succeeded]
+1999-03-02 09:44:33 10HmbA-0005vi-00 signer: test.ex bits: 512
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
index fb0f2256784fa7d304b5039d00ac583686fbc84c..027169df06fc3294bf0b51590b8d8c6d1eb41d6a 100644 (file)
@@ -13,3 +13,6 @@
 1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: validation error: RSA_LONG_LINE
 1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: Error during validation, disabling signature verification: RSA_LONG_LINE
 1999-03-02 09:44:33 10HmbA-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 10HmbB-0005vi-00 DKIM: d=test.ex s=ses_sha256 c=simple/simple a=rsa-sha1 b=512 [verification failed - unspecified reason]
+1999-03-02 09:44:33 10HmbB-0005vi-00 signer: test.ex bits: 512
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@bloggs.com H=(xxx) [127.0.0.1] P=smtp S=sss id=qwerty1234@disco-zombie.net
index 6728b141d3056137c6c5608b870daf5e5dac001c..6b3ff5fcf3409527824e0161f37a20cfa1df932e 100644 (file)
@@ -93,6 +93,40 @@ 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
+****
+#
+#
+# This should pass.  The pubkey dns decord has a additional sha1-only h= field
+#
+#  - sha1, 512b
+# Mail original in aux-fixed/4500.msg1.txt
+# Sig generated by: perl aux-fixed/dkim/sign.pl --keyfile=aux-fixed/dkim/dkim512.private \
+#                       --method=simple/simple --selector=ses_sha1 < 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-sha1; c=simple/simple; d=test.ex; h=from:to
+       :date:message-id:subject; s=ses_sha1; bh=OB9dZVu7+5/ufs3TH9leIcE
+       pXSo=; b=hG14R3Eb/f13Pw6J0LmovHAL01KHVmVrTZ7KJrqieYTQemUaseoU2pB
+       7/g8NUwG/AsYoaw3gaAK8PqxSk2lcIQ==
+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
index 6eb81cc16ee1c805cbca918cf1d2ccaf98979972..e8d7c41f021fc82182cfc640b3caf432a42a4c32 100644 (file)
@@ -134,6 +134,41 @@ QUIT
 ****
 #
 #
+# This should fail as the sig on the mail uses sha1 but the dns record requires sha256
+#
+#  - sha256, 512b
+# Mail original in aux-fixed/4500.msg1.txt
+# Sig generated by: perl aux-fixed/dkim/sign.pl --keyfile=aux-fixed/dkim/dkim512.private \
+#                       --method=simple/simple --selector=ses_sha1 < aux-fixed/4500.msg1.txt
+# and then modifying the s= manually
+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-sha1; c=simple/simple; d=test.ex; h=from:to
+       :date:message-id:subject; s=ses_sha256; bh=OB9dZVu7+5/ufs3TH9leIcE
+       pXSo=; b=hG14R3Eb/f13Pw6J0LmovHAL01KHVmVrTZ7KJrqieYTQemUaseoU2pB
+       7/g8NUwG/AsYoaw3gaAK8PqxSk2lcIQ==
+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
index c1bafcdcc3d219dedeca0d5b7937f33f8abcc421..fc64a9e93cbb305d624931e17881b14f2b94f6b6 100644 (file)
@@ -28,6 +28,7 @@ PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  Raw record: v=DKIM1\;{SP}p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+YdhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB
  v=DKIM1\
  p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+YdhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB
+ Bad v= field
  Error while parsing public key record
 WARNING: bad dkim key in dns
 PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<