Support dnssec in verify-callout use of smtp transport.
[exim.git] / src / src / verify.c
index 6e3e6a3afba7ca906fb2bd5e00bc62222e1e7193..c5ffdae4e7d9a4045f594c1453b4f8dd47d23bbb 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with verifying things. The original code for callout
@@ -373,10 +373,13 @@ if (!addr->transport)
   {
   HDEBUG(D_verify) debug_printf("cannot callout via null transport\n");
   }
+else if (Ustrcmp(addr->transport->driver_name, "smtp") != 0)
+  log_write(0, LOG_MAIN|LOG_PANIC|LOG_CONFIG_FOR, "callout transport '%s': %s is non-smtp",
+    addr->transport->name, addr->transport->driver_name);
 else
   {
   smtp_transport_options_block *ob =
-    (smtp_transport_options_block *)(addr->transport->options_block);
+    (smtp_transport_options_block *)addr->transport->options_block;
 
   /* The information wasn't available in the cache, so we have to do a real
   callout and save the result in the cache for next time, unless no_cache is set,
@@ -538,7 +541,7 @@ else
     #endif
       if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
         goto RESPONSE_FAILED;
-    
+
     /* 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))
@@ -601,7 +604,10 @@ else
     #ifdef SUPPORT_TLS
     if (tls_offered &&
        verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name,
-         host->address, NULL) != OK)
+         host->address, NULL) != OK &&
+       verify_check_this_host(&(ob->hosts_verify_avoid_tls), NULL, host->name,
+         host->address, NULL) != OK
+       )
       {
       uschar buffer2[4096];
       if (  !smtps
@@ -631,12 +637,15 @@ else
       else
         {
         int rc = tls_client_start(inblock.sock, host, addr,
-        NULL,                    /* No DH param */
         ob->tls_certificate, ob->tls_privatekey,
         ob->tls_sni,
         ob->tls_verify_certificates, ob->tls_crl,
-        ob->tls_require_ciphers,     ob->tls_dh_min_bits,
-        callout);
+        ob->tls_require_ciphers,
+#ifdef EXPERIMENTAL_OCSP
+        ob->hosts_require_ocsp,
+#endif
+        ob->tls_dh_min_bits, callout,
+         ob->tls_verify_hosts, ob->tls_try_verify_hosts);
 
         /* TLS negotiation failed; give an error.  Try in clear on a new connection,
            if the options permit it for this host. */
@@ -689,13 +698,25 @@ else
 
     done = TRUE; /* so far so good; have response to HELO */
 
-    /*XXX the EHLO response would be analyzed here for IGNOREQUOTA, SIZE, PIPELINING, AUTH */
-    /* If we haven't authenticated, but are required to, give up. */
+    /*XXX the EHLO response would be analyzed here for IGNOREQUOTA, SIZE, PIPELINING */
 
-    /*XXX "filter command specified for this transport" ??? */
-    /* for now, transport_filter by cutthrough-delivery is not supported */
+    /* For now, transport_filter by cutthrough-delivery is not supported */
     /* Need proper integration with the proper transport mechanism. */
-
+    if (cutthrough_delivery)
+      {
+      if (addr->transport->filter_command)
+        {
+        cutthrough_delivery= FALSE;
+        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
+        }
+      #ifndef DISABLE_DKIM
+      if (ob->dkim_domain)
+        {
+        cutthrough_delivery= FALSE;
+        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
+        }
+      #endif
+      }
 
     SEND_FAILED:
     RESPONSE_FAILED:
@@ -717,11 +738,27 @@ else
         }
       }
 
+    /* If we haven't authenticated, but are required to, give up. */
+    /* Try to AUTH */
+
+    else done = smtp_auth(responsebuffer, sizeof(responsebuffer),
+       addr, host, ob, esmtp, &inblock, &outblock) == OK  &&
+
+               /* Copy AUTH info for logging */
+      ( (addr->authenticator = client_authenticator),
+        (addr->auth_id = client_authenticated_id),
+
+    /* Build a mail-AUTH string (re-using responsebuffer for convenience */
+        !smtp_mail_auth_str(responsebuffer, sizeof(responsebuffer), addr, ob)
+      )  &&
+
+      ( (addr->auth_sndr = client_authenticated_sender),
+
     /* Send the MAIL command */
+        (smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>%s\r\n",
+          from_address, responsebuffer) >= 0)
+      )  &&
 
-    else done =
-      smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n",
-        from_address) >= 0 &&
       smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
         '2', callout);
 
@@ -942,6 +979,7 @@ else
       {
       cutthrough_fd= outblock.sock;    /* We assume no buffer in use in the outblock */
       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;
@@ -1210,27 +1248,43 @@ return cutthrough_response('3', NULL) == '3';
 }
 
 
+/* fd and use_crlf args only to match write_chunk() */
+static BOOL
+cutthrough_write_chunk(int fd, uschar * s, int len, BOOL use_crlf)
+{
+uschar * s2;
+while(s && (s2 = Ustrchr(s, '\n')))
+ {
+ if(!cutthrough_puts(s, s2-s) || !cutthrough_put_nl())
+  return FALSE;
+ s = s2+1;
+ }
+return TRUE;
+}
+
+
 /* Buffered send of headers.  Return success boolean. */
 /* Expands newlines to wire format (CR,NL).           */
 /* Also sends header-terminating blank line.          */
 BOOL
 cutthrough_headers_send( void )
 {
-header_line * h;
-uschar * cp1, * cp2;
-
 if(cutthrough_fd < 0)
   return FALSE;
 
-for(h= header_list; h != NULL; h= h->next)
-  if(h->type != htype_old  &&  h->text != NULL)
-    for (cp1 = h->text; *cp1 && (cp2 = Ustrchr(cp1, '\n')); cp1 = cp2+1)
-      if(  !cutthrough_puts(cp1, cp2-cp1)
-        || !cutthrough_put_nl())
-        return FALSE;
+/* We share a routine with the mainline transport to handle header add/remove/rewrites,
+   but having a separate buffered-output function (for now)
+*/
+HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n");
+
+if (!transport_headers_send(&cutthrough_addr, cutthrough_fd,
+       cutthrough_addr.transport->add_headers, cutthrough_addr.transport->remove_headers,
+       &cutthrough_write_chunk, TRUE,
+       cutthrough_addr.transport->rewrite_rules, cutthrough_addr.transport->rewrite_existflags))
+  return FALSE;
 
-HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>>(nl)\n");
-return cutthrough_put_nl();
+HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n");
+return TRUE;
 }
 
 
@@ -1518,6 +1572,12 @@ addresses, such rewriting fails. */
 
 if (address[0] == 0) return OK;
 
+/* Flip the legacy TLS-related variables over to the outbound set in case
+they're used in the context of a transport used by verification. Reset them
+at exit from this routine. */
+
+tls_modify_variables(&tls_out);
+
 /* Save a copy of the sender address for re-instating if we change it to <>
 while verifying a sender address (a nice bit of self-reference there). */
 
@@ -1689,8 +1749,20 @@ while (addr_new != NULL)
                   string_is_ip_address(host->name, NULL) != 0)
                 (void)host_find_byname(host, NULL, flags, &canonical_name, TRUE);
               else
+               {
+               uschar * d_request = NULL, * d_require = NULL;
+               if (Ustrcmp(addr->transport->driver_name, "smtp") == 0)
+                 {
+                 smtp_transport_options_block * ob =
+                     (smtp_transport_options_block *)
+                       addr->transport->options_block;
+                 d_request = ob->dnssec_request_domains;
+                 d_require = ob->dnssec_require_domains;
+                 }
+
                 (void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
-                  &canonical_name, NULL);
+                 d_request, d_require, &canonical_name, NULL);
+               }
               }
             }
           }
@@ -1766,8 +1838,12 @@ while (addr_new != NULL)
       }
     cancel_cutthrough_connection("routing hard fail");
 
-    if (!full_info) return copy_error(vaddr, addr, FAIL);
-      else yield = FAIL;
+    if (!full_info)
+    {
+      yield = copy_error(vaddr, addr, FAIL);
+      goto out;
+    }
+    else yield = FAIL;
     }
 
   /* Soft failure */
@@ -1801,8 +1877,12 @@ while (addr_new != NULL)
       }
     cancel_cutthrough_connection("routing soft fail");
 
-    if (!full_info) return copy_error(vaddr, addr, DEFER);
-      else if (yield == OK) yield = DEFER;
+    if (!full_info)
+      {
+      yield = copy_error(vaddr, addr, DEFER);
+      goto out;
+      }
+    else if (yield == OK) yield = DEFER;
     }
 
   /* If we are handling EXPN, we do not want to continue to route beyond
@@ -1825,7 +1905,8 @@ while (addr_new != NULL)
       if (addr_new == NULL) ok_prefix = US"250 ";
       respond_printf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
       }
-    return OK;
+    yield = OK;
+    goto out;
     }
 
   /* Successful routing other than EXPN. */
@@ -1860,7 +1941,8 @@ while (addr_new != NULL)
       of $address_data to be that of the child */
 
       vaddr->p.address_data = addr->p.address_data;
-      return OK;
+      yield = OK;
+      goto out;
       }
     }
   }     /* Loop for generated addresses */
@@ -1877,7 +1959,7 @@ discarded, usually because of the use of :blackhole: in an alias file. */
 if (allok && addr_local == NULL && addr_remote == NULL)
   {
   fprintf(f, "mail to %s is discarded\n", address);
-  return yield;
+  goto out;
   }
 
 for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
@@ -1961,9 +2043,12 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
     }
   }
 
-/* Will be DEFER or FAIL if any one address has, only for full_info (which is
+/* Yield will be DEFER or FAIL if any one address has, only for full_info (which is
 the -bv or -bt case). */
 
+out:
+tls_modify_variables(&tls_in);
+
 return yield;
 }
 
@@ -2092,6 +2177,41 @@ return yield;
 }
 
 
+/*************************************************
+*      Check header names for 8-bit characters   *
+*************************************************/
+
+/* This function checks for invalid charcters in header names. See
+RFC 5322, 2.2. and RFC 6532, 3.
+
+Arguments:
+  msgptr     where to put an error message
+
+Returns:     OK
+             FAIL
+*/
+
+int
+verify_check_header_names_ascii(uschar **msgptr)
+{
+header_line *h;
+uschar *colon, *s;
+
+for (h = header_list; h != NULL; h = h->next)
+  {
+   colon = Ustrchr(h->text, ':');
+   for(s = h->text; s < colon; s++)
+     {
+        if ((*s < 33) || (*s > 126))
+        {
+                *msgptr = string_sprintf("Invalid character in header \"%.*s\" found",
+                                         colon - h->text, h->text);
+                return FAIL;
+        }
+     }
+  }
+return OK;
+}
 
 /*************************************************
 *          Check for blind recipients            *
@@ -3438,7 +3558,7 @@ revadd[0] = 0;
 
 /* In case this is the first time the DNS resolver is being used. */
 
-dns_init(FALSE, FALSE);
+dns_init(FALSE, FALSE, FALSE); /*XXX dnssec? */
 
 /* Loop through all the domains supplied, until something matches */
 
@@ -3609,4 +3729,6 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
 return FAIL;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of verify.c */