Add tls_verify_hosts and tls_try_verify_hosts to smtp transport. Bug 1371
authorWolfgang Breyha <wbreyha@gmx.net>
Sat, 15 Mar 2014 14:16:05 +0000 (14:16 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 15 Mar 2014 14:18:16 +0000 (14:18 +0000)
Code by Wolfgang Breyha, docs and testsuite by Jeremy Harris

1  2 
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/functions.h
src/src/verify.c

index cab1c82dd56ec925b188f5b9450fb187c3076540,abf69a00de0ff260b022cdacee696c3fb8706445..ae4d75ecb924398e4fe613df4f602af918fe83d7
@@@ -10171,14 -10171,6 +10171,14 @@@ number of larger units and output in Ex
  .cindex "expansion" "case forcing"
  .cindex "&%uc%& expansion item"
  This forces the letters in the string into upper-case.
 +
 +.vitem &*${utf8clean:*&<&'string'&>&*}*&
 +.cindex "correction of invalid utf-8 sequences in strings"
 +.cindex "utf-8" "utf-8 sequences"
 +.cindex "incorrect utf-8"
 +.cindex "expansion" "utf-8 forcing"
 +.cindex "&%utf8clean%& expansion item"
 +This replaces any invalid utf-8 sequence in the string by the character &`?`&.
  .endlist
  
  
@@@ -11986,7 -11978,7 +11986,7 @@@ other times, this variable is false
  It is likely that you will need to coerce DNSSEC support on in the resolver
  library, by setting:
  .code
 -dns_use_dnssec = 1
 +dns_dnssec_ok = 1
  .endd
  
  Exim does not perform DNSSEC validation itself, instead leaving that to a
@@@ -13198,10 -13190,10 +13198,10 @@@ See also the &'Policy controls'& sectio
  .row &%disable_ipv6%&                "do no IPv6 processing"
  .row &%dns_again_means_nonexist%&    "for broken domains"
  .row &%dns_check_names_pattern%&     "pre-DNS syntax check"
 +.row &%dns_dnssec_ok%&               "parameter for resolver"
  .row &%dns_ipv4_lookup%&             "only v4 lookup for these domains"
  .row &%dns_retrans%&                 "parameter for resolver"
  .row &%dns_retry%&                   "parameter for resolver"
 -.row &%dns_use_dnssec%&              "parameter for resolver"
  .row &%dns_use_edns0%&               "parameter for resolver"
  .row &%hold_domains%&                "hold delivery for these domains"
  .row &%local_interfaces%&            "for routing checks"
@@@ -13822,17 -13814,6 +13822,17 @@@ This option controls whether or not an 
  reversed and looked up in the reverse DNS, as described in more detail in
  section &<<SECTverifyCSA>>&.
  
 +
 +.option dns_dnssec_ok main integer -1
 +.cindex "DNS" "resolver options"
 +.cindex "DNS" "DNSSEC"
 +If this option is set to a non-negative number then Exim will initialise the
 +DNS resolver library to either use or not use DNSSEC, overriding the system
 +default. A value of 0 coerces DNSSEC off, a value of 1 coerces DNSSEC on.
 +
 +If the resolver library does not support DNSSEC then this option has no effect.
 +
 +
  .option dns_ipv4_lookup main "domain list&!!" unset
  .cindex "IPv6" "DNS lookup for AAAA records"
  .cindex "DNS" "IPv6 lookup for AAAA records"
@@@ -13863,6 -13844,16 +13863,6 @@@ to set in them
  See &%dns_retrans%& above.
  
  
 -.option dns_use_dnssec main integer -1
 -.cindex "DNS" "resolver options"
 -.cindex "DNS" "DNSSEC"
 -If this option is set to a non-negative number then Exim will initialise the
 -DNS resolver library to either use or not use DNSSEC, overriding the system
 -default. A value of 0 coerces DNSSEC off, a value of 1 coerces DNSSEC on.
 -
 -If the resolver library does not support DNSSEC then this option has no effect.
 -
 -
  .option dns_use_edns0 main integer -1
  .cindex "DNS" "resolver options"
  .cindex "DNS" "EDNS0"
@@@ -23027,6 -23018,14 +23027,14 @@@ unknown state), opens a new one to the 
  in clear.
  
  
+ .option tls_try_verify_hosts smtp "host list&!! unset
+ .cindex "TLS" "server certificate verification"
+ .cindex "certificate" "verification of server"
+ For OpenSSL only, this option gives a list of hosts for which, on encrypted connections,
+ certificate verification will be tried but need not succeed.
+ The &%tls_verify_certificates%& option must also be set.
  .option tls_verify_certificates smtp string&!! unset
  .cindex "TLS" "server certificate verification"
  .cindex "certificate" "verification of server"
@@@ -23041,6 -23040,20 +23049,20 @@@ single file if you are using GnuTLS. Th
  &$host_address$& are set to the name and address of the server during the
  expansion of this option. See chapter &<<CHAPTLS>>& for details of TLS.
  
+ For back-compatability, or when GnuTLS is used,
+ if neither tls_verify_hosts nor tls_try_verify_hosts are set
+ and certificate verification fails the TLS connection is closed.
+ .option tls_verify_hosts smtp "host list&!! unset
+ .cindex "TLS" "server certificate verification"
+ .cindex "certificate" "verification of server"
+ For OpenSSL only, this option gives a list of hosts for which. on encrypted connections,
+ certificate verification must succeed.
+ The &%tls_verify_certificates%& option must also be set.
+ If both this option and &%tls_try_verify_hosts%& are unset
+ operation is as if this option selected all hosts.
  
  
  
@@@ -25942,6 -25955,12 +25964,12 @@@ for OpenSSL only (not GnuTLS), a direct
  expected server certificates. The client verifies the server's certificate
  against this collection, taking into account any revoked certificates that are
  in the list defined by &%tls_crl%&.
+ Failure to verify fails the TLS connection unless either of the
+ &%tls_verify_hosts%& or &%tls_try_verify_hosts%& options are set.
+ The &%tls_verify_hosts%& and &%tls_try_verify_hosts%& options restrict
+ certificate verification to the listed servers.  Verification either must
+ or need not succeed respectively.
  
  If
  &%tls_require_ciphers%& is set on the &(smtp)& transport, it must contain a
@@@ -27991,23 -28010,6 +28019,23 @@@ This condition checks whether the sendi
  send email. Details of how this works are given in section
  &<<SECTverifyCSA>>&.
  
 +.new
 +.vitem &*verify&~=&~header_names_ascii*&
 +.cindex "&%verify%& ACL condition"
 +.cindex "&ACL;" "verifying header names only ASCII"
 +.cindex "header lines" "verifying header names only ASCII"
 +.cindex "verifying" "header names only ASCII"
 +This condition is relevant only in an ACL that is run after a message has been
 +received, that is, in an ACL specified by &%acl_smtp_data%& or
 +&%acl_not_smtp%&.  It checks all header names (not the content) to make sure
 +there are no non-ASCII characters, also excluding control characters.  The
 +allowable characters are decimal ASCII values 33 through 126.
 +
 +Exim itself will handle headers with non-ASCII characters, but it can cause
 +problems for downstream applications, so this option will allow their
 +detection and rejection in the DATA ACL's.
 +.wen
 +
  .vitem &*verify&~=&~header_sender/*&<&'options'&>
  .cindex "&%verify%& ACL condition"
  .cindex "&ACL;" "verifying sender in the header"
@@@ -28619,13 -28621,6 +28647,13 @@@ deny   condition = ${if isip4{$sender_h
         dnslists  = some.list.example
  .endd
  
 +If an explicit key is being used for a DNS lookup and it may be an IPv6
 +address you should specify alternate list separators for both the outer
 +(DNS list name) list and inner (lookup keys) list:
 +.code
 +       dnslists = <; dnsbl.example.com/<|$acl_m_addrslist
 +.endd
 +
  .section "Rate limiting incoming messages" "SECTratelimiting"
  .cindex "rate limiting" "client sending"
  .cindex "limiting client sending rates"
@@@ -29810,24 -29805,6 +29838,24 @@@ av_scanner = mksd:
  .endd
  You can safely omit this option (the default value is 1).
  
 +.vitem &%sock%&
 +.cindex "virus scanners" "simple socket-connected"
 +This is a general-purpose way of talking to simple scanner daemons
 +running on the local machine.
 +There are four options:
 +an address (which may be an IP addres and port, or the path of a Unix socket),
 +a commandline to send (may include a single %s which will be replaced with
 +the path to the mail file to be scanned),
 +an RE to trigger on from the returned data,
 +an RE to extract malware_name from the returned data.
 +For example:
 +.code
 +av_scanner = sock:127.0.0.1 6001:%s:(SPAM|VIRUS):(.*)\$
 +.endd
 +Default for the socket specifier is &_/tmp/malware.sock_&.
 +Default for the commandline is &_%s\n_&.
 +Both regular-expressions are required.
 +
  .vitem &%sophie%&
  .cindex "virus scanners" "Sophos and Sophie"
  Sophie is a daemon that uses Sophos' &%libsavi%& library to scan for viruses.
@@@ -34074,7 -34051,6 +34102,7 @@@ the following table
  &`R   `&        on &`<=`& lines: reference for local bounce
  &`    `&        on &`=>`&  &`**`& and &`==`& lines: router name
  &`S   `&        size of message
 +&`SNI `&        server name indication from TLS client hello
  &`ST  `&        shadow transport name
  &`T   `&        on &`<=`& lines: message subject (topic)
  &`    `&        on &`=>`& &`**`& and &`==`& lines: transport name
@@@ -34384,8 -34360,7 +34412,8 @@@ The message that is written is &"spool 
  .next
  .cindex "log" "smtp confirmation"
  .cindex "SMTP" "logging confirmation"
 -&%smtp_confirmation%&: The response to the final &"."& in the SMTP dialogue for
 +.cindex "LMTP" "logging confirmation"
 +&%smtp_confirmation%&: The response to the final &"."& in the SMTP or LMTP dialogue for
  outgoing messages is added to delivery log lines in the form &`C=`&<&'text'&>.
  A number of MTAs (including Exim) return an identifying string in this
  response.
diff --combined doc/doc-txt/ChangeLog
index 04a7ce02e69dbaf67653c123ea6d3548687caa92,c1640f73ad1e9202c59106ecaeef8e61581d77c5..c29f21cbf5f95691c924b37b09f926db9b8fd21f
@@@ -32,30 -32,8 +32,32 @@@ JH/02 Add ${listextract {number}{list}{
  TL/03 Bugzilla 1433: Fix DMARC SEGV with specific From header contents.
        Properly escape header and check for NULL return.
  
 -JH/03 Add tls_{,try_}verify_hosts to smtp transport. OpenSSL only.
 +PP/01 Continue incomplete 4.82 PP/19 by fixing docs too: use dns_dnssec_ok
 +      not dns_use_dnssec.
 +
 +JH/03 Bugzilla 1157: support log_selector smtp_confirmation for lmtp.
 +
 +TL/04 Add verify = header_names_ascii check to reject email with non-ASCII
 +      characters in header names, implemented as a verify condition.
 +      Contributed by Michael Fischer v. Mollard.
 +
 +TL/05 Rename SPF condition results err_perm and err_temp to standardized
 +      results permerror and temperror. Is a backward incompatibility if
 +      the ACL tests for either of these two results. Patch contributed by
 +      user bes-internal on the mailing list.
 +
 +JH/04 Add ${utf8clean:} operator. Contributed by Alex Rau.
 +
 +JH/05 Bugzilla 305: Log incoming-TLS details on rejects, subject to log
 +      selectors, in both main and reject logs.
 +
 +JH/06 Log outbound-TLS and port details, subject to log selectors, for a
 +      failed delivery.
 +
 +JH/07 Add malware type "sock" for talking to simple daemon.
 +
++JH/08 Bugzilla 1371: Add tls_{,try_}verify_hosts to smtp transport. OpenSSL only.
  
  Exim version 4.82
  -----------------
diff --combined doc/doc-txt/NewStuff
index c4de902c000be4732b8bf6314ca8a75ccdb78172,e4f2e29a1bc1d4386082bcbce60e8658690d4363..95b4119d167c853800511cbec885af1ec755eb9b
@@@ -14,19 -14,11 +14,24 @@@ Version 4.8
      actual external source IP:host be used in exim instead of the IP of the
      proxy that is connecting to it.
  
 - 2. When built with OpenSSL the smtp transport now supports options
 + 2. New verify option header_names_ascii, which will check to make sure
 +    there are no non-ASCII characters in header names.  Exim itself handles
 +    those non-ASCII characters, but downstream apps may not, so Exim can
 +    detect and reject if those characters are present.
 +
 + 3. New expansion operator ${utf8clean:string} to replace malformed UTF8
 +    codepoints with valid ones.
 +
 + 4. New malware type "sock".  Talks over a Unix or TCP socket, sending one
 +    command line and matching a regex against the return data for trigger
 +    and a second regex to extract malware_name.  The mail spoofile name can
 +    be included in the command line.
 +
++ 5. When built with OpenSSL the smtp transport now supports options
+     "tls_verify_hosts" and "tls_try_verify_hosts".  If either is set the
+     certificate verification is split from the encryption operation. The
+     default remains that a failed verification cancels the encryption.
  
  Version 4.82
  ------------
diff --combined src/src/functions.h
index a94fac3f2242100879b5d5bbd5828b2544b54b25,32bd0bc079902b55919514318a324da5e81daeea..e92c2455f342e5366af62e7d68a7f82aa0ba0d20
@@@ -30,7 -30,7 +30,7 @@@ extern int     tls_client_start(int, ho
  # ifdef EXPERIMENTAL_OCSP
                   uschar *,
  # endif
-                  int, int);
+                  int, int, uschar *, uschar *);
  extern void    tls_close(BOOL, BOOL);
  extern int     tls_feof(void);
  extern int     tls_ferror(void);
@@@ -171,8 -171,6 +171,8 @@@ extern int     host_scan_for_local_host
  extern void    invert_address(uschar *, uschar *);
  extern int     ip_bind(int, int, uschar *, int);
  extern int     ip_connect(int, int, uschar *, int, int);
 +extern int     ip_connectedsocket(int, const uschar *, int, int,
 +                 int, host_item *, uschar **);
  extern int     ip_get_address_family(int);
  extern void    ip_keepalive(int, uschar *, BOOL);
  extern int     ip_recv(int, uschar *, int, int);
@@@ -360,7 -358,7 +360,7 @@@ extern uschar *string_dequote(uschar **
  extern BOOL    string_format(uschar *, int, const char *, ...) ALMOST_PRINTF(3,4);
  extern uschar *string_format_size(int, uschar *);
  extern int     string_interpret_escape(uschar **);
 -extern int     string_is_ip_address(uschar *, int *);
 +extern int     string_is_ip_address(const uschar *, int *);
  extern uschar *string_log_address(address_item *, BOOL, BOOL);
  extern uschar *string_nextinlist(uschar **, int *, uschar *, int);
  extern uschar *string_open_failed(int, const char *, ...) PRINTF_FUNCTION(2,3);
@@@ -404,7 -402,6 +404,7 @@@ extern int     verify_check_dnsbl(uscha
  extern int     verify_check_header_address(uschar **, uschar **, int, int, int,
                   uschar *, uschar *, int, int *);
  extern int     verify_check_headers(uschar **);
 +extern int     verify_check_header_names_ascii(uschar **);
  extern int     verify_check_host(uschar **);
  extern int     verify_check_notblind(void);
  extern int     verify_check_this_host(uschar **, unsigned int *, uschar*,
diff --combined src/src/verify.c
index 3c464fe93a173a2893a762349e2fa5790711a6cd,c83748a12f5c7f067d0e2d6ce68c85d1a7062501..cd91b0560966474fb71c122dab31ce47926c2456
@@@ -538,7 -538,7 +538,7 @@@ els
      #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))
  #ifdef EXPERIMENTAL_OCSP
         ob->hosts_require_ocsp,
  #endif
-        ob->tls_dh_min_bits,         callout);
+        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. */
          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:
@@@ -2157,41 -2156,6 +2158,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            *