Logging: TLSA lookups should honor slow_lookup_log
[exim.git] / src / src / transports / smtp.c
index ad0de52b6bce7b26988d76020c0919a7d5f252fd..9f86033231d093f5a750f6bf0d05eb8836a38570 100644 (file)
@@ -8,6 +8,10 @@
 #include "../exim.h"
 #include "smtp.h"
 
+#if defined(SUPPORT_DANE) && defined(DISABLE_TLS)
+# error TLS is required for DANE
+#endif
+
 
 /* Options specific to the smtp transport. This transport also supports LMTP
 over TCP/IP. The options must be in alphabetic order (note that "_" comes
@@ -277,7 +281,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .gethostbyname =             FALSE,
   .dns_qualify_single =                TRUE,
   .dns_search_parents =                FALSE,
-  .dnssec = { .request=NULL, .require=NULL },
+  .dnssec = { .request= US"*", .require=NULL },
   .delay_after_cutoff =                TRUE,
   .hosts_override =            FALSE,
   .hosts_randomize =           FALSE,
@@ -620,7 +624,7 @@ switch(*errno_value)
     return FALSE;
 
   case ERRNO_WRITEINCOMPLETE:  /* failure to write a complete data block */
-    *message = string_sprintf("failed to write a data block");
+    *message = US"failed to write a data block";
     return FALSE;
 
 #ifdef SUPPORT_I18N
@@ -810,6 +814,10 @@ if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
 #ifdef EXPERIMENTAL_DSN_INFO
 sx->helo_response = string_copy(sx->buffer);
 #endif
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->conn_args.tblock->event_action,
+  US"smtp:ehlo", sx->buffer);
+#endif
 return TRUE;
 }
 
@@ -1184,8 +1192,14 @@ while (count-- > 0)
 
   else if (errno != 0 || sx->buffer[0] == 0)
     {
-    string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
+    gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs;
+
+    /* Use taint-unchecked routines for writing into big_buffer, trusting
+    that we'll never expand it. */
+
+    g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>",
       transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
+    string_from_gstring(g);
     return -2;
     }
 
@@ -1555,20 +1569,20 @@ Globals         f.smtp_authenticated
 Return True on error, otherwise buffer has (possibly empty) terminated string
 */
 
-BOOL
+static BOOL
 smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
                    smtp_transport_options_block *ob)
 {
-uschar *local_authenticated_sender = authenticated_sender;
+uschar * local_authenticated_sender = authenticated_sender;
 
 #ifdef notdef
   debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
 #endif
 
-if (ob->authenticated_sender != NULL)
+if (ob->authenticated_sender)
   {
   uschar *new = expand_string(ob->authenticated_sender);
-  if (new == NULL)
+  if (!new)
     {
     if (!f.expand_string_forcedfail)
       {
@@ -1578,17 +1592,17 @@ if (ob->authenticated_sender != NULL)
       return TRUE;
       }
     }
-  else if (new[0] != 0) local_authenticated_sender = new;
+  else if (*new) local_authenticated_sender = new;
   }
 
 /* Add the authenticated sender address if present */
 
-if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
-    local_authenticated_sender != NULL)
+if (  (f.smtp_authenticated || ob->authenticated_sender_force)
+   && local_authenticated_sender)
   {
-  string_format(buffer, bufsize, " AUTH=%s",
+  string_format_nt(buffer, bufsize, " AUTH=%s",
     auth_xtextencode(local_authenticated_sender,
-    Ustrlen(local_authenticated_sender)));
+      Ustrlen(local_authenticated_sender)));
   client_authenticated_sender = string_copy(local_authenticated_sender);
   }
 else
@@ -1599,77 +1613,6 @@ return FALSE;
 
 
 
-#ifdef SUPPORT_DANE
-/* Lookup TLSA record for host/port.
-Return:  OK            success with dnssec; DANE mode
-         DEFER         Do not use this host now, may retry later
-        FAIL_FORCED    No TLSA record; DANE not usable
-        FAIL           Do not use this connection
-*/
-
-int
-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);
-
-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_AGAIN:
-    return DEFER; /* just defer this TLS'd conn */
-
-  case DNS_SUCCEED:
-    if (sec)
-      {
-      DEBUG(D_transport)
-       {
-       dns_scan dnss;
-       for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
-            rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
-         if (rr->type == T_TLSA && rr->size > 3)
-           {
-           uint16_t payload_length = rr->size - 3;
-           uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
-
-           sp += sprintf(CS sp, "%d ", *p++); /* usage */
-           sp += sprintf(CS sp, "%d ", *p++); /* selector */
-           sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
-           while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
-             sp += sprintf(CS sp, "%02x", *p++);
-
-           debug_printf(" %s\n", s);
-           }
-       }
-      return OK;
-      }
-    log_write(0, LOG_MAIN,
-      "DANE error: TLSA lookup for %s not DNSSEC", host->name);
-    /*FALLTRHOUGH*/
-
-  case DNS_NODATA:     /* no TLSA RR for this lookup */
-  case DNS_NOMATCH:    /* no records at all for this lookup */
-    return dane_required ? FAIL : FAIL_FORCED;
-
-  default:
-  case DNS_FAIL:
-    return dane_required ? FAIL : DEFER;
-  }
-}
-#endif
-
-
-
 typedef struct smtp_compare_s
 {
     uschar                          *current_sender_address;
@@ -1953,7 +1896,6 @@ smtp_transport_options_block * ob = sx->conn_args.tblock->options_block;
 BOOL pass_message = FALSE;
 uschar * message = NULL;
 int yield = OK;
-int rc;
 #ifndef DISABLE_TLS
 uschar * tls_errstr;
 #endif
@@ -2064,6 +2006,7 @@ if (!continue_hostname)
 
     if (sx->conn_args.host->dnssec == DS_YES)
       {
+      int rc;
       if(  sx->dane_required
        || verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
        )
@@ -3040,7 +2983,7 @@ if (  sx->peer_offered & OPTION_UTF8
    && addrlist->prop.utf8_msg
    && !addrlist->prop.utf8_downcvt
    )
-  Ustrcpy(p, " SMTPUTF8"), p += 9;
+  Ustrcpy(p, US" SMTPUTF8"), p += 9;
 #endif
 
 /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
@@ -3061,9 +3004,9 @@ for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
 if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
   {
   if (dsn_ret == dsn_ret_hdrs)
-    { Ustrcpy(p, " RET=HDRS"); p += 9; }
+    { Ustrcpy(p, US" RET=HDRS"); p += 9; }
   else if (dsn_ret == dsn_ret_full)
-    { Ustrcpy(p, " RET=FULL"); p += 9; }
+    { Ustrcpy(p, US" RET=FULL"); p += 9; }
 
   if (dsn_envid)
     {
@@ -3100,7 +3043,7 @@ if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop))
     {
     BOOL first = TRUE;
 
-    Ustrcpy(p, " NOTIFY=");
+    Ustrcpy(p, US" NOTIFY=");
     while (*p) p++;
     for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
       {
@@ -3326,7 +3269,7 @@ if ((rc = fork()))
   _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
   }
 
-if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+testharness_pause_ms(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
 FD_SET(tls_out.active.sock, &rfds);
@@ -3400,7 +3343,7 @@ for (int fd_bits = 3; fd_bits; )
   }
 
 done:
-  if (f.running_in_test_harness) millisleep(100);      /* let logging complete */
+  testharness_pause_ms(100);   /* let logging complete */
   exim_exit(0, US"TLS proxy");
 }
 #endif
@@ -3628,7 +3571,8 @@ for handling the SMTP dot-handling protocol, flagging to apply to headers as
 well as body. Set the appropriate timeout value to be used for each chunk.
 (Haven't been able to make it work using select() for writing yet.) */
 
-if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
+if (  !sx.ok
+   && (!(sx.peer_offered & OPTION_CHUNKING) || !pipelining_active))
   {
   /* Save the first address of the next batch. */
   sx.first_addr = sx.next_addr;
@@ -3689,6 +3633,11 @@ else
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
+  {
+# ifdef MEASURE_TIMING
+  struct timeval t0;
+  gettimeofday(&t0, NULL);
+# endif
   dkim_exim_sign_init();
 # ifdef EXPERIMENTAL_ARC
     {
@@ -3713,6 +3662,10 @@ else
       }
     }
 # endif
+# ifdef MEASURE_TIMING
+  report_time_since(&t0, US"dkim_exim_sign_init (delta)");
+# endif
+  }
   sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
 #else
   sx.ok = transport_write_message(&tctx, 0);
@@ -4262,7 +4215,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          /* Set up a pipe for proxying TLS for the new transport process */
 
          smtp_peer_options |= OPTION_TLS;
-         if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+         if ((sx.ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
            socket_fd = pfd[1];
          else
            set_errno(sx.first_addr, errno, US"internal allocation problem",
@@ -4299,7 +4252,7 @@ propagate it from the initial
          int pid = fork();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
-           if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+           testharness_pause_ms(100); /* let parent debug out */
            /* does not return */
            smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
                            ob->command_timeout);
@@ -4380,7 +4333,8 @@ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
   shutdown(sx.cctx.sock, SHUT_WR);
-  millisleep(f.running_in_test_harness ? 200 : 20);
+  millisleep(20);
+  testharness_pause_ms(200);
   if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
     for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
       i--;                             /* drain socket */
@@ -4598,6 +4552,17 @@ if (!hostlist || (ob->hosts_override && ob->hosts))
     else
       if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
 
+    if (is_tainted(s))
+      {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+       "attempt to use tainted host list '%s' from '%s' in transport %s",
+       s, ob->hosts, tblock->name);
+      /* Avoid leaking info to an attacker */
+      addrlist->message = US"internal configuration error";
+      addrlist->transport_return = PANIC;
+      return FALSE;
+      }
+
     host_build_hostlist(&hostlist, s, ob->hosts_randomize);
 
     /* Check that the expansion yielded something useful. */
@@ -5082,7 +5047,7 @@ retry_non_continued:
 
       if (expanded_hosts)
        {
-       thost = store_get(sizeof(host_item));
+       thost = store_get(sizeof(host_item), FALSE);
        *thost = *host;
        thost->name = string_copy(host->name);
        thost->address = string_copy(host->address);
@@ -5323,7 +5288,7 @@ retry_non_continued:
          ob->hosts_max_try_hardlimit);
       }
 
-    if (f.running_in_test_harness) millisleep(500); /* let server debug out */
+    testharness_pause_ms(500); /* let server debug out */
     }   /* End of loop for trying multiple hosts. */
 
   /* If we failed to find a matching host in the list, for an already-open