DANE: avoid info leak by checking TLSA dnssec before connecting to MX
[exim.git] / src / src / transports / smtp.c
index 758f1143aeb28480eddeb09d4b4d4e7fb13dd5cb..8f1e0bff8ac7592fe4a303b93c9eb68b8732cca5 100644 (file)
@@ -1186,20 +1186,24 @@ tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
 /* move this out to host.c given the similarity to dns_lookup() ? */
 uschar buffer[300];
 const uschar * fullname = buffer;
+int rc;
+BOOL sec;
 
 /* TLSA lookup string */
 (void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
 
-switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname))
+rc = dns_lookup(dnsa, buffer, T_TLSA, &fullname);
+sec = dns_is_secure(dnsa);
+DEBUG(D_transport)
+  debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
+
+switch (rc)
   {
   case DNS_SUCCEED:
-    if (!dns_is_secure(dnsa))
-      {
-      log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
-      return DEFER;
-      }
-    return OK;
+    if (sec) return OK;
 
+    log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
+    /*FALLTHROUGH*/
   case DNS_AGAIN:
     return DEFER; /* just defer this TLS'd conn */
 
@@ -1553,34 +1557,17 @@ if (sx->smtps)
 the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled
 specially so they can be identified for retries. */
 
-if (continue_hostname == NULL)
+if (!continue_hostname)
   {
   if (sx->verify)
     HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->interface, sx->port);
 
-  /* This puts port into host->port */
-  sx->inblock.sock = sx->outblock.sock =
-    smtp_connect(sx->host, sx->host_af, sx->port, sx->interface,
-                 sx->ob->connect_timeout, sx->tblock);
+  /* Get the actual port the connection will use, into sx->host */
 
-  if (sx->inblock.sock < 0)
-    {
-    uschar * msg = NULL;
-    if (sx->verify)
-      {
-      msg = US strerror(errno);
-      HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
-      }
-    set_errno_nohost(sx->addrlist,
-      errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
-      sx->verify ? string_sprintf("could not connect: %s", msg)
-            : NULL,
-      DEFER, FALSE);
-    sx->send_quit = FALSE;
-    return DEFER;
-    }
+  smtp_port_for_connect(sx->host, sx->port);
 
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+    /* Do TLSA lookup for DANE */
     {
     tls_out.dane_verified = FALSE;
     tls_out.tlsa_usage = 0;
@@ -1592,7 +1579,9 @@ if (continue_hostname == NULL)
        )
        switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
          {
-         case OK:              sx->dane = TRUE; break;
+         case OK:              sx->dane = TRUE;
+                               sx->ob->tls_tempfail_tryclear = FALSE;
+                               break;
          case FAIL_FORCED:     break;
          default:              set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
                                  string_sprintf("DANE error: tlsa lookup %s",
@@ -1608,12 +1597,32 @@ if (continue_hostname == NULL)
        FAIL, FALSE);
       return FAIL;
       }
-
-    if (sx->dane)
-      sx->ob->tls_tempfail_tryclear = FALSE;
     }
 #endif /*DANE*/
 
+  /* Make the TCP connection */
+
+  sx->inblock.sock = sx->outblock.sock =
+    smtp_connect(sx->host, sx->host_af, sx->interface,
+                 sx->ob->connect_timeout, sx->tblock);
+
+  if (sx->inblock.sock < 0)
+    {
+    uschar * msg = NULL;
+    if (sx->verify)
+      {
+      msg = US strerror(errno);
+      HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
+      }
+    set_errno_nohost(sx->addrlist,
+      errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+      sx->verify ? string_sprintf("could not connect: %s", msg)
+            : NULL,
+      DEFER, FALSE);
+    sx->send_quit = FALSE;
+    return DEFER;
+    }
+
   /* Expand the greeting message while waiting for the initial response. (Makes
   sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
   delayed till here so that $sending_interface and $sending_port are set. */