From 94c1328507098238ae5ec784150c1ae58f3b3118 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Fri, 22 Dec 2017 17:19:37 +0000 Subject: [PATCH] DANE/GnuTLS: split verification of mixed sets of TLSA records by usage 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 | 117 ++++++++++++++++++++++++----- test/dnszones-src/db.test.ex | 7 +- test/log/5820 | 25 +++--- test/scripts/5820-DANE-GnuTLS/5820 | 11 ++- test/stderr/5820 | 2 + test/stdout/5820 | 2 + 6 files changed, 132 insertions(+), 32 deletions(-) diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 1fee6c107..8cf28fca3 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -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<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 */ diff --git a/test/dnszones-src/db.test.ex b/test/dnszones-src/db.test.ex index 0b8e3f1f4..9bd39dfeb 100644 --- a/test/dnszones-src/db.test.ex +++ b/test/dnszones-src/db.test.ex @@ -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 diff --git a/test/log/5820 b/test/log/5820 index 81d4d0799..b1dc73294 100644 --- a/test/log/5820 +++ b/test/log/5820 @@ -59,12 +59,15 @@ 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 @@ -111,10 +114,14 @@ 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: 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: 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: 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: R=server +1999-03-02 09:44:33 10HmcA-0005vi-00 Completed diff --git a/test/scripts/5820-DANE-GnuTLS/5820 b/test/scripts/5820-DANE-GnuTLS/5820 index f5ac4a7fe..652661cc1 100644 --- a/test/scripts/5820-DANE-GnuTLS/5820 +++ b/test/scripts/5820-DANE-GnuTLS/5820 @@ -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 **** diff --git a/test/stderr/5820 b/test/stderr/5820 index 43492b5f7..34fcb0fd4 100644 --- a/test/stderr/5820 +++ b/test/stderr/5820 @@ -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 diff --git a/test/stdout/5820 b/test/stdout/5820 index 35e52c5d1..9bdf21c3f 100644 --- a/test/stdout/5820 +++ b/test/stdout/5820 @@ -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 -- 2.25.1