DANE: Fix 2-rcpt message, diff domins case. Bug 2265
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 23 Aug 2020 14:32:48 +0000 (15:32 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 23 Aug 2020 16:05:52 +0000 (17:05 +0100)
doc/doc-docbook/spec.xfpt
src/src/debug.c
src/src/deliver.c
src/src/macros.h
src/src/transports/smtp.c
src/src/verify.c
test/confs/5801 [new file with mode: 0644]
test/dnszones-src/db.test.ex
test/log/5801 [new file with mode: 0644]
test/scripts/5800-DANE/5801 [new file with mode: 0644]

index ab13a427b67ebc741328a80165a5b4f061107938..9a4e0a1a91e4f998a59ba755ba5b649a8ca82973 100644 (file)
@@ -28771,6 +28771,12 @@ Some other recently added features may only be available in one or the other.
 This should be documented with the feature.  If the documentation does not
 explicitly state that the feature is infeasible in the other TLS
 implementation, then patches are welcome.
+.new
+.next
+The output from "exim -bV" will show which (if any) support was included
+in the build.
+Also, the macro "_HAVE_OPENSSL" or "_HAVE_GNUTLS" will be defined.
+.wen
 .endlist
 
 
index 90c48dde407e2ebf3b8abe5a12585ee11a7fd08d..fee0b7a810c5fd6cee4fe2a1aeb57f6dc0e562f1 100644 (file)
@@ -31,6 +31,7 @@ const uschar * rc_names[] = {         /* Mostly for debug output */
   [CANCELLED] =                US"CANCELLED",
   [FAIL_SEND] =                US"FAIL_SEND",
   [FAIL_DROP] =                US"FAIL_DROP",
+  [DANE] =             US"DANE",
 };
 
 const uschar * dns_rc_names[] = {
index dd922c72828c79819b3dd4edf1e9b00312078ad3..a47440695220223ab5dc1e5d22bcb54eaf0ee3ed 100644 (file)
@@ -460,6 +460,9 @@ TRUE if the lists refer to the same hosts in the same order, except that
 This enables Exim to use a single SMTP transaction for sending to two entirely
 different domains that happen to end up pointing at the same hosts.
 
+We do not try to batch up different A-record host names that refer to the
+same IP.
+
 Arguments:
   one       points to the first host list
   two       points to the second host list
index 5c3fa06f6b7fe50721f0405dd05497f7950599f7..8e2050e22e11b4355a221c76cfa95979dca8bfeb 100644 (file)
@@ -305,6 +305,7 @@ Use rc_names[] for debug strings. */
 #define CANCELLED     13    /* Authentication cancelled */
 #define FAIL_SEND     14    /* send() failed in authenticator */
 #define FAIL_DROP     15    /* Fail and drop connection (used in ACL) */
+#define DANE         16    /* Deferred for domain mismatch (used in transport) */
 
 /* Returns from the deliver_message() function */
 
index d1deffa6f77a8c4f4f034644511123008a7fdba7..447f76a9b5c9dddcbbb9c387b793e7218fcecba9 100644 (file)
@@ -2017,11 +2017,12 @@ if (!continue_hostname)
        switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
          {
          case OK:              sx->conn_args.dane = TRUE;
-                               ob->tls_tempfail_tryclear = FALSE;
-                               ob->tls_sni = sx->addrlist->domain;
+                               ob->tls_tempfail_tryclear = FALSE;      /* force TLS */
+                               ob->tls_sni = sx->first_addr->domain;   /* force SNI */
                                break;
          case FAIL_FORCED:     break;
-         default:              set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
+         default:
+         set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
                                  string_sprintf("DANE error: tlsa lookup %s",
                                    rc_to_string(rc)),
                                  rc, FALSE, &sx->delivery_start);
@@ -3442,6 +3443,7 @@ BOOL pass_message = FALSE;
 uschar *message = NULL;
 uschar new_message_id[MESSAGE_ID_LENGTH + 1];
 smtp_context * sx = store_get(sizeof(*sx), TRUE);      /* tainted, for the data buffers */
+BOOL dane_held;
 
 *message_defer = FALSE;
 
@@ -3457,13 +3459,36 @@ sx->conn_args.tblock = tblock;
 gettimeofday(&sx->delivery_start, NULL);
 sx->sync_addr = sx->first_addr = addrlist;
 
-/* Get the channel set up ready for a message (MAIL FROM being the next
-SMTP command to send */
+DANE_DOMAINS:
+dane_held = FALSE;
+
+/* Get the channel set up ready for a message, MAIL FROM being the next
+SMTP command to send. */
 
 if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK)
   {
   timesince(&addrlist->delivery_time, &sx->delivery_start);
-  return rc;
+  yield = rc;
+  goto TIDYUP;
+  }
+
+/*XXX*/
+/* If the connection used DANE, ignore for now any addresses with incompatible
+domains.  The SNI has to be the domain.  Arrange a whole new TCP conn later,
+just in case only TLS isn't enough. */
+
+if (sx->conn_args.dane)
+  {
+  const uschar * dane_domain = sx->first_addr->domain;
+
+  for (address_item * a = sx->first_addr->next; a; a = a->next)
+    if (  a->transport_return == PENDING_DEFER
+       && Ustrcmp(dane_domain, a->domain) != 0)
+      {
+      DEBUG(D_transport) debug_printf("DANE: holding %s for later\n", a->domain);
+      dane_held = TRUE;
+      a->transport_return = DANE;
+      }
   }
 
 /* If there is a filter command specified for this transport, we can now
@@ -4213,7 +4238,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
 
 
       if (sx->first_addr != NULL)      /* More addresses still to be sent */
-        {                              /*   in this run of the transport */
+        {                              /*   on this connection            */
         continue_sequence++;           /* Causes * in logging */
        pipelining_active = sx->pipelining_used;    /* was cleared at DATA */
         goto SEND_MESSAGE;
@@ -4249,7 +4274,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
                                      '2', ob->command_timeout);
 
          if (sx->ok && f.continue_more)
-           return yield;               /* More addresses for another run */
+           goto TIDYUP;                /* More addresses for another run */
          }
        else
          {
@@ -4269,7 +4294,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
       else
 #endif
        if (f.continue_more)
-         return yield;                 /* More addresses for another run */
+         goto TIDYUP;                  /* More addresses for another run */
 
       /* If the socket is successfully passed, we mustn't send QUIT (or
       indeed anything!) from here. */
@@ -4309,7 +4334,7 @@ propagate it from the initial
            sx->cctx.sock = -1;
            continue_transport = NULL;
            continue_hostname = NULL;
-           return yield;
+           goto TIDYUP;
            }
          log_write(0, LOG_PANIC_DIE, "fork failed");
          }
@@ -4384,9 +4409,35 @@ if (sx->send_quit)
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
 #endif
 
+/*XXX*/
+if (dane_held)
+  {
+  sx->first_addr = NULL;
+  for (address_item * a = sx->addrlist->next; a; a = a->next)
+    if (a->transport_return == DANE)
+      {
+      a->transport_return = PENDING_DEFER;
+      if (!sx->first_addr)
+       {
+       /* Remember the new start-point in the addrlist, for smtp_setup_conn()
+       to get the domain string for SNI */
+
+       sx->first_addr = a;
+       DEBUG(D_transport) debug_printf("DANE: go-around for %s\n", a->domain);
+       }
+      }
+  goto DANE_DOMAINS;
+  }
+
 continue_transport = NULL;
 continue_hostname = NULL;
 return yield;
+
+TIDYUP:
+if (dane_held) for (address_item * a = sx->addrlist->next; a; a = a->next)
+  if (a->transport_return == DANE)
+    a->transport_return = PENDING_DEFER;
+return yield;
 }
 
 
index efc05fcf188ab10889e70263caf678aae0dade56..a50ac8b7b3984eae03592e6d9a1828e7d84de1ab 100644 (file)
@@ -654,7 +654,7 @@ coding means skipping this whole loop and doing the append separately.  */
     if (!sx) sx = store_get(sizeof(*sx), TRUE);        /* tainted buffers */
     memset(sx, 0, sizeof(*sx));
 
-    sx->addrlist = addr;
+    sx->addrlist = sx->first_addr = addr;
     sx->conn_args.host = host;
     sx->conn_args.host_af = host_af,
     sx->port = port;
diff --git a/test/confs/5801 b/test/confs/5801
new file mode 100644 (file)
index 0000000..f0f21e2
--- /dev/null
@@ -0,0 +1,89 @@
+# Exim test configuration 5801
+# DANE common
+
+SERVER=
+CONTROL= *
+
+.include DIR/aux-var/tls_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+.ifndef OPT
+acl_smtp_rcpt = accept
+.else
+acl_smtp_rcpt = accept verify = recipient/callout
+.endif
+
+log_selector =  +received_recipients +tls_certificate_verified +tls_sni
+
+queue_run_in_order
+
+tls_advertise_hosts = *
+.ifdef _HAVE_GNUTLS
+# needed to force generation
+tls_dhparam = historic
+.endif
+
+# Set certificate only if server
+CDIR1 = DIR/aux-fixed/exim-ca/example.net/server1.example.net
+CDIR2 = DIR/aux-fixed/exim-ca/example.com/server1.example.com
+
+
+tls_certificate = ${if eq {SERVER}{server} \
+       {${if or {{eq {DETAILS}{ta}} {eq {DETAILS}{ca}} {eq {DETAILS}{ee}}} \
+               {CDIR2/fullchain.pem}\
+               {CDIR1/fullchain.pem}}}\
+       fail}
+
+tls_privatekey = ${if eq {SERVER}{server} \
+       {${if or {{eq {DETAILS}{ta}} {eq {DETAILS}{ca}} {eq {DETAILS}{ee}}} \
+               {CDIR2/server1.example.com.unlocked.key}\
+               {CDIR1/server1.example.net.unlocked.key}}}\
+       fail}
+
+# ----- Routers -----
+
+begin routers
+
+client:
+  driver = dnslookup
+  condition = ${if eq {SERVER}{}}
+  dnssec_request_domains = *
+  self = send
+  transport = send_to_server
+  errors_to = ""
+
+server:
+  driver = redirect
+  data = :blackhole:
+
+
+# ----- Transports -----
+
+begin transports
+
+send_to_server:
+  driver = smtp
+  allow_localhost
+  port = PORT_D
+  hosts_try_fastopen = :
+
+  hosts_try_dane =     CONTROL
+  hosts_require_dane = HOSTIPV4
+  tls_verify_cert_hostnames = ${if eq {OPT}{no_certname} {}{*}}
+  tls_try_verify_hosts = thishost.test.ex
+  tls_verify_certificates = ${if eq {DETAILS}{ca} {CDIR2/ca_chain.pem} {}}
+
+
+
+# ----- Retry -----
+
+
+begin retry
+
+* * F,5d,10s
+
+
+# End
index 9b6684ac7adb1aff75ec32cdb7c710f043017471..f15bf7a7fcea211dad20429080b8dd4762defbc9 100644 (file)
@@ -441,6 +441,7 @@ AA a-aa        A V4NET.0.0.100
 ; | awk '{print $2}'
 ;
 DNSSEC mxdane512ee          MX  1  dane512ee
+DNSSEC mxdane512ee1         MX  1  dane512ee
 DNSSEC dane512ee            A      HOSTIPV4
 DNSSEC _1225._tcp.dane512ee TLSA  3 1 2 c0c2fc12e9fe1abf0ae7b1f2ad2798a4689668db8cf7f7b771a43bf8a4f1d9741ef103bad470b1201157150fbd6182054b0170e90ce66b944a82a0a9c81281af
 
diff --git a/test/log/5801 b/test/log/5801
new file mode 100644 (file)
index 0000000..3cf1369
--- /dev/null
@@ -0,0 +1,13 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for t00@mxdane512ee.test.ex t01@mxdane512ee1.test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => t00@mxdane512ee.test.ex R=client T=send_to_server H=dane512ee.test.ex [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=dane C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 => t01@mxdane512ee1.test.ex R=client T=send_to_server H=dane512ee.test.ex [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=dane C="250 OK id=10HmaZ-0005vi-00"
+1999-03-02 09:44:33 10HmaX-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 PORT_D
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no SNI=mxdane512ee.test.ex S=sss id=E10HmaX-0005vi-00@myhost.test.ex for t00@mxdane512ee.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <t00@mxdane512ee.test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= <> H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no SNI=mxdane512ee1.test.ex S=sss id=E10HmaX-0005vi-00@myhost.test.ex for t01@mxdane512ee1.test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: <t01@mxdane512ee1.test.ex> R=server
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
diff --git a/test/scripts/5800-DANE/5801 b/test/scripts/5800-DANE/5801
new file mode 100644 (file)
index 0000000..98fa6b1
--- /dev/null
@@ -0,0 +1,12 @@
+# DANE client: conflicting domain
+#
+exim -DSERVER=server -DDETAILS=ee -bd -oX PORT_D
+****
+#
+#
+# A single message with 2 receipients, different domains though same MX host
+exim -odf t00@mxdane512ee.test.ex t01@mxdane512ee1.test.ex
+****
+#
+killdaemon
+no_msglog_check