diff --git a/postfix/HISTORY b/postfix/HISTORY index cda458ed7..67d718529 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -19168,18 +19168,44 @@ Apologies for any names omitted. 20131119 - Bugfix (introduced: 20111211): the Postfix memcache client - did not propagate a persistent "open()" lock to the optional - backup database. File: global/dict_memcache.c. - Feature: a Postfix LMDB database can now be used as shared - cache. Until now only the Postfix memcache database could - be used in this manner. This is implemented by allowing a - database to downgrade the permanent DICT_FLAG_OPEN_LOCK - method to the temporary DICT_FLAG_LOCK method. Files: - util/dict.h, util/dict_alloc.c, util/dict_open.c, - util/dict_lmdb.c. + persistent cache with multiple postscreen(8) or verify(8) + daemons (but not both), without the need for a shared + proxymap server. Files: util/dict.h, util/dict_alloc.c, + util/dict_open.c, util/dict_lmdb.c. Internal: DNS client support to report reply RCODE information, in addition to the simplified DNS_NOTFOUND, DNS_RETRY etc. + Portability note: this requires the C99 __VA_ARGS__ feature. Files: dns/dns.h. dns/dns_lookup.c, dns/test_dns_lookup.c. + +20131120 + + Cleanup: reduced the code footprint for the LMDB < 0.9.10 + heap-to-file information leak workaround, and simplified + the implementation to "good enough". Files: util/dict.h, + util/dict.c, util/dict_lmdb.c, postalias/postalias.c, + postmap/postmap.c. + + Cleanup: reduced the code footprint for the handling of + multi-writer safe maps. A map only needs to assert that it + is multi-writer safe, and the rest just happens. Files: + util/dict.h, util/dict_open.c, util/dict_lmdb.c, + global/dict_memcache.c. + + Cleanup: Postfix daemons no longer restart when a multi-writer + safe map is updated. File: util/dict.c. + + Documentation: sharing an LMDB cache between multiple + verify(8) or postscreen(8) servers (but not both). Files: + proto/ADDRESS_VERIFICATION_README.html, + proto/POSTSCREEN_README.html. + + Cleanup: improve suppression of TLSA lookups in insecure + zones. This is now applied not only to non-MX destinations, + but also to each MX record. Viktor Dukhovni. Files: + src/posttls-finger/posttls-finger.c, src/smtp/smtp_tls_policy.c, + src/tls/tls.h, src/tls/tls_dane.c. + + Workaround: increased the 5s connection timeout to 30s. + Viktor Dukhovni. File: posttls-finger/posttls-finger.c. diff --git a/postfix/README_FILES/ADDRESS_VERIFICATION_README b/postfix/README_FILES/ADDRESS_VERIFICATION_README index 1241d875b..7aef096dd 100644 --- a/postfix/README_FILES/ADDRESS_VERIFICATION_README +++ b/postfix/README_FILES/ADDRESS_VERIFICATION_README @@ -292,23 +292,35 @@ parameter specifies persistent storage for sender or recipient address verification results. If you specify an empty value, all address verification results are lost after "postfix reload" or "postfix stop". + # Example 1: Default setting for Postfix 2.7 and later. + # Note: avoid hash files here. Use btree instead. /etc/postfix/main.cf: - # Default setting for Postfix 2.7 and later. - # Note: avoid hash files here. Use btree instead. address_verify_map = btree:$data_directory/verify_cache - # Shared persistent cache (requires Postfix 2.9 or later). - address_verify_map = proxy:btree:$data_directory/verify_cache - # Disable automatic cache cleanup in all Postfix instances except - # for one instance that will be responsible for cache cleanup. + # Example 2: Shared persistent lmdb: cache (Postfix 2.11 or later). + # Disable automatic cache cleanup in all Postfix instances except + # for one instance that will be responsible for cache cleanup. + /etc/postfix/main.cf: + address_verify_map = lmdb:$data_directory/verify_cache # address_verify_cache_cleanup_interval = 0 - # Shared memory cache (requires Postfix 2.9 or later). - # See memcache_table(5) for details. - address_verify_map = memcache:/etc/postfix/verify-memcache.cf + # Example 3: Shared persistent btree: cache (Postfix 2.9 or later). + # Disable automatic cache cleanup in all Postfix instances except + # for one instance that will be responsible for cache cleanup. + /etc/postfix/main.cf: + address_verify_map = proxy:btree:$data_directory/verify_cache + # address_verify_cache_cleanup_interval = 0 - # Default setting for Postfix 2.6 and earlier. - # This uses non-persistent storage only. + # Example 4: Shared memory cache (requires Postfix 2.9 or later). + # Disable automatic cache cleanup in all Postfix instances. + # See memcache_table(5) for details. + /etc/postfix/main.cf: + address_verify_map = memcache:/etc/postfix/verify-memcache.cf + address_verify_cache_cleanup_interval = 0 + + # Example 5: Default setting for Postfix 2.6 and earlier. + # This uses non-persistent storage only. + /etc/postfix/main.cf: address_verify_map = NOTE 1: The database file should be stored under a Postfix-owned directory, diff --git a/postfix/README_FILES/POSTSCREEN_README b/postfix/README_FILES/POSTSCREEN_README index a7253c670..1ca03a5c9 100644 --- a/postfix/README_FILES/POSTSCREEN_README +++ b/postfix/README_FILES/POSTSCREEN_README @@ -165,17 +165,8 @@ postscreen_cache_map parameter specifies the location of the temporary whitelist. The temporary whitelist is not used for SMTP client addresses that appear on the permanent access list. - NOTE: To share a postscreen(8) cache between multiple postscreen(8) - instances under the same master(8) daemon, use "postscreen_cache_map = - proxy:btree:$data_directory/postscreen_cache", and disable cache cleanup - (postscreen_cache_cleanup_interval = 0) in all postscreen(8) instances - except one that is responsible for cache cleanup. - - postscreen(8) cache sharing requires Postfix 2.9 or later; earlier proxymap - (8) implementations don't support cache cleanup. - - For an alternative postscreen(8) cache sharing approach, see the - memcache_table(5) manpage. +By default the temporary whitelist is not shared with other postscreen(8) +daemons. See Sharing the temporary whitelist below for alternatives. When the SMTP client address appears on the temporary whitelist, postscreen(8) logs this with the client address and port number as: @@ -551,6 +542,7 @@ systems. * postscreen(8) TLS configuration * Blocking mail with postscreen(8) * Turning off postscreen(8) + * Sharing the temporary whitelist TTuurrnniinngg oonn ppoossttssccrreeeenn((88)) wwiitthhoouutt bblloocckkiinngg mmaaiill @@ -754,6 +746,72 @@ processes: 6. Read the new configuration with "postfix reload". +SShhaarriinngg tthhee tteemmppoorraarryy wwhhiitteelliisstt + +By default, the temporary whitelist is not shared between multiple postscreen +(8) daemons. To enable sharing, choose one of the following options: + + * A non-persistent memcache: temporary whitelist can be shared between + postscreen(8) daemons on the same host or different hosts. Disable cache + cleanup (postscreen_cache_cleanup_interval = 0) in all postscreen(8) + daemons because memcache: does not implement this (but see example 4 below + for memcache: with persistent backup). This requires Postfix 2.9 or later. + + # Example 1: non-persistent memcache: whitelist. + /etc/postfix/main.cf: + postscreen_cache_map = memcache:/etc/postfix/postscreen_cache + postscreen_cache_cleanup_interval = 0 + + /etc/postfix/postscreen_cache: + memcache = inet:127.0.0.1:11211 + key_format = postscreen:%s + + * A persistent lmdb: temporary whitelist can be shared between postscreen(8) + daemons that run under the same master(8) daemon, or under different master + (8) daemons on the same host. Disable cache cleanup + (postscreen_cache_cleanup_interval = 0) in all postscreen(8) daemons except + one that is responsible for cache cleanup. This requires Postfix 2.11 or + later. + + # Example 2: persistent lmdb: whitelist. + /etc/postfix/main.cf: + postscreen_cache_map = lmdb:$data_directory/postscreen_cache + # See note 1 below. + # postscreen_cache_cleanup_interval = 0 + + * Other kinds of persistent temporary whitelist can be shared only between + postscreen(8) daemons that run under the same master(8) daemon. In this + case, temporary whitelist access must be shared through the proxymap(8) + daemon. This requires Postfix 2.9 or later. + + # Example 3: proxied btree: whitelist. + /etc/postfix/main.cf: + postscreen_cache_map = + proxy:btree:/var/lib/postfix/postscreen_cache + # See note 1 below. + # postscreen_cache_cleanup_interval = 0 + + # Example 4: proxied btree: whitelist with memcache: accelerator. + /etc/postfix/main.cf: + postscreen_cache_map = memcache:/etc/postfix/postscreen_cache + proxy_write_maps = + proxy:btree:/var/lib/postfix/postscreen_cache + ... other proxied tables ... + # See note 1 below. + # postscreen_cache_cleanup_interval = 0 + + /etc/postfix/postscreen_cache: + # Note: the $data_directory macro is not defined in this context. + memcache = inet:127.0.0.1:11211 + backup = proxy:btree:/var/lib/postfix/postscreen_cache + key_format = postscreen:%s + + Note 1: disable cache cleanup (postscreen_cache_cleanup_interval = 0) in + all postscreen(8) daemons except one that is responsible for cache cleanup. + + Note 2: postscreen(8) cache sharing via proxymap(8) requires Postfix 2.9 or + later; earlier proxymap(8) implementations don't support cache cleanup. + HHiissttoorriiccaall nnootteess aanndd ccrreeddiittss Many ideas in postscreen(8) were explored in earlier work by Michael Tokarev, diff --git a/postfix/html/ADDRESS_VERIFICATION_README.html b/postfix/html/ADDRESS_VERIFICATION_README.html index 1aac06357..541c55619 100644 --- a/postfix/html/ADDRESS_VERIFICATION_README.html +++ b/postfix/html/ADDRESS_VERIFICATION_README.html @@ -454,23 +454,35 @@ results are lost after "postfix reload" or "postfix stop".

+# Example 1: Default setting for Postfix 2.7 and later.
+# Note: avoid hash files here. Use btree instead.
 /etc/postfix/main.cf:
-    # Default setting for Postfix 2.7 and later.
-    # Note: avoid hash files here. Use btree instead.
     address_verify_map = btree:$data_directory/verify_cache
 
-    # Shared persistent cache (requires Postfix 2.9 or later).  
-    address_verify_map = proxy:btree:$data_directory/verify_cache
-    # Disable automatic cache cleanup in all Postfix instances except
-    # for one instance that will be responsible for cache cleanup.
+# Example 2: Shared persistent lmdb: cache (Postfix 2.11 or later).  
+# Disable automatic cache cleanup in all Postfix instances except
+# for one instance that will be responsible for cache cleanup.
+/etc/postfix/main.cf:
+    address_verify_map = lmdb:$data_directory/verify_cache
     # address_verify_cache_cleanup_interval = 0
 
-    # Shared memory cache (requires Postfix 2.9 or later).
-    # See memcache_table(5) for details.
-    address_verify_map = memcache:/etc/postfix/verify-memcache.cf
+# Example 3: Shared persistent btree: cache (Postfix 2.9 or later).  
+# Disable automatic cache cleanup in all Postfix instances except
+# for one instance that will be responsible for cache cleanup.
+/etc/postfix/main.cf:
+    address_verify_map = proxy:btree:$data_directory/verify_cache
+    # address_verify_cache_cleanup_interval = 0
 
-    # Default setting for Postfix 2.6 and earlier.
-    # This uses non-persistent storage only.
+# Example 4: Shared memory cache (requires Postfix 2.9 or later).
+# Disable automatic cache cleanup in all Postfix instances.
+# See memcache_table(5) for details.
+/etc/postfix/main.cf:
+    address_verify_map = memcache:/etc/postfix/verify-memcache.cf
+    address_verify_cache_cleanup_interval = 0
+
+# Example 5: Default setting for Postfix 2.6 and earlier.
+# This uses non-persistent storage only.
+/etc/postfix/main.cf:
     address_verify_map =
 
diff --git a/postfix/html/POSTSCREEN_README.html b/postfix/html/POSTSCREEN_README.html index ca007ed3f..6aecf5948 100644 --- a/postfix/html/POSTSCREEN_README.html +++ b/postfix/html/POSTSCREEN_README.html @@ -227,24 +227,9 @@ specifies the location of the temporary whitelist. The temporary whitelist is not used for SMTP client addresses that appear on the permanent access list.

-
- -

NOTE: To share a postscreen(8) cache between multiple - postscreen(8) instances under the same master(8) daemon, use - "postscreen_cache_map = - proxy:btree:$data_directory/postscreen_cache", and disable - cache cleanup (postscreen_cache_cleanup_interval = 0) in all - postscreen(8) instances except one that is responsible for cache - cleanup.

- -

postscreen(8) cache sharing requires Postfix 2.9 or later; - earlier proxymap(8) implementations don't support cache cleanup. -

- -

For an alternative postscreen(8) cache sharing approach, - see the memcache_table(5) manpage.

- -
+

By default the temporary whitelist is not shared with other +postscreen(8) daemons. See Sharing +the temporary whitelist below for alternatives.

When the SMTP client address appears on the temporary whitelist, postscreen(8) logs this with the client address and port @@ -775,6 +760,9 @@ mail

  • Turning off postscreen(8) +
  • Sharing the temporary whitelist + +

    Turning on postscreen(8) without blocking mail

    @@ -1057,6 +1045,87 @@ may follow.

    +

    Sharing the temporary whitelist

    + +

    By default, the temporary whitelist is not shared between +multiple postscreen(8) daemons. To enable sharing, choose one +of the following options:

    + + +

    Historical notes and credits

    Many ideas in postscreen(8) were explored in earlier work by diff --git a/postfix/html/posttls-finger.1.html b/postfix/html/posttls-finger.1.html index 3e449ec61..1848d6d24 100644 --- a/postfix/html/posttls-finger.1.html +++ b/postfix/html/posttls-finger.1.html @@ -286,7 +286,7 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1) tive ports can specified by appending ":service- name" or ":portnumber" to the destination argument. - -t timeout (default: 5) + -t timeout (default: 30) The TCP connection timeout to use. This is also the timeout for reading the remote server's 220 banner. diff --git a/postfix/man/man1/posttls-finger.1 b/postfix/man/man1/posttls-finger.1 index 3ba4e7b43..3848c9f8b 100644 --- a/postfix/man/man1/posttls-finger.1 +++ b/postfix/man/man1/posttls-finger.1 @@ -226,7 +226,7 @@ Disable SMTP; that is, connect to an LMTP server. The default port for LMTP over TCP is 24. Alternative ports can specified by appending "\fI:servicename\fR" or ":\fIportnumber\fR" to the destination argument. -.IP "\fB-t \fItimeout\fR (default: \fB5\fR)" +.IP "\fB-t \fItimeout\fR (default: \fB30\fR)" The TCP connection timeout to use. This is also the timeout for reading the remote server's 220 banner. .IP "\fB-T \fItimeout\fR (default: \fB30\fR)" diff --git a/postfix/proto/ADDRESS_VERIFICATION_README.html b/postfix/proto/ADDRESS_VERIFICATION_README.html index f79ebc672..c62ee75e6 100644 --- a/postfix/proto/ADDRESS_VERIFICATION_README.html +++ b/postfix/proto/ADDRESS_VERIFICATION_README.html @@ -454,23 +454,35 @@ results are lost after "postfix reload" or "postfix stop".

    +# Example 1: Default setting for Postfix 2.7 and later.
    +# Note: avoid hash files here. Use btree instead.
     /etc/postfix/main.cf:
    -    # Default setting for Postfix 2.7 and later.
    -    # Note: avoid hash files here. Use btree instead.
         address_verify_map = btree:$data_directory/verify_cache
     
    -    # Shared persistent cache (requires Postfix 2.9 or later).  
    -    address_verify_map = proxy:btree:$data_directory/verify_cache
    -    # Disable automatic cache cleanup in all Postfix instances except
    -    # for one instance that will be responsible for cache cleanup.
    +# Example 2: Shared persistent lmdb: cache (Postfix 2.11 or later).  
    +# Disable automatic cache cleanup in all Postfix instances except
    +# for one instance that will be responsible for cache cleanup.
    +/etc/postfix/main.cf:
    +    address_verify_map = lmdb:$data_directory/verify_cache
         # address_verify_cache_cleanup_interval = 0
     
    -    # Shared memory cache (requires Postfix 2.9 or later).
    -    # See memcache_table(5) for details.
    -    address_verify_map = memcache:/etc/postfix/verify-memcache.cf
    +# Example 3: Shared persistent btree: cache (Postfix 2.9 or later).  
    +# Disable automatic cache cleanup in all Postfix instances except
    +# for one instance that will be responsible for cache cleanup.
    +/etc/postfix/main.cf:
    +    address_verify_map = proxy:btree:$data_directory/verify_cache
    +    # address_verify_cache_cleanup_interval = 0
     
    -    # Default setting for Postfix 2.6 and earlier.
    -    # This uses non-persistent storage only.
    +# Example 4: Shared memory cache (requires Postfix 2.9 or later).
    +# Disable automatic cache cleanup in all Postfix instances.
    +# See memcache_table(5) for details.
    +/etc/postfix/main.cf:
    +    address_verify_map = memcache:/etc/postfix/verify-memcache.cf
    +    address_verify_cache_cleanup_interval = 0
    +
    +# Example 5: Default setting for Postfix 2.6 and earlier.
    +# This uses non-persistent storage only.
    +/etc/postfix/main.cf:
         address_verify_map =
     
    diff --git a/postfix/proto/POSTSCREEN_README.html b/postfix/proto/POSTSCREEN_README.html index f91c63a96..8be94cbc4 100644 --- a/postfix/proto/POSTSCREEN_README.html +++ b/postfix/proto/POSTSCREEN_README.html @@ -227,24 +227,9 @@ specifies the location of the temporary whitelist. The temporary whitelist is not used for SMTP client addresses that appear on the permanent access list.

    -
    - -

    NOTE: To share a postscreen(8) cache between multiple - postscreen(8) instances under the same master(8) daemon, use - "postscreen_cache_map = - proxy:btree:$data_directory/postscreen_cache", and disable - cache cleanup (postscreen_cache_cleanup_interval = 0) in all - postscreen(8) instances except one that is responsible for cache - cleanup.

    - -

    postscreen(8) cache sharing requires Postfix 2.9 or later; - earlier proxymap(8) implementations don't support cache cleanup. -

    - -

    For an alternative postscreen(8) cache sharing approach, - see the memcache_table(5) manpage.

    - -
    +

    By default the temporary whitelist is not shared with other +postscreen(8) daemons. See Sharing +the temporary whitelist below for alternatives.

    When the SMTP client address appears on the temporary whitelist, postscreen(8) logs this with the client address and port @@ -775,6 +760,9 @@ mail

  • Turning off postscreen(8) +
  • Sharing the temporary whitelist + +

    Turning on postscreen(8) without blocking mail

    @@ -1057,6 +1045,87 @@ may follow.

    +

    Sharing the temporary whitelist

    + +

    By default, the temporary whitelist is not shared between +multiple postscreen(8) daemons. To enable sharing, choose one +of the following options:

    + + +

    Historical notes and credits

    Many ideas in postscreen(8) were explored in earlier work by diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 4fe4b2ef3..0eb8ab4f4 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -98,9 +98,6 @@ /* Request DNSSEC validation. This flag is silently ignored /* when the system stub resolver API, resolver(3), does not /* implement DNSSEC. -/* .IP -/* Pointer to storage for the reply RCODE value. This gives -/* more detailed information than DNS_FAIL, DNS_RETRY, etc. /* .RE /* .IP lflags /* Multi-type request control for dns_lookup_l() and dns_lookup_v(). @@ -132,6 +129,9 @@ /* name found for \fIname\fR. /* .IP why /* A null pointer, or storage for the reason for failure. +/* .IP rcode +/* Pointer to storage for the reply RCODE value. This gives +/* more detailed information than DNS_FAIL, DNS_RETRY, etc. /* DIAGNOSTICS /* dns_lookup() returns one of the following codes and sets the /* \fIwhy\fR argument accordingly: diff --git a/postfix/src/global/dict_memcache.c b/postfix/src/global/dict_memcache.c index 34c0a7383..161e0c880 100644 --- a/postfix/src/global/dict_memcache.c +++ b/postfix/src/global/dict_memcache.c @@ -574,29 +574,10 @@ DICT *dict_memcache_open(const char *name, int open_flags, int dict_flags) (char *) 0, 0, 0); if (backup) { dict_mc->backup = dict_open(backup, open_flags, dict_flags); - /* Expose backup lock and status to caller. */ - dict_mc->dict.lock = dict_mc->backup->lock; - dict_mc->dict.lock_type = dict_mc->backup->lock_type; - dict_mc->dict.lock_fd = dict_mc->backup->lock_fd; - dict_mc->dict.stat_fd = dict_mc->backup->stat_fd; myfree(backup); } else dict_mc->backup = 0; - /* - * Memcached is write-share safe. If the backup database is also - * write-share safe, e.g. it has downgraded its persistent lock to - * temporary, then expose that downgraded lock to the caller. - */ - if ((dict_flags & DICT_FLAG_OPEN_LOCK) != 0 - && (dict_mc->backup == 0 - || dict_mc->backup->lock_fd < 0 - || ((dict_mc->backup->flags & DICT_FLAG_OPEN_LOCK) == 0 - && (dict_mc->backup->flags & DICT_FLAG_LOCK) != 0))) { - dict_mc->dict.flags &= ~DICT_FLAG_OPEN_LOCK; - dict_mc->dict.flags |= DICT_FLAG_LOCK; - } - /* * Parse templates and common database parameters. Maps that use * substring keys should only be used with the full input key. @@ -611,5 +592,7 @@ DICT *dict_memcache_open(const char *name, int open_flags, int dict_flags) else dict_mc->dict.flags |= DICT_FLAG_FIXED; + dict_mc->dict.flags |= DICT_FLAG_MULTI_WRITER; + return (&dict_mc->dict); } diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 3799c7bbf..27001e164 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 "20131119" +#define MAIL_RELEASE_DATE "20131120" #define MAIL_VERSION_NUMBER "2.11" #ifdef SNAPSHOT diff --git a/postfix/src/postalias/postalias.c b/postfix/src/postalias/postalias.c index fd5351534..430c15641 100644 --- a/postfix/src/postalias/postalias.c +++ b/postfix/src/postalias/postalias.c @@ -290,7 +290,6 @@ static void postalias(char *map_type, char *path_name, int postalias_flags, if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", path_name); } - dict_flags |= DICT_FLAG_WORLD_READ; if (fstat(vstream_fileno(source_fp), &st) < 0) msg_fatal("fstat %s: %m", path_name); diff --git a/postfix/src/postmap/postmap.c b/postfix/src/postmap/postmap.c index 26348041e..e10ac1669 100644 --- a/postfix/src/postmap/postmap.c +++ b/postfix/src/postmap/postmap.c @@ -353,7 +353,6 @@ static void postmap(char *map_type, char *path_name, int postmap_flags, if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", path_name); } - dict_flags |= DICT_FLAG_WORLD_READ; if (fstat(vstream_fileno(source_fp), &st) < 0) msg_fatal("fstat %s: %m", path_name); diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index 2971c30b3..82d3aeb7d 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -220,7 +220,7 @@ /* LMTP over TCP is 24. Alternative ports can specified by appending /* "\fI:servicename\fR" or ":\fIportnumber\fR" to the destination /* argument. -/* .IP "\fB-t \fItimeout\fR (default: \fB5\fR)" +/* .IP "\fB-t \fItimeout\fR (default: \fB30\fR)" /* The TCP connection timeout to use. This is also the timeout for /* reading the remote server's 220 banner. /* .IP "\fB-T \fItimeout\fR (default: \fB30\fR)" @@ -358,7 +358,7 @@ */ #include "tlsmgrmem.h" -static int conn_tmout = 5; +static int conn_tmout = 30; static int smtp_tmout = 30; #define HOST_FLAG_DNS (1<<0) @@ -1163,26 +1163,10 @@ 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) { - - /* - * 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->mx == 0 || state->mx->dnssec_valid) { if (state->log_mask & (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE)) tls_dane_verbose(1); else @@ -1192,19 +1176,19 @@ static int dane_host_level(STATE *state, DNS_RR *addr) if (state->ddane) tls_dane_free(state->ddane); - /* When TLSA lookups fail, next host */ - state->ddane = tls_dane_resolve(addr->qname, - valid ? addr->rname : 0, - "tcp", state->port); + /* + * When TLSA lookups fail, next host. If unusable or not found, + * fallback to "secure" + */ + state->ddane = tls_dane_resolve(state->port, "tcp", addr, + state->force_tlsa); if (!state->ddane) { dsb_simple(state->why, "4.7.5", "TLSA lookup error for %s:%u", HNAME(addr), ntohs(state->port)); - return (TLS_LEV_INVALID); - } - /* If unusable or not found, same fallback to "secure" */ - if (tls_dane_notfound(state->ddane) - || tls_dane_unusable(state->ddane)) { + level = TLS_LEV_INVALID; + } else if (tls_dane_notfound(state->ddane) + || tls_dane_unusable(state->ddane)) { if (msg_verbose) msg_info("no %sTLSA records found, " "resorting to \"secure\"", diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c index 9d881903d..4f3fae10b 100644 --- a/postfix/src/smtp/smtp_tls_policy.c +++ b/postfix/src/smtp/smtp_tls_policy.c @@ -712,8 +712,6 @@ static int global_tls_level(void) static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) { TLS_DANE *dane; - int valid; - int mxvalid; if (!iter->port) { msg_warn("%s: the \"dane\" security level is invalid for delivery via" @@ -764,20 +762,8 @@ static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter) * 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 (iter->mx && !iter->mx->dnssec_valid) { if (tls->level == TLS_LEV_DANE) { tls->level = TLS_LEV_MAY; if (msg_verbose) @@ -789,8 +775,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(iter->rr->qname, valid ? iter->rr->rname : 0, - "tcp", iter->port)) == 0) { + if ((dane = tls_dane_resolve(iter->port, "tcp", iter->rr, + var_smtp_tls_force_tlsa)) == 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)); diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 9d1fde2f6..cd4c2fcd2 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -90,6 +90,7 @@ extern const NAME_CODE tls_level_table[]; #include #include #include +#include /* * Names of valid tlsmgr(8) session caches. @@ -173,8 +174,7 @@ 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 *, const char *, - unsigned); +extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int); extern int tls_dane_load_trustfile(TLS_DANE *, const char *); /* diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c index 640cb47bf..4fb62ae31 100644 --- a/postfix/src/tls/tls_dane.c +++ b/postfix/src/tls/tls_dane.c @@ -41,11 +41,11 @@ /* SSL_CTX *ssl_ctx; /* TLS_SESS_STATE *TLScontext; /* -/* TLS_DANE *tls_dane_resolve(qname, rname, proto, port) -/* const char *qname; -/* const char *rname; -/* const char *proto; +/* TLS_DANE *tls_dane_resolve(port, proto, hostrr, forcetlsa) /* unsigned port; +/* const char *proto; +/* DNS_RR *hostrr; +/* int forcetlsa; /* /* int tls_dane_unusable(dane) /* const TLS_DANE *dane; @@ -96,7 +96,7 @@ /* anchors always override the legacy public CA PKI. Otherwise, the /* callback MUST be cleared. /* -/* tls_dane_resolve() maps a (qname, rname, protocol, port) tuple to a +/* tls_dane_resolve() maps a (port, protocol, hostrr) 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 @@ -117,14 +117,17 @@ /* .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 qname -/* FQDN of target service (original input form). -/* .IP rname -/* DNSSEC validated (cname resolved) FQDN of target service. -/* .IP proto -/* Almost certainly "tcp". /* .IP port /* The TCP port in network byte order. +/* .IP proto +/* Almost certainly "tcp". +/* .IP hostrr +/* DNS_RR pointer to TLSA base domain data. When dnssec_valid is false, +/* the rname (and the qname if same as rname) are insecure. +/* .IP forcetlsa +/* When true, TLSA lookups are performed even when the qname and rname +/* are insecure. This is only useful in the unlikely case that DLV is +/* used to secure the TLSA RRset in an otherwise insecure zone. /* .IP flags /* Only one flag is part of the public interface at this time: /* .IP TLScontext @@ -314,7 +317,7 @@ static EVP_PKEY *gencakey(void) #ifdef WRAP_SIGNED EC_KEY *eckey; - EC_GROUP *group; + EC_GROUP *group = 0; ERR_clear_error(); @@ -676,14 +679,14 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) for ( /* nop */ ; rr; rr = rr->next) { const char *mdalg = 0; char *digest; - int same = (strcasecmp(rr->rname, rr->qname) == 0); + int iscname = strcasecmp(rr->rname, rr->qname); uint8_t *ip = (uint8_t *) rr->data; X509 *x = 0; /* OpenSSL tries to re-use *x if x!=0 */ EVP_PKEY *k = 0; /* OpenSSL tries to re-use *k if k!=0 */ digest_info *di; -#define rcname(rr) (same ? "" : rr->qname) -#define rarrow(rr) (same ? "" : " -> ") +#define rcname(rr) (iscname ? rr->qname : "") +#define rarrow(rr) (iscname ? " -> " : "") if (rr->type != T_TLSA) msg_panic("unexpected non-TLSA RR type %u for %s%s%s", rr->type, @@ -946,6 +949,8 @@ static TLS_DANE *resolve_host(const char *host, const char *proto, dane = (TLS_DANE *) ctable_refresh(dane_cache, STR(query_domain)); if (dane->base_domain == 0) dane->base_domain = mystrdup(host); + /* Increment ref-count of cached entry */ + ++dane->refs; return (dane); } @@ -953,10 +958,11 @@ static TLS_DANE *resolve_host(const char *host, const char *proto, /* tls_dane_resolve - cached map: (name, proto, port) -> TLS_DANE */ -TLS_DANE *tls_dane_resolve(const char *qname, const char *rname, - const char *proto, unsigned port) +TLS_DANE *tls_dane_resolve(unsigned port, const char *proto, DNS_RR *hostrr, + int forcetlsa) { TLS_DANE *dane = 0; + int iscname; #ifdef DANE_TLSA_SUPPORT if (!tls_dane_avail()) @@ -966,19 +972,32 @@ TLS_DANE *tls_dane_resolve(const char *qname, const char *rname, dane_cache = ctable_create(CACHE_SIZE, dane_lookup, dane_free, 0); /* - * 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. + * By default suppress TLSA lookups for non-DNSSEC + 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. */ - 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); + iscname = strcasecmp(hostrr->rname, hostrr->qname); + if (!forcetlsa && !hostrr->dnssec_valid && !iscname) { + dane = tls_dane_alloc(0); + dane->flags = TLS_DANE_FLAG_NORRS; + } else { + + /* + * 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. + */ + if (hostrr->dnssec_valid) + dane = resolve_host(hostrr->rname, proto, port); + if (!dane || (iscname && tls_dane_notfound(dane))) + dane = resolve_host(hostrr->qname, proto, port); + if (dane->flags & TLS_DANE_FLAG_ERROR) { + /* We don't return this object. */ + tls_dane_free(dane); + dane = 0; + } + } - ++dane->refs; #endif return (dane); } diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index e1e11cf4d..f962fe6be 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -555,7 +555,9 @@ const char *dict_changed_name(void) msg_warn("%s: table %s: null time stamp", myname, h->key); if (fstat(dict->stat_fd, &st) < 0) msg_fatal("%s: fstat: %m", myname); - if (st.st_mtime != dict->mtime || st.st_nlink == 0) + if (((dict->flags & DICT_FLAG_MULTI_WRITER) == 0 + && st.st_mtime != dict->mtime) + || st.st_nlink == 0) status = h->key; } myfree((char *) ht_info_list); @@ -590,7 +592,7 @@ static const NAME_MASK dict_mask[] = { "fold_mul", DICT_FLAG_FOLD_MUL, /* case-fold with multi-case key map */ "open_lock", DICT_FLAG_OPEN_LOCK, /* permanent lock upon open */ "bulk_update", DICT_FLAG_BULK_UPDATE, /* bulk update if supported */ - "world_read", DICT_FLAG_WORLD_READ, /* assume writer != reader */ + "multi_writer", DICT_FLAG_MULTI_WRITER, /* multi-writer safe */ 0, }; diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 2efb1f5b4..a217603e6 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -97,7 +97,7 @@ extern DICT *dict_debug(DICT *); #define DICT_FLAG_FOLD_ANY (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL) #define DICT_FLAG_OPEN_LOCK (1<<16) /* perm lock if not multi-writer safe */ #define DICT_FLAG_BULK_UPDATE (1<<17) /* optimize for bulk updates */ -#define DICT_FLAG_WORLD_READ (1<<18) /* assume writer != reader */ +#define DICT_FLAG_MULTI_WRITER (1<<18) /* multi-writer safe map */ /* IMPORTANT: Update the dict_mask[] table when the above changes */ @@ -124,7 +124,8 @@ extern DICT *dict_debug(DICT *); */ #define DICT_FLAG_PARANOID \ (DICT_FLAG_NO_REGSUB | DICT_FLAG_NO_PROXY | DICT_FLAG_NO_UNAUTH) -#define DICT_FLAG_IMPL_MASK (DICT_FLAG_FIXED | DICT_FLAG_PATTERN) +#define DICT_FLAG_IMPL_MASK (DICT_FLAG_FIXED | DICT_FLAG_PATTERN | \ + DICT_FLAG_MULTI_WRITER) #define DICT_FLAG_RQST_MASK (DICT_FLAG_FOLD_ANY | DICT_FLAG_LOCK | \ DICT_FLAG_DUP_REPLACE | DICT_FLAG_DUP_WARN | \ DICT_FLAG_DUP_IGNORE | DICT_FLAG_SYNC_UPDATE | \ diff --git a/postfix/src/util/dict_lmdb.c b/postfix/src/util/dict_lmdb.c index 5b079e374..0db22b937 100644 --- a/postfix/src/util/dict_lmdb.c +++ b/postfix/src/util/dict_lmdb.c @@ -591,16 +591,16 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags) * * As a workaround the postmap(1) and postalias(1) commands turn on * MDB_WRITEMAP which disables the use of malloc() in LMDB. However, that - * does not address several disclosures of stack memory. Other Postfix - * databases are maintained by Postfix daemon processes, and are - * accessible only by the postfix user. + * does not address several disclosures of stack memory. We don't enable + * this workaround for Postfix databases are maintained by Postfix daemon + * processes, because those are accessible only by the postfix user. * * LMDB 0.9.10 by default does not write uninitialized heap memory to file * (specify MDB_NOMEMINIT to revert that change). We use the MDB_WRITEMAP * workaround for older LMDB versions. */ #ifndef MDB_NOMEMINIT - if (dict_flags & DICT_FLAG_WORLD_READ) + if (dict_flags & DICT_FLAG_BULK_UPDATE) /* XXX Good enough */ mdb_flags |= MDB_WRITEMAP; #endif @@ -664,7 +664,9 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags) && st.st_mtime < time((time_t *) 0) - 100) msg_warn("database %s is older than source file %s", mdb_path, path); - dict_lmdb->dict.flags = dict_flags | DICT_FLAG_FIXED; +#define DICT_LMDB_IMPL_FLAGS (DICT_FLAG_FIXED | DICT_FLAG_MULTI_WRITER) + + dict_lmdb->dict.flags = dict_flags | DICT_LMDB_IMPL_FLAGS; if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0) dict_lmdb->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL); if (dict_flags & DICT_FLAG_FOLD_FIX) @@ -673,12 +675,6 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags) if (dict_flags & DICT_FLAG_BULK_UPDATE) dict_jmp_alloc(&dict_lmdb->dict); - /* LMDB is write-share safe; downgrade a persistent lock to temporary. */ - if (dict_flags & DICT_FLAG_OPEN_LOCK) { - dict_lmdb->dict.flags &= ~DICT_FLAG_OPEN_LOCK; - dict_lmdb->dict.flags |= DICT_FLAG_LOCK; - } - /* * The following requests return an error result only if we have serious * memory corruption problem. diff --git a/postfix/src/util/dict_open.c b/postfix/src/util/dict_open.c index d0a9bacff..c8706da4e 100644 --- a/postfix/src/util/dict_open.c +++ b/postfix/src/util/dict_open.c @@ -92,13 +92,17 @@ /* before writing, and acquire a shared lock before reading. /* Release the lock when the operation completes. /* .IP DICT_FLAG_OPEN_LOCK -/* With databases that are not multi-writer safe, request that -/* dict_open() acquires an exclusive lock, or that it terminates -/* with a fatal run-time error. +/* The behavior of this flag depends on whether a database +/* sets the DICT_FLAG_MULTI_WRITER flag to indicate that it +/* is multi-writer safe. /* -/* With databases that are multi-writer safe, downgrade from -/* DICT_FLAG_OPEN_LOCK (persistent lock) to DICT_FLAG_LOCK -/* (temporary lock). +/* With databases that are not multi-writer safe, dict_open() +/* acquires a persistent exclusive lock, or it terminates with +/* a fatal run-time error. +/* +/* With databases that are multi-writer safe, dict_open() +/* downgrades the DICT_FLAG_OPEN_LOCK flag (persistent lock) +/* to DICT_FLAG_LOCK (temporary lock). /* .IP DICT_FLAG_FOLD_FIX /* With databases whose lookup fields are fixed-case strings, /* fold the search string to lower case before accessing the @@ -131,9 +135,6 @@ /* Enable preliminary code for bulk-mode database updates. /* The caller must create an exception handler with dict_jmp_alloc() /* and must trap exceptions from the database client with dict_setjmp(). -/* .IP DICT_FLAG_WORLD_READ -/* Assume that the database file will be read by users other -/* than the writer. /* .IP DICT_FLAG_DEBUG /* Enable additional logging. /* .PP @@ -389,13 +390,18 @@ DICT *dict_open3(const char *dict_type, const char *dict_name, "cannot open %s:%s: %m", dict_type, dict_name)); if (msg_verbose) msg_info("%s: %s:%s", myname, dict_type, dict_name); - /* Write-share safe maps may downgrade a persistent lock to temporary. */ /* XXX The choice between wait-for-lock or no-wait is hard-coded. */ if (dict->flags & DICT_FLAG_OPEN_LOCK) { if (dict->flags & DICT_FLAG_LOCK) msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock", myname, dict_type, dict_name); - if (dict->lock(dict, MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0) + /* Multi-writer safe map: downgrade persistent lock to temporary. */ + if (dict->flags & DICT_FLAG_MULTI_WRITER) { + dict->flags &= ~DICT_FLAG_OPEN_LOCK; + dict->flags |= DICT_FLAG_LOCK; + } + /* Multi-writer unsafe map: acquire exclusive lock or bust. */ + else if (dict->lock(dict, MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0) msg_fatal("%s:%s: unable to get exclusive lock: %m", dict_type, dict_name); }