summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2017-12-22 17:19:37 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2017-12-22 20:42:38 +0000
commit94c1328507098238ae5ec784150c1ae58f3b3118 (patch)
tree33f9a1ecdf808459581ec9f5254cc5e5fd33ccb1 /src
parent2b01e5359b79cfa9b31296700eb7fc5ae69162c5 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/src/tls-gnu.c117
1 files changed, 97 insertions, 20 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<<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 */