When following a CNAME chain, if any lookup is insecure the whole must be too
[exim.git] / src / src / verify.c
index d2ecb9cdeebec18a9c0365a382e52fd0c0279de0..96740f8f382acdf44f13ac99e940005efbfe992a 100644 (file)
@@ -426,6 +426,10 @@ else
     BOOL esmtp;
     BOOL suppress_tls = FALSE;
     uschar *interface = NULL;  /* Outgoing interface to use; NULL => any */
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+    BOOL dane = FALSE;
+    dns_answer tlsa_dnsa;
+#endif
     uschar inbuffer[4096];
     uschar outbuffer[1024];
     uschar responsebuffer[4096];
@@ -464,6 +468,7 @@ else
     deliver_host_address = host->address;
     deliver_host_port = host->port;
     deliver_domain = addr->domain;
+    transport_name = addr->transport->name;
 
     if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
             US"callout") ||
@@ -478,6 +483,36 @@ else
 
     HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port);
 
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+      {
+      BOOL dane_required;
+      int rc;
+
+      tls_out.dane_verified = FALSE;
+      tls_out.tlsa_usage = 0;
+
+      dane_required =
+       verify_check_given_host(&ob->hosts_require_dane, host) == OK;
+
+      if (host->dnssec == DS_YES)
+       {
+       if(  dane_required
+         || verify_check_given_host(&ob->hosts_try_dane, host) == OK
+         )
+         if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK)
+           return rc;
+       }
+      else if (dane_required)
+       {
+       log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name);
+       return FAIL;
+       }
+
+      if (dane)
+       ob->tls_tempfail_tryclear = FALSE;
+      }
+#endif  /*DANE*/
+
     /* Set up the buffer for reading SMTP response packets. */
 
     inblock.buffer = inbuffer;
@@ -503,8 +538,8 @@ else
 
     inblock.sock = outblock.sock =
       smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL
-#ifdef EXPERIMENTAL_TPDA
-    /*XXX tpda action? NULL for now. */
+#ifdef EXPERIMENTAL_EVENT
+    /*XXX event action? NULL for now. */
                  , NULL
 #endif
                  );
@@ -513,6 +548,7 @@ else
       {
       addr->message = string_sprintf("could not connect to %s [%s]: %s",
           host->name, host->address, strerror(errno));
+      transport_name = NULL;
       deliver_host = deliver_host_address = NULL;
       deliver_domain = save_deliver_domain;
       continue;
@@ -546,19 +582,22 @@ else
       if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
         goto RESPONSE_FAILED;
 
-#ifdef EXPERIMENTAL_TPDA
-      if (tpda_raise_event(addr->transport->tpda_event_action,
-                           US"smtp:connect", responsebuffer) == DEFER)
+#ifdef EXPERIMENTAL_EVENT
+      lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
+       : host->dnssec==DS_NO ? US"no" : NULL;
+      if (event_raise(addr->transport->event_action,
+                           US"smtp:connect", responsebuffer))
        {
+       lookup_dnssec_authenticated = NULL;
        /* Logging?  Debug? */
        goto RESPONSE_FAILED;
        }
+      lookup_dnssec_authenticated = NULL;
 #endif
       }
 
     /* Not worth checking greeting line for ESMTP support */
-    if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
-      host->name, host->address, NULL) != OK))
+    if (!(esmtp = verify_check_given_host(&(ob->hosts_avoid_esmtp), host) != OK))
       DEBUG(D_transport)
         debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
 
@@ -616,11 +655,9 @@ else
     for error analysis. */
 
 #ifdef SUPPORT_TLS
-    if (tls_offered &&
-       verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name,
-         host->address, NULL) != OK &&
-       verify_check_this_host(&(ob->hosts_verify_avoid_tls), NULL, host->name,
-         host->address, NULL) != OK
+    if (  tls_offered
+       && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK
+       && verify_check_given_host(&ob->hosts_verify_avoid_tls, host) != OK
        )
       {
       uschar buffer2[4096];
@@ -654,7 +691,11 @@ else
        int rc;
 
        ob->command_timeout = callout;
-        rc = tls_client_start(inblock.sock, host, addr, addr->transport);
+        rc = tls_client_start(inblock.sock, host, addr, addr->transport
+#ifdef EXPERIMENTAL_DANE
+                           , dane ? &tlsa_dnsa : NULL
+#endif
+                           );
        ob->command_timeout = oldtimeout;
 
         /* TLS negotiation failed; give an error.  Try in clear on a new connection,
@@ -664,17 +705,12 @@ else
          if (  rc == DEFER
             && ob->tls_tempfail_tryclear
             && !smtps
-            && verify_check_this_host(&(ob->hosts_require_tls), NULL,
-              host->name, host->address, NULL) != OK
-#ifdef EXPERIMENTAL_DANE
-            && verify_check_this_host(&(ob->hosts_require_dane), NULL,
-              host->name, host->address, NULL) != OK
-#endif
+            && verify_check_given_host(&ob->hosts_require_tls, host) != OK
             )
            {
            (void)close(inblock.sock);
-#ifdef EXPERIMENTAL_TPDA
-           (void) tpda_raise_event(addr->transport->tpda_event_action,
+#ifdef EXPERIMENTAL_EVENT
+           (void) event_raise(addr->transport->event_action,
                                    US"tcp:close", NULL);
 #endif
            log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
@@ -704,18 +740,19 @@ else
 
     /* If the host is required to use a secure channel, ensure that we have one. */
     if (tls_out.active < 0)
-      if (  verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
-             host->address, NULL) == OK
+      if (
 #ifdef EXPERIMENTAL_DANE
-        || verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name,
-             host->address, NULL) == OK
+        dane ||
 #endif
+         verify_check_given_host(&ob->hosts_require_tls, host) == OK
         )
         {
         /*save_errno = ERRNO_TLSREQUIRED;*/
-        log_write(0, LOG_MAIN, "a TLS session is required for %s [%s], but %s",
+        log_write(0, LOG_MAIN,
+         "H=%s [%s]: a TLS session is required for this host, but %s",
           host->name, host->address,
-       tls_offered? "an attempt to start TLS failed" : "the server did not offer TLS support");
+         tls_offered ? "an attempt to start TLS failed"
+                     : "the server did not offer TLS support");
         done= FALSE;
         goto TLS_FAILED;
         }
@@ -842,9 +879,7 @@ else
         /* If accepted, we aren't going to do any further tests below. */
 
         if (random_ok)
-          {
           new_domain_record.random_result = ccache_accept;
-          }
 
         /* Otherwise, cache a real negative response, and get back to the right
         state to send RCPT. Unless there's some problem such as a dropped
@@ -1009,9 +1044,7 @@ else
       cutthrough_addr = *addr;         /* Save the address_item for later logging */
       cutthrough_addr.next =     NULL;
       cutthrough_addr.host_used = store_get(sizeof(host_item));
-      cutthrough_addr.host_used->name =    host->name;
-      cutthrough_addr.host_used->address = host->address;
-      cutthrough_addr.host_used->port =    port;
+      *(cutthrough_addr.host_used) = *host;
       if (addr->parent)
         *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent;
       ctblock.buffer = ctbuffer;
@@ -1031,8 +1064,8 @@ else
       tls_close(FALSE, TRUE);
 #endif
       (void)close(inblock.sock);
-#ifdef EXPERIMENTAL_TPDA
-      (void) tpda_raise_event(addr->transport->tpda_event_action,
+#ifdef EXPERIMENTAL_EVENT
+      (void) event_raise(addr->transport->event_action,
                              US"tcp:close", NULL);
 #endif
       }
@@ -1817,8 +1850,10 @@ while (addr_new != NULL)
 #ifdef SUPPORT_TLS
          deliver_set_expansions(addr);
 #endif
+         verify_mode = is_recipient ? US"R" : US"S";
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
+         verify_mode = NULL;
           }
         }
       else
@@ -3121,6 +3156,15 @@ return rc;
 
 
 
+/*************************************************
+*      Check the given host item matches a list  *
+*************************************************/
+int
+verify_check_given_host(uschar **listptr, host_item *host)
+{
+return verify_check_this_host(listptr, NULL, host->name, host->address, NULL);
+}
+
 /*************************************************
 *      Check the remote host matches a list      *
 *************************************************/