diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2012-05-05 21:10:19 +0100 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2012-05-05 21:10:19 +0100 |
commit | 76fbc01d6f1814bdbc2dedb31a405105b38a70f2 (patch) | |
tree | 195f9da7e19961a4e9164ab0c5032678f970174c | |
parent | 9fd2d621273cc5d9f38af480ed40bb16521bd86d (diff) | |
parent | ef8406816ea0fc82b5d80009b30cb83ad9af6f2f (diff) |
Merge branch 'master' of git://git.exim.org/exim
34 files changed, 612 insertions, 131 deletions
diff --git a/doc/doc-docbook/filter.xfpt b/doc/doc-docbook/filter.xfpt index b0e176205..4821ef173 100644 --- a/doc/doc-docbook/filter.xfpt +++ b/doc/doc-docbook/filter.xfpt @@ -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" ?> diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index e719855f8..c4739a80f 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -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 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index b41783d71..80e8edf97 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index b962b61a2..2872d241f 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -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 ------------ diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt index 5313fd17c..52a24b198 100644 --- a/doc/doc-txt/OptionLists.txt +++ b/doc/doc-txt/OptionLists.txt @@ -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 diff --git a/src/README.UPDATING b/src/README.UPDATING index 3dff7c094..5b6bea869 100644 --- a/src/README.UPDATING +++ b/src/README.UPDATING @@ -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 diff --git a/src/src/EDITME b/src/src/EDITME index c4aedb6bd..f247f44a9 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -1199,6 +1199,12 @@ TMPDIR="/tmp" #------------------------------------------------------------------------------ +# 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 # file can be specified here. Some installations may want something like this: diff --git a/src/src/acl.c b/src/src/acl.c index 50d5d8594..b93ac6965 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -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) diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 31de7c2d2..e93d03845 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -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; diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index e01789e31..c6e973ad9 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -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); diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index f3e3d880a..c082b9269 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -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 diff --git a/src/src/daemon.c b/src/src/daemon.c index 4ac34332b..27b4cb265 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -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. */ diff --git a/src/src/drtables.c b/src/src/drtables.c index c87e9c23b..06f7e30ed 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -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; diff --git a/src/src/exipick.src b/src/src/exipick.src index 811092dc1..ed3b66154 100644 --- a/src/src/exipick.src +++ b/src/src/exipick.src @@ -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. diff --git a/src/src/expand.c b/src/src/expand.c index 54501de0b..22f7d9a66 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -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 }, diff --git a/src/src/functions.h b/src/src/functions.h index f1af42ee5..220235235 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -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); diff --git a/src/src/globals.c b/src/src/globals.c index 6124cb585..f11c7c2db 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -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 } }; diff --git a/src/src/globals.h b/src/src/globals.h index a51e3bc50..f9540785c 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -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 */ diff --git a/src/src/local_scan.h b/src/src/local_scan.h index 25b194407..aedfc9f92 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -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); diff --git a/src/src/macros.h b/src/src/macros.h index c1c4cc33f..9b41226e5 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -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 | \ diff --git a/src/src/mytypes.h b/src/src/mytypes.h index 5215777f8..ade294e5d 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -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 diff --git a/src/src/readconf.c b/src/src/readconf.c index c62235916..b35811e48 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -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; } diff --git a/src/src/receive.c b/src/src/receive.c index 71052657c..aaaf64ce6 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -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) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 23bc5315e..d1c10f00f 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -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)? diff --git a/src/src/spam.c b/src/src/spam.c index 0597084de..63395f2d5 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -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), diff --git a/src/src/spool_in.c b/src/src/spool_in.c index e0d7fcffe..bdc3903c0 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -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 diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 7b8229934..fa4f1b6e2 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -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 diff --git a/src/src/store.c b/src/src/store.c index 1d1458d0f..0a5a11fc2 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -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); } diff --git a/src/src/string.c b/src/src/string.c index 0a321ee1f..3fea7c048 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -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); diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 2f952e47b..7e87dded0 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -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; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e2e150c0a..e609670ee 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -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 @@ -280,6 +299,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) { diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c571d874c..b1fedd2d4 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -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, diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index a2ea4ff0a..605be4800 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -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; diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index 66760d1b0..8e57d28b2 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -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) |