Merge branch 'master' of git://git.exim.org/exim
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 5 May 2012 20:10:19 +0000 (21:10 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 5 May 2012 20:10:19 +0000 (21:10 +0100)
34 files changed:
doc/doc-docbook/filter.xfpt
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
src/README.UPDATING
src/src/EDITME
src/src/acl.c
src/src/auths/cram_md5.c
src/src/auths/heimdal_gssapi.c
src/src/config.h.defaults
src/src/daemon.c
src/src/drtables.c
src/src/exipick.src
src/src/expand.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/local_scan.h
src/src/macros.h
src/src/mytypes.h
src/src/readconf.c
src/src/receive.c
src/src/smtp_in.c
src/src/spam.c
src/src/spool_in.c
src/src/spool_out.c
src/src/store.c
src/src/string.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transports/smtp.c
src/src/transports/smtp.h
src/src/transports/tf_maildir.c

index b0e17620526df43ea5bc51aecb8b8be04042f0e5..4821ef17344680da98a85ff1da65a551851bd708 100644 (file)
@@ -21,7 +21,7 @@
 <?sdop
   foot_right_recto="&chaptertitle;"
   foot_right_verso="&chaptertitle;"
-  table_warn_soft_overflow="no"
+  table_warn_overflow="overprint"
   toc_chapter_blanks="yes,yes"
   toc_title="Exim's interfaces to mail filtering"
 ?>
index e719855f8e0dc6e1ff9aac7717d491aa84808ec7..c4739a80fb94549212e1c6a62acfd744c9ae7bab 100644 (file)
@@ -9755,11 +9755,13 @@ dotted-quad decimal form, while for IPv6 addreses the result is in
 dotted-nibble hexadecimal form.  In both cases, this is the "natural" form
 for DNS.  For example,
 .code
-${reverse_ip:192.0.2.4} and ${reverse_ip:2001:0db8:c42:9:1:abcd:192.0.2.3}
+${reverse_ip:192.0.2.4}
+${reverse_ip:2001:0db8:c42:9:1:abcd:192.0.2.3}
 .endd
 returns
 .code
-4.2.0.192 and 3.0.2.0.0.0.0.c.d.c.b.a.1.0.0.0.9.0.0.0.2.4.c.0.8.b.d.0.1.0.0.2
+4.2.0.192
+3.0.2.0.0.0.0.c.d.c.b.a.1.0.0.0.9.0.0.0.2.4.c.0.8.b.d.0.1.0.0.2
 .endd
 
 
@@ -11888,6 +11890,26 @@ the value of the Distinguished Name of the certificate is made available in the
 value is retained during message delivery, except during outbound SMTP
 deliveries.
 
+.new
+.vitem &$tls_sni$&
+.vindex "&$tls_sni$&"
+.cindex "TLS" "Server Name Indication"
+When a TLS session is being established, if the client sends the Server
+Name Indication extension, the value will be placed in this variable.
+If the variable appears in &%tls_certificate%& then this option and
+some others, described in &<<SECTtlssni>>&,
+will be re-expanded early in the TLS session, to permit
+a different certificate to be presented (and optionally a different key to be
+used) to the client, based upon the value of the SNI extension.
+
+The value will be retained for the lifetime of the message.  During outbound
+SMTP deliveries, it reflects the value of the &%tls_sni%& option on
+the transport.
+
+This is currently only available when using OpenSSL, built with support for
+SNI.
+.wen
+
 .vitem &$tod_bsdinbox$&
 .vindex "&$tod_bsdinbox$&"
 The time of day and the date, in the format required for BSD-style mailbox
@@ -14333,16 +14355,12 @@ harm. This option overrides the &%pipe_as_creator%& option of the &(pipe)&
 transport driver.
 
 
-.option openssl_options main "string list" +dont_insert_empty_fragments
+.option openssl_options main "string list" unset
 .cindex "OpenSSL "compatibility options"
 This option allows an administrator to adjust the SSL options applied
 by OpenSSL to connections.  It is given as a space-separated list of items,
-each one to be +added or -subtracted from the current value.  The default
-value is one option which happens to have been set historically.  You can
-remove all options with:
-.code
-openssl_options = -all
-.endd
+each one to be +added or -subtracted from the current value.
+
 This option is only available if Exim is built against OpenSSL.  The values
 available for this option vary according to the age of your OpenSSL install.
 The &"all"& value controls a subset of flags which are available, typically
@@ -14354,66 +14372,75 @@ names lose the leading &"SSL_OP_"& and are lower-cased.
 Note that adjusting the options can have severe impact upon the security of
 SSL as used by Exim.  It is possible to disable safety checks and shoot
 yourself in the foot in various unpleasant ways.  This option should not be
-adjusted lightly.  An unrecognised item will be detected at by invoking Exim
-with the &%-bV%& flag.
+adjusted lightly.  An unrecognised item will be detected at startup, by
+invoking Exim with the &%-bV%& flag.
+
+.new
+Historical note: prior to release 4.78, Exim defaulted this value to
+"+dont_insert_empty_fragments", which may still be needed for compatibility
+with some clients, but which lowers security by increasing exposure to
+some now infamous attacks.
+.wen
 
 An example:
 .code
-openssl_options = -all +microsoft_big_sslv3_buffer
+# Make both old MS and old Eudora happy:
+openssl_options = -all +microsoft_big_sslv3_buffer \
+                       +dont_insert_empty_fragments
 .endd
 
 Possible options may include:
 .ilist
 &`all`&
-.ilist
+.next
 &`allow_unsafe_legacy_renegotiation`&
-.ilist
+.next
 &`cipher_server_preference`&
-.ilist
+.next
 &`dont_insert_empty_fragments`&
-.ilist
+.next
 &`ephemeral_rsa`&
-.ilist
+.next
 &`legacy_server_connect`&
-.ilist
+.next
 &`microsoft_big_sslv3_buffer`&
-.ilist
+.next
 &`microsoft_sess_id_bug`&
-.ilist
+.next
 &`msie_sslv2_rsa_padding`&
-.ilist
+.next
 &`netscape_challenge_bug`&
-.ilist
+.next
 &`netscape_reuse_cipher_change_bug`&
-.ilist
+.next
 &`no_compression`&
-.ilist
+.next
 &`no_session_resumption_on_renegotiation`&
-.ilist
+.next
 &`no_sslv2`&
-.ilist
+.next
 &`no_sslv3`&
-.ilist
+.next
 &`no_ticket`&
-.ilist
+.next
 &`no_tlsv1`&
-.ilist
+.next
 &`no_tlsv1_1`&
-.ilist
+.next
 &`no_tlsv1_2`&
-.ilist
+.next
 &`single_dh_use`&
-.ilist
+.next
 &`single_ecdh_use`&
-.ilist
+.next
 &`ssleay_080_client_dh_bug`&
-.ilist
+.next
 &`sslref2_reuse_cert_type_bug`&
-.ilist
+.next
 &`tls_block_padding_bug`&
-.ilist
+.next
 &`tls_d5_bug`&
-.ilist
+.next
 &`tls_rollback_bug`&
 .endlist
 
@@ -15606,6 +15633,12 @@ receiving incoming messages as a server. If you want to supply certificates for
 use when sending messages as a client, you must set the &%tls_certificate%&
 option in the relevant &(smtp)& transport.
 
+.new
+If the option contains &$tls_sni$& and Exim is built against OpenSSL, then
+if the OpenSSL build supports TLS extensions and the TLS client sends the
+Server Name Indication extension, then this option and others documented in
+&<<SECTtlssni>>& will be re-expanded.
+.wen
 
 .option tls_crl main string&!! unset
 .cindex "TLS" "server certificate revocation list"
@@ -15613,6 +15646,10 @@ option in the relevant &(smtp)& transport.
 This option specifies a certificate revocation list. The expanded value must
 be the name of a file that contains a CRL in PEM format.
 
+.new
+See &<<SECTtlssni>>& for discussion of when this option might be re-expanded.
+.wen
+
 
 .option tls_dhparam main string&!! unset
 .cindex "TLS" "D-H parameters for server"
@@ -15638,6 +15675,10 @@ the expansion is forced to fail, or the result is an empty string, the private
 key is assumed to be in the same file as the server's certificates. See chapter
 &<<CHAPTLS>>& for further details.
 
+.new
+See &<<SECTtlssni>>& for discussion of when this option might be re-expanded.
+.wen
+
 
 .option tls_remember_esmtp main boolean false
 .cindex "TLS" "esmtp state; remembering"
@@ -15684,6 +15725,10 @@ connecting clients, defining the list of accepted certificate authorities.
 Thus the values defined should be considered public data.  To avoid this,
 use OpenSSL with a directory.
 
+.new
+See &<<SECTtlssni>>& for discussion of when this option might be re-expanded.
+.wen
+
 
 .option tls_verify_hosts main "host list&!!" unset
 .cindex "TLS" "client certificate verification"
@@ -21766,12 +21811,15 @@ that are in force when the &%helo_data%&, &%hosts_try_auth%&, &%interface%&,
 
 
 .section "Use of $tls_cipher and $tls_peerdn" "usecippeer"
+.vindex &$tls_bits$&
 .vindex &$tls_cipher$&
 .vindex &$tls_peerdn$&
-At the start of a run of the &(smtp)& transport, the values of &$tls_cipher$&
-and &$tls_peerdn$& are the values that were set when the message was received.
+.vindex &$tls_sni$&
+At the start of a run of the &(smtp)& transport, the values of &$tls_bits$&,
+&$tls_cipher$&, &$tls_peerdn$& and &$tls_sni$&
+are the values that were set when the message was received.
 These are the values that are used for options that are expanded before any
-SMTP connections are made. Just before each connection is made, these two
+SMTP connections are made. Just before each connection is made, these four
 variables are emptied. If TLS is subsequently started, they are set to the
 appropriate values for the outgoing connection, and these are the values that
 are in force when any authenticators are run and when the
@@ -22350,6 +22398,22 @@ ciphers is a preference order.
 
 
 
+.new
+.option tls_sni smtp string&!! unset
+.cindex "TLS" "Server Name Indication"
+.vindex "&$tls_sni$&"
+If this option is set then it sets the $tls_sni variable and causes any
+TLS session to pass this value as the Server Name Indication extension to
+the remote side, which can be used by the remote side to select an appropriate
+certificate and private key for the session.
+
+See &<<SECTtlssni>>& for more information.
+
+OpenSSL only, also requiring a build of OpenSSL that supports TLS extensions.
+.wen
+
+
+
 .option tls_tempfail_tryclear smtp boolean true
 .cindex "4&'xx'& responses" "to STARTTLS"
 When the server host is not in &%hosts_require_tls%&, and there is a problem in
@@ -24114,9 +24178,10 @@ login:
   server_prompts = Username:: : Password::
   server_condition = ${if and{{ \
     !eq{}{$auth1} }{ \
-    ldapauth{user="cn=${quote_ldap_dn:$auth1},ou=people,o=example.org" \
-             pass=${quote:$auth2} \
-             ldap://ldap.example.org/} }} }
+    ldapauth{\
+      user="uid=${quote_ldap_dn:$auth1},ou=people,o=example.org" \
+      pass=${quote:$auth2} \
+      ldap://ldap.example.org/} }} }
   server_set_id = uid=$auth1,ou=people,o=example.org
 .endd
 We have to check that the username is not empty before using it, because LDAP
@@ -24874,6 +24939,13 @@ option).
 .next
 The &%tls_require_ciphers%& options operate differently, as described in the
 sections &<<SECTreqciphssl>>& and &<<SECTreqciphgnu>>&.
+.new
+.next
+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.
+.wen
 .endlist
 
 
@@ -25270,9 +25342,12 @@ All the TLS options in the &(smtp)& transport are expanded before use, with
 which the client is connected. Forced failure of an expansion causes Exim to
 behave as if the relevant option were unset.
 
+.vindex &$tls_bits$&
 .vindex &$tls_cipher$&
 .vindex &$tls_peerdn$&
-Before an SMTP connection is established, the &$tls_cipher$& and &$tls_peerdn$&
+.vindex &$tls_sni$&
+Before an SMTP connection is established, the
+&$tls_bits$&, &$tls_cipher$&, &$tls_peerdn$& and &$tls_sni$&
 variables are emptied. (Until the first connection, they contain the values
 that were set when the message was received.) If STARTTLS is subsequently
 successfully obeyed, these variables are set to the relevant values for the
@@ -25280,6 +25355,76 @@ outgoing connection.
 
 
 
+.new
+.section "Use of TLS Server Name Indication" "SECTtlssni"
+.cindex "TLS" "Server Name Indication"
+.vindex "&$tls_sni$&"
+.oindex "&%tls_sni%&"
+With TLS1.0 or above, there is an extension mechanism by which extra
+information can be included at various points in the protocol.  One of these
+extensions, documented in RFC 6066 (and before that RFC 4366) is
+&"Server Name Indication"&, commonly &"SNI"&.  This extension is sent by the
+client in the initial handshake, so that the server can examine the servername
+within and possibly choose to use different certificates and keys (and more)
+for this session.
+
+This is analagous to HTTP's &"Host:"& header, and is the main mechanism by
+which HTTPS-enabled web-sites can be virtual-hosted, many sites to one IP
+address.
+
+With SMTP to MX, there are the same problems here as in choosing the identity
+against which to validate a certificate: you can't rely on insecure DNS to
+provide the identity which you then cryptographically verify.  So this will
+be of limited use in that environment.
+
+With SMTP to Submission, there is a well-defined hostname which clients are
+connecting to and can validate certificates against.  Thus clients &*can*&
+choose to include this information in the TLS negotiation.  If this becomes
+wide-spread, then hosters can choose to present different certificates to
+different clients.  Or even negotiate different cipher suites.
+
+The &%tls_sni%& option on an SMTP transport is an expanded string; the result,
+if not empty, will be sent on a TLS session as part of the handshake.  There's
+nothing more to it.  Choosing a sensible value not derived insecurely is the
+only point of caution.  The &$tls_sni$& variable will be set to this string
+for the lifetime of the client connection (including during authentication).
+
+Except during SMTP client sessions, if &$tls_sni$& is set then it is a string
+received from a client.
+It can be logged with the &%log_selector%& item &`+tls_sni`&.
+
+If the string &`tls_sni`& appears in the main section's &%tls_certificate%&
+option (prior to expansion) then the following options will be re-expanded
+during TLS session handshake, to permit alternative values to be chosen:
+
+.ilist
+.vindex "&%tls_certificate%&"
+&%tls_certificate%&
+.next
+.vindex "&%tls_crl%&"
+&%tls_crl%&
+.next
+.vindex "&%tls_privatekey%&"
+&%tls_privatekey%&
+.next
+.vindex "&%tls_verify_certificates%&"
+&%tls_verify_certificates%&
+.endlist
+
+Great care should be taken to deal with matters of case, various injection
+attacks in the string (&`../`& or SQL), and ensuring that a valid filename
+can always be referenced; it is important to remember that &$tls_sni$& is
+arbitrary unverified data provided prior to authentication.
+
+The Exim developers are proceeding cautiously and so far no other TLS options
+are re-expanded.
+
+Currently SNI support is only available if using OpenSSL, with TLS Extensions
+support enabled therein.
+.wen
+
+
+
 .section "Multiple messages on the same encrypted TCP/IP connection" &&&
          "SECTmulmessam"
 .cindex "multiple SMTP deliveries with TLS"
@@ -27775,14 +27920,14 @@ in the MAIL ACL. Subsequent connections from the same client will check this
 new rate.
 .code
 acl_check_connect:
 deny ratelimit = 100 / 5m / readonly
-       log_message = RATE CHECK: $sender_rate/$sender_rate_period \
-                     (max $sender_rate_limit)
+ deny ratelimit = 100 / 5m / readonly
+    log_message = RATE CHECK: $sender_rate/$sender_rate_period \
+                  (max $sender_rate_limit)
 # ...
 acl_check_mail:
 warn ratelimit = 100 / 5m / strict
-       log_message  = RATE UPDATE: $sender_rate/$sender_rate_period \
-                      (max $sender_rate_limit)
+ warn ratelimit = 100 / 5m / strict
+    log_message = RATE UPDATE: $sender_rate/$sender_rate_period \
+                  (max $sender_rate_limit)
 .endd
 
 If Exim encounters multiple &%ratelimit%& conditions with the same key when
@@ -33134,6 +33279,7 @@ selection marked by asterisks:
 &` tls_certificate_verified   `&  certificate verification status
 &`*tls_cipher                 `&  TLS cipher suite on <= and => lines
 &` tls_peerdn                 `&  TLS peer DN on <= and => lines
+&` tls_sni                    `&  TLS SNI on <= lines
 &` unknown_in_list            `&  DNS lookup failed in list match
 
 &` all                        `&  all of the above
@@ -33429,6 +33575,12 @@ connection, the cipher suite used is added to the log line, preceded by X=.
 connection, and a certificate is supplied by the remote host, the peer DN is
 added to the log line, preceded by DN=.
 .next
+.cindex "log" "TLS SNI"
+.cindex "TLS" "logging SNI"
+&%tls_sni%&: When a message is received over an encrypted connection, and
+the remote host provided the Server Name Indication extension, the SNI is
+added to the log line, preceded by SNI=.
+.next
 .cindex "log" "DNS failure in list"
 &%unknown_in_list%&: This setting causes a log entry to be written when the
 result of a list match is failure because a DNS lookup failed.
@@ -35233,10 +35385,15 @@ unqualified domain &'foundation'&.
 . ////////////////////////////////////////////////////////////////////////////
 . ////////////////////////////////////////////////////////////////////////////
 
-.chapter "Support for DKIM (DomainKeys Identified Mail) - RFC4871" "CHID12" &&&
+.chapter "Support for DKIM (DomainKeys Identified Mail)" "CHID12" &&&
          "DKIM Support"
 .cindex "DKIM"
 
+DKIM is a mechanism by which messages sent by some entity can be provably
+linked to a domain which that entity controls.  It permits reputation to
+be tracked on a per-domain basis, rather than merely upon source IP address.
+DKIM is documented in RFC 4871.
+
 Since version 4.70, DKIM support is compiled into Exim by default. It can be
 disabled by setting DISABLE_DKIM=yes in Local/Makefile.
 
@@ -35257,9 +35414,12 @@ Exim's standard controls.
 Please note that verification of DKIM signatures in incoming mail is turned
 on by default for logging purposes. For each signature in incoming email,
 exim will log a line displaying the most important signature details, and the
-signature status. Here is an example:
+signature status. Here is an example (with line-breaks added for clarity):
 .code
-2009-09-09 10:22:28 1MlIRf-0003LU-U3 DKIM: d=facebookmail.com s=q1-2009b c=relaxed/relaxed a=rsa-sha1 i=@facebookmail.com t=1252484542 [verification succeeded]
+2009-09-09 10:22:28 1MlIRf-0003LU-U3 DKIM:
+    d=facebookmail.com s=q1-2009b
+    c=relaxed/relaxed a=rsa-sha1
+    i=@facebookmail.com t=1252484542 [verification succeeded]
 .endd
 You might want to turn off DKIM verification processing entirely for internal
 or relay mail sources. To do that, set the &%dkim_disable_verify%& ACL
@@ -35467,7 +35627,7 @@ for a match against the domain or identity that the ACL is currently verifying
 verb to a group of domains or identities. For example:
 
 .code
-# Warn when message apparently from GMail has no signature at all
+# Warn when Mail purportedly from GMail has no signature at all
 warn log_message = GMail sender without DKIM signature
      sender_domains = gmail.com
      dkim_signers = gmail.com
@@ -35477,10 +35637,10 @@ warn log_message = GMail sender without DKIM signature
 .vitem &%dkim_status%&
 ACL condition that checks a colon-separated list of possible DKIM verification
 results agains the actual result of verification. This is typically used
-to restrict an ACL verb to a list of verification outcomes, like:
+to restrict an ACL verb to a list of verification outcomes, for example:
 
 .code
-deny message = Message from Paypal with invalid or missing signature
+deny message = Mail from Paypal with invalid/missing signature
      sender_domains = paypal.com:paypal.de
      dkim_signers = paypal.com:paypal.de
      dkim_status = none:invalid:fail
index b41783d7183192cb5a46c1235872d2f9193df36e..80e8edf97a51b2dca6392b5f5f921047c3f664fc 100644 (file)
@@ -69,6 +69,18 @@ PP/15 LDAP: Check for errors of TLS initialisation, to give correct
       diagnostics.
       Report and patch from Dmitry Banschikov.
 
+PP/16 Removed "dont_insert_empty_fragments" fron "openssl_options".
+      Removed SSL_clear() after SSL_new() which led to protocol negotiation
+      failures.  We appear to now support TLS1.1+ with Exim.
+
+PP/17 OpenSSL: new expansion var $tls_sni, which if used in tls_certificate
+      lets Exim select keys and certificates based upon TLS SNI from client.
+      Also option tls_sni on SMTP Transports.  Also clear $tls_bits correctly
+      before an outbound SMTP session.  New log_selector, +tls_sni.
+
+PP/18 Bugzilla 1122 - check localhost_number expansion for failure, avoid
+      NULL dereference.  Report and patch from Alun Jones.
+
 
 Exim version 4.77
 -----------------
index b962b61a24a71481a3c3da7c19f2266827d1634a..2872d241f0f74857095d85ba1b521e3c4e44142e 100644 (file)
@@ -33,6 +33,29 @@ Version 4.78
     into the DBM library.  Can be used with gsasl to access sasldb2 files as
     used by Cyrus SASL.
 
+ 6. OpenSSL now supports TLS1.1 and TLS1.2 with OpenSSL 1.0.1.
+
+    Avoid release 1.0.1a if you can.  Note that the default value of
+    "openssl_options" is no longer "+dont_insert_empty_fragments", as that
+    increased susceptibility to attack.  This may still have interoperability
+    implications for very old clients (see version 4.31 change 37) but
+    administrators can choose to make the trade-off themselves and restore
+    compatibility at the cost of session security.
+
+ 7. Use of the new expansion variable $tls_sni in the main configuration option
+    tls_certificate will cause Exim to re-expand the option, if the client
+    sends the TLS Server Name Indication extension, to permit choosing a
+    different certificate; tls_privatekey will also be re-expanded.  You must
+    still set these options to expand to valid files when $tls_sni is not set.
+
+    The SMTP Transport has gained the option tls_sni, which will set a hostname
+    for outbound TLS sessions, and set $tls_sni too.
+
+    A new log_selector, +tls_sni, has been added, to log received SNI values
+    for Exim as a server.
+
+    Currently OpenSSL only.
+
 
 Version 4.77
 ------------
index 5313fd17c53d06be70e69410d56191d948185254..52a24b1984584045bc5964ba0bf7cc88c1e55c4c 100644 (file)
@@ -373,7 +373,7 @@ once                                 string*         unset         autoreply
 once_file_size                       integer         0             autoreply         3.20
 once_repeat                          time            0s            autoreply         2.95
 one_time                             boolean         false         redirect          4.00
-openssl_options                      string "+dont_insert_empty_fragments" main      4.73
+openssl_options                      string          unset         main              4.73 default to unset in 4.78
 optional                             boolean         false         iplookup          4.00
 oracle_servers                       string          unset         main              4.00
 owners                               string list     unset         redirect          4.00
@@ -554,6 +554,7 @@ tls_privatekey                       string*         unset         main
 tls_remember_emstp                   boolean         false         main              4.21
 tls_require_ciphers                  string*         unset         smtp              4.00 replaces tls_verify_ciphers
                                      string*         unset         main              4.33
+tls_sni                              string*         unset         main              4.78
 tls_tempfail_tryclear                boolean         true          smtp              4.05
 tls_try_verify_hosts                 host list       unset         main              4.00
 tls_verify_certificates              string*         unset         main              3.20
index 3dff7c09411fe0a150e6425f956ccf9ad62103e2..5b6bea869ae5ab3a26b634ef742d6d2144873451 100644 (file)
@@ -47,6 +47,22 @@ Exim version 4.78
 
    "openssl_options" gains "no_tlsv1_1", "no_tlsv1_2" and "no_compression".
 
+   COMPATIBILITY WARNING: The default value of "openssl_options" is no longer
+   "+dont_insert_empty_fragments".  We default to unset.  That old default was
+   grandfathered in from before openssl_options became a configuration option.
+   Empty fragments are inserted by default through TLS1.0, to partially defend
+   against certain attacks; TLS1.1+ change the protocol so that this is not
+   needed.  The DIEF SSL option was required for some old releases of mail
+   clients which did not gracefully handle the empty fragments, and was
+   initially set in Exim release 4.31 (see ChangeLog, item 37).
+
+   If you still have affected mail-clients, and you see SSL protocol failures
+   with this release of Exim, set:
+     openssl_options = +dont_insert_empty_fragments
+   in the main section of your Exim configuration file.  You're trading off
+   security for compatibility.  Exim is now defaulting to higher security and
+   rewarding more modern clients.
+
  * Ldap lookups returning multi-valued attributes now separate the attributes
    with only a comma, not a comma-space sequence.  Also, an actual comma within
    a returned attribute is doubled.  This makes it possible to parse the
index c4aedb6bd982109cfdbd9be70db4e0eac64547fa..f247f44a9a981626d9ed6abf79a4a68641958493 100644 (file)
@@ -1198,6 +1198,12 @@ TMPDIR="/tmp"
 # PERL_LIBS=
 
 
+#------------------------------------------------------------------------------
+# If you wish to disable valgrind in the binary, define NVALGRIND=1.
+# This should not be needed.
+
+# NVALGRIND=1
+
 #------------------------------------------------------------------------------
 # Identifying the daemon: When an Exim daemon starts up, it writes its pid
 # (process id) to a file so that it can easily be identified. The path of the
index 50d5d85941e7c1107d3082c8b0d148a1d0b1d4f2..b93ac6965ab1b95e17241c3834bd0dd9a0e76d5f 100644 (file)
@@ -992,7 +992,7 @@ for (p = q = hstring; *p != 0; )
     if (*s == ':' || !isgraph(*s)) break;
     }
 
-  s = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", q - p, p);
+  s = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", (int) (q - p), p);
   hlen = Ustrlen(s);
 
   /* See if this line has already been added */
@@ -1723,16 +1723,16 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
               != NULL)
           {
          callout_opt_t * op;
-         double period;
+         double period = 1.0F;
 
          for (op= callout_opt_list; op->name; op++)
-           if (strncmpic(opt, op->name, strlen(op->name)) == 0)
+           if (strncmpic(opt, op->name, Ustrlen(op->name)) == 0)
              break;
 
          verify_options |= op->flag;
          if (op->has_option)
            {
-           opt += strlen(op->name);
+           opt += Ustrlen(op->name);
             while (isspace(*opt)) opt++;
             if (*opt++ != '=')
               {
@@ -2093,7 +2093,7 @@ uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
 va_start(ap, format);
 if (!string_vformat(buffer, sizeof(buffer), format, ap))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE,
-    "string_sprintf expansion was longer than %d", sizeof(buffer));
+    "string_sprintf expansion was longer than " SIZE_T_FMT, sizeof(buffer));
 va_end(ap);
 *log_msgptr = string_sprintf(
   "error in arguments to \"ratelimit\" condition: %s", buffer);
@@ -2228,7 +2228,7 @@ while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
   else if (strcmpic(ss, US"per_addr") == 0)
     {
     RATE_SET(mode, PER_RCPT);
-    if (where != ACL_WHERE_RCPT) badacl = TRUE, unique = "*";
+    if (where != ACL_WHERE_RCPT) badacl = TRUE, unique = US"*";
       else unique = string_sprintf("%s@%s", deliver_localpart, deliver_domain);
     }
   else if (strncmpic(ss, US"count=", 6) == 0)
index 31de7c2d2e606139e137954d167d0ce019238994..e93d0384540e3b9c8c2f35dd64c3538d841bb893 100644 (file)
@@ -153,8 +153,8 @@ auth_cram_md5_server(auth_instance *ablock, uschar *data)
 {
 auth_cram_md5_options_block *ob =
   (auth_cram_md5_options_block *)(ablock->options_block);
-uschar *challenge = string_sprintf("<%d.%d@%s>", getpid(), time(NULL),
-  primary_hostname);
+uschar *challenge = string_sprintf("<%d.%ld@%s>", getpid(),
+    (long int) time(NULL), primary_hostname);
 uschar *clear, *secret;
 uschar digest[16];
 int i, rc, len;
index e01789e31e49b67179006e53270178c60dc3a298..c6e973ad9af80c613eb961803666e50d2ecbe3eb 100644 (file)
@@ -526,7 +526,7 @@ exim_gssapi_error_defer(uschar *store_reset_point,
   va_start(ap, format);
   if (!string_vformat(buffer, sizeof(buffer), format, ap))
     log_write(0, LOG_MAIN|LOG_PANIC_DIE,
-        "exim_gssapi_error_defer expansion larger than %d",
+        "exim_gssapi_error_defer expansion larger than %lu",
         sizeof(buffer));
   va_end(ap);
 
index f3e3d880a393774c34b4f9efe52ee218dcc3dbcd..c082b9269c6bf74e68334c708ba0bcc3d4107f4a 100644 (file)
@@ -101,6 +101,8 @@ it's a default value. */
 #define MAX_NAMED_LIST               16
 #define MSGLOG_DIRECTORY_MODE      0750
 
+#define NVALGRIND
+
 #define PID_FILE_PATH
 
 #define RADIUS_CONFIG_FILE
index 4ac34332b84a20b01412cc283e57fa4d52e94022..27b4cb26567045add35870477d9f3f30e21a3714 100644 (file)
@@ -828,8 +828,17 @@ pid_t pid;
 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
   {
   int i;
-  DEBUG(D_any) debug_printf("child %d ended: status=0x%x\n", (int)pid,
-    status);
+  DEBUG(D_any)
+    {
+    debug_printf("child %d ended: status=0x%x\n", (int)pid, status);
+#ifdef WCOREDUMP
+    if (WIFEXITED(status))
+      debug_printf("  normal exit, %d\n", WEXITSTATUS(status));
+    else if (WIFSIGNALED(status))
+      debug_printf("  signal exit, signal %d%s\n", WTERMSIG(status),
+          WCOREDUMP(status) ? " (core dumped)" : "");
+#endif
+    }
 
   /* If it's a listening daemon for which we are keeping track of individual
   subprocesses, deal with an accepting process that has terminated. */
index c87e9c23bf313165d646f117f3befc296b330cd1..06f7e30ed87071399b096ae809454756286175a4 100644 (file)
@@ -489,13 +489,15 @@ extern lookup_module_info cdb_lookup_module_info;
 
 void init_lookup_list(void)
 {
+#ifdef LOOKUP_MODULE_DIR
   DIR *dd;
   struct dirent *ent;
-  const pcre *regex_islookupmod = regex_must_compile(
-      US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE);
   int countmodules = 0;
   int moduleerrors = 0;
+#endif
   struct lookupmodulestr *p;
+  const pcre *regex_islookupmod = regex_must_compile(
+      US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE);
 
   if (lookup_list_init_done)
     return;
index 811092dc142bde6b904c00f8f8383ba60576e6ec..ed3b6615436ed6df7bfccd0780abe9b24190a6e1 100644 (file)
@@ -955,6 +955,8 @@ sub _parse_header {
         $self->{_vars}{tls_cipher} = $arg;
       } elsif ($tag eq '-tls_peerdn') {
         $self->{_vars}{tls_peerdn} = $arg;
+      } elsif ($tag eq '-tls_sni') {
+        $self->{_vars}{tls_sni} = $arg;
       } elsif ($tag eq '-host_address') {
         $self->{_vars}{sender_host_port} = $self->_get_host_and_port(\$arg);
         $self->{_vars}{sender_host_address} = $arg;
@@ -1793,6 +1795,10 @@ The cipher suite that was negotiated for encrypted SMTP connections.
 
 The value of the Distinguished Name of the certificate if Exim is configured to request one
 
+=item S . $tls_sni
+
+The value of the Server Name Indication TLS extension sent by a client, if one was sent.
+
 =item N + $warning_count
 
 The number of delay warnings which have been sent for this message.
index 54501de0b92497984038cefbb9c90b7fb281a183..22f7d9a66ff0c366d7cafc085caf85c3906913df 100644 (file)
@@ -615,6 +615,9 @@ static var_entry var_table[] = {
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
   { "tls_cipher",          vtype_stringptr,   &tls_cipher },
   { "tls_peerdn",          vtype_stringptr,   &tls_peerdn },
+#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+  { "tls_sni",             vtype_stringptr,   &tls_sni },
+#endif
   { "tod_bsdinbox",        vtype_todbsdin,    NULL },
   { "tod_epoch",           vtype_tode,        NULL },
   { "tod_full",            vtype_todf,        NULL },
index f1af42ee589a5449000616a37baf9dc211516c80..220235235db63757b2bc4fba1e06e90df5e33a0c 100644 (file)
@@ -23,7 +23,7 @@ extern uschar *init_perl(uschar *);
 #ifdef SUPPORT_TLS
 extern int     tls_client_start(int, host_item *, address_item *, uschar *,
                  uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
-                 uschar *, uschar *, int);
+                 uschar *, uschar *, uschar *, int);
 extern void    tls_close(BOOL);
 extern int     tls_feof(void);
 extern int     tls_ferror(void);
index 6124cb585591972f1dcc17300f8a48c1eb7bb865..f11c7c2dbacfac87bd72041f8b813da306fd123c 100644 (file)
@@ -116,6 +116,9 @@ BOOL    tls_offered            = FALSE;
 uschar *tls_privatekey         = NULL;
 BOOL    tls_remember_esmtp     = FALSE;
 uschar *tls_require_ciphers    = NULL;
+#ifndef USE_GNUTLS
+uschar *tls_sni                = NULL;
+#endif
 uschar *tls_try_verify_hosts   = NULL;
 uschar *tls_verify_certificates= NULL;
 uschar *tls_verify_hosts       = NULL;
@@ -694,7 +697,7 @@ uschar *log_file_path          = US LOG_FILE_PATH
 /* Those log options with L_xxx identifiers have values less than 0x800000 and
 are the ones that get put into log_write_selector. They can be used in calls to
 log_write() to test for the bit. The options with LX_xxx identifiers have
-values greater than 0x80000000 and are put int log_extra_selector (without the
+values greater than 0x80000000 and are put into log_extra_selector (without the
 top bit). They are never used in calls to log_write(), but are tested
 independently. This separation became necessary when the number of log
 selectors was getting close to filling a 32-bit word. */
@@ -743,6 +746,7 @@ bit_table log_options[]        = {
   { US"tls_certificate_verified",     LX_tls_certificate_verified },
   { US"tls_cipher",                   LX_tls_cipher },
   { US"tls_peerdn",                   LX_tls_peerdn },
+  { US"tls_sni",                      LX_tls_sni },
   { US"unknown_in_list",              LX_unknown_in_list }
 };
 
index a51e3bc50657475312548f3efbd7bf035541798a..f9540785ce52abe2365ddd8b82842aa28ff7c738 100644 (file)
@@ -98,6 +98,9 @@ extern BOOL    tls_offered;            /* Server offered TLS */
 extern uschar *tls_privatekey;         /* Private key file */
 extern BOOL    tls_remember_esmtp;     /* For YAEB */
 extern uschar *tls_require_ciphers;    /* So some can be avoided */
+#ifndef USE_GNUTLS
+extern uschar *tls_sni;                /* Server Name Indication */
+#endif
 extern uschar *tls_try_verify_hosts;   /* Optional client verification */
 extern uschar *tls_verify_certificates;/* Path for certificates to check */
 extern uschar *tls_verify_hosts;       /* Mandatory client verification */
index 25b194407ad04a967cc49fe98f0ef7fd55917506..aedfc9f92f5c06bdd4ee734e252e102b95ae6805 100644 (file)
@@ -186,7 +186,7 @@ extern uschar *rfc2047_decode(uschar *, BOOL, uschar *, int, int *, uschar **);
 extern int     smtp_fflush(void);
 extern void    smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2);
 extern void    smtp_vprintf(const char *, va_list);
-extern uschar *string_copy(uschar *);
+extern uschar *string_copy(const uschar *);
 extern uschar *string_copyn(uschar *, int);
 extern uschar *string_sprintf(const char *, ...) PRINTF_FUNCTION(1,2);
 
index c1c4cc33f703faeedaf500d5268f86daf336443c..9b41226e51470feb47b2095b09604d49fcf19415 100644 (file)
@@ -407,7 +407,8 @@ set all the bits in a multi-word selector. */
 #define LX_tls_certificate_verified    0x80100000
 #define LX_tls_cipher                  0x80200000
 #define LX_tls_peerdn                  0x80400000
-#define LX_unknown_in_list             0x80800000
+#define LX_tls_sni                     0x80800000
+#define LX_unknown_in_list             0x81000000
 
 #define L_default     (L_connection_reject        | \
                        L_delay_delivery           | \
index 5215777f8e369d9409e21e6a12278d5deb133ddf..ade294e5df82688cc5f1d4b1e0b9d85b5dfbf080 100644 (file)
@@ -31,8 +31,10 @@ the arguments of printf-like functions. This is done by a macro. */
 
 #if defined(__GNUC__) || defined(__clang__)
 #define PRINTF_FUNCTION(A,B)  __attribute__((format(printf,A,B)))
+#define ARG_UNUSED  __attribute__((__unused__))
 #else
 #define PRINTF_FUNCTION(A,B)
+#define ARG_UNUSED  /**/
 #endif
 
 
index c62235916328e62ff46f5ad3f24359389cb325be..b35811e4869b322c283879ac8521958c58587d2d 100644 (file)
@@ -520,7 +520,7 @@ while (isalnum(*s) || *s == '_')
   {
   if (namelen >= sizeof(name) - 1)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-      "macro name too long (maximum is %d characters)", sizeof(name) - 1);
+      "macro name too long (maximum is " SIZE_T_FMT " characters)", sizeof(name) - 1);
   name[namelen++] = *s++;
   }
 name[namelen] = 0;
@@ -3189,9 +3189,14 @@ so as to ensure that everything else is set up before the expansion. */
 
 if (host_number_string != NULL)
   {
+  long int n;
   uschar *end;
   uschar *s = expand_string(host_number_string);
-  long int n = Ustrtol(s, &end, 0);
+  if (s == NULL)
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+        "failed to expand localhost_number \"%s\": %s",
+        host_number_string, expand_string_message);
+  n = Ustrtol(s, &end, 0);
   while (isspace(*end)) end++;
   if (*end != 0)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
@@ -3607,7 +3612,7 @@ else if (strncmpic(pp, US"tls_required", p - pp) == 0)
   *basic_errno = ERRNO_TLSREQUIRED;
 
 else if (len != 1 || Ustrncmp(pp, "*", 1) != 0)
-  return string_sprintf("unknown or malformed retry error \"%.*s\"", p-pp, pp);
+  return string_sprintf("unknown or malformed retry error \"%.*s\"", (int) (p-pp), pp);
 
 return NULL;
 }
index 71052657cdc83e26af7b9bb94b755527498b9cb7..aaaf64ce6f1e9f797462af60bddfc9c5d3fbff68 100644 (file)
@@ -3488,6 +3488,11 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
 if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
   s = string_append(s, &size, &sptr, 3, US" DN=\"",
     string_printing(tls_peerdn), US"\"");
+#ifndef USE_GNUTLS
+if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL)
+  s = string_append(s, &size, &sptr, 3, US" SNI=\"",
+    string_printing(tls_sni), US"\"");
+#endif
 #endif
 
 if (sender_host_authenticated != NULL)
index 23bc5315e8f22978a6809c46bdd113d8af0cc0d8..d1c10f00fb5b81f3a8ddb9f26390d25425245684 100644 (file)
@@ -841,6 +841,11 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
 if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
   s = string_append(s, &size, &ptr, 3, US" DN=\"",
     string_printing(tls_peerdn), US"\"");
+#ifndef USE_GNUTLS
+if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL)
+  s = string_append(s, &size, &ptr, 3, US" SNI=\"",
+    string_printing(tls_sni), US"\"");
+#endif
 #endif
 
 sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
index 0597084de50bc217e24418204990c230e0738712..63395f2d54cc0521f5ff31954666dd3c0e3c2aa5 100644 (file)
@@ -27,7 +27,7 @@ int spam(uschar **listptr) {
   uschar user_name_buffer[128];
   unsigned long mbox_size;
   FILE *mbox_file;
-  int spamd_sock;
+  int spamd_sock = -1;
   uschar spamd_buffer[32600];
   int i, j, offset, result;
   uschar spamd_version[8];
@@ -220,6 +220,14 @@ int spam(uschar **listptr) {
 
   }
 
+  if (spamd_sock == -1) {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+        "programming fault, spamd_sock unexpectedly unset");
+    (void)fclose(mbox_file);
+    (void)close(spamd_sock);
+    return DEFER;
+  }
+
   /* now we are connected to spamd on spamd_sock */
   (void)string_format(spamd_buffer,
            sizeof(spamd_buffer),
index e0d7fcffe6a25ae5ca1626834f4d47b438b550c1..bdc3903c048a3201aad2f6812487a369233e7f5b 100644 (file)
@@ -286,6 +286,9 @@ dkim_collect_input = FALSE;
 tls_certificate_verified = FALSE;
 tls_cipher = NULL;
 tls_peerdn = NULL;
+#ifndef USE_GNUTLS
+tls_sni = NULL;
+#endif
 #endif
 
 #ifdef WITH_CONTENT_SCAN
@@ -549,6 +552,10 @@ for (;;)
       tls_cipher = string_copy(big_buffer + 12);
     else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
       tls_peerdn = string_unprinting(string_copy(big_buffer + 12));
+    #ifndef USE_GNUTLS
+    else if (Ustrncmp(p, "ls_sni", 6) == 0)
+      tls_sni = string_unprinting(string_copy(big_buffer + 9));
+    #endif
     break;
     #endif
 
index 7b8229934d63ab688034c865b8a83465a2bc749b..fa4f1b6e2f5a49ed098fd0b7e59f35ed3ffd82fb 100644 (file)
@@ -229,6 +229,9 @@ if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts);
 if (tls_certificate_verified) fprintf(f, "-tls_certificate_verified\n");
 if (tls_cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_cipher);
 if (tls_peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_peerdn));
+#ifndef USE_GNUTLS
+if (tls_sni != NULL) fprintf(f, "-tls_sni %s\n", string_printing(tls_sni));
+#endif
 #endif
 
 /* To complete the envelope, write out the tree of non-recipients, followed by
index 1d1458d0fade1afa8608e5ea46e157f8f17869b4..0a5a11fc2d4b439bc1c87a754dde3ba68a341af9 100644 (file)
@@ -39,6 +39,9 @@ The following different types of store are recognized:
 
 
 #include "exim.h"
+/* keep config.h before memcheck.h, for NVALGRIND */
+#include "config.h"
+
 #include "memcheck.h"
 
 
@@ -342,7 +345,7 @@ if ((char *)ptr < bc || (char *)ptr > bc + b->length)
     if ((char *)ptr >= bc && (char *)ptr <= bc + b->length) break;
     }
   if (b == NULL)
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal error: store_reset(%d) "
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal error: store_reset(%p) "
       "failed: pool=%d %-14s %4d", ptr, store_pool, filename, linenumber);
   }
 
index 0a321ee1f761b998449fa3a7a455148f879cac8f..3fea7c048393e4006d2cf1b8adc2414438f46dc6 100644 (file)
@@ -415,7 +415,7 @@ Returns:  copy of string in new store
 */
 
 uschar *
-string_copy(uschar *s)
+string_copy(const uschar *s)
 {
 int len = Ustrlen(s) + 1;
 uschar *ss = store_get(len);
index 2f952e47b3aeef121f294ae8e6d521f46fa57841..7e87dded0e0859f93dc6196ac047bb31e470fa91 100644 (file)
@@ -1055,6 +1055,7 @@ Arguments:
   dhparam           DH parameter file
   certificate       certificate file
   privatekey        private key file
+  sni               TLS SNI to send to remote host
   verify_certs      file for certificate verify
   verify_crl        CRL for verify
   require_ciphers   list of allowed ciphers or NULL
@@ -1069,8 +1070,9 @@ Returns:            OK/DEFER/FAIL (because using common functions),
 
 int
 tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
-  uschar *certificate, uschar *privatekey, uschar *verify_certs,
-  uschar *verify_crl, uschar *require_ciphers, uschar *require_mac,
+  uschar *certificate, uschar *privatekey, uschar *sni ARG_UNUSED,
+  uschar *verify_certs, uschar *verify_crl,
+  uschar *require_ciphers, uschar *require_mac,
   uschar *require_kx, uschar *require_proto, int timeout)
 {
 const gnutls_datum *server_certs;
index e2e150c0a139c9ca4cec7f25c4ba3e16c03a8bf4..e609670ee73e57d0ebb311dfdf98178e9d222e75 100644 (file)
@@ -34,6 +34,7 @@ static BOOL verify_callback_called = FALSE;
 static const uschar *sid_ctx = US"exim";
 
 static SSL_CTX *ctx = NULL;
+static SSL_CTX *ctx_sni = NULL;
 static SSL *ssl = NULL;
 
 static char ssl_errstring[256];
@@ -41,8 +42,26 @@ static char ssl_errstring[256];
 static int  ssl_session_timeout = 200;
 static BOOL verify_optional = FALSE;
 
+static BOOL    reexpand_tls_files_for_sni = FALSE;
 
 
+typedef struct tls_ext_ctx_cb {
+  uschar *certificate;
+  uschar *privatekey;
+  uschar *dhparam;
+  /* these are cached from first expand */
+  uschar *server_cipher_list;
+  /* only passed down to tls_error: */
+  host_item *host;
+} tls_ext_ctx_cb;
+
+/* should figure out a cleanup of API to handle state preserved per
+implementation, for various reasons, which can be void * in the APIs.
+For now, we hack around it. */
+tls_ext_ctx_cb *static_cbinfo = NULL;
+
+static int
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional);
 
 
 /*************************************************
@@ -201,8 +220,8 @@ return 1;   /* accept */
 *************************************************/
 
 /* The SSL library functions call this from time to time to indicate what they
-are doing. We copy the string to the debugging output when the level is high
-enough.
+are doing. We copy the string to the debugging output when TLS debugging has
+been requested.
 
 Arguments:
   s         the SSL connection
@@ -279,6 +298,148 @@ return yield;
 
 
 
+/*************************************************
+*        Expand key and cert file specs          *
+*************************************************/
+
+/* Called once during tls_init and possibly againt during TLS setup, for a
+new context, if Server Name Indication was used and tls_sni was seen in
+the certificate string.
+
+Arguments:
+  sctx            the SSL_CTX* to update
+  cbinfo          various parts of session state
+
+Returns:          OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(SSL_CTX *sctx, const tls_ext_ctx_cb *cbinfo)
+{
+uschar *expanded;
+
+if (cbinfo->certificate == NULL)
+  return OK;
+
+if (Ustrstr(cbinfo->certificate, US"tls_sni"))
+  reexpand_tls_files_for_sni = TRUE;
+
+if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded))
+  return DEFER;
+
+if (expanded != NULL)
+  {
+  DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
+  if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded))
+    return tls_error(string_sprintf(
+      "SSL_CTX_use_certificate_chain_file file=%s", expanded),
+        cbinfo->host, NULL);
+  }
+
+if (cbinfo->privatekey != NULL &&
+    !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded))
+  return DEFER;
+
+/* If expansion was forced to fail, key_expanded will be NULL. If the result
+of the expansion is an empty string, ignore it also, and assume the private
+key is in the same file as the certificate. */
+
+if (expanded != NULL && *expanded != 0)
+  {
+  DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
+  if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
+    return tls_error(string_sprintf(
+      "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL);
+  }
+
+return OK;
+}
+
+
+
+
+/*************************************************
+*            Callback to handle SNI              *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if a Server Name
+Indication extension was sent by the client.
+
+API documentation is OpenSSL s_server.c implementation.
+
+Arguments:
+  s               SSL* of the current session
+  ad              unknown (part of OpenSSL API) (unused)
+  arg             Callback of "our" registered data
+
+Returns:          SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK}
+*/
+
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
+/* pre-declared for SSL_CTX_set_tlsext_servername_callback call within func */
+
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+{
+const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+int rc;
+int old_pool = store_pool;
+
+if (!servername)
+  return SSL_TLSEXT_ERR_OK;
+
+DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
+    reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
+
+/* Make the extension value available for expansion */
+store_pool = POOL_PERM;
+tls_sni = string_copy(US servername);
+store_pool = old_pool;
+
+if (!reexpand_tls_files_for_sni)
+  return SSL_TLSEXT_ERR_OK;
+
+/* Can't find an SSL_CTX_clone() or equivalent, so we do it manually;
+not confident that memcpy wouldn't break some internal reference counting.
+Especially since there's a references struct member, which would be off. */
+
+ctx_sni = SSL_CTX_new(SSLv23_server_method());
+if (!ctx_sni)
+  {
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
+  return SSL_TLSEXT_ERR_NOACK;
+  }
+
+/* Not sure how many of these are actually needed, since SSL object
+already exists.  Might even need this selfsame callback, for reneg? */
+
+SSL_CTX_set_info_callback(ctx_sni, SSL_CTX_get_info_callback(ctx));
+SSL_CTX_set_mode(ctx_sni, SSL_CTX_get_mode(ctx));
+SSL_CTX_set_options(ctx_sni, SSL_CTX_get_options(ctx));
+SSL_CTX_set_timeout(ctx_sni, SSL_CTX_get_timeout(ctx));
+SSL_CTX_set_tlsext_servername_callback(ctx_sni, tls_servername_cb);
+SSL_CTX_set_tlsext_servername_arg(ctx_sni, cbinfo);
+if (cbinfo->server_cipher_list)
+  SSL_CTX_set_cipher_list(ctx_sni, CS cbinfo->server_cipher_list);
+
+rc = tls_expand_session_files(ctx_sni, cbinfo);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+DEBUG(D_tls) debug_printf("Switching SSL context.\n");
+SSL_set_SSL_CTX(s, ctx_sni);
+
+return SSL_TLSEXT_ERR_OK;
+}
+
+
+
+
 /*************************************************
 *            Initialize for TLS                  *
 *************************************************/
@@ -301,7 +462,15 @@ tls_init(host_item *host, uschar *dhparam, uschar *certificate,
   uschar *privatekey, address_item *addr)
 {
 long init_options;
+int rc;
 BOOL okay;
+tls_ext_ctx_cb *cbinfo;
+
+cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
+cbinfo->certificate = certificate;
+cbinfo->privatekey = privatekey;
+cbinfo->dhparam = dhparam;
+cbinfo->host = host;
 
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
@@ -379,36 +548,19 @@ if (!init_dh(dhparam, host)) return DEFER;
 
 /* Set up certificate and key */
 
-if (certificate != NULL)
-  {
-  uschar *expanded;
-  if (!expand_check(certificate, US"tls_certificate", &expanded))
-    return DEFER;
-
-  if (expanded != NULL)
-    {
-    DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
-    if (!SSL_CTX_use_certificate_chain_file(ctx, CS expanded))
-      return tls_error(string_sprintf(
-        "SSL_CTX_use_certificate_chain_file file=%s", expanded), host, NULL);
-    }
-
-  if (privatekey != NULL &&
-      !expand_check(privatekey, US"tls_privatekey", &expanded))
-    return DEFER;
-
-  /* If expansion was forced to fail, key_expanded will be NULL. If the result
-  of the expansion is an empty string, ignore it also, and assume the private
-  key is in the same file as the certificate. */
+rc = tls_expand_session_files(ctx, cbinfo);
+if (rc != OK) return rc;
 
-  if (expanded != NULL && *expanded != 0)
-    {
-    DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
-    if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM))
-      return tls_error(string_sprintf(
-        "SSL_CTX_use_PrivateKey_file file=%s", expanded), host, NULL);
-    }
+/* If we need to handle SNI, do so */
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+if (host == NULL)
+  {
+  /* We always do this, so that $tls_sni is available even if not used in
+  tls_certificate */
+  SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
+  SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
   }
+#endif
 
 /* Set up the RSA callback */
 
@@ -418,6 +570,9 @@ SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback);
 
 SSL_CTX_set_timeout(ctx, ssl_session_timeout);
 DEBUG(D_tls) debug_printf("Initialized TLS\n");
+
+static_cbinfo = cbinfo;
+
 return OK;
 }
 
@@ -496,6 +651,7 @@ DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
 /* Called by both client and server startup
 
 Arguments:
+  sctx          SSL_CTX* to initialise
   certs         certs file or NULL
   crl           CRL file or NULL
   host          NULL in a server; the remote host in a client
@@ -506,7 +662,7 @@ Returns:        OK/DEFER/FAIL
 */
 
 static int
-setup_certs(uschar *certs, uschar *crl, host_item *host, BOOL optional)
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional)
 {
 uschar *expcerts, *expcrl;
 
@@ -516,7 +672,7 @@ if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
 if (expcerts != NULL)
   {
   struct stat statbuf;
-  if (!SSL_CTX_set_default_verify_paths(ctx))
+  if (!SSL_CTX_set_default_verify_paths(sctx))
     return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
 
   if (Ustat(expcerts, &statbuf) < 0)
@@ -539,12 +695,12 @@ if (expcerts != NULL)
     says no certificate was supplied.) But this is better. */
 
     if ((file == NULL || statbuf.st_size > 0) &&
-          !SSL_CTX_load_verify_locations(ctx, CS file, CS dir))
+          !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
       return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
 
     if (file != NULL)
       {
-      SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file));
+      SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file));
       }
     }
 
@@ -576,7 +732,7 @@ if (expcerts != NULL)
       {
       /* is it a file or directory? */
       uschar *file, *dir;
-      X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx);
+      X509_STORE *cvstore = SSL_CTX_get_cert_store(sctx);
       if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR)
         {
         file = NULL;
@@ -603,7 +759,7 @@ if (expcerts != NULL)
 
   /* If verification is optional, don't fail if no certificate */
 
-  SSL_CTX_set_verify(ctx,
+  SSL_CTX_set_verify(sctx,
     SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
     verify_callback);
   }
@@ -641,6 +797,7 @@ tls_server_start(uschar *require_ciphers, uschar *require_mac,
 {
 int rc;
 uschar *expciphers;
+tls_ext_ctx_cb *cbinfo;
 
 /* Check for previous activation */
 
@@ -656,6 +813,7 @@ the error. */
 
 rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL);
 if (rc != OK) return rc;
+cbinfo = static_cbinfo;
 
 if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
   return FAIL;
@@ -671,6 +829,7 @@ if (expciphers != NULL)
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
     return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL);
+  cbinfo->server_cipher_list = expciphers;
   }
 
 /* If this is a host for which certificate verification is mandatory or
@@ -681,13 +840,13 @@ verify_callback_called = FALSE;
 
 if (verify_check_host(&tls_verify_hosts) == OK)
   {
-  rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE);
+  rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, FALSE);
   if (rc != OK) return rc;
   verify_optional = FALSE;
   }
 else if (verify_check_host(&tls_try_verify_hosts) == OK)
   {
-  rc = setup_certs(tls_verify_certificates, tls_crl, NULL, TRUE);
+  rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, TRUE);
   if (rc != OK) return rc;
   verify_optional = TRUE;
   }
@@ -695,7 +854,19 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK)
 /* Prepare for new connection */
 
 if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
-SSL_clear(ssl);
+
+/* Warning: we used to SSL_clear(ssl) here, it was removed.
+ *
+ * With the SSL_clear(), we get strange interoperability bugs with
+ * OpenSSL 1.0.1b and TLS1.1/1.2.  It looks as though this may be a bug in
+ * OpenSSL itself, as a clear should not lead to inability to follow protocols.
+ *
+ * The SSL_clear() call is to let an existing SSL* be reused, typically after
+ * session shutdown.  In this case, we have a brand new object and there's no
+ * obvious reason to immediately clear it.  I'm guessing that this was
+ * originally added because of incomplete initialisation which the clear fixed,
+ * in some historic release.
+ */
 
 /* Set context and tell client to go ahead, except in the case of TLS startup
 on connection, where outputting anything now upsets the clients and tends to
@@ -779,6 +950,7 @@ Argument:
   dhparam          DH parameter file
   certificate      certificate file
   privatekey       private key file
+  sni              TLS SNI to send to remote host
   verify_certs     file for certificate verify
   crl              file containing CRL
   require_ciphers  list of allowed ciphers
@@ -796,7 +968,8 @@ Returns:           OK on success
 
 int
 tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
-  uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl,
+  uschar *certificate, uschar *privatekey, uschar *sni,
+  uschar *verify_certs, uschar *crl,
   uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
   uschar *require_proto, int timeout)
 {
@@ -827,7 +1000,7 @@ if (expciphers != NULL)
     return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
   }
 
-rc = setup_certs(verify_certs, crl, host, FALSE);
+rc = setup_certs(ctx, verify_certs, crl, host, FALSE);
 if (rc != OK) return rc;
 
 if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
@@ -835,6 +1008,19 @@ SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
 SSL_set_fd(ssl, fd);
 SSL_set_connect_state(ssl);
 
+if (sni)
+  {
+  if (!expand_check(sni, US"tls_sni", &tls_sni))
+    return FAIL;
+  if (!Ustrlen(tls_sni))
+    tls_sni = NULL;
+  else
+    {
+    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
+    SSL_set_tlsext_host_name(ssl, tls_sni);
+    }
+  }
+
 /* There doesn't seem to be a built-in timeout on connection. */
 
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
@@ -913,8 +1099,10 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     SSL_free(ssl);
     ssl = NULL;
     tls_active = -1;
+    tls_bits = 0;
     tls_cipher = NULL;
     tls_peerdn = NULL;
+    tls_sni = NULL;
 
     return smtp_getc();
     }
@@ -1332,10 +1520,8 @@ uschar keep_c;
 BOOL adding, item_parsed;
 
 result = 0L;
-/* We grandfather in as default the one option which we used to set always. */
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-result |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
-#endif
+/* Prior to 4.78 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
+ * from default because it increases BEAST susceptibility. */
 
 if (option_spec == NULL)
   {
index c571d874c4a1d4fa83d038c77984c7136981fd9d..b1fedd2d4a9d1ccc9f626addf56a9ad65f19adf3 100644 (file)
@@ -128,8 +128,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, tls_crl) },
   { "tls_privatekey",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_privatekey) },
-  { "tls_require_ciphers",   opt_stringptr,
+  { "tls_require_ciphers",  opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
+  { "tls_sni",              opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, tls_sni) },
   { "tls_tempfail_tryclear", opt_bool,
       (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
   { "tls_verify_certificates", opt_stringptr,
@@ -191,7 +193,8 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* gnutls_require_mac */
   NULL,                /* gnutls_require_proto */
   NULL,                /* tls_verify_certificates */
-  TRUE                 /* tls_tempfail_tryclear */
+  TRUE,                /* tls_tempfail_tryclear */
+  NULL                 /* tls_sni */
 #endif
 #ifndef DISABLE_DKIM
  ,NULL,                /* dkim_canon */
@@ -889,8 +892,10 @@ outblock.authenticating = FALSE;
 
 /* Reset the parameters of a TLS session. */
 
+tls_bits = 0;
 tls_cipher = NULL;
 tls_peerdn = NULL;
+tls_sni = NULL;
 
 /* If an authenticated_sender override has been specified for this transport
 instance, expand it. If the expansion is forced to fail, and there was already
@@ -1122,6 +1127,7 @@ if (tls_offered && !suppress_tls &&
       NULL,                    /* No DH param */
       ob->tls_certificate,
       ob->tls_privatekey,
+      ob->tls_sni,
       ob->tls_verify_certificates,
       ob->tls_crl,
       ob->tls_require_ciphers,
index a2ea4ff0a2488aa270f25eeff95b2c814993e4c0..605be4800297927c629c03a322134fb37344146e 100644 (file)
@@ -54,6 +54,7 @@ typedef struct {
   uschar *gnutls_require_proto;
   uschar *tls_verify_certificates;
   BOOL    tls_tempfail_tryclear;
+  uschar *tls_sni;
   #endif
   #ifndef DISABLE_DKIM
   uschar *dkim_domain;
index 66760d1b0f4287f7a6fcb5759927eac433dec102..8e57d28b2041777fb47a14573a30b75f1cb5aeb6 100644 (file)
@@ -555,7 +555,7 @@ else
 
   (void)gettimeofday(&tv, NULL);
   tempname = string_sprintf("%s/tmp/%lu.H%luP%lu.%s", path, tv.tv_sec,
-    tv.tv_usec, getpid(), primary_hostname);
+    tv.tv_usec, (long unsigned) getpid(), primary_hostname);
 
   fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600);
   if (fd >= 0)