-TDICT_NISPLUS
-TDICT_NODE
-TDICT_OPEN_INFO
+-TDICT_OWNER
-TDICT_PCRE
-TDICT_PCRE_ENGINE
-TDICT_PCRE_EXPAND_CONTEXT
master/mail_server.h master/multi_server.c master/single_server.c
master/trigger_server.c virtual/virtual.c postconf/extract.awk
postconf/postconf.c.
+
+20110220
+
+ Cleanup: compiler gripe. File: util/vstream.c.
+
+20110223
+
+ Cleanup: Debian build tool gripe. File: smtpstone/smtp-sink.c.
+
+20110224
+
+ postscreen(8) support to enforce proper client MX lookup
+ policy. Some spambots connect first to a backup MX address
+ in the hope that the server has a weaker anti-spam policy.
+ By listening on both primary and backup MX addresses,
+ postscreen(8) can deny the temporary whitelist status to
+ clients that connect only to backup MX hosts, and prevent
+ them from talking to a Postfix SMTP server process.
+
+ For example, when 1.2.3.4 is a local backup IP address,
+ specify "postscreen_whitelist_interfaces = !1.2.3.4 static:all"
+ to disable dynamic whitelisting for clients that connect
+ (only) to the backup MX address. Files: mantools/postlink,
+ proto/postconf.proto, proto/POSTSCREEN_README.html,
+ global/mail_params.h, postscreen/postscreen.c,
+ postscreen/postscreen.h, postscreen/postscreen_state.c.
+
+20110225
+
+ Workaround (problem introduced with IPv6 support in Postfix
+ 2.2): the SMTP client did not support mail to [ipv6:ipv6addr].
+ Fix based on a patch by Gurusamy Sarathy (Sophos). File:
+ util/host_port.c and regression test files.
+
+20110227
+
+ Portability: FreeBSD closefrom() support time window. Sahil
+ Tandon. File: util/sys_defs.h.
+
+ Cleanup: each lookup table now has an owner status and UID
+ attributes for provenance purposes, even memory-resident
+ tables such as pcre, regexp and cidr. This fixes a problem
+ where local(8) ignored the non-root ownership of a regular
+ expression-based aliases(5) file. The table owner status
+ is TRUSTED (data straight from root-owned configuration
+ file), UNKNOWN (unauthenticated data from proxy or tcp) or
+ KNOWN (we actually have an owner UID). With most tables,
+ the owner UID is the file owner UID. With LDAP and *SQL,
+ the owner UID is the Postfix configuration file owner.
+ Files: src/util/dict_unix.c src/util/dict_thash.c
+ src/util/dict_static.c src/util/dict_sdbm.c src/util/dict_regexp.c
+ src/util/dict_pcre.c src/util/dict_nisplus.c src/util/dict_nis.c
+ src/util/dict_ni.c src/util/dict_ht.c src/util/dict_env.c
+ src/util/dict_dbm.c src/util/dict_db.c src/util/dict_cidr.c
+ src/util/dict_cdb.c src/util/dict_alloc.c src/util/dict.h
+ src/util/dict.c src/local/alias.c src/global/dict_sqlite.c
+ src/global/dict_pgsql.c src/global/dict_mysql.c
+ src/global/dict_ldap.c src/global/cfg_parser.h
+ src/global/cfg_parser.c.
+
* Permanent white/blacklist test
* Temporary whitelist test
+ * MX Policy test
P\bPe\ber\brm\bma\ban\bne\ben\bnt\bt w\bwh\bhi\bit\bte\be/\b/b\bbl\bla\bac\bck\bkl\bli\bis\bst\bt t\bte\bes\bst\bt
addresses that have passed all the tests described below. The
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 blacklist or whitelist.
+appear on the permanent access list.
When the SMTP client address appears on the temporary whitelist, postscreen(8)
logs this with the client address and port number as:
its temporary whitelist entry expires, as controlled with the postscreen_*_ttl
parameters. Expired entries are silently renewed if possible.
+M\bMX\bX P\bPo\bol\bli\bic\bcy\by t\bte\bes\bst\bt
+
+When the remote SMTP client is not on the static access list or temporary
+whitelist, postscreen(8) can implement a number of whitelist tests before it
+grants the client a temporary whitelist status to talk to a Postfix SMTP server
+process.
+
+By listening on both primary and backup MX addresses, postscreen(8) can deny
+the temporary whitelist status to clients that connect only to backup MX hosts
+(an old trick to take advantage of backup MX hosts with weaker anti-spam
+policies).
+
+Note 1: The status of this feature is still experimental, and implementation
+details are likely to change.
+
+Note 2: MX policy enforcement is currently supported only for domains with one
+Postfix MTA. Support for domains with multiple Postfix MTAs will have to wait
+until Postfix has a database client that can update a shared postscreen(8)
+database.
+
+ * First, configure the host to listen on both primary and backup MX
+ addresses. Use the appropriate ifconfig command for the local operating
+ system, or update the appropriate configuration files and "refresh" the
+ network protocol stack.
+
+ * Then, configure postscreen(8) to deny the temporary whitelist status on the
+ backup MX address(es). An example for Wietse's server is:
+
+ /etc/postfix/main.cf:
+ postscreen_whitelist_interfaces = !168.100.189.8 static:all
+
+ Translation: allow clients to obtain the temporary whitelist status on all
+ server IP addresses except 168.100.189.8, which is a backup MX address.
+
+When a non-whitelisted client connects the backup MX address, postscreen(8)
+logs this with the client address and port number as:
+
+ C\bCO\bON\bNN\bNE\bEC\bCT\bT f\bfr\bro\bom\bm [address]:port t\bto\bo [\b[1\b16\b68\b8.\b.1\b10\b00\b0.\b.1\b18\b89\b9.\b.8\b8]\b]:\b:2\b25\b5
+ W\bWH\bHI\bIT\bTE\bEL\bLI\bIS\bST\bT V\bVE\bET\bTO\bO [address]:port
+
+Translation: the client at [address]:port connected to the backup MX address
+168.100.189.8 while it was not whitelisted. The client will not be granted the
+temporary whitelist status, even if passes all the whitelist tests described
+below.
+
T\bTe\bes\bst\bts\bs b\bbe\bef\bfo\bor\bre\be t\bth\bhe\be 2\b22\b20\b0 S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br g\bgr\bre\bee\bet\bti\bin\bng\bg
The postscreen_greet_wait parameter specifies a short time interval before the
<li> <a href="#temp_white"> Temporary whitelist test </a>
+<li> <a href="#white_veto"> MX Policy test </a>
+
</ul>
<h3> <a name="perm_white_black"> Permanent white/blacklist test </a> </h3>
the tests described below. The <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> parameter
specifies the location of the temporary whitelist. The
temporary whitelist is not used for SMTP client addresses
-that appear on the <i>permanent</i> blacklist or whitelist. </p>
+that appear on the <i>permanent</i> access list. </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
entry expires, as controlled with the postscreen_*_ttl
parameters. Expired entries are silently renewed if possible. </p>
+<h3> <a name="white_veto"> MX Policy test </a> </h3>
+
+<p> When the remote SMTP client is not on the static access list
+or temporary whitelist, <a href="postscreen.8.html">postscreen(8)</a> can implement a number of
+whitelist tests before it grants the client a temporary whitelist
+status to talk to a Postfix SMTP server process. </p>
+
+<p> By listening on both primary and backup MX addresses, <a href="postscreen.8.html">postscreen(8)</a>
+can deny the temporary whitelist status to clients that connect
+only to backup MX hosts (an old trick to take advantage of backup
+MX hosts with weaker anti-spam policies). </p>
+
+<p> Note 1: The status of this feature is still experimental, and
+implementation details are likely to change. </p>
+
+<p> Note 2: MX policy enforcement is currently supported only for
+domains with one Postfix MTA. Support for domains with multiple
+Postfix MTAs will have to wait until Postfix has a database client
+that can update a shared <a href="postscreen.8.html">postscreen(8)</a> database. </p>
+
+<ul>
+
+<li> <p> First, configure the host to listen on both primary and
+backup MX addresses. Use the appropriate <tt>ifconfig</tt> command
+for the local operating system, or update the appropriate configuration
+files and "refresh" the network protocol stack. </p>
+
+<li> <p> Then, configure <a href="postscreen.8.html">postscreen(8)</a> to deny the temporary whitelist
+status on the backup MX address(es). An example for Wietse's
+server is: </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#postscreen_whitelist_interfaces">postscreen_whitelist_interfaces</a> = !168.100.189.8 <a href="DATABASE_README.html#types">static</a>:all
+</pre>
+
+<p> Translation: allow clients to obtain the temporary whitelist
+status on all server IP addresses except 168.100.189.8, which is a
+backup MX address. </p>
+
+</ul>
+
+<p> When a non-whitelisted client connects the backup MX address,
+<a href="postscreen.8.html">postscreen(8)</a> logs this with the client address and port number as:
+</p>
+
+<pre>
+ <b>CONNECT from</b> <i>[address]:port</i> <b>to [168.100.189.8]:25</b>
+ <b>WHITELIST VETO</b> <i>[address]:port</i>
+</pre>
+
+<p> Translation: the client at <i>[address]:port</i> connected to
+the backup MX address 168.100.189.8 while it was not whitelisted.
+The client will not be granted the temporary whitelist status, even
+if passes all the whitelist tests described below. </p>
+
<h2> <a name="before_220"> Tests before the 220 SMTP server greeting </a> </h2>
<p> The <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> parameter specifies a short time
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
- <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> = texthash:/etc/postfix/dnsbl_reply
+ <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> = <a href="DATABASE_README.html#types">texthash</a>:/etc/postfix/dnsbl_reply
</pre>
<pre>
secret.zen.spamhaus.org zen.spamhaus.org
</pre>
-<p> The texthash: format is similar to hash: except that there is
+<p> The <a href="DATABASE_README.html#types">texthash</a>: format is similar to hash: except that there is
no need to run <a href="postmap.1.html">postmap(1)</a> before the file can be used, and that it
does not detect changes after the file is read. It is new with
Postfix version 2.8. </p>
<b>hosts</b> The hosts that Postfix will try to connect to and
query from. Specify <i>unix:</i> for UNIX-domain sockets,
<i>inet:</i> for TCP connections (default). Example:
- hosts = host1.some.domain host2.some.domain
+ hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name
The hosts are tried in random order, with all con-
</ul>
<p> To get the behavior before Postfix version 2.2, specify
-"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all". </p>
<p>
Example:
</ul>
<p> To get the behavior before Postfix version 2.2, specify
-"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all". </p>
</DD>
</ul>
<p> To get the behavior before Postfix version 2.2, specify
-"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all". </p>
</DD>
</DD>
<DT><b><a name="authorized_flush_users">authorized_flush_users</a>
-(default: static:anyone)</b></DT><DD>
+(default: <a href="DATABASE_README.html#types">static</a>:anyone)</b></DT><DD>
<p>
List of users who are authorized to flush the queue.
</DD>
<DT><b><a name="authorized_mailq_users">authorized_mailq_users</a>
-(default: static:anyone)</b></DT><DD>
+(default: <a href="DATABASE_README.html#types">static</a>:anyone)</b></DT><DD>
<p>
List of users who are authorized to view the queue.
</DD>
<DT><b><a name="authorized_submit_users">authorized_submit_users</a>
-(default: static:anyone)</b></DT><DD>
+(default: <a href="DATABASE_README.html#types">static</a>:anyone)</b></DT><DD>
<p>
List of users who are authorized to submit mail with the <a href="sendmail.1.html">sendmail(1)</a>
</p>
<pre>
-<a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> = !www, static:all
+<a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> = !www, <a href="DATABASE_README.html#types">static</a>:all
</pre>
<p>
</ul>
<p> To get the behavior before Postfix version 2.2, specify
-"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all". </p>
<p>
Examples:
<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a>
configuration parameter. See there for details. </p>
-<p> This feature is available in Postfix 2.8 and later. </p>
+<p> This feature is available in Postfix 2.9 and later. </p>
</DD>
<blockquote>
<pre>
-<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all
</pre>
</blockquote>
</ul>
<p> To get the behavior before Postfix version 2.2, specify
-"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all". </p>
<p>
Example:
domain will be used. </p>
<p> For maximal stability it is best to use a file that is read
-into memory such as <a href="pcre_table.5.html">pcre</a>:, <a href="regexp_table.5.html">regexp</a>: or texthash: (texthash: is similar
+into memory such as <a href="pcre_table.5.html">pcre</a>:, <a href="regexp_table.5.html">regexp</a>: or <a href="DATABASE_README.html#types">texthash</a>: (<a href="DATABASE_README.html#types">texthash</a>: is similar
to hash:, except a) there is no need to run <a href="postmap.1.html">postmap(1)</a> before the
-file can be used, and b) texthash: does not detect changes after
+file can be used, and b) <a href="DATABASE_README.html#types">texthash</a>: does not detect changes after
the file is read). </p>
<p> Example: </p>
<pre>
/etc/postfix/<a href="postconf.5.html">main.cf</a>:
- <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> = texthash:/etc/postfix/dnsbl_reply
+ <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> = <a href="DATABASE_README.html#types">texthash</a>:/etc/postfix/dnsbl_reply
</pre>
<pre>
<p> This feature is available in Postfix 2.8. </p>
+</DD>
+
+<DT><b><a name="postscreen_whitelist_interfaces">postscreen_whitelist_interfaces</a>
+(default: <a href="DATABASE_README.html#types">static</a>:all)</b></DT><DD>
+
+<p> A list of local <a href="postscreen.8.html">postscreen(8)</a> server IP addresses where a
+non-whitelisted SMTP client can obtain <a href="postscreen.8.html">postscreen(8)</a>'s temporary
+whitelist status to talk to a Postfix SMTP server process. By
+default, a client can pass <a href="postscreen.8.html">postscreen(8)</a>'s whitelist tests on any
+local <a href="postscreen.8.html">postscreen(8)</a> server IP address. </p>
+
+<p> When <a href="postscreen.8.html">postscreen(8)</a> listens on both primary and backup MX
+addresses, the <a href="postconf.5.html#postscreen_whitelist_interfaces">postscreen_whitelist_interfaces</a> parameter can be
+used to disable whitelisting on backup MX addresses. With this
+configuration, <a href="postscreen.8.html">postscreen(8)</a> denies whitelisting status to clients
+that connect only to backup MX addresses, and prevents them from
+talking to a Postfix SMTP server process. </p>
+
+<p> Example: </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ # Don't whitelist connections to the backup IP address.
+ <a href="postconf.5.html#postscreen_whitelist_interfaces">postscreen_whitelist_interfaces</a> = !168.100.189.8, <a href="DATABASE_README.html#types">static</a>:all
+</pre>
+
+<p> This feature is available in Postfix 2.9 and later. </p>
+
+
</DD>
<DT><b><a name="prepend_delivered_header">prepend_delivered_header</a>
<pre>
<a href="postconf.5.html#smtp_sasl_mechanism_filter">smtp_sasl_mechanism_filter</a> = plain, login
<a href="postconf.5.html#smtp_sasl_mechanism_filter">smtp_sasl_mechanism_filter</a> = /etc/postfix/smtp_mechs
-<a href="postconf.5.html#smtp_sasl_mechanism_filter">smtp_sasl_mechanism_filter</a> = !gssapi, !login, static:rest
+<a href="postconf.5.html#smtp_sasl_mechanism_filter">smtp_sasl_mechanism_filter</a> = !gssapi, !login, <a href="DATABASE_README.html#types">static</a>:rest
</pre>
</ul>
<p> To get the behavior before Postfix version 2.2, specify
-"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = <a href="DATABASE_README.html#types">static</a>:all". </p>
<p>
Example:
client is permanently blacklisted with the
<a href="postconf.5.html#postscreen_access_list">postscreen_access_list</a> parameter.
+<b>MAIL EXCHANGER POLICY TESTS</b>
+ When a remote SMTP client is not on the permanent access
+ list, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> can implement a number of whitelist
+ tests before it grants the client a temporary whitelist
+ status to talk to a Postfix SMTP server process.
+
+ By listening on both primary and backup MX addresses,
+ <a href="postscreen.8.html"><b>postscreen</b>(8)</a> can deny the temporary whitelist status to
+ clients that connect only to backup MX hosts.
+
+ <b><a href="postconf.5.html#postscreen_whitelist_interfaces">postscreen_whitelist_interfaces</a> (<a href="DATABASE_README.html#types">static</a>:all)</b>
+ A list of local <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server IP addresses
+ where a non-whitelisted SMTP client can obtain
+ <a href="postscreen.8.html"><b>postscreen</b>(8)</a>'s temporary whitelist status to talk
+ to a Postfix SMTP server process.
+
<b>BEFORE-GREETING TESTS</b>
These tests are executed before the remote SMTP client
receives the "220 servername" greeting. If no tests remain
Specify \fIunix:\fR for UNIX-domain sockets, \fIinet:\fR for TCP
connections (default). Example:
.nf
- hosts = host1.some.domain host2.some.domain
+ hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name
.fi
The LMTP-specific version of the smtp_per_record_deadline
configuration parameter. See there for details.
.PP
-This feature is available in Postfix 2.8 and later.
+This feature is available in Postfix 2.9 and later.
.SH lmtp_pix_workaround_delay_time (default: 10s)
The LMTP-specific version of the smtp_pix_workaround_delay_time
configuration parameter. See there for details.
(seconds), m (minutes), h (hours), d (days), w (weeks).
.PP
This feature is available in Postfix 2.8.
+.SH postscreen_whitelist_interfaces (default: static:all)
+A list of local \fBpostscreen\fR(8) server IP addresses where a
+non-whitelisted SMTP client can obtain \fBpostscreen\fR(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process. By
+default, a client can pass \fBpostscreen\fR(8)'s whitelist tests on any
+local \fBpostscreen\fR(8) server IP address.
+.PP
+When \fBpostscreen\fR(8) listens on both primary and backup MX
+addresses, the postscreen_whitelist_interfaces parameter can be
+used to disable whitelisting on backup MX addresses. With this
+configuration, \fBpostscreen\fR(8) denies whitelisting status to clients
+that connect only to backup MX addresses, and prevents them from
+talking to a Postfix SMTP server process.
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+ # Don't whitelist connections to the backup IP address.
+ postscreen_whitelist_interfaces = !168.100.189.8, static:all
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.9 and later.
.SH prepend_delivered_header (default: command, file, forward)
The message delivery contexts where the Postfix \fBlocal\fR(8) delivery
agent prepends a Delivered-To: message header with the address
.IP "\fBpostscreen_blacklist_action (ignore)\fR"
The action that \fBpostscreen\fR(8) takes when an SMTP client is
permanently blacklisted with the postscreen_access_list parameter.
+.SH "MAIL EXCHANGER POLICY TESTS"
+.na
+.nf
+.ad
+.fi
+When a remote SMTP client is not on the permanent access
+list, \fBpostscreen\fR(8) can implement a number of whitelist
+tests before it grants the client a temporary whitelist
+status to talk to a Postfix SMTP server process.
+
+By listening on both primary and backup MX addresses,
+\fBpostscreen\fR(8) can deny the temporary whitelist status
+to clients that connect only to backup MX hosts.
+.IP "\fBpostscreen_whitelist_interfaces (static:all)\fR"
+A list of local \fBpostscreen\fR(8) server IP addresses where a
+non-whitelisted SMTP client can obtain \fBpostscreen\fR(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process.
.SH "BEFORE-GREETING TESTS"
.na
.nf
s;\bpostscreen_expansion_filter\b;<a href="postconf.5.html#postscreen_expansion_filter">$&</a>;g;
s;\bpostscreen_reject_footer\b;<a href="postconf.5.html#postscreen_reject_footer">$&</a>;g;
s;\bpostscreen_command_filter\b;<a href="postconf.5.html#postscreen_command_filter">$&</a>;g;
+ s;\bpostscreen_whitelist_interfaces\b;<a href="postconf.5.html#postscreen_whitelist_interfaces">$&</a>;g;
s;\btlsproxy_watchdog_timeout\b;<a href="postconf.5.html#tlsproxy_watchdog_timeout">$&</a>;g;
s;\btlsproxy_enforce_tls\b;<a href="postconf.5.html#tlsproxy_enforce_tls">$&</a>;g;
s/\b(ldap):/<a href="ldap_table.5.html">$1<\/a>:/g;
s/\b(regexp):/<a href="regexp_table.5.html">$1<\/a>:/g;
s/\b(sqlite):/<a href="sqlite_table.5.html">$1<\/a>:/g;
+ s/\b(static):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
s/\b(tcp):/<a href="tcp_table.5.html">$1<\/a>:/g;
+ s/\b(texthash):/<a href="DATABASE_README.html#types">$1<\/a>:/g;
# Do nice links for smtp:host:port etc.
<li> <a href="#temp_white"> Temporary whitelist test </a>
+<li> <a href="#white_veto"> MX Policy test </a>
+
</ul>
<h3> <a name="perm_white_black"> Permanent white/blacklist test </a> </h3>
the tests described below. The 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 <i>permanent</i> blacklist or whitelist. </p>
+that appear on the <i>permanent</i> access list. </p>
<p> When the SMTP client address appears on the temporary
whitelist, postscreen(8) logs this with the client address and port
entry expires, as controlled with the postscreen_*_ttl
parameters. Expired entries are silently renewed if possible. </p>
+<h3> <a name="white_veto"> MX Policy test </a> </h3>
+
+<p> When the remote SMTP client is not on the static access list
+or temporary whitelist, postscreen(8) can implement a number of
+whitelist tests before it grants the client a temporary whitelist
+status to talk to a Postfix SMTP server process. </p>
+
+<p> By listening on both primary and backup MX addresses, postscreen(8)
+can deny the temporary whitelist status to clients that connect
+only to backup MX hosts (an old trick to take advantage of backup
+MX hosts with weaker anti-spam policies). </p>
+
+<p> Note 1: The status of this feature is still experimental, and
+implementation details are likely to change. </p>
+
+<p> Note 2: MX policy enforcement is currently supported only for
+domains with one Postfix MTA. Support for domains with multiple
+Postfix MTAs will have to wait until Postfix has a database client
+that can update a shared postscreen(8) database. </p>
+
+<ul>
+
+<li> <p> First, configure the host to listen on both primary and
+backup MX addresses. Use the appropriate <tt>ifconfig</tt> command
+for the local operating system, or update the appropriate configuration
+files and "refresh" the network protocol stack. </p>
+
+<li> <p> Then, configure postscreen(8) to deny the temporary whitelist
+status on the backup MX address(es). An example for Wietse's
+server is: </p>
+
+<pre>
+/etc/postfix/main.cf:
+ postscreen_whitelist_interfaces = !168.100.189.8 static:all
+</pre>
+
+<p> Translation: allow clients to obtain the temporary whitelist
+status on all server IP addresses except 168.100.189.8, which is a
+backup MX address. </p>
+
+</ul>
+
+<p> When a non-whitelisted client connects the backup MX address,
+postscreen(8) logs this with the client address and port number as:
+</p>
+
+<pre>
+ <b>CONNECT from</b> <i>[address]:port</i> <b>to [168.100.189.8]:25</b>
+ <b>WHITELIST VETO</b> <i>[address]:port</i>
+</pre>
+
+<p> Translation: the client at <i>[address]:port</i> connected to
+the backup MX address 168.100.189.8 while it was not whitelisted.
+The client will not be granted the temporary whitelist status, even
+if passes all the whitelist tests described below. </p>
+
<h2> <a name="before_220"> Tests before the 220 SMTP server greeting </a> </h2>
<p> The postscreen_greet_wait parameter specifies a short time
# Specify \fIunix:\fR for UNIX-domain sockets, \fIinet:\fR for TCP
# connections (default). Example:
# .nf
-# hosts = host1.some.domain host2.some.domain
+# hosts = host1.some.domain host2.some.domain:port
# hosts = unix:/file/name
# .fi
#
<p> The LMTP-specific version of the smtp_per_record_deadline
configuration parameter. See there for details. </p>
-<p> This feature is available in Postfix 2.8 and later. </p>
+<p> This feature is available in Postfix 2.9 and later. </p>
+
+%PARAM postscreen_whitelist_interfaces static:all
+
+<p> A list of local postscreen(8) server IP addresses where a
+non-whitelisted SMTP client can obtain postscreen(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process. By
+default, a client can pass postscreen(8)'s whitelist tests on any
+local postscreen(8) server IP address. </p>
+
+<p> When postscreen(8) listens on both primary and backup MX
+addresses, the postscreen_whitelist_interfaces parameter can be
+used to disable whitelisting on backup MX addresses. With this
+configuration, postscreen(8) denies whitelisting status to clients
+that connect only to backup MX addresses, and prevents them from
+talking to a Postfix SMTP server process. </p>
+
+<p> Example: </p>
+
+<pre>
+/etc/postfix/main.cf:
+ # Don't whitelist connections to the backup IP address.
+ postscreen_whitelist_interfaces = !168.100.189.8, static:all
+</pre>
+
+<p> This feature is available in Postfix 2.9 and later. </p>
/* const CFG_PARSER *parser;
/* const char *name;
/* int defval;
+/*
+/* DICT_OWNER cfg_get_owner(parser)
+/* const CFG_PARSER *parser;
/* DESCRIPTION
/* This module implements utilities for parsing parameters defined
/* either as "\fIname\fR = \fBvalue\fR" in a file pointed to by
/* Conveniently, \fIcfg_get_str\fR returns \fBNULL\fR if
/* \fIdefval\fR is \fBNULL\fR and no value was found. The returned
/* string has to be freed by the caller if not \fBNULL\fR.
+/*
+/* cfg_get_owner() looks up the configuration file owner.
/* DIAGNOSTICS
/* Fatal errors: bad string length, malformed numerical value, malformed
/* boolean value.
{
const char *myname = "cfg_parser_alloc";
CFG_PARSER *parser;
+ DICT *dict;
if (pname == 0 || *pname == 0)
msg_fatal("%s: null parser name", myname);
parser->get_str = get_dict_str;
parser->get_int = get_dict_int;
parser->get_bool = get_dict_bool;
+ dict = dict_handle(parser->name);
} else {
parser->get_str = get_main_str;
parser->get_int = get_main_int;
parser->get_bool = get_main_bool;
+ dict = dict_handle(CONFIG_DICT); /* XXX Use proper API */
}
+ if (dict == 0)
+ msg_panic("%s: dict_handle failed", myname);
+ parser->owner = dict->owner;
return (parser);
}
.nf
/*
- * External interface.
+ * Utility library.
*/
+#include <dict.h>
+ /*
+ * External interface.
+ */
typedef struct CFG_PARSER {
char *name;
char *(*get_str) (const struct CFG_PARSER *, const char *, const char *,
int, int);
int (*get_int) (const struct CFG_PARSER *, const char *, int, int, int);
int (*get_bool) (const struct CFG_PARSER *, const char *, int);
+ DICT_OWNER owner;
} CFG_PARSER;
extern CFG_PARSER *cfg_parser_alloc(const char *);
extern int cfg_get_bool(const CFG_PARSER *, const char *, int);
extern CFG_PARSER *cfg_parser_free(CFG_PARSER *);
+#define cfg_get_owner(cfg) ((cfg)->owner)
+
/* LICENSE
/* .ad
/* .fi
/*
* Return the new dict_ldap structure.
*/
+ dict_ldap->dict.owner = cfg_get_owner(dict_ldap->parser);
return (DICT_DEBUG (&dict_ldap->dict));
}
dict_mysql->pldb = plmysql_init(dict_mysql->hosts);
if (dict_mysql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
+ dict_mysql->dict.owner = cfg_get_owner(dict_mysql->parser);
return (DICT_DEBUG (&dict_mysql->dict));
}
dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
if (dict_pgsql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
- return &dict_pgsql->dict;
+ dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser);
+ return (DICT_DEBUG(&dict_pgsql->dict));
}
/* plpgsql_init - initalize a PGSQL database */
msg_fatal("%s:%s: Can't open database: %s\n",
DICT_TYPE_SQLITE, name, sqlite3_errmsg(dict_sqlite->db));
+ dict_sqlite->dict.owner = cfg_get_owner(dict_sqlite->parser);
+
return (DICT_DEBUG (&dict_sqlite->dict));
}
#define DEF_PSC_ACL PSC_ACL_NAME_WL_MYNETWORKS
extern char *var_psc_acl;
+#define VAR_PSC_WLIST_IF "postscreen_whitelist_interfaces"
+#define DEF_PSC_WLIST_IF "static:all"
+extern char *var_psc_wlist_if;
+
#define VAR_DNSBLOG_SERVICE "dnsblog_service_name"
#define DEF_DNSBLOG_SERVICE MAIL_SERVICE_DNSBLOG
extern char *var_dnsblog_service;
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20110219"
+#define MAIL_RELEASE_DATE "20110228"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
#define NO 0
#define YES 1
-/* dict_owner - find out alias database owner */
-
-static uid_t dict_owner(char *table)
-{
- const char *myname = "dict_owner";
- DICT *dict;
- struct stat st;
-
- /*
- * This code sits here for now, but we may want to move it to the library
- * some time.
- */
- if ((dict = dict_handle(table)) == 0)
- msg_panic("%s: can't find dictionary: %s", myname, table);
- if (dict->stat_fd < 0)
- return (0);
- if (fstat(dict->stat_fd, &st) < 0)
- msg_fatal("%s: fstat dictionary %s: %m", myname, table);
- return (st.st_uid);
-}
-
/* deliver_alias - expand alias file entry */
int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
char *saved_alias_result;
char *owner;
char **cpp;
- uid_t alias_uid;
struct mypasswd *alias_pwd;
VSTRING *canon_owner;
DICT *dict;
* database is owned by root, otherwise it will use the rights of
* the alias database owner.
*/
- if ((alias_uid = dict_owner(*cpp)) == 0) {
+ if (dict->owner.status == DICT_OWNER_TRUSTED) {
alias_pwd = 0;
RESET_USER_ATTR(usr_attr, state.level);
} else {
- if ((alias_pwd = mypwuid(alias_uid)) == 0) {
+ if (dict->owner.status == DICT_OWNER_UNKNOWN) {
+ msg_warn("%s: no owner UID for alias database %s",
+ myname, *cpp);
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ if ((alias_pwd = mypwuid(dict->owner.uid)) == 0) {
msg_warn("cannot find alias database owner for %s", *cpp);
dsb_simple(state.msg_attr.why, "4.3.0",
"cannot find alias database owner");
/* .IP "\fBpostscreen_blacklist_action (ignore)\fR"
/* The action that \fBpostscreen\fR(8) takes when an SMTP client is
/* permanently blacklisted with the postscreen_access_list parameter.
+/* MAIL EXCHANGER POLICY TESTS
+/* .ad
+/* .fi
+/* When a remote SMTP client is not on the permanent access
+/* list, \fBpostscreen\fR(8) can implement a number of whitelist
+/* tests before it grants the client a temporary whitelist
+/* status to talk to a Postfix SMTP server process.
+/*
+/* By listening on both primary and backup MX addresses,
+/* \fBpostscreen\fR(8) can deny the temporary whitelist status
+/* to clients that connect only to backup MX hosts.
+/* .IP "\fBpostscreen_whitelist_interfaces (static:all)\fR"
+/* A list of local \fBpostscreen\fR(8) server IP addresses where a
+/* non-whitelisted SMTP client can obtain \fBpostscreen\fR(8)'s temporary
+/* whitelist status to talk to a Postfix SMTP server process.
/* BEFORE-GREETING TESTS
/* .ad
/* .fi
char *var_smtpd_exp_filter;
char *var_psc_exp_filter;
+char *var_psc_wlist_if;
+
/*
* Global variables.
*/
*/
static ARGV *psc_acl; /* permanent white/backlist */
static int psc_blist_action; /* PSC_ACT_DROP/ENFORCE/etc */
+static ADDR_MATCH_LIST *psc_wlist_if; /* whitelist interfaces */
/* psc_dump - dump some statistics before exit */
SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
MAI_HOSTADDR_STR smtp_client_addr;
MAI_SERVPORT_STR smtp_client_port;
+ MAI_HOSTADDR_STR smtp_server_addr;
+ MAI_SERVPORT_STR smtp_server_port;
int aierr;
const char *stamp_str;
int saved_flags;
* connections so we have to invoke getpeername() to find out the remote
* address and port.
*/
+
+ /* Best effort - if this non-blocking write(2) fails, so be it. */
#define PSC_SERVICE_DISCONNECT_AND_RETURN(stream) do { \
+ (void) write(vstream_fileno(stream), \
+ "421 4.3.2 No system resources\r\n", \
+ sizeof("421 4.3.2 No system resources\r\n") - 1); \
event_server_disconnect(stream); \
return; \
} while (0);
if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
& addr_storage, &addr_storage_len) < 0) {
msg_warn("getpeername: %m -- dropping this connection");
- /* Best effort - if this non-blocking write(2) fails, so be it. */
- (void) write(vstream_fileno(smtp_client_stream),
- "421 4.3.2 No system resources\r\n",
- sizeof("421 4.3.2 No system resources\r\n") - 1);
PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
}
msg_warn("cannot convert client address/port to string: %s"
" -- dropping this connection",
MAI_STRERROR(aierr));
- /* Best effort - if this non-blocking write(2) fails, so be it. */
- (void) write(vstream_fileno(smtp_client_stream),
- "421 4.3.2 No system resources\r\n",
- sizeof("421 4.3.2 No system resources\r\n") - 1);
PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
}
if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0)
myname, psc_post_queue_length, psc_check_queue_length,
smtp_client_addr.buf, smtp_client_port.buf);
- msg_info("CONNECT from [%s]:%s", smtp_client_addr.buf, smtp_client_port.buf);
+ /*
+ * Look up the local SMTP server address and port.
+ */
+ if (getsockname(vstream_fileno(smtp_client_stream), (struct sockaddr *)
+ & addr_storage, &addr_storage_len) < 0) {
+ msg_warn("getsockname: %m -- dropping this connection");
+ PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
+ }
+
+ /*
+ * Convert the local SMTP server address and port to printable form for
+ * logging and access control.
+ */
+ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage,
+ addr_storage_len, &smtp_server_addr,
+ &smtp_server_port, 0)) != 0) {
+ msg_warn("cannot convert server address/port to string: %s"
+ " -- dropping this connection",
+ MAI_STRERROR(aierr));
+ PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
+ }
+ if (strncasecmp("::ffff:", smtp_server_addr.buf, 7) == 0)
+ memmove(smtp_server_addr.buf, smtp_server_addr.buf + 7,
+ sizeof(smtp_server_addr.buf) - 7);
+
+ msg_info("CONNECT from [%s]:%s to [%s]:%s",
+ smtp_client_addr.buf, smtp_client_port.buf,
+ smtp_server_addr.buf, smtp_server_port.buf);
/*
* Bundle up all the loose session pieces. This zeroes all flags and time
myname, psc_print_state_flags(state->flags, myname));
}
+ /*
+ * Don't whitelist clients that connect to backup MX addresses.
+ */
+ if (addr_match_list_match(psc_wlist_if, smtp_server_addr.buf) == 0) {
+ state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD);
+ msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state));
+ }
+
/*
* Reply with 421 when we can't analyze more connections. That also means
* no deep protocol tests when the noforward flag is raised.
var_psc_barlf_action)) < 0)
msg_fatal("bad %s value: %s", VAR_PSC_BARLF_ACTION,
var_psc_barlf_action);
+ psc_wlist_if = addr_match_list_init(MATCH_FLAG_NONE, var_psc_wlist_if);
/*
* Start the cache maintenance pseudo thread last. Early cleanup makes
VAR_PSC_CMD_FILTER, DEF_PSC_CMD_FILTER, &var_psc_cmd_filter, 0, 0,
VAR_DNSBLOG_SERVICE, DEF_DNSBLOG_SERVICE, &var_dnsblog_service, 1, 0,
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
+ VAR_PSC_WLIST_IF, DEF_PSC_WLIST_IF, &var_psc_wlist_if, 0, 0,
0,
};
static const CONFIG_INT_TABLE int_table[] = {
#define PSC_STATE_FLAG_BLIST_FAIL (1<<4) /* blacklisted */
#define PSC_STATE_FLAG_HANGUP (1<<5) /* NOT a test failure */
#define PSC_STATE_FLAG_CACHE_EXPIRED (1<<6) /* cache retention expired */
+#define PSC_STATE_FLAG_WLIST_FAIL (1<<7) /* do not whitelist */
/*
* Important: every MUMBLE_TODO flag must have a MUMBLE_PASS flag, such that
*/
#define PSC_STATE_MASK_ANY_FAIL \
(PSC_STATE_FLAG_BLIST_FAIL | PSC_STATE_FLAG_PENAL_FAIL | \
- PSC_STATE_MASK_EARLY_FAIL | PSC_STATE_MASK_SMTPD_FAIL)
+ PSC_STATE_MASK_EARLY_FAIL | PSC_STATE_MASK_SMTPD_FAIL | \
+ PSC_STATE_FLAG_WLIST_FAIL)
#define PSC_STATE_MASK_ANY_PASS \
(PSC_STATE_MASK_EARLY_PASS | PSC_STATE_MASK_SMTPD_PASS)
"BLIST_FAIL", PSC_STATE_FLAG_BLIST_FAIL,
"HANGUP", PSC_STATE_FLAG_HANGUP,
"CACHE_EXPIRED", PSC_STATE_FLAG_CACHE_EXPIRED,
+ "WLIST_FAIL", PSC_STATE_FLAG_WLIST_FAIL,
"PENAL_UPDATE", PSC_STATE_FLAG_PENAL_UPDATE,
"PENAL_FAIL", PSC_STATE_FLAG_PENAL_FAIL,
static void hard_err_resp(SINK_STATE *state)
{
- smtp_printf(state->stream, hard_error_resp);
+ smtp_printf(state->stream, "%s", hard_error_resp);
smtp_flush(state->stream);
}
static void soft_err_resp(SINK_STATE *state)
{
- smtp_printf(state->stream, soft_error_resp);
+ smtp_printf(state->stream, "%s", soft_error_resp);
smtp_flush(state->stream);
}
{
if (enable_lmtp) {
while (state->rcpts-- > 0) /* XXX this could block */
- smtp_printf(state->stream, hard_error_resp);
+ smtp_printf(state->stream, "%s", hard_error_resp);
} else {
- smtp_printf(state->stream, hard_error_resp);
+ smtp_printf(state->stream, "%s", hard_error_resp);
}
smtp_flush(state->stream);
}
{
if (enable_lmtp) {
while (state->rcpts-- > 0) /* XXX this could block */
- smtp_printf(state->stream, soft_error_resp);
+ smtp_printf(state->stream, "%s", soft_error_resp);
} else {
- smtp_printf(state->stream, soft_error_resp);
+ smtp_printf(state->stream, "%s", soft_error_resp);
}
smtp_flush(state->stream);
}
char *val;
int lineno;
const char *err;
+ struct stat st;
buf = vstring_alloc(100);
lineno = 0;
+ if (fstat(vstream_fileno(fp), &st) < 0)
+ msg_fatal("fstat %s: %m", VSTREAM_PATH(fp));
while (readlline(buf, fp, &lineno)) {
if ((err = split_nameval(STR(buf), &member, &val)) != 0)
msg_fatal("%s, line %d: %s: \"%s\"",
dict_update(dict_name, member, val);
}
vstring_free(buf);
+ dict_handle(dict_name)->owner.uid = st.st_uid;
+ dict_handle(dict_name)->owner.status = (st.st_uid != 0);
}
/* dict_eval_lookup - macro parser call-back routine */
#include <argv.h>
#include <vstring.h>
+ /*
+ * Provenance information.
+ */
+typedef struct DICT_OWNER {
+ int status; /* see below */
+ uid_t uid; /* use only if status == UNTRUSTED */
+} DICT_OWNER;
+
+#define DICT_OWNER_UNKNOWN (-1) /* ex: unauthenticated tcp, proxy */
+#define DICT_OWNER_TRUSTED (!1) /* ex: root-owned config file */
+#define DICT_OWNER_UNTRUSTED (!0) /* ex: non-root config file */
+
/*
* Generic dictionary interface - in reality, a dictionary extends this
* structure with private members to maintain internal state.
int stat_fd; /* change detection */
time_t mtime; /* mod time at open */
VSTRING *fold_buf; /* key folding buffer */
+ DICT_OWNER owner; /* provenance */
} DICT;
extern DICT *dict_alloc(const char *, const char *, ssize_t);
dict->stat_fd = -1;
dict->mtime = 0;
dict->fold_buf = 0;
+ dict->owner.status = DICT_OWNER_UNKNOWN;
+ dict->owner.uid = ~0;
return dict;
}
if (fstat(fd, &st) < 0)
msg_fatal("dict_dbq_open: fstat: %m");
dict_cdbq->dict.mtime = st.st_mtime;
+ dict_cdbq->dict.owner.uid = st.st_uid;
+ dict_cdbq->dict.owner.status = (st.st_uid != 0);
close_on_exec(fd, CLOSE_ON_EXEC);
/*
dict_cdbm->dict.update = dict_cdbm_update;
dict_cdbm->cdb_path = cdb_path;
dict_cdbm->tmp_path = tmp_path;
+ dict_cdbm->dict.owner.uid = st1.st_uid;
+ dict_cdbm->dict.owner.status = (st1.st_uid != 0);
close_on_exec(fd, CLOSE_ON_EXEC);
/*
/* System library. */
#include <sys_defs.h>
+#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
{
DICT_CIDR *dict_cidr;
VSTREAM *map_fp;
+ struct stat st;
VSTRING *line_buffer = vstring_alloc(100);
VSTRING *why = vstring_alloc(100);
DICT_CIDR_ENTRY *rule;
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+ dict_cidr->dict.owner.uid = st.st_uid;
+ dict_cidr->dict.owner.status = (st.st_uid != 0);
while (readlline(line_buffer, map_fp, &lineno)) {
rule = dict_cidr_parse_rule(vstring_str(line_buffer), why);
if (fstat(dict_db->dict.stat_fd, &st) < 0)
msg_fatal("dict_db_open: fstat: %m");
dict_db->dict.mtime = st.st_mtime;
+ dict_db->dict.owner.uid = st.st_uid;
+ dict_db->dict.owner.status = (st.st_uid != 0);
/*
* Warn if the source file is newer than the indexed file, except when
vstring_strcpy(dict->fold_buf, name);
name = lowercase(vstring_str(dict->fold_buf));
}
-
dbm_key.dptr = (void *) name;
dbm_value.dptr = (void *) value;
dbm_key.dsize = strlen(name);
if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_dbm_open: fstat: %m");
dict_dbm->dict.mtime = st.st_mtime;
+ dict_dbm->dict.owner.uid = st.st_uid;
+ dict_dbm->dict.owner.status = (st.st_uid != 0);
/*
* Warn if the source file is newer than the indexed file, except when
dict->flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict->fold_buf = vstring_alloc(10);
+ dict->owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (dict));
}
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_ht->dict.fold_buf = vstring_alloc(10);
dict_ht->table = htable_create(0);
+ dict_ht->dict.owner.status = DICT_OWNER_TRUSTED;
return (&dict_ht->dict);
}
d->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
d->dict.fold_buf = vstring_alloc(10);
+ d->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&d->dict));
}
dict_nis->dict.fold_buf = vstring_alloc(10);
if (dict_nis_domain == 0)
dict_nis_init();
+ dict_nis->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&dict_nis->dict));
}
dict_nisplus->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_nisplus->dict.fold_buf = vstring_alloc(10);
+ dict_nisplus->dict.owner.status = DICT_OWNER_TRUSTED;
/*
* Convert the query template into an indexed name and column number. The
/* System library. */
+#include <sys/stat.h>
#include <stdio.h> /* sprintf() prototype */
#include <stdlib.h>
#include <unistd.h>
{
DICT_PCRE *dict_pcre;
VSTREAM *map_fp;
+ struct stat st;
VSTRING *line_buffer;
DICT_PCRE_RULE *last_rule = 0;
DICT_PCRE_RULE *rule;
*/
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+ dict_pcre->dict.owner.uid = st.st_uid;
+ dict_pcre->dict.owner.status = (st.st_uid != 0);
while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
#ifdef HAS_POSIX_REGEXP
+#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
{
DICT_REGEXP *dict_regexp;
VSTREAM *map_fp;
+ struct stat st;
VSTRING *line_buffer;
DICT_REGEXP_RULE *rule;
DICT_REGEXP_RULE *last_rule = 0;
*/
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+ dict_regexp->dict.owner.uid = st.st_uid;
+ dict_regexp->dict.owner.status = (st.st_uid != 0);
while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_sdbm_open: fstat: %m");
dict_sdbm->dict.mtime = st.st_mtime;
+ dict_sdbm->dict.owner.uid = st.st_uid;
+ dict_sdbm->dict.owner.status = (st.st_uid != 0);
/*
* Warn if the source file is newer than the indexed file, except when
dict->lookup = dict_static_lookup;
dict->close = dict_static_close;
dict->flags = dict_flags | DICT_FLAG_FIXED;
+ dict->owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (dict));
}
doze(300000);
}
vstring_free(line_buffer);
+ dict_thash->dict.owner.uid = st.st_uid;
+ dict_thash->dict.owner.status = (st.st_uid != 0);
return (DICT_DEBUG (&dict_thash->dict));
}
dict_unix->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_unix->dict.fold_buf = vstring_alloc(10);
+ dict_unix->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&dict_unix->dict));
}
#include <host_port.h>
+ /*
+ * Point-fix workaround. The libutil library should be email agnostic, but
+ * we can't rip up the library APIs in the stable releases.
+ */
+#include <string.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+#define IPV6_COL "IPv6:" /* RFC 2821 */
+#define IPV6_COL_LEN (sizeof(IPV6_COL) - 1)
+#define HAS_IPV6_COL(str) (strncasecmp((str), IPV6_COL, IPV6_COL_LEN) == 0)
+
/* host_port - parse string into host and port, destroy string */
const char *host_port(char *buf, char **host, char *def_host,
char **port, char *def_service)
{
char *cp = buf;
+ int ipv6 = 0;
/*
* [host]:port, [host]:, [host].
+ * [ipv6:ipv6addr]:port, [ipv6:ipv6addr]:, [ipv6:ipv6addr].
*/
if (*cp == '[') {
- *host = ++cp;
+ ++cp;
+ if ((ipv6 = HAS_IPV6_COL(cp)) != 0)
+ cp += IPV6_COL_LEN;
+ *host = cp;
if ((cp = split_at(cp, ']')) == 0)
return ("missing \"]\"");
if (*cp && *cp++ != ':')
return ("garbage after \"]\"");
+ if (ipv6 && !valid_ipv6_hostaddr(*host, DONT_GRIPE))
+ return ("malformed IPv6 address");
*port = *cp ? cp : def_service;
}
[hh.]
hh.
999
+[::1]
+[ipv6:::1]
+[ipv6:127.0.0.1]
+[ipv6:example.com]
unknown: warning: valid hostname or network address required in hh.
>> 999
unknown: warning: valid hostname or network address required in 999
+>> [::1]
+host ::1 port default-service
+>> [ipv6:::1]
+host ::1 port default-service
+>> [ipv6:127.0.0.1]
+unknown: warning: malformed IPv6 address in [ipv6:127.0.0.1]
+>> [ipv6:example.com]
+unknown: warning: malformed IPv6 address in [ipv6:example.com]
#define HAS_DUPLEX_PIPE /* 4.1 breaks with kqueue(2) */
#endif
-#if __FreeBSD_version >= 800107 /* safe; don't believe the experts */
+#if (__FreeBSD_version >= 702104 && __FreeBSD_version <= 800000) \
+ || __FreeBSD_version >= 800100
#define HAS_CLOSEFROM
#endif
const char *vstream_peek_data(VSTREAM *vp)
{
if (vp->buf.flags & VSTREAM_FLAG_READ) {
- return (vp->buf.ptr);
+ return ((const char *) vp->buf.ptr);
} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
- return (vp->read_buf.ptr);
+ return ((const char *) vp->read_buf.ptr);
} else {
return (0);
}