DANE/GnuTLS: split verification of mixed sets of TLSA records by usage
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 22 Dec 2017 17:19:37 +0000 (17:19 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 22 Dec 2017 20:42:38 +0000 (20:42 +0000)
This is because we cannot do the required CA-anchor and names checks for TA-mode
and not for EE-mode, without knowing which usage TLSA was used.

src/src/tls-gnu.c
test/dnszones-src/db.test.ex
test/log/5820
test/scripts/5820-DANE-GnuTLS/5820
test/stderr/5820
test/stdout/5820

index 1fee6c10741bbdf06527352cb4a321272a840d14..8cf28fca32be9149b50e24b29a17f2b06d0518ce 100644 (file)
@@ -68,6 +68,11 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 #endif
 #if GNUTLS_VERSION_NUMBER >= 0x030000 && defined(EXPERIMENTAL_DANE)
 # define SUPPORT_DANE
+# define DANESSL_USAGE_DANE_TA 2
+# define DANESSL_USAGE_DANE_EE 3
+#endif
+#if GNUTLS_VERSION_NUMBER < 0x999999 && defined(EXPERIMENTAL_DANE)
+# define GNUTLS_BROKEN_DANE_VALIDATION
 #endif
 
 #ifndef DISABLE_OCSP
@@ -1600,24 +1605,91 @@ else
 
     dane_state_t s;
     dane_query_t r;
-    const gnutls_datum_t * certlist;
     uint lsize;
+    const gnutls_datum_t * certlist =
+      gnutls_certificate_get_peers(state->session, &lsize);
+    int usage = tls_out.tlsa_usage;
+
+# ifdef GNUTLS_BROKEN_DANE_VALIDATION
+    /* Split the TLSA records into two sets, TA and EE selectors.  Run the
+    dane-verification separately so that we know which selector verified;
+    then we know whether to do CA-chain-verification and name-verification
+    (needed for TA but not EE). */
+
+    if (usage == ((1<<DANESSL_USAGE_DANE_TA) | (1<<DANESSL_USAGE_DANE_EE)))
+    {                                          /* a mixed-usage bundle */
+      int i, j, nrec;
+      const char ** dd;
+      int * ddl;
+
+      for(nrec = 0; state->dane_data_len[nrec]; ) nrec++;
+      nrec++;
+
+      dd = store_get(nrec * sizeof(uschar *));
+      ddl = store_get(nrec * sizeof(int));
+      nrec--;
+
+      if ((rc = dane_state_init(&s, 0)))
+       goto tlsa_prob;
+
+      for (usage = DANESSL_USAGE_DANE_EE;
+          usage >= DANESSL_USAGE_DANE_TA; usage--)
+       {                               /* take records with this usage */
+       for (j = i = 0; i < nrec; i++)
+         if (state->dane_data[i][0] == usage)
+           {
+           dd[j] = state->dane_data[i];
+           ddl[j++] = state->dane_data_len[i];
+           }
+       if (j)
+         {
+         dd[j] = NULL;
+         ddl[j] = 0;
+
+         if ((rc = dane_raw_tlsa(s, &r, (char * const *)dd, ddl, 1, 0)))
+           goto tlsa_prob;
+
+         if ((rc = dane_verify_crt_raw(s, certlist, lsize,
+                           gnutls_certificate_type_get(state->session),
+                           r, 0,
+                           usage == DANESSL_USAGE_DANE_EE
+                           ? DANE_VFLAG_ONLY_CHECK_EE_USAGE : 0,
+                           &verify)))
+           {
+           DEBUG(D_tls)
+             debug_printf("TLSA record problem: %s\n", dane_strerror(rc));
+           }
+         else if (verify == 0) /* verification passed */
+           {
+           usage = 1 << usage;
+           break;
+           }
+         }
+       }
 
-    certlist = gnutls_certificate_get_peers(state->session, &lsize);
-
-    if (  (rc = dane_state_init(&s, 0))
-       || (rc = dane_raw_tlsa(s, &r, state->dane_data, state->dane_data_len,
-                             1, 0))
-       || (rc = dane_verify_crt_raw(s, certlist, lsize,
-                             gnutls_certificate_type_get(state->session),
-                             r, 0, 0, &verify))
-       )
-
+       if (rc) goto tlsa_prob;
+      }
+    else
+# endif
       {
-      *errstr = string_sprintf("TLSA record problem: %s", dane_strerror(rc));
-      goto badcert;
+      if (  (rc = dane_state_init(&s, 0))
+        || (rc = dane_raw_tlsa(s, &r, state->dane_data, state->dane_data_len,
+                       1, 0))
+        || (rc = dane_verify_crt_raw(s, certlist, lsize,
+                       gnutls_certificate_type_get(state->session),
+                       r, 0, 
+# ifdef GNUTLS_BROKEN_DANE_VALIDATION
+                       usage == (1 << DANESSL_USAGE_DANE_EE)
+                       ? DANE_VFLAG_ONLY_CHECK_EE_USAGE : 0,
+# else
+                       0,
+# endif
+                       &verify))
+        )
+       goto tlsa_prob;
       }
-    if (verify != 0)
+
+    if (verify != 0)           /* verification failed */
       {
       gnutls_datum_t str;
       (void) dane_verification_status_print(verify, &str, 0);
@@ -1626,11 +1698,12 @@ else
       }
     state->peer_dane_verified = TRUE;
 
-    /* If there were only EE-mode TLSA records present, no checks on cert anchor
-    valididation or cert names are required.  For a TA record only, or a mixed
-    set, do them (we cannot tell if an EE record worked). */
+# ifdef GNUTLS_BROKEN_DANE_VALIDATION
+    /* If a TA-mode TLSA record was used for verification we must additionally
+    verify the CA chain and the cert name.  For EE-mode, skip it. */
 
-    if (!(tls_out.tlsa_usage & (1 << 2)))
+    if (usage & (1 << DANESSL_USAGE_DANE_EE))
+# endif
       {
       state->peer_cert_verified = TRUE;
       goto goodcert;
@@ -1688,6 +1761,8 @@ goodcert:
   state->tlsp->peerdn = state->peerdn;
   return TRUE;
 
+tlsa_prob:
+  *errstr = string_sprintf("TLSA record problem: %s", dane_strerror(rc));
 badcert:
   gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
   return FALSE;
@@ -2112,8 +2187,10 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS), i = 0;
   DEBUG(D_tls)
     debug_printf("TLSA: %d %d %d size %d\n", usage, sel, type, rr->size);
 
-  if (usage != 2 && usage != 3) continue;
-  if (sel != 0 && sel != 1) continue;
+  if (  (usage != DANESSL_USAGE_DANE_TA && usage != DANESSL_USAGE_DANE_EE)
+     || (sel != 0 && sel != 1)
+     )
+    continue;
   switch(type)
     {
     case 0:    /* Full: cannot check at present */
index 0b8e3f1f49701f0cae6ce7d5c1996f251c401810..9bd39dfeb553628f7387ce3eac57b10937b71cad 100644 (file)
@@ -446,7 +446,7 @@ DNSSEC _1225._tcp.dane256ee TLSA  3 1 1 827664533176a58b3578e0e91d77d79d036d3a97
 ; full MX, sha256, TA-mode
 ;
 ; openssl x509 -in aux-fixed/exim-ca/example.com/CA/CA.pem -fingerprint -sha256 -noout \
-;  | awk -F= '{print $2}' | tr -d : | tr '[A-F]' '[a-f]'
+; | awk -F= '{print $2}' | tr -d : | tr '[A-F]' '[a-f]'
 ;
 DNSSEC mxdane256ta          MX  1  dane256ta
 DNSSEC dane256ta            A      HOSTIPV4
@@ -519,6 +519,11 @@ DNSSEC dane.no.4            A       127.0.0.1
 DNSSEC NXDOMAIN _1225._tcp.dane.no.3 TLSA 2 0 1 eec923139018c540a344c5191660ecba1ac3708525a98bfc338e17f31d3fa741
 DNSSEC NXDOMAIN _1225._tcp.dane.no.4 TLSA 2 0 1 eec923139018c540a344c5191660ecba1ac3708525a98bfc338e17f31d3fa741
 
+; a mixed-usage set of TLSA records, EE one failing.  TA one coped from dane256ta.
+DNSSEC danemixed            A      127.0.0.1
+DNSSEC _1225._tcp.danemixed TLSA  2 0 1 cb0fa6a633e52c787657f5ca0da1030800223cac459577b9b6a55ac9733348e5
+DNSSEC                      TLSA  3 1 1 8276000000000000000000000000000000000000000000000000000000000000
+
 ; ------- Testing delays ------------
 
 DELAY=500 delay500   A HOSTIPV4
index 81d4d0799a62fe9664282972baa48791d6b3069a..b1dc732947c624a5bbe4c0253d0bbeced1244da1 100644 (file)
 1999-03-02 09:44:33 10HmbV-0005vi-00 ** CALLER@danebroken6.test.ex R=client T=send_to_server: DANE error: danebroken6.test.ex lookup not DNSSEC
 1999-03-02 09:44:33 10HmbV-0005vi-00 CALLER@danebroken6.test.ex: error ignored
 1999-03-02 09:44:33 10HmbV-0005vi-00 Completed
-1999-03-02 09:44:33 10HmbW-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for CALLER@danebroken7.example.com
-1999-03-02 09:44:33 10HmbW-0005vi-00 DANE attempt failed; TLS connection to danebroken7.example.com [127.0.0.1]: (certificate verification failed): certificate invalid
-1999-03-02 09:44:33 10HmbW-0005vi-00 == CALLER@danebroken7.example.com R=client T=send_to_server defer (-37) H=danebroken7.example.com [127.0.0.1]: TLS session: (certificate verification failed): certificate invalid
-1999-03-02 09:44:33 10HmbX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for CALLER@danebroken8.example.com
-1999-03-02 09:44:33 10HmbX-0005vi-00 => CALLER@danebroken8.example.com R=client T=send_to_server H=danebroken8.example.com [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=dane DN="CN=server1.example.net" C="250 OK id=10HmbY-0005vi-00"
-1999-03-02 09:44:33 10HmbX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbW-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for CALLER@danemixed.test.ex
+1999-03-02 09:44:33 10HmbW-0005vi-00 => CALLER@danemixed.test.ex R=client T=send_to_server H=danemixed.test.ex [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=dane DN="CN=server1.example.com" C="250 OK id=10HmbX-0005vi-00"
+1999-03-02 09:44:33 10HmbW-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for CALLER@danebroken7.example.com
+1999-03-02 09:44:33 10HmbY-0005vi-00 DANE attempt failed; TLS connection to danebroken7.example.com [127.0.0.1]: (certificate verification failed): certificate invalid
+1999-03-02 09:44:33 10HmbY-0005vi-00 == CALLER@danebroken7.example.com R=client T=send_to_server defer (-37) H=danebroken7.example.com [127.0.0.1]: TLS session: (certificate verification failed): certificate invalid
+1999-03-02 09:44:33 10HmbZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for CALLER@danebroken8.example.com
+1999-03-02 09:44:33 10HmbZ-0005vi-00 => CALLER@danebroken8.example.com R=client T=send_to_server H=danebroken8.example.com [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=dane DN="CN=server1.example.net" C="250 OK id=10HmcA-0005vi-00"
+1999-03-02 09:44:33 10HmbZ-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 10HmbU-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmbT-0005vi-00@myhost.test.ex for CALLER@danebroken5.test.ex
 1999-03-02 09:44:33 10HmbU-0005vi-00 => :blackhole: <CALLER@danebroken5.test.ex> R=server
 1999-03-02 09:44:33 10HmbU-0005vi-00 Completed
+1999-03-02 09:44:33 "rcpt ACL"
+1999-03-02 09:44:33 10HmbX-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmbW-0005vi-00@myhost.test.ex for CALLER@danemixed.test.ex
+1999-03-02 09:44:33 10HmbX-0005vi-00 => :blackhole: <CALLER@danemixed.test.ex> R=server
+1999-03-02 09:44:33 10HmbX-0005vi-00 Completed
 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 TLS error on connection from localhost [127.0.0.1] (recv): A TLS fatal alert has been received.: Certificate is bad
 1999-03-02 09:44:33 TLS error on connection from localhost [127.0.0.1] (send): The specified session has been invalidated for some reason.
 1999-03-02 09:44:33 "rcpt ACL"
-1999-03-02 09:44:33 10HmbY-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmbX-0005vi-00@myhost.test.ex for CALLER@danebroken8.example.com
-1999-03-02 09:44:33 10HmbY-0005vi-00 => :blackhole: <CALLER@danebroken8.example.com> R=server
-1999-03-02 09:44:33 10HmbY-0005vi-00 Completed
+1999-03-02 09:44:33 10HmcA-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmbZ-0005vi-00@myhost.test.ex for CALLER@danebroken8.example.com
+1999-03-02 09:44:33 10HmcA-0005vi-00 => :blackhole: <CALLER@danebroken8.example.com> R=server
+1999-03-02 09:44:33 10HmcA-0005vi-00 Completed
index f5ac4a7feb58391b34877d44e9f0e1afc0d7dd9e..652661cc1849f9a88b00b295166e67ac6a3d5b16 100644 (file)
@@ -102,9 +102,16 @@ exim -odf CALLER@danebroken6.test.ex
 Testing
 ****
 #
+### A server with a mixed-usage set of TLSAs - the EE-mode one failing verify (should deliver, DANE-mode)
+# that way round to excersize more code in the implementation
+exim -odf CALLER@danemixed.test.ex
+Testing
+****
+#
 killdaemon
-
-
+#
+#
+#
 ### A server with a name not matching the cert.  TA-mode; should fail
 exim -DSERVER=server -DDETAILS=cert.net -bd -oX PORT_D
 ****
index 43492b5f7b9e1417e51080252b542567f0985375..34fcb0fd4629e4a18b0851d347f2c91c0805794b 100644 (file)
@@ -79,6 +79,7 @@ LOG: unexpected disconnection while reading SMTP command from [127.0.0.1] D=qqs
 ### A server insecurely serving a good TLSA record, dane required (delivery should fail)
 ### A server insecurely serving a good A record, dane requested only (should deliver, non-DANE)
 ### A server insecurely serving a good A record, dane required (delivery should fail)
+### A server with a mixed-usage set of TLSAs - the EE-mode one failing verify (should deliver, DANE-mode)
 ### A server with a name not matching the cert.  TA-mode; should fail
 ### A server with a name not matching the cert.  EE-mode; should deliver and claim DANE mode
 
@@ -100,5 +101,6 @@ LOG: unexpected disconnection while reading SMTP command from [127.0.0.1] D=qqs
 ### A server insecurely serving a good TLSA record, dane required (delivery should fail)
 ### A server insecurely serving a good A record, dane requested only (should deliver, non-DANE)
 ### A server insecurely serving a good A record, dane required (delivery should fail)
+### A server with a mixed-usage set of TLSAs - the EE-mode one failing verify (should deliver, DANE-mode)
 ### A server with a name not matching the cert.  TA-mode; should fail
 ### A server with a name not matching the cert.  EE-mode; should deliver and claim DANE mode
index 35e52c5d15a9b269e66fcea5d26b65284344fdac..9bdf21c3fa25813bd583bfb5fb99f1e7f932936f 100644 (file)
@@ -24,6 +24,7 @@
 ### A server insecurely serving a good TLSA record, dane required (delivery should fail)
 ### A server insecurely serving a good A record, dane requested only (should deliver, non-DANE)
 ### A server insecurely serving a good A record, dane required (delivery should fail)
+### A server with a mixed-usage set of TLSAs - the EE-mode one failing verify (should deliver, DANE-mode)
 ### A server with a name not matching the cert.  TA-mode; should fail
 ### A server with a name not matching the cert.  EE-mode; should deliver and claim DANE mode
 
@@ -45,5 +46,6 @@
 ### A server insecurely serving a good TLSA record, dane required (delivery should fail)
 ### A server insecurely serving a good A record, dane requested only (should deliver, non-DANE)
 ### A server insecurely serving a good A record, dane required (delivery should fail)
+### A server with a mixed-usage set of TLSAs - the EE-mode one failing verify (should deliver, DANE-mode)
 ### A server with a name not matching the cert.  TA-mode; should fail
 ### A server with a name not matching the cert.  EE-mode; should deliver and claim DANE mode