]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.9-20110228
authorWietse Venema <wietse@porcupine.org>
Mon, 28 Feb 2011 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:37:14 +0000 (06:37 +0000)
50 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/POSTSCREEN_README
postfix/html/POSTSCREEN_README.html
postfix/html/pgsql_table.5.html
postfix/html/postconf.5.html
postfix/html/postscreen.8.html
postfix/man/man5/pgsql_table.5
postfix/man/man5/postconf.5
postfix/man/man8/postscreen.8
postfix/mantools/postlink
postfix/proto/POSTSCREEN_README.html
postfix/proto/pgsql_table
postfix/proto/postconf.proto
postfix/src/global/cfg_parser.c
postfix/src/global/cfg_parser.h
postfix/src/global/dict_ldap.c
postfix/src/global/dict_mysql.c
postfix/src/global/dict_pgsql.c
postfix/src/global/dict_sqlite.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/local/alias.c
postfix/src/postscreen/postscreen.c
postfix/src/postscreen/postscreen.h
postfix/src/postscreen/postscreen_state.c
postfix/src/smtpstone/smtp-sink.c
postfix/src/util/dict.c
postfix/src/util/dict.h
postfix/src/util/dict_alloc.c
postfix/src/util/dict_cdb.c
postfix/src/util/dict_cidr.c
postfix/src/util/dict_db.c
postfix/src/util/dict_dbm.c
postfix/src/util/dict_env.c
postfix/src/util/dict_ht.c
postfix/src/util/dict_ni.c
postfix/src/util/dict_nis.c
postfix/src/util/dict_nisplus.c
postfix/src/util/dict_pcre.c
postfix/src/util/dict_regexp.c
postfix/src/util/dict_sdbm.c
postfix/src/util/dict_static.c
postfix/src/util/dict_thash.c
postfix/src/util/dict_unix.c
postfix/src/util/host_port.c
postfix/src/util/host_port.in
postfix/src/util/host_port.ref
postfix/src/util/sys_defs.h
postfix/src/util/vstream.c

index 0a1fd7e81d8c672ae34da5dae410d3cecc3b2ef4..560988d1e825661dcdc355fbd369fa06b7dde6f7 100644 (file)
@@ -77,6 +77,7 @@
 -TDICT_NISPLUS
 -TDICT_NODE
 -TDICT_OPEN_INFO
+-TDICT_OWNER
 -TDICT_PCRE
 -TDICT_PCRE_ENGINE
 -TDICT_PCRE_EXPAND_CONTEXT
index a48b22ce5af8b81f60c279b59b17e076e1b53f15..31589748368335bc1f56e4ba77635eaf8466b0fc 100644 (file)
@@ -16625,3 +16625,63 @@ Apologies for any names omitted.
        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.
+
index 9435d2a8413522993b3c934a483395726c18ddb6..4b68315a1684b19161e736424ec3e259450940b3 100644 (file)
@@ -107,6 +107,7 @@ black and whitelists. These tests speed up the handling of known clients.
 
   * 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
 
@@ -151,7 +152,7 @@ The postscreen(8) daemon maintains a temporary whitelist for SMTP client IP
 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:
@@ -163,6 +164,51 @@ Postfix SMTP server process. The client is excluded from further tests until
 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
index 3b4c2a616846e97820f67df15e53450ef225b925..369fbf2a823cd3095ef3affad77c595b0c1efcc7 100644 (file)
@@ -150,6 +150,8 @@ handling of known clients. </p>
 
 <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>
@@ -206,7 +208,7 @@ whitelist for SMTP client IP addresses that have passed all
 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
@@ -222,6 +224,62 @@ excluded from further tests until its temporary whitelist
 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
@@ -754,7 +812,7 @@ For 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>
@@ -763,7 +821,7 @@ For example: </p>
     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>
index 8ec3d2d02b5f924ee011f04752494b84c467c21d..14c337a0d790864506695585c140d4f0abb46cbc 100644 (file)
@@ -88,7 +88,7 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
        <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-
index 16469b44d69e2734df74c13aa6985310f5d314e8..891ea211dc64b457a865a04edacf13c2a87842ea 100644 (file)
@@ -666,7 +666,7 @@ $<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_cli
 </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:
@@ -830,7 +830,7 @@ $<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_cli
 </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>
@@ -867,7 +867,7 @@ $<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_cli
 </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>
@@ -893,7 +893,7 @@ This feature is available in Postfix 2.1 and later.
 </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.
@@ -927,7 +927,7 @@ This feature is available in Postfix 2.2 and later.
 </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.
@@ -961,7 +961,7 @@ This feature is available in Postfix 2.2 and later.
 </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>
@@ -992,7 +992,7 @@ Example:
 </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>
@@ -1331,7 +1331,7 @@ $<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_cli
 </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:
@@ -4042,7 +4042,7 @@ configuration parameter. See there for details. </p>
 <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>
@@ -4885,7 +4885,7 @@ header addresses.  </p>
 
 <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>
 
@@ -5530,7 +5530,7 @@ $<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_cli
 </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:
@@ -6988,16 +6988,16 @@ when it rejects mail.  When no mapping is found, the actual DNSBL
 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>
@@ -7451,6 +7451,35 @@ one-letter suffix that specifies the time unit).  Time units: s
 <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>
@@ -9924,7 +9953,7 @@ Examples:
 <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>
 
 
@@ -14914,7 +14943,7 @@ $<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_cli
 </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:
index 7a656ac261393764a70cd9bdb72a113062ba77b5..3d5716590aa4aa466c17f9dc70b7c419c0456a21 100644 (file)
@@ -156,6 +156,22 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               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
index 3c99dc7e626d3b524b6c1ed2ec61fabb598fdd71..ce072f2fc651745c751af0da1247e9d39c12e372 100644 (file)
@@ -103,7 +103,7 @@ The hosts that Postfix will try to connect to and query from.
 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
 
index 600f530f132651fe720e11882f07e4797c9c5215..b07bbd1dff92b1b1937d0e86a4a62088787ba830 100644 (file)
@@ -2240,7 +2240,7 @@ This feature is available in Postfix 2.5 and later.
 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.
@@ -4259,6 +4259,33 @@ one-letter suffix that specifies the time unit).  Time units: s
 (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
index 97ee768476ef9ee8cba1bc071dd0ffa1a8c8d4cf..e2f73bcaee42c7492376f775671a1cebab18c9a3 100644 (file)
@@ -161,6 +161,23 @@ Permanent white/blacklist for remote SMTP client IP addresses.
 .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
index 416dd95d73cf28301370e02080a044a05a38902f..387d952554f61b6f28359c2b2105c7c7f980e9ff 100755 (executable)
@@ -962,6 +962,7 @@ while (<>) {
     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;
@@ -1051,7 +1052,9 @@ while (<>) {
     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.
 
index 287da6439ccad6ebc37e874088c94c26cf705fcc..0d31c7d467d374e7b16febe6eb68039c6494eb4d 100644 (file)
@@ -150,6 +150,8 @@ handling of known clients. </p>
 
 <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>
@@ -206,7 +208,7 @@ whitelist for SMTP client IP 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 <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
@@ -222,6 +224,62 @@ excluded from further tests until its temporary whitelist
 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
index c62190e6522b36475ada3fab929ff471208b4575..fcb86ab15b27239100b7d5cc1b84d66375b2d1ab 100644 (file)
@@ -91,7 +91,7 @@
 #      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
 #
index 210f787fbb4a94755f076e486f00d7a2313669dd..023051d99ca69d33d23cb307a2828edbcfe46c22 100644 (file)
@@ -14054,4 +14054,29 @@ Postfix releases, the behavior is as if this parameter is set to
 <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>
index 5495c2994c9dd728ab9f572b7a1064d97dc46490..73c81d025b2e6967801261f57820b6ce8b98a5c3 100644 (file)
@@ -30,6 +30,9 @@
 /*     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
@@ -55,6 +58,8 @@
 /*     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.
@@ -222,6 +227,7 @@ CFG_PARSER *cfg_parser_alloc(const char *pname)
 {
     const char *myname = "cfg_parser_alloc";
     CFG_PARSER *parser;
+    DICT   *dict;
 
     if (pname == 0 || *pname == 0)
        msg_fatal("%s: null parser name", myname);
@@ -232,11 +238,16 @@ CFG_PARSER *cfg_parser_alloc(const char *pname)
        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);
 }
 
index 40544a24a7ef9f197c94d8bf054c98d79c877795..c3f8d9b3cf04f6effe36c318241e46c4eeba0a4b 100644 (file)
  .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 *);
@@ -30,6 +35,8 @@ extern int cfg_get_int(const CFG_PARSER *, const char *, int, int, int);
 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
index f9df3c597284152998ce16f1651d842622b8c71f..57be7dfecdb0a763c83d0adbd0699fa9c22faf41 100644 (file)
@@ -1957,6 +1957,7 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
     /*
      * Return the new dict_ldap structure.
      */
+    dict_ldap->dict.owner = cfg_get_owner(dict_ldap->parser);
     return (DICT_DEBUG (&dict_ldap->dict));
 }
 
index 27663ad1e6e857e752caaa4178399a5f9bcc7203..8425cd72a1bbb8f61ed5282cab88580a7618db22 100644 (file)
@@ -667,6 +667,7 @@ DICT   *dict_mysql_open(const char *name, int open_flags, int dict_flags)
     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));
 }
 
index 9899cf4fce7ca24c83d031d5dd75e769ab2b6cbe..c4c0e5fb87ed300b780413ab990d3a6261e0a3ad 100644 (file)
@@ -764,7 +764,8 @@ DICT   *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
     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 */
index c349930e343256e2a060920f4bf36c212aac8d93..69744259248a46c68895b9056adb3b785575f5a6 100644 (file)
@@ -317,6 +317,8 @@ DICT   *dict_sqlite_open(const char *name, int open_flags, int dict_flags)
        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));
 }
 
index 2403c437bba782cbf298ec88c72dc1ebdf5e4d5f..169a9100ad35f47966680d31ae1bc2bda39fa618 100644 (file)
@@ -3441,6 +3441,10 @@ extern char *var_psc_cmd_filter;
 #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;
index 467671c23c4300832aeb7098531835c7f03fa831..782fff818995e6ca85489d97c10a6c701da68285 100644 (file)
@@ -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      "20110219"
+#define MAIL_RELEASE_DATE      "20110228"
 #define MAIL_VERSION_NUMBER    "2.9"
 
 #ifdef SNAPSHOT
index 0fc0b9fbfca39a713bce3f669ecaad3280063591..966a5dcb073efbcfe4e5166b8999090de1d7b5f4 100644 (file)
 #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,
@@ -131,7 +110,6 @@ 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;
@@ -227,11 +205,20 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
             * 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");
index 4a41f7ff3103d76d525bb05a3d637b94359e4531..4776a274dd7a7907c4d7e6841f0a4e969eb227f1 100644 (file)
 /* .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
@@ -460,6 +475,8 @@ int     var_psc_cconn_limit;
 char   *var_smtpd_exp_filter;
 char   *var_psc_exp_filter;
 
+char   *var_psc_wlist_if;
+
  /*
   * Global variables.
   */
@@ -491,6 +508,7 @@ HTABLE *psc_client_concurrency;             /* per-client concurrency */
   */
 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 */
 
@@ -561,6 +579,8 @@ static void psc_service(VSTREAM *smtp_client_stream,
     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;
@@ -579,7 +599,12 @@ static void psc_service(VSTREAM *smtp_client_stream,
      * 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);
@@ -590,10 +615,6 @@ static void psc_service(VSTREAM *smtp_client_stream,
     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);
     }
 
@@ -607,10 +628,6 @@ static void psc_service(VSTREAM *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)
@@ -621,7 +638,34 @@ static void psc_service(VSTREAM *smtp_client_stream,
                 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
@@ -734,6 +778,14 @@ static void psc_service(VSTREAM *smtp_client_stream,
                     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.
@@ -938,6 +990,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
                                      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
@@ -1039,6 +1092,7 @@ int     main(int argc, char **argv)
        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[] = {
index afea976ff1bec1aae9b0d340f7df9b497be328e7..7a82b3697653c93a78936d2f3977b7e229ff996e 100644 (file)
@@ -82,6 +82,7 @@ typedef struct {
 #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
@@ -190,7 +191,8 @@ typedef struct {
   */
 #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)
index fe189ffa217a3187062cea04cbf1866024bb73a7..51c3bf0c923decfd87a1585f7c6847e8e463f687 100644 (file)
@@ -256,6 +256,7 @@ const char *psc_print_state_flags(int flags, const char *context)
        "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,
index af3e06abb28fb4f48fd3fe303c7d91aae44b89e0..60750cc2d97b7ee569eb7413f764b1f8d085ad02 100644 (file)
@@ -386,7 +386,7 @@ static void do_stats(void)
 
 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);
 }
 
@@ -394,7 +394,7 @@ static void hard_err_resp(SINK_STATE *state)
 
 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);
 }
 
@@ -745,9 +745,9 @@ static void dot_resp_hard(SINK_STATE *state)
 {
     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);
 }
@@ -758,9 +758,9 @@ static void dot_resp_soft(SINK_STATE *state)
 {
     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);
 }
index 10b2a3f6db80c8062cd1ee415803a60ab4723609..af41c766385714ba81d368197e05bb74e62e7e9b 100644 (file)
@@ -406,10 +406,13 @@ void    dict_load_fp(const char *dict_name, VSTREAM *fp)
     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\"",
@@ -417,6 +420,8 @@ void    dict_load_fp(const char *dict_name, VSTREAM *fp)
        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 */
index 9829d28effb4ddb1a607bf4cea3f210761c3cf15..fb9efb5fcd4055207698188801f2c77f487eaa2e 100644 (file)
 #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.
@@ -40,6 +52,7 @@ typedef struct DICT {
     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);
index 9c08a90716ba355fcf89faf0899fc2d87590e504..0f97d1b6ea42b54106a83bc32a11a52c4191ef31 100644 (file)
@@ -121,6 +121,8 @@ DICT   *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
     dict->stat_fd = -1;
     dict->mtime = 0;
     dict->fold_buf = 0;
+    dict->owner.status = DICT_OWNER_UNKNOWN;
+    dict->owner.uid = ~0;
     return dict;
 }
 
index 05e6743f982e06a9deacf448167260cc1a2ff683..36437abc3e9001898e8332dcb0e77396dec3f200 100644 (file)
@@ -200,6 +200,8 @@ static DICT *dict_cdbq_open(const char *path, int dict_flags)
     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);
 
     /*
@@ -373,6 +375,8 @@ static DICT *dict_cdbm_open(const char *path, int dict_flags)
     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);
 
     /*
index 6f12f9f87c6a93b213ff0ed13bdffba6e1cf4378..82480119d2a452c9f79881b35c499b30add1cf0a 100644 (file)
@@ -32,6 +32,7 @@
 /* System library. */
 
 #include <sys_defs.h>
+#include <sys/stat.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
@@ -164,6 +165,7 @@ DICT   *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
 {
     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;
@@ -190,6 +192,10 @@ DICT   *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
 
     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);
index c827b8d20dc82b5e57ce58fa40d0a36fc0d5a9eb..7fb00985a96c49c4eae14d3b3215571ccc69c1ba 100644 (file)
@@ -705,6 +705,8 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
     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
index 3603e44de0b0181fd4a3ccb442f143036a67dba6..0a0a256a10897681e5b49ff9f1b1a9c0f538a713 100644 (file)
@@ -169,7 +169,6 @@ static void dict_dbm_update(DICT *dict, const char *name, const char *value)
        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);
@@ -449,6 +448,8 @@ DICT   *dict_dbm_open(const char *path, int open_flags, int dict_flags)
     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
index b1a2a01df0892501e90e794ac53c5ac4f59769c6..bc15d6c92d18ee4c4694baade9417ae6b6efc4ae 100644 (file)
@@ -104,5 +104,6 @@ DICT   *dict_env_open(const char *name, int unused_flags, int dict_flags)
     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));
 }
index 657f1fc383d18117f5b22d170921119e27bd0579..0eff0810a0bc2494bed930a1329e932b7122de79 100644 (file)
@@ -143,5 +143,6 @@ DICT   *dict_ht_open(const char *name, int unused_open_flags, int dict_flags)
     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);
 }
index e9bbd0f6eccac4509df516eb646f109e3d2cfcb2..569188c0cafdfbf5366fda3e0e28c217ba6e9e9d 100644 (file)
@@ -185,6 +185,7 @@ DICT   *dict_ni_open(const char *path, int unused_flags, int dict_flags)
     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));
 }
index 552f550b17c309ac48c7aa3f71e8556ea90e222f..27a20a54ef8a3cbd3d83ccff2cc47693e727fc2a 100644 (file)
@@ -238,6 +238,7 @@ DICT   *dict_nis_open(const char *map, int open_flags, int dict_flags)
        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));
 }
 
index 52ca213f42dd9399e2ae39d7ce42e775b8a5a21d..c10fa45b9dd5449679b3552bbb308a01f1ef3349 100644 (file)
@@ -270,6 +270,7 @@ DICT   *dict_nisplus_open(const char *map, int open_flags, int dict_flags)
     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
index 2f1f3906dba06a671d06fba87d4f1bbbeea0255b..f7a692153f6acce9b2e4356ab30fedc076a055cd 100644 (file)
@@ -35,6 +35,7 @@
 
 /* System library. */
 
+#include <sys/stat.h>
 #include <stdio.h>                     /* sprintf() prototype */
 #include <stdlib.h>
 #include <unistd.h>
@@ -797,6 +798,7 @@ DICT   *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
 {
     DICT_PCRE *dict_pcre;
     VSTREAM *map_fp;
+    struct stat st;
     VSTRING *line_buffer;
     DICT_PCRE_RULE *last_rule = 0;
     DICT_PCRE_RULE *rule;
@@ -827,6 +829,10 @@ DICT   *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
      */
     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);
index f3cb1f9ba4690fc2862b3d452bd06e312bdc7c19..c88d2bd5559cb385587bc4c00709be04dc1f6102 100644 (file)
@@ -39,6 +39,7 @@
 
 #ifdef HAS_POSIX_REGEXP
 
+#include <sys/stat.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
@@ -737,6 +738,7 @@ DICT   *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
 {
     DICT_REGEXP *dict_regexp;
     VSTREAM *map_fp;
+    struct stat st;
     VSTRING *line_buffer;
     DICT_REGEXP_RULE *rule;
     DICT_REGEXP_RULE *last_rule = 0;
@@ -763,6 +765,10 @@ DICT   *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
      */
     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);
index 85e6092f92b511d08c70ff7c7cc07ad01bafd96f..c6a3749142a474d17cbccba6460a70d58289c291 100644 (file)
@@ -439,6 +439,8 @@ DICT   *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
     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
index 346e166b96f7528e76617a722e9aa9283ac786d8..7c9cabcb6e3375cd00c1ed9c49dd2e2fe5680f3a 100644 (file)
@@ -68,5 +68,6 @@ DICT   *dict_static_open(const char *name, int unused_flags, int dict_flags)
     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));
 }
index c6fb9818438978d51a07db9b0473e9211d9e824d..6379d2a809caf83a3539201041d35e526b5b0d66 100644 (file)
@@ -259,6 +259,8 @@ DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
        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));
 }
index 8ba6188f85f309c1f5441a9088eefe59b6e396ab..66baba8eb1758aff3b04316f6ac624a9e5c2d613 100644 (file)
@@ -188,6 +188,7 @@ DICT   *dict_unix_open(const char *map, int unused_flags, int dict_flags)
     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));
 }
index bfcad8c09a37c0d9c3977a34222f3b4f1e5bf001..7cc932479307ac4554b397b360b2b3c76c6368b2 100644 (file)
 
 #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;
     }
 
index 324892fe0e9192c867e76d3b1cf9276c623b9900..160822218a12ae49a6a37c3265875e81127c2ebd 100644 (file)
@@ -10,3 +10,7 @@ hhh:1pp
 [hh.]
 hh.
 999
+[::1]
+[ipv6:::1]
+[ipv6:127.0.0.1]
+[ipv6:example.com]
index 28c518f6307c353f449fc6fed7e7d83e4492957d..1d79745fa899dd2466315134004da1b39835d759 100644 (file)
@@ -20,3 +20,11 @@ unknown: warning: valid hostname or network address required in [hh.]
 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]
index f3d0eaa5b40f86412a88654dac81658514d8c92d..90eb075d6f9aba437fd543153ce91dfe3692d30c 100644 (file)
 #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
 
index 2abd0516c51239a12e39b6b411b0178ebdd65697..fdb1b2101162bcd677c4a2a8e5238b2d909ea3ee 100644 (file)
@@ -1528,9 +1528,9 @@ ssize_t vstream_peek(VSTREAM *vp)
 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);
     }