From: Wietse Venema Date: Sun, 25 Aug 2013 05:00:00 +0000 (-0500) Subject: postfix-2.11-20130825 X-Git-Tag: v2.11.0-RC1~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=26eddc2f7829347c4e4944fd54df51e16b440a35;p=thirdparty%2Fpostfix.git postfix-2.11-20130825 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 3d53b41ca..8e82ff87a 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -314,6 +314,7 @@ -TTLS_SERVER_INIT_PROPS -TTLS_SERVER_START_PROPS -TTLS_SESS_STATE +-TTLS_TICKET_KEY -TTLS_TLSA -TTLS_VINFO -TTLScontext_t diff --git a/postfix/HISTORY b/postfix/HISTORY index 347b75ba1..187107bd4 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -18839,3 +18839,76 @@ Apologies for any names omitted. new MDB_MAP_FULL workaround by Howard Chu that ensures that postfix will make progress as long as the disk is not full. File: util/dict_lmdb.c. + +20130822 + + The status of LMDB databases is "not recommended". Unlike + other Postfix databases, LMDB does not grow beyond a specified + limit even when the file system has room. This show-stopper + bug breaks applications whose requirements grow with load: + postscreen(8), greylisting, tlsmgr(8) and verify(8). + +20130825 + + Bitrot: Arrange for shared keys in SMTP server session + tickets. Otherwise, with clients that enable session + tickets, the SMTP session cache is per-process and largely + ineffective. Older releases should add SSL_OP_NO_TICKET + to the SSL options bit mask in the SMTP server only. The + session ticket key validity interval (sum of initial issuing + and retired key validation intervals) must not exceed the + SSL session lifetime. Otherwise, clients may send valid + tickets for expired sessions, which the OpenSSL server code + mishandles (does not send a replacement ticket, patch + pending...). + + We set the session lifetime to 2 times the configured cache + lifetime which is also the ticket issuing and retired + validation lifetime, so ticketed sessions last 1 to 2 times + the configured session lifetime and never longer than a + session's expiration time. + + Code by Viktor Dukhovni. Files: .indent.pro, mantools/postlink, + proto/TLS_README.html, proto/postconf.proto, global/mail_params.h, + posttls-finger/posttls-finger.c, posttls-finger/tlsmgrmem.c, + smtpd/smtpd.c, tls/tls.h, tls/tls_client.c, tls/tls_mgr.c, + tls/tls_mgr.h, tls/tls_scache.c, tls/tls_scache.h, + tls/tls_server.c, tlsmgr/tlsmgr.c, tlsproxy/tlsproxy.c. + + Robustness: Search for TLSA RRs at the resolved server name + (rname) and failing that request server name (qname), and + use whichever was found as the TLSA base domain for certificate + matching. + + When we find a DNSSEC validated MX RRset, and the initial + next-hop domain is a CNAME, include both the initial and + final (the one with the actual MX RRs) domains in the list + of valid server certificate names. + + When we find no MX records, then the initial next-hop domain + is obtained securely from the recipient domain or transport + next-hop. Without MX records, this is a destination hostname, + so we should generally do a TLSA lookup. If however the + address lookup yields an insecure result, and its rname is + equal to its qname (no CNAMEs), we reasonably assume that + the its child "_port._tcp" sub-domain is likewise insecure + (security here would require DLV just for this sub-domain). + This allows us to skip futile TLSA queries for most non-MX + destinations (those that are in insecure zones and are not + CNAMEs). This heuristic can be disabled by setting the new + main.cf parameter smtp_tls_force_insecure_host_tlsa_lookup + to "yes", the default is "no". + + Finally, with MX hostnames, if the MX RRset is secure, we + look for TLSA RRs at the qname only when the MX host is an + alias with an insecure rname. If both the qname and the + rname are secure, as before we prefer the rname, but when + nothing is found there, fall back to the qname. + + Code by Viktor Dukhovni. Files: mantools/postlink, + proto/postconf.proto, src/global/mail_params.h, + src/posttls-finger/posttls-finger.c, src/smtp/lmtp_params.c, + src/smtp/smtp.c, src/smtp/smtp.h, src/smtp/smtp_addr.c, + src/smtp/smtp_addr.h, src/smtp/smtp_connect.c, + src/smtp/smtp_params.c, src/smtp/smtp_tls_policy.c, + src/tls/tls.h, src/tls/tls_dane.c. diff --git a/postfix/README_FILES/LMDB_README b/postfix/README_FILES/LMDB_README index 4d0281481..fa7e80eb1 100644 --- a/postfix/README_FILES/LMDB_README +++ b/postfix/README_FILES/LMDB_README @@ -4,6 +4,14 @@ PPoossttffiixx OOppeennLLDDAAPP LLMMDDBB HHoowwttoo IInnttrroodduuccttiioonn + WWaarrnniinngg:: LLMMDDBB ddaattaabbaasseess hhaavvee aa sshhooww--ssttooppppeerr bbuugg:: tthheeyy ddoo nnoott ggrrooww bbeeyyoonndd aa + ssppeecciiffiieedd lliimmiitt,, aanndd iinnttrroodduuccee aa ""ddaattaabbaassee ffuullll"" hhaarrdd eerrrroorr ccoonnddiittiioonn tthhaatt + ddooeess nnoott eexxiisstt wwiitthh aannyy ootthheerr PPoossttffiixx ddaattaabbaassee ttyyppee.. TThhiiss iiss aa pprroobblleemm ffoorr + pprrooggrraammss wwhhoossee ddaattaabbaassee ggrroowwss wwiitthh ssyysstteemm llooaadd.. EExxaammpplleess aarree ppoossttssccrreeeenn((88)),, + ggrreeyylliissttiinngg,, vveerriiffyy((88)) aanndd ttllssmmggrr((88)).. + + YYoouu hhaavvee bbeeeenn wwaarrnneedd.. + Postfix uses databases of various kinds to store and look up information. Postfix databases are specified as "type:name". OpenLDAP LMDB implements the Postfix database type "lmdb". The name of a Postfix OpenLDAP LMDB database is diff --git a/postfix/README_FILES/TLS_README b/postfix/README_FILES/TLS_README index 6ec931214..697d614ab 100644 --- a/postfix/README_FILES/TLS_README +++ b/postfix/README_FILES/TLS_README @@ -410,16 +410,40 @@ Example: SSeerrvveerr--ssiiddee TTLLSS sseessssiioonn ccaacchhee The Postfix SMTP server and the remote SMTP client negotiate a session, which -takes some computer time and network bandwidth. By default, this session -information is cached only in the smtpd(8) process actually using this session -and is lost when the process terminates. To share the session information -between multiple smtpd(8) processes, a persistent session cache can be used. -You can specify any database type that can store objects of several kbytes and -that supports the sequence operator. DBM databases are not suitable because -they can only store small objects. The cache is maintained by the tlsmgr(8) -process, so there is no problem with concurrent access. Session caching is -highly recommended, because the cost of repeatedly negotiating TLS session keys -is high. +takes some computer time and network bandwidth. SSL protocol versions other +than SSLv2 support resumption of cached sessions. Not only is this more CPU and +bandwidth efficient, it also reduces latency as only one network round-trip is +used to resume a session while it takes two round-trips to create a session +from scratch. + +Since Postfix uses multiple smtpd(8) service processes, an in-memory cache is +not sufficient for session re-use. Clients store at most one cached session per +server and are very unlikey to repeatedly connect to the same server process. +Thus session caching in the Postfix SMTP server generally requires a shared +cache (an alternative available with Postfix >= 2.11 is described below). + +To share the session information between multiple smtpd(8) processes, a session +cache database is used. You can specify any database type that can store +objects of several kbytes and that supports the sequence operator. DBM +databases are not suitable because they can only store small objects. The cache +is maintained by the tlsmgr(8) process, so there is no problem with concurrent +access. Session caching is highly recommended, because the cost of repeatedly +negotiating TLS session keys is high. + +Starting with Postfix 2.11, linked with a compatible OpenSSL library (at least +0.9.8h, preferably 1.0.0 or later) the Postfix SMTP server supports RFC 5077 +TLS session resumption without server-side state when the remote SMTP client +also supports RFC 5077. The session is encrypted by the server in a session +ticket returned to client for storage. When a client sends a valid session +ticket, the server decrypts it and resumes the session, provided neither the +ticket nor the session have expired. This makes it possible to resume cached +sessions without allocating space for a shared database on the server. This +feature can be disabled by setting the session cache timeout to zero, otherwise +the timeout must be at least 2 minutes and at most 100 days. + +Note, session tickets can only be negotiated if the client disables SSLv2 and +does not use the legacy SSLv2 compatible HELLO message. This is true by default +with the Postfix >= 2.6 SMTP client. Example: @@ -441,11 +465,15 @@ Example: /etc/postfix/main.cf: smtpd_tls_session_cache_timeout = 3600s +As of Postfix 2.11 this setting cannot exceed 100 days. If set <= 0, session +caching is disabled. If set to a positive value less than 2 minutes, the +minimum value of 2 minutes is used instead. + When the Postfix SMTP server does not save TLS sessions to an external cache -database, client-side session caching is unlikely to be useful. To prevent such -wastage, the Postfix SMTP server can be configured to not issue TLS session -ids. By default the Postfix SMTP server always issues TLS session ids. This -works around known interoperability issues with some MUAs, and prevents +database, client-side session caching is unlikely to be useful. To reduce waste +of client resources, the Postfix SMTP server can be configured to not issue TLS +session ids. By default the Postfix SMTP server always issues TLS session ids. +This works around known interoperability issues with some MUAs, and prevents possible interoperability issues with other MTAs. Example: @@ -1486,6 +1514,10 @@ Example: /etc/postfix/main.cf: smtp_tls_session_cache_timeout = 3600s +As of Postfix 2.11 this setting cannot exceed 100 days. If set <= 0, session +caching is disabled. If set to a positive value less than 2 minutes, the +minimum value of 2 minutes is used instead. + CClliieenntt TTLLSS lliimmiittaattiioonnss The security properties of TLS communication channels are application specific. diff --git a/postfix/html/LMDB_README.html b/postfix/html/LMDB_README.html index e9938aa53..81d1484db 100644 --- a/postfix/html/LMDB_README.html +++ b/postfix/html/LMDB_README.html @@ -19,17 +19,13 @@

Introduction

- +

Warning: LMDB databases have a show-stopper +bug: they do not grow beyond a specified limit, and introduce a +"database full" hard error condition that does not exist with any +other Postfix database type. This is a problem for programs whose +database grows with system load. Examples are postscreen(8), +greylisting, verify(8) and tlsmgr(8).

+You have been warned.

Postfix uses databases of various kinds to store and look up information. Postfix databases are specified as "type:name". diff --git a/postfix/html/TLS_README.html b/postfix/html/TLS_README.html index c89e203db..c460a195a 100644 --- a/postfix/html/TLS_README.html +++ b/postfix/html/TLS_README.html @@ -604,23 +604,20 @@ In order to change this behavior, set

Server-side TLS session cache

-

The Postfix SMTP server and the remote SMTP client negotiate -a session, which takes some computer time and network bandwidth. -SSLv3, TLSv1 and later support resumption of cached sessions. Not -is this more CPU and bandwidth efficient, it also reduces latency -as it uses only one network round-trip instead of two.

- -

Since Postfix uses multiple smtpd(8) service processes, an in -memory cache is not sufficient for session re-use. Clients store +

The Postfix SMTP server and the remote SMTP client negotiate a +session, which takes some computer time and network bandwidth. SSL +protocol versions other than SSLv2 support resumption of cached +sessions. Not only is this more CPU and bandwidth efficient, it +also reduces latency as only one network round-trip is used to +resume a session while it takes two round-trips to create a session +from scratch.

+ +

Since Postfix uses multiple smtpd(8) service processes, an +in-memory cache is not sufficient for session re-use. Clients store at most one cached session per server and are very unlikey to -repeatedly connect to the same server process. With a per-process -server cache, when a client attempts to reuse a session with a -different process than the one that created it, a new session is -negotiated and the old forgotten. Returning to the original server -process later (except with back to back connections) does not help, -as that session has most probably been replaced. Thus session -caching in the Postfix SMTP server generally requires a shared cache -(an alternative available with Postfix ≥ 2.11 is described below). +repeatedly connect to the same server process. Thus session caching +in the Postfix SMTP server generally requires a shared cache (an +alternative available with Postfix ≥ 2.11 is described below).

To share the session information between multiple @@ -633,7 +630,7 @@ concurrent access. Session caching is highly recommended, because the cost of repeatedly negotiating TLS session keys is high.

Starting with Postfix 2.11, linked with a compatible OpenSSL -library (at least 0.9.8h, preferrably 1.0.0 or later) the Postfix +library (at least 0.9.8h, preferably 1.0.0 or later) the Postfix SMTP server supports RFC 5077 TLS session resumption without server-side state when the remote SMTP client also supports RFC 5077. The session is encrypted by the server in a session @@ -679,6 +676,10 @@ recommends a maximum of 24 hours.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead.

+

When the Postfix SMTP server does not save TLS sessions to an external cache database, client-side session caching is unlikely to be useful. To reduce waste of client resources, the Postfix SMTP server can @@ -2013,6 +2014,10 @@ recommends a maximum of 24 hours.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead.

+

Client TLS limitations

diff --git a/postfix/html/discard.8.html b/postfix/html/discard.8.html index 48c249598..e4b7a15fe 100644 --- a/postfix/html/discard.8.html +++ b/postfix/html/discard.8.html @@ -37,7 +37,7 @@ DISCARD(8) DISCARD(8) low privilege. STANDARDS - None. + RFC 3463 (Enhanced Status Codes) DIAGNOSTICS Problems and transactions are logged to syslogd(8). diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index 3b8ba36b6..4ef7b501f 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -593,15 +593,10 @@ SMTP(8) SMTP(8) Zero or more PEM-format files with trust-anchor certificates and/or public keys. - smtp_tls_dane_notfound_tlsa_level (may) - The "degraded" security level when the "dane" secu- - rity level is specified, but no validated DANE TLSA - records are published. - - smtp_tls_dane_unusable_tlsa_level (encrypt) - The "degraded" security level when the "dane" secu- - rity level is specified, validated DANE TLSA - records are present, but none are usable. + smtp_tls_force_insecure_host_tlsa_lookup (no) + Lookup the associated DANE TLSA RRset even when a + hostname is not an alias and its address records + lie in an unsigned zone. tls_dane_trust_anchor_digest_enable (trust-anchor-asser- tion) diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 8d83dea6a..a173c8557 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -4824,6 +4824,17 @@ configuration parameter. See there for details.

This feature is available in Postfix 2.5 and later.

+ + +
lmtp_tls_force_insecure_host_tlsa_lookup +(default: no)
+ +

The LMTP-specific version of the smtp_tls_force_insecure_host_tlsa_lookup +configuration parameter. See there for details.

+ +

This feature is available in Postfix 2.11 and later.

+ +
lmtp_tls_key_file @@ -11291,6 +11302,24 @@ to Postfix 2.9.6 or later.

This feature is available in Postfix 2.5 and later.

+ + +
smtp_tls_force_insecure_host_tlsa_lookup +(default: no)
+ +

Lookup the associated DANE TLSA RRset even when a hostname is +not an alias and its address records lie in an unsigned zone. This +is unlikely to ever yield DNSSEC validated results, since child +zones of unsigned zones are also unsigned in the absense of DLV or +locally configured non-root trust-anchors. We anticipate that such +mechanisms will not be used for just the "_tcp" subdomain of a host. +Suppressing the TLSA RRset lookup reduces latency and avoids potential +interoperability problems with nameservers for unsigned zones that +are not prepared to handle the new TLSA RRset.

+ +

This feature is available in Postfix 2.11.

+ +
smtp_tls_key_file @@ -12081,6 +12110,10 @@ $smtp_tls_session_cach tlsmgr(8) daemon and therefore per-smtp-instance master.cf overrides are not possible.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead.

+

This feature is available in Postfix 2.2 and later.

@@ -15759,7 +15792,16 @@ $smtpd_tls_session_ca tlsmgr(8) daemon and therefore per-smtpd-instance master.cf overrides are not possible.

-

This feature is available in Postfix 2.2 and later.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled, not just via the database, but +also via RFC 5077 TLS session tickets, which don't require server-side +storage. If set to a positive value less than 2 minutes, the minimum +value of 2 minutes is used instead. TLS session tickets require +an OpenSSL library (at least version 0.9.8h) that provides full +support for this TLS extension.

+ +

This feature is available in Postfix 2.2 and later, and updated +for TLS session ticket support in Postfix 2.11.

@@ -16983,10 +17025,10 @@ parameters smtpd_use_tls and tlsproxy_tls_session_cache_timeout (default: $smtpd_tls_session_cache_timeout)
-

The expiration time of Postfix tlsproxy(8) server TLS session -cache information. A cache cleanup is performed periodically every -$smtpd_tls_session_cache_timeout seconds. See -smtpd_tls_session_cache_timeout for further details.

+

Obsolete expiration time of Postfix tlsproxy(8) server TLS session +cache information. Since the cache is shared with smtpd(8) and managed +by tlsmgr(8), there is only one expiration time for the SMTP server cache +shared by all three services, namely smtpd_tls_session_cache_timeout.

This feature is available in Postfix 2.8 and later.

diff --git a/postfix/html/posttls-finger.1.html b/postfix/html/posttls-finger.1.html index 81705a895..3e449ec61 100644 --- a/postfix/html/posttls-finger.1.html +++ b/postfix/html/posttls-finger.1.html @@ -126,55 +126,60 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1) (with DANE TLSA records the algorithm is specified in the DNS). + -f Lookup the associated DANE TLSA RRset even when a + hostname is not an alias and its address records + lie in an unsigned zone. See smtp_tls_force_inse- + cure_host_tlsa_lookup for details. + -F CAfile.pem (default: none) - The PEM formatted CAfile for remote SMTP server - certificate verification. By default no CAfile is + The PEM formatted CAfile for remote SMTP server + certificate verification. By default no CAfile is used and no public CAs are trusted. -g grade (default: medium) - The minimum TLS cipher grade used by posttls-fin- + The minimum TLS cipher grade used by posttls-fin- ger. See smtp_tls_mandatory_ciphers for details. -h host_lookup (default: dns) - The hostname lookup methods used for the connec- - tion. See the documentation of smtp_host_lookup + The hostname lookup methods used for the connec- + tion. See the documentation of smtp_host_lookup for syntax and semantics. -l level (default: dane or secure) The security level for the connection, default dane or secure depending on whether DNSSEC is available. - For syntax and semantics, see the documentation of + For syntax and semantics, see the documentation of smtp_tls_security_level. When dane or dane-only is - supported and selected, if no TLSA records are - found, or all the records found are unusable, the + supported and selected, if no TLSA records are + found, or all the records found are unusable, the secure level will be used instead. The fingerprint - security level allows you to test certificate or - public-key fingerprint matches before you deploy + security level allows you to test certificate or + public-key fingerprint matches before you deploy them in the policy table. - Note, since posttls-finger does not actually - deliver any email, the none, may and encrypt secu- - rity levels are not very useful. Since may and - encrypt don't require peer certificates, they will - often negotiate anonymous TLS ciphersuites, so you - won't learn much about the remote SMTP server's - certificates at these levels if it also supports + Note, since posttls-finger does not actually + deliver any email, the none, may and encrypt secu- + rity levels are not very useful. Since may and + encrypt don't require peer certificates, they will + often negotiate anonymous TLS ciphersuites, so you + won't learn much about the remote SMTP server's + certificates at these levels if it also supports anonymous TLS (though you may learn that the server supports anonymous TLS). -L logopts (default: routine,certmatch) - Fine-grained TLS logging options. To tune the TLS - features logged during the TLS handshake, specify + Fine-grained TLS logging options. To tune the TLS + features logged during the TLS handshake, specify one or more of: 0, none These yield no TLS logging; you'll generally - want more, but this is handy if you just + want more, but this is handy if you just want the trust chain: $ posttls-finger -cC -L none destination 1, routine, summary - These synonymous values yield a normal one- + These synonymous values yield a normal one- line summary of the TLS connection. 2, debug @@ -182,129 +187,129 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1) ssl-debug, cache and verbose. 3, ssl-expert - These synonymous values combine debug with - ssl-handshake-packet-dump. For experts + These synonymous values combine debug with + ssl-handshake-packet-dump. For experts only. 4, ssl-developer - These synonymous values combine ssl-expert - with ssl-session-packet-dump. For experts + These synonymous values combine ssl-expert + with ssl-session-packet-dump. For experts only, and in most cases, use wireshark instead. ssl-debug - Turn on OpenSSL logging of the progress of + Turn on OpenSSL logging of the progress of the SSL handshake. ssl-handshake-packet-dump - Log hexadecimal packet dumps of the SSL + Log hexadecimal packet dumps of the SSL handshake; for experts only. ssl-session-packet-dump - Log hexadecimal packet dumps of the entire - SSL session; only useful to those who can - debug SSL protocol problems from hex dumps. + Log hexadecimal packet dumps of the entire + SSL session; only useful to those who can + debug SSL protocol problems from hex dumps. untrusted - Logs trust chain verification problems. - This is turned on automatically at security - levels that use peer names signed by cer- - tificate authorities to validate certifi- + Logs trust chain verification problems. + This is turned on automatically at security + levels that use peer names signed by cer- + tificate authorities to validate certifi- cates. So while this setting is recognized, - you should never need to set it explicitly. + you should never need to set it explicitly. peercert - This logs a one line summary of the remote + This logs a one line summary of the remote SMTP server certificate subject, issuer, and fingerprints. certmatch - This logs remote SMTP server certificate - matching, showing the CN and each subjec- + This logs remote SMTP server certificate + matching, showing the CN and each subjec- tAltName and which name matched. With DANE, - logs matching of TLSA record trust-anchor + logs matching of TLSA record trust-anchor and end-entity certificates. - cache This logs session cache operations, showing - whether session caching is effective with - the remote SMTP server. Automatically used + cache This logs session cache operations, showing + whether session caching is effective with + the remote SMTP server. Automatically used when reconnecting with the -r option; rarely needs to be set explicitly. verbose - Enables verbose logging in the Postfix TLS - driver; includes all of peercert..cache and + Enables verbose logging in the Postfix TLS + driver; includes all of peercert..cache and more. - The default is routine,certmatch. After a recon- + The default is routine,certmatch. After a recon- nect, peercert, certmatch and verbose are automati- cally disabled while cache and summary are enabled. -m count (default: 5) - When the -r delay option is specified, the -m - option determines the maximum number of reconnect - attempts to use with a server behind a load-bal- - acer, to see whether connection caching is likely - to be effective for this destination. Some MTAs - don't expose the underlying server identity in - their EHLO response; with these servers there will + When the -r delay option is specified, the -m + option determines the maximum number of reconnect + attempts to use with a server behind a load-bal- + acer, to see whether connection caching is likely + to be effective for this destination. Some MTAs + don't expose the underlying server identity in + their EHLO response; with these servers there will never be more than 1 reconnection attempt. -o name=value Specify zero or more times to override the value of - the main.cf parameter name with value. Possible - use-cases include overriding the values of TLS - library parameters, or "myhostname" to configure + the main.cf parameter name with value. Possible + use-cases include overriding the values of TLS + library parameters, or "myhostname" to configure the SMTP EHLO name sent to the remote server. -p protocols (default: !SSLv2) - List of TLS protocols that posttls-finger will - exclude or include. See smtp_tls_mandatory_proto- + List of TLS protocols that posttls-finger will + exclude or include. See smtp_tls_mandatory_proto- cols for details. -P CApath/ (default: none) - The OpenSSL CApath/ directory (indexed via - c_rehash(1)) for remote SMTP server certificate - verification. By default no CApath is used and no + The OpenSSL CApath/ directory (indexed via + c_rehash(1)) for remote SMTP server certificate + verification. By default no CApath is used and no public CAs are trusted. -r delay - With a cachable TLS session, disconnect and recon- - nect after delay seconds. Report whether the ses- - sion is re-used. Retry if a new server is encoun- - tered, up to 5 times or as specified with the -m + With a cachable TLS session, disconnect and recon- + nect after delay seconds. Report whether the ses- + sion is re-used. Retry if a new server is encoun- + tered, up to 5 times or as specified with the -m option. By default reconnection is disabled, spec- ify a positive delay to enable this behavior. - -S Disable SMTP; that is, connect to an LMTP server. + -S Disable SMTP; that is, connect to an LMTP server. The default port for LMTP over TCP is 24. Alterna- - tive ports can specified by appending ":service- + tive ports can specified by appending ":service- name" or ":portnumber" to the destination argument. -t timeout (default: 5) - The TCP connection timeout to use. This is also - the timeout for reading the remote server's 220 + The TCP connection timeout to use. This is also + the timeout for reading the remote server's 220 banner. -T timeout (default: 30) The SMTP/LMTP command timeout for EHLO/LHLO, START- TLS and QUIT. - -v Enable verose Postfix logging. Specify more than + -v Enable verose Postfix logging. Specify more than once to increase the level of verbose logging. [inet:]domain[:port] - Connect via TCP to domain domain, port port. The - default port is smtp (or 24 with LMTP). With SMTP - an MX lookup is performed to resolve the domain to - a host, unless the domain is enclosed in []. If - you want to connect to a specific MX host, for + Connect via TCP to domain domain, port port. The + default port is smtp (or 24 with LMTP). With SMTP + an MX lookup is performed to resolve the domain to + a host, unless the domain is enclosed in []. If + you want to connect to a specific MX host, for instance mx1.example.com, specify [mx1.example.com] as the destination and example.com as a match argu- - ment. When using DNS, the destination domain is - assumed fully qualified and no default domain or - search suffixes are applied; you must use fully- - qualified names or also enable native host lookups + ment. When using DNS, the destination domain is + assumed fully qualified and no default domain or + search suffixes are applied; you must use fully- + qualified names or also enable native host lookups (these don't support dane or dane-only as no DNSSEC validation information is available via native lookups). @@ -317,17 +322,17 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1) With no match arguments specified, certificate peername matching uses the compiled-in default strategies for each security level. If you specify - one or more arguments, these will be used as the - list of certificate or public-key digests to match - for the fingerprint level, or as the list of DNS + one or more arguments, these will be used as the + list of certificate or public-key digests to match + for the fingerprint level, or as the list of DNS names to match in the certificate at the verify and - secure levels. If the security level is dane, or - dane-only the match names are ignored, and host- + secure levels. If the security level is dane, or + dane-only the match names are ignored, and host- name, nexthop strategies are used. ENVIRONMENT MAIL_CONFIG - Read configuration parameters from a non-default + Read configuration parameters from a non-default location. MAIL_VERBOSE @@ -341,7 +346,7 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1) TLS_README, Postfix STARTTLS howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 3b8ba36b6..4ef7b501f 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -593,15 +593,10 @@ SMTP(8) SMTP(8) Zero or more PEM-format files with trust-anchor certificates and/or public keys. - smtp_tls_dane_notfound_tlsa_level (may) - The "degraded" security level when the "dane" secu- - rity level is specified, but no validated DANE TLSA - records are published. - - smtp_tls_dane_unusable_tlsa_level (encrypt) - The "degraded" security level when the "dane" secu- - rity level is specified, validated DANE TLSA - records are present, but none are usable. + smtp_tls_force_insecure_host_tlsa_lookup (no) + Lookup the associated DANE TLSA RRset even when a + hostname is not an alias and its address records + lie in an unsigned zone. tls_dane_trust_anchor_digest_enable (trust-anchor-asser- tion) diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index f2bad96c4..663f03ef8 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -496,14 +496,6 @@ SMTPD(8) SMTPD(8) remote SMTP client certificate in order to allow TLS connections to proceed. - smtpd_tls_session_cache_database (empty) - Name of the file containing the optional Postfix - SMTP server TLS session cache. - - smtpd_tls_session_cache_timeout (3600s) - The expiration time of Postfix SMTP server TLS ses- - sion cache information. - smtpd_tls_wrappermode (no) Run the Postfix SMTP server in the non-standard "wrapper" mode, instead of using the STARTTLS com- diff --git a/postfix/html/tlsproxy.8.html b/postfix/html/tlsproxy.8.html index 30c9c52e9..dfd20d4f7 100644 --- a/postfix/html/tlsproxy.8.html +++ b/postfix/html/tlsproxy.8.html @@ -178,56 +178,51 @@ TLSPROXY(8) TLSPROXY(8) ified, this overrides the obsolete parameters smtpd_use_tls and smtpd_enforce_tls. - tlsproxy_tls_session_cache_timeout ($smtpd_tls_ses- - sion_cache_timeout) - The expiration time of Postfix tlsproxy(8) server - TLS session cache information. - Available in Postfix version 2.11 and later: tlsmgr_service_name (tlsmgr) - The name of the tlsmgr(8) service entry in mas- + The name of the tlsmgr(8) service entry in mas- ter.cf. OBSOLETE STARTTLS SUPPORT CONTROLS - These parameters are supported for compatibility with + These parameters are supported for compatibility with smtpd(8) legacy parameters. tlsproxy_use_tls ($smtpd_use_tls) - Opportunistic TLS: announce STARTTLS support to + Opportunistic TLS: announce STARTTLS support to remote SMTP clients, but do not require that clients use TLS encryption. tlsproxy_enforce_tls ($smtpd_enforce_tls) - Mandatory TLS: announce STARTTLS support to remote - SMTP clients, and require that clients use TLS + Mandatory TLS: announce STARTTLS support to remote + SMTP clients, and require that clients use TLS encryption. RESOURCE CONTROLS tlsproxy_watchdog_timeout (10s) - How much time a tlsproxy(8) process may take to + How much time a tlsproxy(8) process may take to process local or remote I/O before it is terminated by a built-in watchdog timer. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". SEE ALSO @@ -237,7 +232,7 @@ TLSPROXY(8) TLSPROXY(8) syslogd(5), system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY diff --git a/postfix/man/man1/posttls-finger.1 b/postfix/man/man1/posttls-finger.1 index f418e54c4..3ba4e7b43 100644 --- a/postfix/man/man1/posttls-finger.1 +++ b/postfix/man/man1/posttls-finger.1 @@ -107,6 +107,10 @@ The message digest algorithm to use for reporting remote SMTP server fingerprints and matching against user provided certificate fingerprints (with DANE TLSA records the algorithm is specified in the DNS). +.IP "\fB-f\fR" +Lookup the associated DANE TLSA RRset even when a hostname is not an +alias and its address records lie in an unsigned zone. See +smtp_tls_force_insecure_host_tlsa_lookup for details. .IP "\fB-F \fICAfile.pem\fR (default: none)" The PEM formatted CAfile for remote SMTP server certificate verification. By default no CAfile is used and no public CAs diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 2a69e1c39..02d7d8824 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -2757,6 +2757,11 @@ The LMTP-specific version of the smtp_tls_fingerprint_digest configuration parameter. See there for details. .PP This feature is available in Postfix 2.5 and later. +.SH lmtp_tls_force_insecure_host_tlsa_lookup (default: no) +The LMTP-specific version of the smtp_tls_force_insecure_host_tlsa_lookup +configuration parameter. See there for details. +.PP +This feature is available in Postfix 2.11 and later. .SH lmtp_tls_key_file (default: $lmtp_tls_cert_file) The LMTP-specific version of the smtp_tls_key_file configuration parameter. See there for details. @@ -7069,6 +7074,18 @@ fingerprint incorrectly. To use public-key fingerprints, upgrade to Postfix 2.9.6 or later. .PP This feature is available in Postfix 2.5 and later. +.SH smtp_tls_force_insecure_host_tlsa_lookup (default: no) +Lookup the associated DANE TLSA RRset even when a hostname is +not an alias and its address records lie in an unsigned zone. This +is unlikely to ever yield DNSSEC validated results, since child +zones of unsigned zones are also unsigned in the absense of DLV or +locally configured non-root trust-anchors. We anticipate that such +mechanisms will not be used for just the "_tcp" subdomain of a host. +Suppressing the TLSA RRset lookup reduces latency and avoids potential +interoperability problems with nameservers for unsigned zones that +are not prepared to handle the new TLSA RRset. +.PP +This feature is available in Postfix 2.11. .SH smtp_tls_key_file (default: $smtp_tls_cert_file) File with the Postfix SMTP client RSA private key in PEM format. This file may be combined with the Postfix SMTP client RSA certificate @@ -7818,6 +7835,10 @@ $smtp_tls_session_cache_database, this parameter is implemented in the \fBtlsmgr\fR(8) daemon and therefore per-smtp-instance master.cf overrides are not possible. .PP +As of Postfix 2.11 this setting cannot exceed 100 days. If set +<= 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead. +.PP This feature is available in Postfix 2.2 and later. .SH smtp_tls_trust_anchor_file (default: empty) Zero or more PEM-format files with trust-anchor certificates @@ -10731,7 +10752,16 @@ $smtpd_tls_session_cache_database, this parameter is implemented in the \fBtlsmgr\fR(8) daemon and therefore per-smtpd-instance master.cf overrides are not possible. .PP -This feature is available in Postfix 2.2 and later. +As of Postfix 2.11 this setting cannot exceed 100 days. If set +<= 0, session caching is disabled, not just via the database, but +also via RFC 5077 TLS session tickets, which don't require server-side +storage. If set to a positive value less than 2 minutes, the minimum +value of 2 minutes is used instead. TLS session tickets require +an OpenSSL library (at least version 0.9.8h) that provides full +support for this TLS extension. +.PP +This feature is available in Postfix 2.2 and later, and updated +for TLS session ticket support in Postfix 2.11. .SH smtpd_tls_wrappermode (default: no) Run the Postfix SMTP server in the non-standard "wrapper" mode, instead of using the STARTTLS command. @@ -11520,10 +11550,10 @@ smtpd_tls_security_level for further details. .PP This feature is available in Postfix 2.8 and later. .SH tlsproxy_tls_session_cache_timeout (default: $smtpd_tls_session_cache_timeout) -The expiration time of Postfix \fBtlsproxy\fR(8) server TLS session -cache information. A cache cleanup is performed periodically every -$smtpd_tls_session_cache_timeout seconds. See -smtpd_tls_session_cache_timeout for further details. +Obsolete expiration time of Postfix \fBtlsproxy\fR(8) server TLS session +cache information. Since the cache is shared with \fBsmtpd\fR(8) and managed +by \fBtlsmgr\fR(8), there is only one expiration time for the SMTP server cache +shared by all three services, namely smtpd_tls_session_cache_timeout. .PP This feature is available in Postfix 2.8 and later. .SH tlsproxy_use_tls (default: $smtpd_use_tls) diff --git a/postfix/man/man8/discard.8 b/postfix/man/man8/discard.8 index cd603ed3d..a96cd7d6d 100644 --- a/postfix/man/man8/discard.8 +++ b/postfix/man/man8/discard.8 @@ -39,7 +39,7 @@ to the network, and can be run chrooted at fixed low privilege. .SH "STANDARDS" .na .nf -None. +RFC 3463 (Enhanced Status Codes) .SH DIAGNOSTICS .ad .fi diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index e00f5628d..fcc914029 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -476,13 +476,9 @@ Available in Postfix version 2.11 and later: .IP "\fBsmtp_tls_trust_anchor_file (empty)\fR" Zero or more PEM-format files with trust-anchor certificates and/or public keys. -.IP "\fBsmtp_tls_dane_notfound_tlsa_level (may)\fR" -The "degraded" security level when the "dane" security level -is specified, but no validated DANE TLSA records are published. -.IP "\fBsmtp_tls_dane_unusable_tlsa_level (encrypt)\fR" -The "degraded" security level when the "dane" security level -is specified, validated DANE TLSA records are present, but none are -usable. +.IP "\fBsmtp_tls_force_insecure_host_tlsa_lookup (no)\fR" +Lookup the associated DANE TLSA RRset even when a hostname is +not an alias and its address records lie in an unsigned zone. .IP "\fBtls_dane_trust_anchor_digest_enable (trust-anchor-assertion)\fR" RFC 6698 trust-anchor digest support in the Postfix TLS library. .IP "\fBtlsmgr_service_name (tlsmgr)\fR" @@ -599,8 +595,9 @@ line, SMTP message content line, or TLS protocol message). .PP Available in Postfix version 2.11 and later: .IP "\fBsmtp_connection_reuse_count_limit (0)\fR" -When SMTP connection caching is enabled, the number of times that -an SMTP session may be reused before it is closed, or zero (no limit). +When SMTP connection caching is enabled, the number of times +that an SMTP session may be reused before it is closed, or zero (no +limit). .SH "TROUBLE SHOOTING CONTROLS" .na .nf diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 2dad0b546..3c4bc859a 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -419,12 +419,6 @@ CommonName. .IP "\fBsmtpd_tls_req_ccert (no)\fR" With mandatory TLS encryption, require a trusted remote SMTP client certificate in order to allow TLS connections to proceed. -.IP "\fBsmtpd_tls_session_cache_database (empty)\fR" -Name of the file containing the optional Postfix SMTP server -TLS session cache. -.IP "\fBsmtpd_tls_session_cache_timeout (3600s)\fR" -The expiration time of Postfix SMTP server TLS session cache -information. .IP "\fBsmtpd_tls_wrappermode (no)\fR" Run the Postfix SMTP server in the non-standard "wrapper" mode, instead of using the STARTTLS command. diff --git a/postfix/man/man8/tlsproxy.8 b/postfix/man/man8/tlsproxy.8 index caadcdd63..61a010656 100644 --- a/postfix/man/man8/tlsproxy.8 +++ b/postfix/man/man8/tlsproxy.8 @@ -148,9 +148,6 @@ client certificate in order to allow TLS connections to proceed. The SMTP TLS security level for the Postfix \fBtlsproxy\fR(8) server; when a non-empty value is specified, this overrides the obsolete parameters smtpd_use_tls and smtpd_enforce_tls. -.IP "\fBtlsproxy_tls_session_cache_timeout ($smtpd_tls_session_cache_timeout)\fR" -The expiration time of Postfix \fBtlsproxy\fR(8) server TLS session -cache information. .PP Available in Postfix version 2.11 and later: .IP "\fBtlsmgr_service_name (tlsmgr)\fR" diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 53e810f1b..6da58ae74 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -281,6 +281,7 @@ while (<>) { s;\blmtp_tls_enforce_peername\b;$&;g; s;\blmtp_tls_note_starttls_offer\b;$&;g; s;\blmtp_tls_block_early_mail_reply\b;$&;g; + s;\blmtp_tls_force_insecure_host_tlsa_lookup\b;$&;g; s;\blmtp_sender_dependent_authentication\b;$&;g; s;\blmtp_sasl_path\b;$&;g; s;\blmtp_lhlo_name\b;$&;g; @@ -646,6 +647,7 @@ while (<>) { s;\bsmtp_tls_session_cache_database\b;$&;g; s;\bsmtp_tls_session_cache_timeout\b;$&;g; s;\bsmtp_tls_block_early_mail_reply\b;$&;g; + s;\bsmtp_tls_force_insecure_host_tlsa_lookup\b;$&;g; s;\bsmtp_use_tls\b;$&;g; s;\bsmtp_header_checks\b;$&;g; s;\bsmtp_mime_header_checks\b;$&;g; @@ -1026,7 +1028,6 @@ while (<>) { s;\btlsproxy_tls_protocols\b;$&;g; s;\btlsproxy_tls_req_ccert\b;$&;g; s;\btlsproxy_tls_security_level\b;$&;g; - s;\btlsproxy_tls_session_cache_timeout\b;$&;g; s;\btlsproxy_use_tls\b;$&;g; # Service-defined parameters... diff --git a/postfix/proto/LMDB_README.html b/postfix/proto/LMDB_README.html index 292e9920f..724512819 100644 --- a/postfix/proto/LMDB_README.html +++ b/postfix/proto/LMDB_README.html @@ -19,17 +19,13 @@

Introduction

- +

Warning: LMDB databases have a show-stopper +bug: they do not grow beyond a specified limit, and introduce a +"database full" hard error condition that does not exist with any +other Postfix database type. This is a problem for programs whose +database grows with system load. Examples are postscreen(8), +greylisting, verify(8) and tlsmgr(8).

+You have been warned.

Postfix uses databases of various kinds to store and look up information. Postfix databases are specified as "type:name". diff --git a/postfix/proto/TLS_README.html b/postfix/proto/TLS_README.html index 28c1f42f8..ad5177224 100644 --- a/postfix/proto/TLS_README.html +++ b/postfix/proto/TLS_README.html @@ -604,12 +604,24 @@ In order to change this behavior, set

Server-side TLS session cache

-

The Postfix SMTP server and the remote SMTP client negotiate -a session, which takes some computer time and network bandwidth. -By default, this session information is cached only in the smtpd(8) -process actually using this session and is lost when the process -terminates. To share the session information between multiple -smtpd(8) processes, a persistent session cache can be used. You +

The Postfix SMTP server and the remote SMTP client negotiate a +session, which takes some computer time and network bandwidth. SSL +protocol versions other than SSLv2 support resumption of cached +sessions. Not only is this more CPU and bandwidth efficient, it +also reduces latency as only one network round-trip is used to +resume a session while it takes two round-trips to create a session +from scratch.

+ +

Since Postfix uses multiple smtpd(8) service processes, an +in-memory cache is not sufficient for session re-use. Clients store +at most one cached session per server and are very unlikey to +repeatedly connect to the same server process. Thus session caching +in the Postfix SMTP server generally requires a shared cache (an +alternative available with Postfix ≥ 2.11 is described below). +

+ +

To share the session information between multiple +smtpd(8) processes, a session cache database is used. You can specify any database type that can store objects of several kbytes and that supports the sequence operator. DBM databases are not suitable because they can only store small objects. The cache @@ -617,6 +629,24 @@ is maintained by the tlsmgr(8) process, so there is no problem with concurrent access. Session caching is highly recommended, because the cost of repeatedly negotiating TLS session keys is high.

+

Starting with Postfix 2.11, linked with a compatible OpenSSL +library (at least 0.9.8h, preferably 1.0.0 or later) the Postfix +SMTP server supports RFC 5077 TLS session resumption without +server-side state when the remote SMTP client also supports RFC +5077. The session is encrypted by the server in a session +ticket returned to client for storage. When a client sends a +valid session ticket, the server decrypts it and resumes the session, +provided neither the ticket nor the session have expired. This +makes it possible to resume cached sessions without allocating space +for a shared database on the server. This feature can be disabled +by setting the session cache timeout to zero, otherwise the timeout +must be at least 2 minutes and at most 100 days.

+ +

Note, session tickets can only be negotiated if the client +disables SSLv2 and does not use the legacy SSLv2 compatible HELLO +message. This is true by default with the Postfix ≥ 2.6 SMTP +client.

+

Example:

@@ -646,9 +676,13 @@ recommends a maximum of 24 hours.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead.

+

When the Postfix SMTP server does not save TLS sessions to an external cache database, client-side session caching is unlikely -to be useful. To prevent such wastage, the Postfix SMTP server can +to be useful. To reduce waste of client resources, the Postfix SMTP server can be configured to not issue TLS session ids. By default the Postfix SMTP server always issues TLS session ids. This works around known interoperability issues with some MUAs, and prevents possible @@ -1980,6 +2014,10 @@ recommends a maximum of 24 hours.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead.

+

Client TLS limitations

diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 5a1608c66..c4b6c53a1 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -9272,7 +9272,16 @@ $smtpd_tls_session_cache_database, this parameter is implemented in the tlsmgr(8) daemon and therefore per-smtpd-instance master.cf overrides are not possible.

-

This feature is available in Postfix 2.2 and later.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled, not just via the database, but +also via RFC 5077 TLS session tickets, which don't require server-side +storage. If set to a positive value less than 2 minutes, the minimum +value of 2 minutes is used instead. TLS session tickets require +an OpenSSL library (at least version 0.9.8h) that provides full +support for this TLS extension.

+ +

This feature is available in Postfix 2.2 and later, and updated +for TLS session ticket support in Postfix 2.11.

%PARAM relay_clientcerts @@ -9574,6 +9583,10 @@ $smtp_tls_session_cache_database, this parameter is implemented in the tlsmgr(8) daemon and therefore per-smtp-instance master.cf overrides are not possible.

+

As of Postfix 2.11 this setting cannot exceed 100 days. If set +≤ 0, session caching is disabled. If set to a positive value +less than 2 minutes, the minimum value of 2 minutes is used instead.

+

This feature is available in Postfix 2.2 and later.

%PARAM smtp_use_tls no @@ -14602,10 +14615,10 @@ smtpd_tls_security_level for further details.

%PARAM tlsproxy_tls_session_cache_timeout $smtpd_tls_session_cache_timeout -

The expiration time of Postfix tlsproxy(8) server TLS session -cache information. A cache cleanup is performed periodically every -$smtpd_tls_session_cache_timeout seconds. See -smtpd_tls_session_cache_timeout for further details.

+

Obsolete expiration time of Postfix tlsproxy(8) server TLS session +cache information. Since the cache is shared with smtpd(8) and managed +by tlsmgr(8), there is only one expiration time for the SMTP server cache +shared by all three services, namely smtpd_tls_session_cache_timeout.

This feature is available in Postfix 2.8 and later.

@@ -15394,3 +15407,24 @@ the most connections to that destination. This limitation does not exist with the smtp_connection_reuse_time_limit feature.

This feature is available in Postfix 2.11.

+ +%PARAM lmtp_tls_force_insecure_host_tlsa_lookup no + +

The LMTP-specific version of the smtp_tls_force_insecure_host_tlsa_lookup +configuration parameter. See there for details.

+ +

This feature is available in Postfix 2.11 and later.

+ +%PARAM smtp_tls_force_insecure_host_tlsa_lookup no + +

Lookup the associated DANE TLSA RRset even when a hostname is +not an alias and its address records lie in an unsigned zone. This +is unlikely to ever yield DNSSEC validated results, since child +zones of unsigned zones are also unsigned in the absense of DLV or +locally configured non-root trust-anchors. We anticipate that such +mechanisms will not be used for just the "_tcp" subdomain of a host. +Suppressing the TLSA RRset lookup reduces latency and avoids potential +interoperability problems with nameservers for unsigned zones that +are not prepared to handle the new TLSA RRset.

+ +

This feature is available in Postfix 2.11.

diff --git a/postfix/src/discard/discard.c b/postfix/src/discard/discard.c index d1d467edb..4f90926b6 100644 --- a/postfix/src/discard/discard.c +++ b/postfix/src/discard/discard.c @@ -29,7 +29,7 @@ /* The \fBdiscard\fR(8) mailer is not security-sensitive. It does not talk /* to the network, and can be run chrooted at fixed low privilege. /* STANDARDS -/* None. +/* RFC 3463 (Enhanced Status Codes) /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8). /* diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 7bd987da6..93d2c3569 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1327,6 +1327,7 @@ extern bool var_smtpd_tls_received_header; #define DEF_SMTPD_TLS_SCACHE_DB "" extern char *var_smtpd_tls_scache_db; +#define MAX_SMTPD_TLS_SCACHETIME 8640000 #define VAR_SMTPD_TLS_SCACHTIME "smtpd_tls_session_cache_timeout" #define DEF_SMTPD_TLS_SCACHTIME "3600s" extern int var_smtpd_tls_scache_timeout; @@ -1479,8 +1480,10 @@ extern bool var_smtp_tls_note_starttls_offer; extern char *var_smtp_tls_scache_db; extern char *var_lmtp_tls_scache_db; +#define MAX_SMTP_TLS_SCACHETIME 8640000 #define VAR_SMTP_TLS_SCACHTIME "smtp_tls_session_cache_timeout" #define DEF_SMTP_TLS_SCACHTIME "3600s" +#define MAX_LMTP_TLS_SCACHETIME 8640000 #define VAR_LMTP_TLS_SCACHTIME "lmtp_tls_session_cache_timeout" #define DEF_LMTP_TLS_SCACHTIME "3600s" extern int var_smtp_tls_scache_timeout; @@ -1532,6 +1535,12 @@ extern char *var_smtp_tls_fpt_cmatch; #define DEF_LMTP_TLS_BLK_EARLY_MAIL_REPLY 0 extern bool var_smtp_tls_blk_early_mail_reply; +#define VAR_SMTP_TLS_FORCE_TLSA "smtp_tls_force_insecure_host_tlsa_lookup" +#define DEF_SMTP_TLS_FORCE_TLSA 0 +#define VAR_LMTP_TLS_FORCE_TLSA "lmtp_tls_force_insecure_host_tlsa_lookup" +#define DEF_LMTP_TLS_FORCE_TLSA 0 +extern bool var_smtp_tls_force_tlsa; + /* * SASL authentication support, SMTP server side. */ @@ -3653,14 +3662,6 @@ extern char *var_tlsp_tls_loglevel; #define DEF_TLSP_TLS_RECHEAD "$" VAR_SMTPD_TLS_RECHEAD extern bool var_tlsp_tls_received_header; -#define VAR_TLSP_TLS_SCACHE_DB "tlsproxy_tls_session_cache_database" -#define DEF_TLSP_TLS_SCACHE_DB "$" VAR_SMTPD_TLS_SCACHE_DB -extern char *var_tlsp_tls_scache_db; - -#define VAR_TLSP_TLS_SCACHTIME "tlsproxy_tls_session_cache_timeout" -#define DEF_TLSP_TLS_SCACHTIME "$" VAR_SMTPD_TLS_SCACHTIME -extern int var_tlsp_tls_scache_timeout; - #define VAR_TLSP_TLS_SET_SESSID "tlsproxy_tls_always_issue_session_ids" #define DEF_TLSP_TLS_SET_SESSID "$" VAR_SMTPD_TLS_SET_SESSID extern bool var_tlsp_tls_set_sessid; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 7efe5848c..2dc2e54da 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20130818" +#define MAIL_RELEASE_DATE "20130825" #define MAIL_VERSION_NUMBER "2.11" #ifdef SNAPSHOT diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index 66edbe9f6..407dfb3db 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -101,6 +101,10 @@ /* fingerprints and matching against user provided certificate /* fingerprints (with DANE TLSA records the algorithm is specified /* in the DNS). +/* .IP "\fB-f\fR" +/* Lookup the associated DANE TLSA RRset even when a hostname is not an +/* alias and its address records lie in an unsigned zone. See +/* smtp_tls_force_insecure_host_tlsa_lookup for details. /* .IP "\fB-F \fICAfile.pem\fR (default: none)" /* The PEM formatted CAfile for remote SMTP server certificate /* verification. By default no CAfile is used and no public CAs @@ -400,6 +404,7 @@ typedef struct STATE { int log_mask; /* via tls_log_mask() */ int reconnect; /* -r option */ int max_reconnect; /* -m option */ + int force_tlsa; /* -f option */ unsigned port; /* TCP port */ char *dest; /* Full destination spec */ char *addrport; /* [addr]:port */ @@ -407,6 +412,7 @@ typedef struct STATE { char *nexthop; /* Nexthop domain for verification */ char *hostname; /* Hostname for verification */ DNS_RR *addr; /* IPv[46] Address to (re)connect to */ + DNS_RR *mx; /* MX RRset qname, rname, valid */ int pass; /* Pass number, 2 for reconnect */ int nochat; /* disable chat logging */ char *helo; /* Server name from EHLO reply */ @@ -433,7 +439,7 @@ typedef struct STATE { static DNS_RR *host_addr(STATE *, const char *); -#define HNAME(addr) (addr->dnssec_valid ? addr->rname : addr->qname) +#define HNAME(addr) (addr->qname) /* * Structure with broken-up SMTP server response. @@ -1105,6 +1111,7 @@ static DNS_RR *domain_addr(STATE *state, char *domain) case DNS_OK: mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any); addr_list = mx_addr_list(state, mx_names); + state->mx = dns_rr_copy(mx_names); dns_rr_free(mx_names); if (addr_list == 0) { msg_warn("no MX host for %s has a valid address record", domain); @@ -1156,10 +1163,26 @@ static DNS_RR *host_addr(STATE *state, const char *host) static int dane_host_level(STATE *state, DNS_RR *addr) { int level = state->level; + int valid; + int mxvalid; #ifdef USE_TLS if (level == TLS_LEV_DANE) { - if (addr->dnssec_valid) { + + /* + * Suppress TLSA lookups for non-DNSSEC + non-MX + non-CNAME hosts. + * If the host address is not DNSSEC validated, the TLSA RRset is + * safely assumed to not be in a DNSSEC Look-aside Validation child + * zone. + */ + mxvalid = state->mx == 0 || state->mx->dnssec_valid; + valid = addr->dnssec_valid; + if (!state->force_tlsa + && !valid + && state->mx == 0 + && strcmp(addr->qname, addr->rname) == 0) + mxvalid = 0; + if (mxvalid) { if (state->log_mask & (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE)) tls_dane_verbose(1); else @@ -1170,7 +1193,9 @@ static int dane_host_level(STATE *state, DNS_RR *addr) tls_dane_free(state->ddane); /* When TLSA lookups fail, next host */ - state->ddane = tls_dane_resolve(HNAME(addr), "tcp", state->port); + state->ddane = tls_dane_resolve(addr->qname, + valid ? addr->rname : 0, + "tcp", state->port); if (!state->ddane) { dsb_simple(state->why, "4.7.5", "TLSA lookup error for %s:%u", @@ -1193,7 +1218,14 @@ static int dane_host_level(STATE *state, DNS_RR *addr) if (state->match) argv_free(state->match); argv_add(state->match = argv_alloc(2), - "hostname", "nexthop", ARGV_END); + state->ddane->base_domain, ARGV_END); + if (state->mx) { + if (strcmp(state->mx->qname, state->mx->rname) == 0) + argv_add(state->match, state->mx->qname, ARGV_END); + else + argv_add(state->match, state->mx->rname, + state->mx->qname, ARGV_END); + } } } else { level = TLS_LEV_SECURE; @@ -1346,6 +1378,9 @@ static void disconnect_dest(STATE *state) if (state->addr) dns_rr_free(state->addr); state->addr = 0; + if (state->mx) + dns_rr_free(state->mx); + state->mx = 0; if (state->nexthop) myfree(state->nexthop); @@ -1392,7 +1427,7 @@ static int finger(STATE *state) if (cache_enabled && cache_count == 0) { msg_info("Server declined session caching. Done reconnecting."); state->reconnect = 0; - } else if (cache_hits > 0) { + } else if (cache_hits > 0 && (state->log_mask & TLS_LOG_SESSTKT) != 0) { msg_info("Found a previously used server. Done reconnecting."); state->reconnect = 0; } else if (state->max_reconnect-- <= 0) { @@ -1483,7 +1518,7 @@ static void usage(void) #ifdef USE_TLS fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s \\\n\t%s" " destination [match ...]\n", var_procname, - "[-acCSv] [-t conn_tmout] [-T cmd_tmout] [-L logopts]", + "[-acCfSv] [-t conn_tmout] [-T cmd_tmout] [-L logopts]", "[-h host_lookup] [-l level] [-d mdalg] [-g grade] [-p protocols]", "[-A tafile] [-F CAfile.pem] [-P CApath/] [-m count] [-r delay]", "[-o name=value]"); @@ -1555,7 +1590,7 @@ static void parse_options(STATE *state, int argc, char *argv[]) #define OPTS "a:ch:o:St:T:v" #ifdef USE_TLS -#define TLSOPTS "A:Cd:F:g:l:L:m:p:P:r:" +#define TLSOPTS "A:Cd:fF:g:l:L:m:p:P:r:" state->mdalg = mystrdup("sha1"); state->CApath = mystrdup(""); @@ -1609,6 +1644,9 @@ static void parse_options(STATE *state, int argc, char *argv[]) myfree(state->mdalg); state->mdalg = mystrdup(optarg); break; + case 'f': + state->force_tlsa = 1; + break; case 'F': myfree(state->CAfile); state->CAfile = mystrdup(optarg); diff --git a/postfix/src/posttls-finger/tlsmgrmem.c b/postfix/src/posttls-finger/tlsmgrmem.c index d6b09d87c..500932f12 100644 --- a/postfix/src/posttls-finger/tlsmgrmem.c +++ b/postfix/src/posttls-finger/tlsmgrmem.c @@ -82,11 +82,12 @@ int tls_mgr_seed(VSTRING *buf, int len) return (TLS_MGR_STAT_OK); } -int tls_mgr_policy(const char *unused_type, int *cachable) +int tls_mgr_policy(const char *unused_type, int *cachable, int *timeout) { if (cache_enabled && tls_cache == 0) tls_cache = htable_create(1); *cachable = cache_enabled; + *timeout = TLS_SESSION_LIFEMIN; return (TLS_MGR_STAT_OK); } diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c index faaa6dee1..68a2739ac 100644 --- a/postfix/src/smtp/lmtp_params.c +++ b/postfix/src/smtp/lmtp_params.c @@ -106,6 +106,7 @@ VAR_LMTP_TLS_ENFORCE_PN, DEF_LMTP_TLS_ENFORCE_PN, &var_smtp_tls_enforce_peername, VAR_LMTP_TLS_NOTEOFFER, DEF_LMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer, VAR_LMTP_TLS_BLK_EARLY_MAIL_REPLY, DEF_LMTP_TLS_BLK_EARLY_MAIL_REPLY, &var_smtp_tls_blk_early_mail_reply, + VAR_LMTP_TLS_FORCE_TLSA, DEF_LMTP_TLS_FORCE_TLSA, &var_smtp_tls_force_tlsa, #endif VAR_LMTP_SENDER_AUTH, DEF_LMTP_SENDER_AUTH, &var_smtp_sender_auth, VAR_LMTP_CNAME_OVERR, DEF_LMTP_CNAME_OVERR, &var_smtp_cname_overr, diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 82684575b..e11a53746 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -446,13 +446,9 @@ /* .IP "\fBsmtp_tls_trust_anchor_file (empty)\fR" /* Zero or more PEM-format files with trust-anchor certificates /* and/or public keys. -/* .IP "\fBsmtp_tls_dane_notfound_tlsa_level (may)\fR" -/* The "degraded" security level when the "dane" security level -/* is specified, but no validated DANE TLSA records are published. -/* .IP "\fBsmtp_tls_dane_unusable_tlsa_level (encrypt)\fR" -/* The "degraded" security level when the "dane" security level -/* is specified, validated DANE TLSA records are present, but none are -/* usable. +/* .IP "\fBsmtp_tls_force_insecure_host_tlsa_lookup (no)\fR" +/* Lookup the associated DANE TLSA RRset even when a hostname is +/* not an alias and its address records lie in an unsigned zone. /* .IP "\fBtls_dane_trust_anchor_digest_enable (trust-anchor-assertion)\fR" /* RFC 6698 trust-anchor digest support in the Postfix TLS library. /* .IP "\fBtlsmgr_service_name (tlsmgr)\fR" @@ -565,8 +561,9 @@ /* .PP /* Available in Postfix version 2.11 and later: /* .IP "\fBsmtp_connection_reuse_count_limit (0)\fR" -/* When SMTP connection caching is enabled, the number of times that -/* an SMTP session may be reused before it is closed, or zero (no limit). +/* When SMTP connection caching is enabled, the number of times +/* that an SMTP session may be reused before it is closed, or zero (no +/* limit). /* TROUBLE SHOOTING CONTROLS /* .ad /* .fi @@ -853,6 +850,7 @@ char *var_smtp_tls_ciph; char *var_smtp_tls_eccert_file; char *var_smtp_tls_eckey_file; bool var_smtp_tls_blk_early_mail_reply; +bool var_smtp_tls_force_tlsa; #endif diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 255302979..c69470830 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -55,17 +55,18 @@ typedef struct SMTP_ITERATOR { VSTRING *addr; /* printable address or empty */ unsigned port; /* network byte order or null */ struct DNS_RR *rr; /* DNS resource record or null */ + struct DNS_RR *mx; /* DNS resource record or null */ /* Private members. */ VSTRING *saved_dest; /* saved current nexthop */ struct SMTP_STATE *parent; /* parent linkage */ } SMTP_ITERATOR; -#define SMTP_ITER_INIT(iter, _dest, _host, _addr, _port, _rr, state) do { \ +#define SMTP_ITER_INIT(iter, _dest, _host, _addr, _port, state) do { \ vstring_strcpy((iter)->dest, (_dest)); \ vstring_strcpy((iter)->host, (_host)); \ vstring_strcpy((iter)->addr, (_addr)); \ (iter)->port = (_port); \ - (iter)->rr = (_rr); \ + (iter)->mx = (iter)->rr = 0; \ vstring_strcpy((iter)->saved_dest, ""); \ (iter)->parent = (state); \ } while (0) @@ -354,10 +355,9 @@ extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *); extern SMTP_SESSION *smtp_session_activate(int, SMTP_ITERATOR *, VSTRING *, VSTRING *); /* - * What's in a name? With DANE TLSA we need the rr->rname (if validated). + * What's in a name? */ -#define SMTP_HNAME(rr) ( (var_smtp_cname_overr || rr->dnssec_valid) ? \ - (rr)->rname : (rr)->qname ) +#define SMTP_HNAME(rr) (var_smtp_cname_overr ? (rr)->rname : (rr)->qname) /* * smtp_connect.c diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index b4b253e7b..deba52f7b 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -6,8 +6,9 @@ /* SYNOPSIS /* #include "smtp_addr.h" /* -/* DNS_RR *smtp_domain_addr(name, misc_flags, why, found_myself) +/* DNS_RR *smtp_domain_addr(name, mxrr, misc_flags, why, found_myself) /* char *name; +/* DNS_RR **mxrr; /* int misc_flags; /* DSN_BUF *why; /* int *found_myself; @@ -29,7 +30,9 @@ /* so that it contains only hosts that are more preferred than the /* local mail server itself. The found_myself result parameter /* is updated when the local MTA is MX host for the specified -/* destination. +/* destination. If MX records were found, the rname, qname, +/* and dnssec validation status of the MX RRset are returned +/* via mxrr, which the caller must free with dns_rr_free(). /* /* When no mail exchanger is listed in the DNS for \fIname\fR, the /* request is passed to smtp_host_addr(). @@ -339,8 +342,8 @@ static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref) /* smtp_domain_addr - mail exchanger address lookup */ -DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why, - int *found_myself) +DNS_RR *smtp_domain_addr(char *name, DNS_RR **mxrr, int misc_flags, + DSN_BUF *why, int *found_myself) { DNS_RR *mx_names; DNS_RR *addr_list = 0; @@ -426,6 +429,8 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why, mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any); best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); addr_list = smtp_addr_list(mx_names, why); + if (mxrr) + *mxrr = dns_rr_copy(mx_names); /* copies one record! */ dns_rr_free(mx_names); if (addr_list == 0) { /* Text does not change. */ diff --git a/postfix/src/smtp/smtp_addr.h b/postfix/src/smtp/smtp_addr.h index 67ff5322a..cf0b6898c 100644 --- a/postfix/src/smtp/smtp_addr.h +++ b/postfix/src/smtp/smtp_addr.h @@ -17,7 +17,7 @@ * Internal interfaces. */ extern DNS_RR *smtp_host_addr(const char *, int, DSN_BUF *); -extern DNS_RR *smtp_domain_addr(char *, int, DSN_BUF *, int *); +extern DNS_RR *smtp_domain_addr(char *, DNS_RR **, int, DSN_BUF *, int *); /* LICENSE /* .ad diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 337a8371b..70e8dc56d 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -488,10 +488,9 @@ static void smtp_connect_local(SMTP_STATE *state, const char *path) * * We set dest=path for backwards compatibility. */ -#define NO_RR ((DNS_RR *) 0) #define NO_PORT 0 - SMTP_ITER_INIT(iter, path, var_myhostname, path, NO_PORT, NO_RR, state); + SMTP_ITER_INIT(iter, path, var_myhostname, path, NO_PORT, state); /* * Opportunistic TLS for unix domain sockets does not make much sense, @@ -833,9 +832,8 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, } #define NO_HOST "" /* safety */ #define NO_ADDR "" /* safety */ -#define NO_RR ((DNS_RR *) 0) /* safety */ - SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, NO_RR, state); + SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state); /* * Resolve an SMTP server. Skip mail exchanger lookups when a quoted @@ -857,7 +855,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, } else { int i_am_mx = 0; - addr_list = smtp_domain_addr(domain, state->misc_flags, + addr_list = smtp_domain_addr(domain, &iter->mx, state->misc_flags, why, &i_am_mx); /* If we're MX host, don't connect to non-MX backups. */ if (i_am_mx) @@ -1026,6 +1024,10 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, /* XXX Code above assumes there is no code at this loop ending. */ } dns_rr_free(addr_list); + if (iter->mx) { + dns_rr_free(iter->mx); + iter->mx = 0; /* Just in case */ + } myfree(dest_buf); if (state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) break; diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c index 16c51159b..c8478b5ef 100644 --- a/postfix/src/smtp/smtp_params.c +++ b/postfix/src/smtp/smtp_params.c @@ -110,6 +110,7 @@ VAR_SMTP_TLS_ENFORCE_PN, DEF_SMTP_TLS_ENFORCE_PN, &var_smtp_tls_enforce_peername, VAR_SMTP_TLS_NOTEOFFER, DEF_SMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer, VAR_SMTP_TLS_BLK_EARLY_MAIL_REPLY, DEF_SMTP_TLS_BLK_EARLY_MAIL_REPLY, &var_smtp_tls_blk_early_mail_reply, + VAR_SMTP_TLS_FORCE_TLSA, DEF_SMTP_TLS_FORCE_TLSA, &var_smtp_tls_force_tlsa, #endif VAR_SMTP_SENDER_AUTH, DEF_SMTP_SENDER_AUTH, &var_smtp_sender_auth, VAR_SMTP_CNAME_OVERR, DEF_SMTP_CNAME_OVERR, &var_smtp_cname_overr, diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c index c41cc9917..9d881903d 100644 --- a/postfix/src/smtp/smtp_tls_policy.c +++ b/postfix/src/smtp/smtp_tls_policy.c @@ -628,6 +628,7 @@ int smtp_tls_policy_cache_query(DSN_BUF *why, SMTP_TLS_POLICY *tls, { VSTRING *key; int valid = iter->rr && iter->rr->dnssec_valid; + int mxvalid = iter->mx == 0 || iter->mx->dnssec_valid; /* * Create an empty TLS Policy cache on the fly. @@ -644,7 +645,7 @@ int smtp_tls_policy_cache_query(DSN_BUF *why, SMTP_TLS_POLICY *tls, smtp_key_prefix(key, ":", iter, SMTP_KEY_FLAG_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | SMTP_KEY_FLAG_PORT); - vstring_sprintf_append(key, "%d", !!valid); + vstring_sprintf_append(key, "%d:%d", !!valid, !!mxvalid); ctable_newcontext(policy_cache, (void *) iter); *tls = *(SMTP_TLS_POLICY *) ctable_locate(policy_cache, STR(key)); vstring_free(key); @@ -711,7 +712,8 @@ static int global_tls_level(void) static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) { TLS_DANE *dane; - int valid = iter->rr && iter->rr->dnssec_valid; + int valid; + int mxvalid; if (!iter->port) { msg_warn("%s: the \"dane\" security level is invalid for delivery via" @@ -756,7 +758,26 @@ static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) } return; } - if (!valid) { + + /* + * If there were no MX records and the destination host is the original + * nexthop domain, or if the MX RRset is DNS validated, we can at least + * try DANE with the destination host prior to CNAME expansion, but we + * prefer CNAME expanded MX hosts if those are also secure. + * + * By default suppress TLSA lookups for non-DNSSEC + non-MX + non-CNAME + * hosts. If the host address is not DNSSEC validated, the TLSA RRset is + * safely assumed to not be in a DNSSEC Look-aside Validation child zone. + */ + mxvalid = iter->mx == 0 || iter->mx->dnssec_valid; + valid = iter->rr && iter->rr->dnssec_valid; + if (!var_smtp_tls_force_tlsa + && !valid + && iter->mx == 0 + && strcmp(iter->rr->qname, iter->rr->rname) == 0) + mxvalid = 0; + + if (!mxvalid) { if (tls->level == TLS_LEV_DANE) { tls->level = TLS_LEV_MAY; if (msg_verbose) @@ -768,7 +789,8 @@ static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) return; } /* When TLSA lookups fail, we defer the message */ - if ((dane = tls_dane_resolve(STR(iter->host), "tcp", iter->port)) == 0) { + if ((dane = tls_dane_resolve(iter->rr->qname, valid ? iter->rr->rname : 0, + "tcp", iter->port)) == 0) { tls->level = TLS_LEV_INVALID; dsb_simple(tls->why, "4.7.5", "TLSA lookup error for %s:%u", STR(iter->host), ntohs(iter->port)); @@ -818,7 +840,14 @@ static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) */ if (TLS_DANE_HASTA(dane)) { tls->matchargv = argv_alloc(2); - argv_add(tls->matchargv, "hostname", "nexthop", ARGV_END); + argv_add(tls->matchargv, dane->base_domain, ARGV_END); + if (iter->mx) { + if (strcmp(iter->mx->qname, iter->mx->rname) == 0) + argv_add(tls->matchargv, iter->mx->qname, ARGV_END); + else + argv_add(tls->matchargv, iter->mx->rname, + iter->mx->qname, ARGV_END); + } } else if (!TLS_DANE_HASEE(dane)) msg_panic("empty DANE match list"); tls->dane = dane; diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 613570f5e..3ff8959f7 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -385,12 +385,6 @@ /* .IP "\fBsmtpd_tls_req_ccert (no)\fR" /* With mandatory TLS encryption, require a trusted remote SMTP client /* certificate in order to allow TLS connections to proceed. -/* .IP "\fBsmtpd_tls_session_cache_database (empty)\fR" -/* Name of the file containing the optional Postfix SMTP server -/* TLS session cache. -/* .IP "\fBsmtpd_tls_session_cache_timeout (3600s)\fR" -/* The expiration time of Postfix SMTP server TLS session cache -/* information. /* .IP "\fBsmtpd_tls_wrappermode (no)\fR" /* Run the Postfix SMTP server in the non-standard "wrapper" mode, /* instead of using the STARTTLS command. @@ -1264,7 +1258,6 @@ char *var_smtpd_tls_loglevel; char *var_smtpd_tls_mand_proto; bool var_smtpd_tls_received_header; bool var_smtpd_tls_req_ccert; -int var_smtpd_tls_scache_timeout; bool var_smtpd_tls_set_sessid; char *var_smtpd_tls_fpt_dgst; char *var_smtpd_tls_ciph; @@ -5152,8 +5145,6 @@ static void pre_jail_init(char *unused_name, char **unused_argv) log_level = var_smtpd_tls_loglevel, verifydepth = var_smtpd_tls_ccert_vd, cache_type = TLS_MGR_SCACHE_SMTPD, - scache_timeout - = var_smtpd_tls_scache_timeout, set_sessid = var_smtpd_tls_set_sessid, cert_file = cert_file, key_file = var_smtpd_tls_key_file, @@ -5326,7 +5317,6 @@ int main(int argc, char **argv) VAR_SMTPD_POLICY_TTL, DEF_SMTPD_POLICY_TTL, &var_smtpd_policy_ttl, 1, 0, #ifdef USE_TLS VAR_SMTPD_STARTTLS_TMOUT, DEF_SMTPD_STARTTLS_TMOUT, &var_smtpd_starttls_tmout, 1, 0, - VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0, #endif VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, &var_milt_conn_time, 1, 0, VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, &var_milt_cmd_time, 1, 0, diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 47f60e996..9d1fde2f6 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -154,6 +154,7 @@ typedef struct TLS_DANE { TLS_TLSA *ee; /* End-entity cert/pubkey digests */ TLS_CERTS *certs; /* Full trust-anchor certificates */ TLS_PKEYS *pkeys; /* Full trust-anchor public keys */ + char *base_domain; /* Base domain of TLSA RRset */ int flags; /* Conflate cert and pkey digests */ time_t expires; /* Expiration time of this record */ int refs; /* Reference count */ @@ -172,7 +173,8 @@ extern TLS_DANE *tls_dane_alloc(int); extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *, const char *); extern void tls_dane_free(TLS_DANE *); -extern TLS_DANE *tls_dane_resolve(const char *, const char *, unsigned); +extern TLS_DANE *tls_dane_resolve(const char *, const char *, const char *, + unsigned); extern int tls_dane_load_trustfile(TLS_DANE *, const char *); /* @@ -197,6 +199,7 @@ typedef struct { /* Private. */ SSL *con; char *cache_type; /* tlsmgr(8) cache type if enabled */ + int ticketed; /* Session ticket issued */ char *serverid; /* unique server identifier */ char *namaddr; /* nam[addr] for logging */ int log_mask; /* What to log */ @@ -254,6 +257,7 @@ extern int tls_log_mask(const char *, const char *); #define TLS_LOG_DEBUG (1<<7) #define TLS_LOG_TLSPKTS (1<<8) #define TLS_LOG_ALLPKTS (1<<9) +#define TLS_LOG_SESSTKT (1<<10) /* * Client and Server application contexts @@ -410,7 +414,6 @@ typedef struct { const char *log_level; int verifydepth; const char *cache_type; - long scache_timeout; int set_sessid; const char *cert_file; const char *key_file; @@ -449,13 +452,12 @@ extern TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *); tls_session_stop(ctx, (stream), (timeout), (failure), (TLScontext)) #define TLS_SERVER_INIT(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \ - a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) \ + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) \ tls_server_init((((props)->a1), ((props)->a2), ((props)->a3), \ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \ - ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \ - ((props)->a20), (props))) + ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), (props))) #define TLS_SERVER_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ tls_server_start((((props)->a1), ((props)->a2), ((props)->a3), \ diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 321ed6575..f84955e7e 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -275,6 +275,7 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) { long off = 0; int cachable; + int scache_timeout; SSL_CTX *client_ctx; TLS_APPL_STATE *app_ctx; int log_mask; @@ -445,7 +446,10 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) * sessions from the external cache, so we must delete them directly (not * via a callback). */ - if (tls_mgr_policy(props->cache_type, &cachable) != TLS_MGR_STAT_OK) + if (tls_mgr_policy(props->cache_type, &cachable, + &scache_timeout) != TLS_MGR_STAT_OK) + scache_timeout = 0; + if (scache_timeout <= 0) cachable = 0; /* @@ -484,6 +488,15 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) SSL_SESS_CACHE_NO_INTERNAL_STORE | SSL_SESS_CACHE_NO_AUTO_CLEAR); SSL_CTX_sess_set_new_cb(client_ctx, new_client_session_cb); + + /* + * OpenSSL ignores timed-out sessions. We need to set the internal + * cache timeout at least as high as the external cache timeout. This + * applies even if no internal cache is used. We set the session to + * twice the cache lifetime. This way a session always lasts longer + * than its lifetime in the cache. + */ + SSL_CTX_set_timeout(client_ctx, 2 * scache_timeout); } return (app_ctx); } @@ -546,12 +559,12 @@ static int match_servername(const char *certid, */ if (!strcasecmp(certid, domain) || (certid[0] == '*' && certid[1] == '.' && certid[2] != 0 - && (parent = strchr(domain, '.')) != 0 - && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent)) - && strcasecmp(var_tls_multi_wildcard == 0 ? parent : - parent + domlen - idlen, - certid + 1) == 0)) - return (1); + && (parent = strchr(domain, '.')) != 0 + && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent)) + && strcasecmp(var_tls_multi_wildcard == 0 ? parent : + parent + domlen - idlen, + certid + 1) == 0)) + return (1); } return (0); } diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c index cc068af4a..8e499832b 100644 --- a/postfix/src/tls/tls_dane.c +++ b/postfix/src/tls/tls_dane.c @@ -41,8 +41,9 @@ /* SSL_CTX *ssl_ctx; /* TLS_SESS_STATE *TLScontext; /* -/* TLS_DANE *tls_dane_resolve(host, proto, port) -/* const char *host; +/* TLS_DANE *tls_dane_resolve(qname, rname, proto, port) +/* const char *qname; +/* const char *rname; /* const char *proto; /* unsigned port; /* @@ -95,7 +96,7 @@ /* anchors always override the legacy public CA PKI. Otherwise, the /* callback MUST be cleared. /* -/* tls_dane_resolve() maps a (host, protocol, port) triple to a +/* tls_dane_resolve() maps a (qname, rname, protocol, port) tuple to a /* a corresponding TLS_DANE policy structure found in the DNS. The port /* argument is in network byte order. A null pointer is returned when /* the DNS query for the TLSA record tempfailed. In all other cases the @@ -116,7 +117,9 @@ /* .IP dane /* Pointer to a TLS_DANE structure that lists the valid trust-anchor /* and end-entity full-certificate and/or public-key digests. -/* .IP host +/* .IP qname +/* FQDN of target service (original input form). +/* .IP rname /* DNSSEC validated (cname resolved) FQDN of target service. /* .IP proto /* Almost certainly "tcp". @@ -366,6 +369,7 @@ TLS_DANE *tls_dane_alloc(int flags) dane->ee = 0; dane->certs = 0; dane->pkeys = 0; + dane->base_domain = 0; dane->flags = flags; dane->expires = 0; dane->refs = 1; @@ -450,6 +454,8 @@ void tls_dane_free(TLS_DANE *dane) /* De-allocate full trust-anchor certs and pkeys */ free_ta_certs(dane); free_ta_pkeys(dane); + if (dane->base_domain) + myfree(dane->base_domain); myfree((char *) dane); } @@ -820,14 +826,33 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx) return (void *) dane; } +/* resolve_host - resolve TLSA RRs for hostname (rname or qname) */ + +static TLS_DANE *resolve_host(const char *host, const char *proto, + unsigned port) +{ + static VSTRING *query_domain; + TLS_DANE *dane; + + if (query_domain == 0) + query_domain = vstring_alloc(64); + + vstring_sprintf(query_domain, "_%u._%s.%s", ntohs(port), proto, host); + dane = (TLS_DANE *) ctable_locate(dane_cache, STR(query_domain)); + if (timecmp(event_time(), dane->expires) > 0) + dane = (TLS_DANE *) ctable_refresh(dane_cache, STR(query_domain)); + if (dane->base_domain == 0) + dane->base_domain = mystrdup(host); + return (dane); +} + #endif -/* tls_dane_resolve - cached map: (host, proto, port) -> TLS_DANE */ +/* tls_dane_resolve - cached map: (name, proto, port) -> TLS_DANE */ -TLS_DANE *tls_dane_resolve(const char *host, const char *proto, - unsigned port) +TLS_DANE *tls_dane_resolve(const char *qname, const char *rname, + const char *proto, unsigned port) { - static VSTRING *qname; TLS_DANE *dane = 0; #ifdef DANE_TLSA_SUPPORT @@ -837,13 +862,16 @@ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, if (!dane_cache) dane_cache = ctable_create(CACHE_SIZE, dane_lookup, dane_free, 0); - if (qname == 0) - qname = vstring_alloc(64); - vstring_sprintf(qname, "_%u._%s.%s", ntohs(port), proto, host); - dane = (TLS_DANE *) ctable_locate(dane_cache, STR(qname)); - if (timecmp(event_time(), dane->expires) > 0) - dane = (TLS_DANE *) ctable_refresh(dane_cache, STR(qname)); - + /* + * Try the rname first, if nothing there, try the qname. Note, lookup + * errors are distinct from success with nothing found. If the rname + * lookup fails we don't try the qname. The rname may be null when only + * the qname is in a secure zone. + */ + if (rname) + dane = resolve_host(rname, proto, port); + if (!rname || (tls_dane_notfound(dane) && strcmp(qname, rname) != 0)) + dane = resolve_host(qname, proto, port); if (dane->flags & TLS_DANE_FLAG_ERROR) return (0); @@ -1193,7 +1221,7 @@ static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject, || !add_skid(cert, akid) || (wrap_signed && (!X509_sign(cert, danekey, signmd) - || (key && !wrap_key(TLScontext, 0, cert, depth + 1))))) { + || (key && !wrap_key(TLScontext, 0, cert, depth + 1))))) { msg_warn("error generating DANE wrapper certificate"); tls_print_errors(); ret = 0; @@ -1391,4 +1419,3 @@ void tls_dane_set_callback(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext) } #endif /* USE_TLS */ - diff --git a/postfix/src/tls/tls_mgr.c b/postfix/src/tls/tls_mgr.c index ef4014370..683f36734 100644 --- a/postfix/src/tls/tls_mgr.c +++ b/postfix/src/tls/tls_mgr.c @@ -28,6 +28,10 @@ /* int tls_mgr_delete(cache_type, cache_id) /* const char *cache_type; /* const char *cache_id; +/* +/* TLS_TICKET_KEY *tls_mgr_key(keyname, timeout) +/* unsigned char *keyname; +/* int timeout; /* DESCRIPTION /* These routines communicate with the tlsmgr(8) server for /* entropy and session cache management. Since these are @@ -48,7 +52,10 @@ /* tls_mgr_delete() removes specified session from /* the specified session cache. /* -/* Arguments +/* tls_mgr_key() is used to retrieve the current TLS session ticket +/* encryption or decryption keys. +/* +/* Arguments: /* .IP cache_type /* One of TLS_MGR_SCACHE_SMTPD, TLS_MGR_SCACHE_SMTP or /* TLS_MGR_SCACHE_LMTP. @@ -61,6 +68,18 @@ /* The result or input buffer. /* .IP len /* The length of the input buffer, or the amount of data requested. +/* .IP keyname +/* Is null when requesting the current encryption keys. Otherwise, +/* keyname is a pointer to an array of TLS_TICKET_NAMELEN unsigned +/* chars (not NUL terminated) that is an identifier for a key +/* previously used to encrypt a session ticket. When encrypting +/* a null result indicates that session tickets are not supported, when +/* decrypting it indicates that no matching keys were found. +/* .IP timeout +/* The encryption key timeout. Once a key has been active for this many +/* seconds it is retired and used only for decrypting previously issued +/* session tickets for another timeout seconds, and is then destroyed. +/* The timeout must not be longer than half the SSL session lifetime. /* DIAGNOSTICS /* All client functions return one of the following status codes: /* .IP TLS_MGR_STAT_OK @@ -111,10 +130,15 @@ #include #include + +/* TLS library. */ #include /* Application-specific. */ +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + static ATTR_CLNT *tls_mgr; /* tls_mgr_open - create client handle */ @@ -174,7 +198,7 @@ int tls_mgr_seed(VSTRING *buf, int len) /* tls_mgr_policy - request caching policy */ -int tls_mgr_policy(const char *cache_type, int *cachable) +int tls_mgr_policy(const char *cache_type, int *cachable, int *timeout) { int status; @@ -195,7 +219,8 @@ int tls_mgr_policy(const char *cache_type, int *cachable) ATTR_FLAG_MISSING, /* Reply attributes */ ATTR_TYPE_INT, TLS_MGR_ATTR_STATUS, &status, ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable, - ATTR_TYPE_END) != 2) + ATTR_TYPE_INT, TLS_MGR_ATTR_SESSTOUT, timeout, + ATTR_TYPE_END) != 3) status = TLS_MGR_STAT_FAIL; return (status); } @@ -288,6 +313,65 @@ int tls_mgr_delete(const char *cache_type, const char *cache_id) return (status); } +/* request_scache_key - ask tlsmgr(8) for matching key */ + +static TLS_TICKET_KEY *request_scache_key(unsigned char *keyname) +{ + TLS_TICKET_KEY tmp; + static VSTRING *keybuf; + char *name; + size_t len; + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + if (keybuf == 0) + keybuf = vstring_alloc(sizeof(tmp)); + + /* In tlsmgr requests we encode null key names as empty strings. */ + name = keyname ? (char *) keyname : ""; + len = keyname ? TLS_TICKET_NAMELEN : 0; + + /* + * Send the request and receive the reply. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request */ + ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_TKTKEY, + ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYNAME, len, name, + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply */ + ATTR_TYPE_INT, TLS_MGR_ATTR_STATUS, &status, + ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYBUF, keybuf, + ATTR_TYPE_END) != 2 + || status != TLS_MGR_STAT_OK + || LEN(keybuf) != sizeof(tmp)) + return (0); + + memcpy((char *) &tmp, STR(keybuf), sizeof(tmp)); + return (tls_scache_key_rotate(&tmp)); +} + +/* tls_mgr_key - session ticket key lookup, local cache, then tlsmgr(8) */ + +TLS_TICKET_KEY *tls_mgr_key(unsigned char *keyname, int timeout) +{ + TLS_TICKET_KEY *key = 0; + time_t now = time((time_t *) 0); + + /* A zero timeout disables session tickets. */ + if (timeout <= 0) + return (0); + + if ((key = tls_scache_key(keyname, now, timeout)) == 0) + key = request_scache_key(keyname); + return (key); +} + #ifdef TEST /* System library. */ @@ -307,9 +391,6 @@ int tls_mgr_delete(const char *cache_type, const char *cache_id) /* Application-specific. */ -#define STR(x) vstring_str(x) -#define LEN(x) VSTRING_LEN(x) - int main(int unused_ac, char **av) { VSTRING *inbuf = vstring_alloc(10); @@ -332,7 +413,6 @@ int main(int unused_ac, char **av) argv_free(argv); continue; } - #define COMMAND(argv, str, len) \ (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len) diff --git a/postfix/src/tls/tls_mgr.h b/postfix/src/tls/tls_mgr.h index bc9effffc..c58417524 100644 --- a/postfix/src/tls/tls_mgr.h +++ b/postfix/src/tls/tls_mgr.h @@ -11,6 +11,11 @@ /* DESCRIPTION /* .nf + /* + * TLS library + */ +#include /* Session ticket keys */ + /* * TLS manager protocol. */ @@ -23,6 +28,7 @@ #define TLS_MGR_REQ_LOOKUP "lookup" #define TLS_MGR_REQ_UPDATE "update" #define TLS_MGR_REQ_DELETE "delete" +#define TLS_MGR_REQ_TKTKEY "tktkey" #define TLS_MGR_ATTR_CACHABLE "cachable" #define TLS_MGR_ATTR_CACHE_TYPE "cache_type" #define TLS_MGR_ATTR_SEED "seed" @@ -30,6 +36,9 @@ #define TLS_MGR_ATTR_SESSION "session" #define TLS_MGR_ATTR_SIZE "size" #define TLS_MGR_ATTR_STATUS "status" +#define TLS_MGR_ATTR_KEYNAME "keyname" +#define TLS_MGR_ATTR_KEYBUF "keybuf" +#define TLS_MGR_ATTR_SESSTOUT "timeout" /* * TLS manager request status codes. @@ -42,10 +51,11 @@ * Functional interface. */ extern int tls_mgr_seed(VSTRING *, int); -extern int tls_mgr_policy(const char *, int *); +extern int tls_mgr_policy(const char *, int *, int *); extern int tls_mgr_lookup(const char *, const char *, VSTRING *); extern int tls_mgr_update(const char *, const char *, const char *, ssize_t); extern int tls_mgr_delete(const char *, const char *); +extern TLS_TICKET_KEY *tls_mgr_key(unsigned char *, int); /* LICENSE /* .ad diff --git a/postfix/src/tls/tls_scache.c b/postfix/src/tls/tls_scache.c index 3fd8b5313..9eadfc410 100644 --- a/postfix/src/tls/tls_scache.c +++ b/postfix/src/tls/tls_scache.c @@ -36,6 +36,14 @@ /* int tls_scache_delete(cache, cache_id) /* TLS_SCACHE *cache; /* const char *cache_id; +/* +/* TLS_TICKET_KEY *tls_scache_key(keyname, now, timeout) +/* unsigned char *keyname; +/* time_t now; +/* int timeout; +/* +/* TLS_TICKET_KEY *tls_scache_key_rotate(newkey) +/* TLS_TICKET_KEY *newkey; /* DESCRIPTION /* This module maintains Postfix TLS session cache files. /* each session is stored under a lookup key (hostname or @@ -67,6 +75,13 @@ /* tls_scache_delete() removes the specified cache entry from /* the specified TLS session cache. /* +/* tls_scache_key() locates a TLS session ticket key in a 2-element +/* in-memory cache. A null result is returned if no unexpired matching +/* key is found. +/* +/* tls_scache_key_rotate() saves a TLS session tickets key in the +/* in-memory cache. +/* /* Arguments: /* .IP dbname /* The base name of the session cache file. @@ -95,6 +110,18 @@ /* /* Specify TLS_SCACHE_DONT_NEED_SESSION to avoid /* saving the session information in the cache entry. +/* .IP keyname +/* Is null when requesting the current encryption keys. Otherwise, +/* keyname is a pointer to an array of TLS_TICKET_NAMELEN unsigned +/* chars (not NUL terminated) that is an identifier for a key +/* previously used to encrypt a session ticket. +/* .IP now +/* Current epoch time passed by caller. +/* .IP timeout +/* TLS session ticket encryption lifetime. +/* .IP newkey +/* TLS session ticket key obtained from tlsmgr(8) to be added to + * internal cache. /* DIAGNOSTICS /* These routines terminate with a fatal run-time error /* for unrecoverable database errors. This allows the @@ -133,6 +160,7 @@ #include #include #include +#include /* Global library. */ @@ -150,6 +178,8 @@ typedef struct { char session[1]; /* actually a bunch of bytes */ } TLS_SCACHE_ENTRY; +static TLS_TICKET_KEY *keys[2]; + /* * SLMs. */ @@ -504,4 +534,60 @@ void tls_scache_close(TLS_SCACHE *cp) myfree((char *) cp); } +/* tls_scache_key - find session ticket key for given key name */ + +TLS_TICKET_KEY *tls_scache_key(unsigned char *keyname, time_t now, int timeout) +{ + int i; + + /* + * The keys array contains 2 elements, the current signing key and the + * previous key. + * + * When name == 0 we are issuing a ticket, otherwise decrypting an existing + * ticket with the given key name. For new tickets we always use the + * current key if unexpired. For existing tickets, we use either the + * current or previous key with a validation expiration that is timeout + * longer than the signing expiration. + */ + if (keyname) { + for (i = 0; i < 2 && keys[i]; ++i) { + if (memcmp(keyname, keys[i]->name, TLS_TICKET_NAMELEN) == 0) { + if (timecmp(keys[i]->tout + timeout, now) > 0) + return (keys[i]); + break; + } + } + } else if (keys[0]) { + if (timecmp(keys[0]->tout, now) > 0) + return (keys[0]); + } + return (0); +} + +/* tls_scache_key_rotate - rotate session ticket keys */ + +TLS_TICKET_KEY *tls_scache_key_rotate(TLS_TICKET_KEY *newkey) +{ + + /* + * Allocate or re-use storage of retired key, then overwrite it, since + * caller's key data is ephemeral. + */ + if (keys[1] == 0) + keys[1] = (TLS_TICKET_KEY *) mymalloc(sizeof(*newkey)); + *keys[1] = *newkey; + newkey = keys[1]; + + /* + * Rotate if required, ensuring that the keys are sorted by expiration + * time with keys[0] expiring last. + */ + if (keys[0] == 0 || keys[0]->tout < keys[1]->tout) { + keys[1] = keys[0]; + keys[0] = newkey; + } + return (newkey); +} + #endif diff --git a/postfix/src/tls/tls_scache.h b/postfix/src/tls/tls_scache.h index 78903416d..fe6b40f85 100644 --- a/postfix/src/tls/tls_scache.h +++ b/postfix/src/tls/tls_scache.h @@ -29,6 +29,19 @@ typedef struct { char *saved_cursor; /* cursor cache ID */ } TLS_SCACHE; +#define TLS_TICKET_NAMELEN 16 /* RFC 5077 ticket key name length */ +#define TLS_TICKET_IVLEN 16 /* RFC 5077 ticket IV length */ +#define TLS_TICKET_KEYLEN 16 /* AES-128-CBC key size */ +#define TLS_TICKET_MACLEN 16 /* SHA-256 collision strength */ +#define TLS_SESSION_LIFEMIN 120 /* May you live to 120! */ + +typedef struct TLS_TICKET_KEY { + unsigned char name[TLS_TICKET_NAMELEN]; + unsigned char bits[TLS_TICKET_KEYLEN]; + unsigned char hmac[TLS_TICKET_MACLEN]; + time_t tout; +} TLS_TICKET_KEY; + #define TLS_SCACHE_FLAG_DEL_SAVED_CURSOR (1<<0) extern TLS_SCACHE *tls_scache_open(const char *, const char *, int, int); @@ -37,6 +50,8 @@ extern int tls_scache_lookup(TLS_SCACHE *, const char *, VSTRING *); extern int tls_scache_update(TLS_SCACHE *, const char *, const char *, ssize_t); extern int tls_scache_delete(TLS_SCACHE *, const char *); extern int tls_scache_sequence(TLS_SCACHE *, int, char **, VSTRING *); +extern TLS_TICKET_KEY *tls_scache_key(unsigned char *, time_t, int); +extern TLS_TICKET_KEY *tls_scache_key_rotate(TLS_TICKET_KEY *); #define TLS_SCACHE_DONT_NEED_CACHE_ID ((char **) 0) #define TLS_SCACHE_DONT_NEED_SESSION ((VSTRING *) 0) diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index d31c772c1..680046fb1 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -276,6 +276,47 @@ static int new_server_session_cb(SSL *ssl, SSL_SESSION *session) return (1); } +#define NOENGINE ((ENGINE *) 0) +#define TLS_TKT_NOKEYS -1 /* No keys for encryption */ +#define TLS_TKT_STALE 0 /* No matching keys for decryption */ +#define TLS_TKT_ACCEPT 1 /* Ticket decryptable and re-usable */ +#define TLS_TKT_REISSUE 2 /* Ticket decryptable, not re-usable */ + +/* ticket_cb - configure tls session ticket encrypt/decrypt context */ + +static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], + EVP_CIPHER_CTX * ctx, HMAC_CTX * hctx, int create) +{ + static const EVP_MD *sha256; + static const EVP_CIPHER *aes128; + TLS_TICKET_KEY *key; + TLS_SESS_STATE *TLScontext = SSL_get_ex_data(con, TLScontext_index); + int timeout = ((int) SSL_CTX_get_timeout(SSL_get_SSL_CTX(con))) / 2; + + if ((!sha256 && (sha256 = EVP_sha256()) == 0) + || (!aes128 && (aes128 = EVP_aes_128_cbc()) == 0) + || (key = tls_mgr_key(create ? 0 : name, timeout)) == 0 + || (create && RAND_bytes(iv, TLS_TICKET_IVLEN) <= 0)) + return (create ? TLS_TKT_NOKEYS : TLS_TKT_STALE); + + HMAC_Init_ex(hctx, key->hmac, TLS_TICKET_MACLEN, sha256, NOENGINE); + + if (create) { + EVP_EncryptInit_ex(ctx, aes128, NOENGINE, key->bits, iv); + memcpy((char *) name, (char *) key->name, TLS_TICKET_NAMELEN); + if (TLScontext->log_mask & TLS_LOG_CACHE) + msg_info("%s: Issuing session ticket, key expiration: %ld", + TLScontext->namaddr, (long) key->tout); + } else { + EVP_DecryptInit_ex(ctx, aes128, NOENGINE, key->bits, iv); + if (TLScontext->log_mask & TLS_LOG_CACHE) + msg_info("%s: Decrypting session ticket, key expiration: %ld", + TLScontext->namaddr, (long) key->tout); + } + TLScontext->ticketed = 1; + return (TLS_TKT_ACCEPT); +} + /* tls_server_init - initialize the server-side TLS engine */ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) @@ -284,6 +325,8 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) long off = 0; int verify_flags = SSL_VERIFY_NONE; int cachable; + int scache_timeout; + int ticketable = 0; int protomask; TLS_APPL_STATE *app_ctx; int log_mask; @@ -380,13 +423,42 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) */ SSL_CTX_set_verify_depth(server_ctx, props->verifydepth + 1); + /* + * The session cache is implemented by the tlsmgr(8) server. + * + * XXX 200502 Surprise: when OpenSSL purges an entry from the in-memory + * cache, it also attempts to purge the entry from the on-disk cache. + * This is undesirable, especially when we set the in-memory cache size + * to 1. For this reason we don't allow OpenSSL to purge on-disk cache + * entries, and leave it up to the tlsmgr process instead. Found by + * Victor Duchovni. + */ + if (tls_mgr_policy(props->cache_type, &cachable, + &scache_timeout) != TLS_MGR_STAT_OK) + scache_timeout = 0; + if (scache_timeout <= 0) + cachable = 0; + /* * Protocol work-arounds, OpenSSL version dependent. */ + off |= tls_bug_bits(); + + /* + * Add SSL_OP_NO_TICKET when the timeout is zero or library support is + * incomplete. The SSL_CTX_set_tlsext_ticket_key_cb feature was added in + * OpenSSL 0.9.8h, while SSL_NO_TICKET was added in 0.9.8f. + */ #ifdef SSL_OP_NO_TICKET - off |= SSL_OP_NO_TICKET; +#if !defined(OPENSSL_NO_TLSEXT) && OPENSSL_VERSION_NUMBER >= 0x0090808fL + ticketable = (scache_timeout > 0 && !(off & SSL_OP_NO_TICKET)); + if (ticketable) + SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_cb); #endif - off |= tls_bug_bits(); + if (!ticketable) + off |= SSL_OP_NO_TICKET; +#endif + SSL_CTX_set_options(server_ctx, off); /* @@ -521,21 +593,7 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) */ app_ctx = tls_alloc_app_context(server_ctx, log_mask); - /* - * The session cache is implemented by the tlsmgr(8) server. - * - * XXX 200502 Surprise: when OpenSSL purges an entry from the in-memory - * cache, it also attempts to purge the entry from the on-disk cache. - * This is undesirable, especially when we set the in-memory cache size - * to 1. For this reason we don't allow OpenSSL to purge on-disk cache - * entries, and leave it up to the tlsmgr process instead. Found by - * Victor Duchovni. - */ - - if (tls_mgr_policy(props->cache_type, &cachable) != TLS_MGR_STAT_OK) - cachable = 0; - - if (cachable || props->set_sessid) { + if (cachable || ticketable || props->set_sessid) { /* * Initialize the session cache. @@ -572,9 +630,14 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) /* * OpenSSL ignores timed-out sessions. We need to set the internal * cache timeout at least as high as the external cache timeout. This - * applies even if no internal cache is used. + * applies even if no internal cache is used. We set the session + * lifetime to twice the cache lifetime, which is also the issuing + * and retired key validation lifetime of session tickets keys. This + * way a session always lasts longer than the server's ability to + * decrypt its session ticket. Otherwise, a bug in OpenSSL may fail + * to re-issue tickets when sessions decrypt, but are expired. */ - SSL_CTX_set_timeout(server_ctx, props->scache_timeout); + SSL_CTX_set_timeout(server_ctx, 2 * scache_timeout); } else { /* @@ -742,7 +805,8 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if ((TLScontext->log_mask & TLS_LOG_CACHE) && TLScontext->session_reused) - msg_info("%s: Reusing old session", TLScontext->namaddr); + msg_info("%s: Reusing old session%s", TLScontext->namaddr, + TLScontext->ticketed ? " (RFC 5077 session ticket)" : ""); /* * Let's see whether a peer certificate is available and what is the diff --git a/postfix/src/tlsmgr/tlsmgr.c b/postfix/src/tlsmgr/tlsmgr.c index cab6caff2..83fd8eda8 100644 --- a/postfix/src/tlsmgr/tlsmgr.c +++ b/postfix/src/tlsmgr/tlsmgr.c @@ -207,7 +207,6 @@ #include #include #include -#include #include #include @@ -219,6 +218,7 @@ /* TLS library. */ #ifdef USE_TLS +#include #define TLS_INTERNAL #include /* TLS_MGR_SCACHE_ */ #include @@ -289,7 +289,7 @@ typedef struct { int *cache_timeout; /* main.cf parameter value */ } TLSMGR_SCACHE; -TLSMGR_SCACHE cache_table[] = { +static TLSMGR_SCACHE cache_table[] = { TLS_MGR_SCACHE_SMTPD, 0, 0, &var_smtpd_tls_scache_db, VAR_SMTPD_TLS_LOGLEVEL, &var_smtpd_tls_loglevel, &var_smtpd_tls_scache_timeout, @@ -302,6 +302,8 @@ TLSMGR_SCACHE cache_table[] = { 0, }; +#define smtpd_cache (cache_table[0]) + /* * SLMs. */ @@ -455,6 +457,44 @@ static void tlsmgr_cache_run_event(int unused_event, char *ctx) cache->cache_info->timeout); } +/* tlsmgr_key - return matching or current RFC 5077 session ticket keys */ + +static int tlsmgr_key(VSTRING *buffer, int timeout) +{ + TLS_TICKET_KEY *key; + TLS_TICKET_KEY tmp; + unsigned char *name; + time_t now = time((time_t *) 0); + + /* In tlsmgr requests we encode null key names as empty strings. */ + name = LEN(buffer) ? (unsigned char *) STR(buffer) : 0; + + /* + * Each key's encrypt and subsequent decrypt-only timeout is half of the + * total session timeout. + */ + timeout /= 2; + + /* Attempt to locate existing key */ + if ((key = tls_scache_key(name, now, timeout)) == 0) { + if (name == 0) { + /* Create new encryption key */ + if (RAND_bytes(tmp.name, TLS_TICKET_NAMELEN) <= 0 + || RAND_bytes(tmp.bits, TLS_TICKET_KEYLEN) <= 0 + || RAND_bytes(tmp.hmac, TLS_TICKET_MACLEN) <= 0) + return (TLS_MGR_STAT_ERR); + tmp.tout = now + timeout - 1; + key = tls_scache_key_rotate(&tmp); + } else { + /* No matching decryption key found */ + return (TLS_MGR_STAT_ERR); + } + } + /* Return value overrites name buffer */ + vstring_memcpy(buffer, (char *) key, sizeof(*key)); + return (TLS_MGR_STAT_OK); +} + /* tlsmgr_loop - TLS manager main loop */ static int tlsmgr_loop(char *unused_name, char **unused_argv) @@ -667,6 +707,31 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, ATTR_TYPE_END); } + /* + * RFC 5077 TLS session ticket keys + */ + else if (STREQ(STR(request), TLS_MGR_REQ_TKTKEY)) { + if (attr_scan(client_stream, ATTR_FLAG_STRICT, + ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYNAME, buffer, + ATTR_TYPE_END) == 1) { + if (LEN(buffer) != 0 && LEN(buffer) != TLS_TICKET_NAMELEN) { + msg_warn("invalid session ticket key name length: %ld", + (long) LEN(buffer)); + VSTRING_RESET(buffer); + } else if (*smtpd_cache.cache_timeout <= 0) { + status = TLS_MGR_STAT_ERR; + VSTRING_RESET(buffer); + } else { + status = tlsmgr_key(buffer, *smtpd_cache.cache_timeout); + } + } + attr_print(client_stream, ATTR_FLAG_NONE, + ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, + ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYBUF, + LEN(buffer), STR(buffer), + ATTR_TYPE_END); + } + /* * Entropy request. */ @@ -698,6 +763,7 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, */ else if (STREQ(STR(request), TLS_MGR_REQ_POLICY)) { int cachable = 0; + int timeout = 0; if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, @@ -710,12 +776,14 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, STR(cache_type), TLS_MGR_REQ_POLICY); } else { cachable = (ent->cache_info != 0) ? 1 : 0; + timeout = *ent->cache_timeout; status = TLS_MGR_STAT_OK; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable, + ATTR_TYPE_INT, TLS_MGR_ATTR_SESSTOUT, timeout, ATTR_TYPE_END); } @@ -847,7 +915,15 @@ static void tlsmgr_pre_init(char *unused_name, char **unused_argv) */ dup_filter = htable_create(sizeof(cache_table) / sizeof(cache_table[0])); for (ent = cache_table; ent->cache_label; ++ent) { - if (**ent->cache_db) { + /* Sanitize session timeout */ + if (*ent->cache_timeout > 0) { + if (*ent->cache_timeout < TLS_SESSION_LIFEMIN) + *ent->cache_timeout = TLS_SESSION_LIFEMIN; + } else { + *ent->cache_timeout = 0; + } + /* External cache database disabled if timeout is non-positive */ + if (*ent->cache_timeout > 0 && **ent->cache_db) { if ((dup_label = htable_find(dup_filter, *ent->cache_db)) != 0) msg_fatal("do not use the same TLS cache file %s for %s and %s", *ent->cache_db, dup_label, ent->cache_label); @@ -948,9 +1024,9 @@ int main(int argc, char **argv) static const CONFIG_TIME_TABLE time_table[] = { VAR_TLS_RESEED_PERIOD, DEF_TLS_RESEED_PERIOD, &var_tls_reseed_period, 1, 0, VAR_TLS_PRNG_UPD_PERIOD, DEF_TLS_PRNG_UPD_PERIOD, &var_tls_prng_exch_period, 1, 0, - VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0, - VAR_SMTP_TLS_SCACHTIME, DEF_SMTP_TLS_SCACHTIME, &var_smtp_tls_scache_timeout, 0, 0, - VAR_LMTP_TLS_SCACHTIME, DEF_LMTP_TLS_SCACHTIME, &var_lmtp_tls_scache_timeout, 0, 0, + VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, MAX_SMTPD_TLS_SCACHETIME, + VAR_SMTP_TLS_SCACHTIME, DEF_SMTP_TLS_SCACHTIME, &var_smtp_tls_scache_timeout, 0, MAX_SMTP_TLS_SCACHETIME, + VAR_LMTP_TLS_SCACHTIME, DEF_LMTP_TLS_SCACHTIME, &var_lmtp_tls_scache_timeout, 0, MAX_LMTP_TLS_SCACHETIME, 0, }; static const CONFIG_INT_TABLE int_table[] = { diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index eff987a6e..eb826b00c 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -132,9 +132,6 @@ /* The SMTP TLS security level for the Postfix \fBtlsproxy\fR(8) server; /* when a non-empty value is specified, this overrides the obsolete /* parameters smtpd_use_tls and smtpd_enforce_tls. -/* .IP "\fBtlsproxy_tls_session_cache_timeout ($smtpd_tls_session_cache_timeout)\fR" -/* The expiration time of Postfix \fBtlsproxy\fR(8) server TLS session -/* cache information. /* .PP /* Available in Postfix version 2.11 and later: /* .IP "\fBtlsmgr_service_name (tlsmgr)\fR" @@ -237,7 +234,6 @@ */ int var_smtpd_tls_ccert_vd; char *var_smtpd_tls_loglevel; -int var_smtpd_tls_scache_timeout; bool var_smtpd_use_tls; bool var_smtpd_enforce_tls; bool var_smtpd_tls_ask_ccert; @@ -266,7 +262,6 @@ char *var_smtpd_tls_level; int var_tlsp_tls_ccert_vd; char *var_tlsp_tls_loglevel; -int var_tlsp_tls_scache_timeout; bool var_tlsp_use_tls; bool var_tlsp_enforce_tls; bool var_tlsp_tls_ask_ccert; @@ -978,7 +973,6 @@ static void pre_jail_init(char *unused_name, char **unused_argv) log_level = var_tlsp_tls_loglevel, verifydepth = var_tlsp_tls_ccert_vd, cache_type = TLS_MGR_SCACHE_SMTPD, - scache_timeout = var_tlsp_tls_scache_timeout, set_sessid = var_tlsp_tls_set_sessid, cert_file = cert_file, key_file = var_tlsp_tls_key_file, @@ -1036,9 +1030,7 @@ int main(int argc, char **argv) 0, }; static const CONFIG_TIME_TABLE time_table[] = { - VAR_SMTPD_TLS_SCACHTIME, DEF_SMTPD_TLS_SCACHTIME, &var_smtpd_tls_scache_timeout, 0, 0, VAR_TLSP_WATCHDOG, DEF_TLSP_WATCHDOG, &var_tlsp_watchdog, 10, 0, - VAR_TLSP_TLS_SCACHTIME, DEF_TLSP_TLS_SCACHTIME, &var_tlsp_tls_scache_timeout, 0, 0, 0, }; static const CONFIG_BOOL_TABLE bool_table[] = {