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.
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).
+ # 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
+
+ # 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
- # Disable automatic cache cleanup in all Postfix instances except
- # for one instance that will be responsible for cache cleanup.
# address_verify_cache_cleanup_interval = 0
- # Shared memory cache (requires Postfix 2.9 or later).
- # See memcache_table(5) for details.
+ # 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
- # Default setting for Postfix 2.6 and earlier.
- # This uses non-persistent storage only.
+ # 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,
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:
* postscreen(8) TLS configuration
* Blocking mail with postscreen(8)
* Turning off postscreen(8)
+ * Sharing the temporary whitelist
T\bTu\bur\brn\bni\bin\bng\bg o\bon\bn p\bpo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn(\b(8\b8)\b) w\bwi\bit\bth\bho\bou\but\bt b\bbl\blo\boc\bck\bki\bin\bng\bg m\bma\bai\bil\bl
6. Read the new configuration with "postfix reload".
+S\bSh\bha\bar\bri\bin\bng\bg t\bth\bhe\be t\bte\bem\bmp\bpo\bor\bra\bar\bry\by w\bwh\bhi\bit\bte\bel\bli\bis\bst\bt
+
+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.
+
H\bHi\bis\bst\bto\bor\bri\bic\bca\bal\bl n\bno\bot\bte\bes\bs a\ban\bnd\bd c\bcr\bre\bed\bdi\bit\bts\bs
Many ideas in postscreen(8) were explored in earlier work by Michael Tokarev,
<blockquote>
<pre>
+# Example 1: Default setting for Postfix 2.7 and later.
+# Note: avoid hash files here. Use btree instead.
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
- # Default setting for Postfix 2.7 and later.
- # Note: avoid hash files here. Use btree instead.
<a href="postconf.5.html#address_verify_map">address_verify_map</a> = btree:$<a href="postconf.5.html#data_directory">data_directory</a>/verify_cache
- # Shared persistent cache (requires Postfix 2.9 or later).
+# Example 2: Shared persistent <a href="LMDB_README.html">lmdb</a>: 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/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#address_verify_map">address_verify_map</a> = <a href="LMDB_README.html">lmdb</a>:$<a href="postconf.5.html#data_directory">data_directory</a>/verify_cache
+ # <a href="postconf.5.html#address_verify_cache_cleanup_interval">address_verify_cache_cleanup_interval</a> = 0
+
+# 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/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#address_verify_map">address_verify_map</a> = <a href="proxymap.8.html">proxy</a>:btree:$<a href="postconf.5.html#data_directory">data_directory</a>/verify_cache
- # Disable automatic cache cleanup in all Postfix instances except
- # for one instance that will be responsible for cache cleanup.
# <a href="postconf.5.html#address_verify_cache_cleanup_interval">address_verify_cache_cleanup_interval</a> = 0
- # Shared memory cache (requires Postfix 2.9 or later).
- # See <a href="memcache_table.5.html">memcache_table(5)</a> for details.
+# Example 4: Shared memory cache (requires Postfix 2.9 or later).
+# Disable automatic cache cleanup in all Postfix instances.
+# See <a href="memcache_table.5.html">memcache_table(5)</a> for details.
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#address_verify_map">address_verify_map</a> = <a href="memcache_table.5.html">memcache</a>:/etc/postfix/verify-memcache.cf
+ <a href="postconf.5.html#address_verify_cache_cleanup_interval">address_verify_cache_cleanup_interval</a> = 0
- # Default setting for Postfix 2.6 and earlier.
- # This uses non-persistent storage only.
+# Example 5: Default setting for Postfix 2.6 and earlier.
+# This uses non-persistent storage only.
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#address_verify_map">address_verify_map</a> =
</pre>
</blockquote>
temporary whitelist is not used for SMTP client addresses
that appear on the <i>permanent</i> access list. </p>
-<blockquote>
-
- <p> NOTE: To share a <a href="postscreen.8.html">postscreen(8)</a> cache between multiple
- <a href="postscreen.8.html">postscreen(8)</a> instances under the same <a href="master.8.html">master(8)</a> daemon, use
- "<tt><a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> =
- <a href="proxymap.8.html">proxy</a>:btree:$<a href="postconf.5.html#data_directory">data_directory</a>/postscreen_cache</tt>", and disable
- cache cleanup (<a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> = 0) in all
- <a href="postscreen.8.html">postscreen(8)</a> instances except one that is responsible for cache
- cleanup. </p>
-
- <p> <a href="postscreen.8.html">postscreen(8)</a> cache sharing requires Postfix 2.9 or later;
- earlier <a href="proxymap.8.html">proxymap(8)</a> implementations don't support cache cleanup.
- </p>
-
- <p> For an alternative <a href="postscreen.8.html">postscreen(8)</a> cache sharing approach,
- see the <a href="memcache_table.5.html">memcache_table(5)</a> manpage. </p>
-
-</blockquote>
+<p> By default the temporary whitelist is not shared with other
+postscreen(8) daemons. See <a href="#temp_white_sharing"> Sharing
+the temporary whitelist </a> below for alternatives. </p>
<p> When the SMTP client address appears on the temporary
whitelist, <a href="postscreen.8.html">postscreen(8)</a> logs this with the client address and port
<li> <a href="#turnoff"> Turning off postscreen(8) </a>
+<li> <a href="#temp_white_sharing"> Sharing the temporary whitelist
+</a>
+
</ul>
<h3> <a name="enable"> Turning on postscreen(8) without blocking mail</a> </h3>
</ol>
+<h3> <a name="temp_white_sharing"> Sharing the temporary whitelist </a> </h3>
+
+<p> By default, the temporary whitelist is not shared between
+multiple <a href="postscreen.8.html">postscreen(8)</a> daemons. To enable sharing, choose one
+of the following options: </p>
+
+<ul>
+
+<li> <p> A non-persistent <a href="memcache_table.5.html">memcache</a>: temporary whitelist can be shared
+ between <a href="postscreen.8.html">postscreen(8)</a> daemons on the same host or different
+ hosts. Disable cache cleanup (<a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a>
+ = 0) in all <a href="postscreen.8.html">postscreen(8)</a> daemons because <a href="memcache_table.5.html">memcache</a>: does not
+ implement this (but see example 4 below for <a href="memcache_table.5.html">memcache</a>: with
+ persistent backup). This requires Postfix 2.9 or later. </p>
+
+ <pre>
+ # Example 1: non-persistent <a href="memcache_table.5.html">memcache</a>: whitelist.
+ /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> = <a href="memcache_table.5.html">memcache</a>:/etc/postfix/postscreen_cache
+ <a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> = 0
+
+ /etc/postfix/postscreen_cache:
+ memcache = inet:127.0.0.1:11211
+ key_format = postscreen:%s
+ </pre>
+
+<li> <p> A persistent <a href="LMDB_README.html">lmdb</a>: temporary whitelist can be shared between
+ <a href="postscreen.8.html">postscreen(8)</a> daemons that run under the same <a href="master.8.html">master(8)</a> daemon,
+ or under different <a href="master.8.html">master(8)</a> daemons on the same host. Disable
+ cache cleanup (<a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> = 0) in all
+ <a href="postscreen.8.html">postscreen(8)</a> daemons except one that is responsible for cache
+ cleanup. This requires Postfix 2.11 or later. </p>
+
+ <pre>
+ # Example 2: persistent <a href="LMDB_README.html">lmdb</a>: whitelist.
+ /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> = <a href="LMDB_README.html">lmdb</a>:$<a href="postconf.5.html#data_directory">data_directory</a>/postscreen_cache
+ # See note 1 below.
+ # <a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> = 0
+ </pre>
+
+<li> <p> Other kinds of persistent temporary whitelist can be shared
+ only between <a href="postscreen.8.html">postscreen(8)</a> daemons that run under the same
+ <a href="master.8.html">master(8)</a> daemon. In this case, temporary whitelist access must
+ be shared through the <a href="proxymap.8.html">proxymap(8)</a> daemon. This requires Postfix
+ 2.9 or later. </p>
+
+ <pre>
+ # Example 3: proxied btree: whitelist.
+ /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> =
+ <a href="proxymap.8.html">proxy</a>:btree:/var/lib/postfix/postscreen_cache
+ # See note 1 below.
+ # <a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> = 0
+
+ # Example 4: proxied btree: whitelist with <a href="memcache_table.5.html">memcache</a>: accelerator.
+ /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> = <a href="memcache_table.5.html">memcache</a>:/etc/postfix/postscreen_cache
+ <a href="postconf.5.html#proxy_write_maps">proxy_write_maps</a> =
+ <a href="proxymap.8.html">proxy</a>:btree:/var/lib/postfix/postscreen_cache
+ ... other proxied tables ...
+ # See note 1 below.
+ # <a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> = 0
+
+ /etc/postfix/postscreen_cache:
+ # Note: the $<a href="postconf.5.html#data_directory">data_directory</a> macro is not defined in this context.
+ memcache = inet:127.0.0.1:11211
+ backup = <a href="proxymap.8.html">proxy</a>:btree:/var/lib/postfix/postscreen_cache
+ key_format = postscreen:%s
+ </pre>
+
+ <p> Note 1: disable cache cleanup (<a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a>
+ = 0) in all <a href="postscreen.8.html">postscreen(8)</a> daemons except one that is responsible
+ for cache cleanup. </p>
+
+ <p> Note 2: <a href="postscreen.8.html">postscreen(8)</a> cache sharing via <a href="proxymap.8.html">proxymap(8)</a> requires Postfix
+ 2.9 or later; earlier <a href="proxymap.8.html">proxymap(8)</a> implementations don't support
+ cache cleanup. </p>
+
+</ul>
+
<h2> <a name="historical"> Historical notes and credits </a> </h2>
<p> Many ideas in <a href="postscreen.8.html">postscreen(8)</a> were explored in earlier work by
tive ports can specified by appending "<i>:service-</i>
<i>name</i>" or ":<i>portnumber</i>" to the destination argument.
- <b>-t</b> <i>timeout</i> (default: <b>5</b>)
+ <b>-t</b> <i>timeout</i> (default: <b>30</b>)
The TCP connection timeout to use. This is also
the timeout for reading the remote server's 220
banner.
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)"
<blockquote>
<pre>
+# 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).
+# 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
+
+# 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
- # Disable automatic cache cleanup in all Postfix instances except
- # for one instance that will be responsible for cache cleanup.
# address_verify_cache_cleanup_interval = 0
- # Shared memory cache (requires Postfix 2.9 or later).
- # See memcache_table(5) for details.
+# 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
- # Default setting for Postfix 2.6 and earlier.
- # This uses non-persistent storage only.
+# Example 5: Default setting for Postfix 2.6 and earlier.
+# This uses non-persistent storage only.
+/etc/postfix/main.cf:
address_verify_map =
</pre>
</blockquote>
temporary whitelist is not used for SMTP client addresses
that appear on the <i>permanent</i> access list. </p>
-<blockquote>
-
- <p> NOTE: To share a postscreen(8) cache between multiple
- postscreen(8) instances under the same master(8) daemon, use
- "<tt>postscreen_cache_map =
- proxy:btree:$data_directory/postscreen_cache</tt>", and disable
- cache cleanup (postscreen_cache_cleanup_interval = 0) in all
- postscreen(8) instances except one that is responsible for cache
- cleanup. </p>
-
- <p> postscreen(8) cache sharing requires Postfix 2.9 or later;
- earlier proxymap(8) implementations don't support cache cleanup.
- </p>
-
- <p> For an alternative postscreen(8) cache sharing approach,
- see the memcache_table(5) manpage. </p>
-
-</blockquote>
+<p> By default the temporary whitelist is not shared with other
+postscreen(8) daemons. See <a href="#temp_white_sharing"> Sharing
+the temporary whitelist </a> below for alternatives. </p>
<p> When the SMTP client address appears on the temporary
whitelist, postscreen(8) logs this with the client address and port
<li> <a href="#turnoff"> Turning off postscreen(8) </a>
+<li> <a href="#temp_white_sharing"> Sharing the temporary whitelist
+</a>
+
</ul>
<h3> <a name="enable"> Turning on postscreen(8) without blocking mail</a> </h3>
</ol>
+<h3> <a name="temp_white_sharing"> Sharing the temporary whitelist </a> </h3>
+
+<p> By default, the temporary whitelist is not shared between
+multiple postscreen(8) daemons. To enable sharing, choose one
+of the following options: </p>
+
+<ul>
+
+<li> <p> 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. </p>
+
+ <pre>
+ # 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
+ </pre>
+
+<li> <p> 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. </p>
+
+ <pre>
+ # 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
+ </pre>
+
+<li> <p> 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. </p>
+
+ <pre>
+ # 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
+ </pre>
+
+ <p> Note 1: disable cache cleanup (postscreen_cache_cleanup_interval
+ = 0) in all postscreen(8) daemons except one that is responsible
+ for cache cleanup. </p>
+
+ <p> Note 2: postscreen(8) cache sharing via proxymap(8) requires Postfix
+ 2.9 or later; earlier proxymap(8) implementations don't support
+ cache cleanup. </p>
+
+</ul>
+
<h2> <a name="historical"> Historical notes and credits </a> </h2>
<p> Many ideas in postscreen(8) were explored in earlier work by
/* 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().
/* 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:
(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.
else
dict_mc->dict.flags |= DICT_FLAG_FIXED;
+ dict_mc->dict.flags |= DICT_FLAG_MULTI_WRITER;
+
return (&dict_mc->dict);
}
* 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
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);
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);
/* 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)"
*/
#include "tlsmgrmem.h"
-static int conn_tmout = 5;
+static int conn_tmout = 30;
static int smtp_tmout = 30;
#define HOST_FLAG_DNS (1<<0)
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
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\"",
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"
* 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)
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));
#include <vstream.h>
#include <name_mask.h>
#include <name_code.h>
+#include <dns.h>
/*
* Names of valid tlsmgr(8) session caches.
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 *);
/*
/* 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;
/* 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
/* .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
#ifdef WRAP_SIGNED
EC_KEY *eckey;
- EC_GROUP *group;
+ EC_GROUP *group = 0;
ERR_clear_error();
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,
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);
}
/* 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())
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);
}
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);
"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,
};
#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 */
*/
#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 | \
*
* 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
&& 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)
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.
/* 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
/* 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
"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);
}