From: Wietse Venema Date: Mon, 28 Feb 2011 05:00:00 +0000 (-0500) Subject: postfix-2.9-20110228 X-Git-Tag: v2.9.0-RC1~51 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=45b77782b707802cb235a98e6603c13a3c3dfaa5;p=thirdparty%2Fpostfix.git postfix-2.9-20110228 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 0a1fd7e81..560988d1e 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -77,6 +77,7 @@ -TDICT_NISPLUS -TDICT_NODE -TDICT_OPEN_INFO +-TDICT_OWNER -TDICT_PCRE -TDICT_PCRE_ENGINE -TDICT_PCRE_EXPAND_CONTEXT diff --git a/postfix/HISTORY b/postfix/HISTORY index a48b22ce5..315897483 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. + diff --git a/postfix/README_FILES/POSTSCREEN_README b/postfix/README_FILES/POSTSCREEN_README index 9435d2a84..4b68315a1 100644 --- a/postfix/README_FILES/POSTSCREEN_README +++ b/postfix/README_FILES/POSTSCREEN_README @@ -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 PPeerrmmaanneenntt wwhhiittee//bbllaacckklliisstt tteesstt @@ -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. +MMXX PPoolliiccyy tteesstt + +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: + + CCOONNNNEECCTT ffrroomm [address]:port ttoo [[116688..110000..118899..88]]::2255 + WWHHIITTEELLIISSTT VVEETTOO [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. + TTeessttss bbeeffoorree tthhee 222200 SSMMTTPP sseerrvveerr ggrreeeettiinngg The postscreen_greet_wait parameter specifies a short time interval before the diff --git a/postfix/html/POSTSCREEN_README.html b/postfix/html/POSTSCREEN_README.html index 3b4c2a616..369fbf2a8 100644 --- a/postfix/html/POSTSCREEN_README.html +++ b/postfix/html/POSTSCREEN_README.html @@ -150,6 +150,8 @@ handling of known clients.

  • Temporary whitelist test +
  • MX Policy test +

    Permanent white/blacklist test

    @@ -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 permanent blacklist or whitelist.

    +that 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 @@ -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.

    +

    MX Policy test

    + +

    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.

    + + + +

    When a non-whitelisted client connects the backup MX address, +postscreen(8) logs this with the client address and port number as: +

    + +
    +    CONNECT from [address]:port to [168.100.189.8]:25
    +    WHITELIST VETO [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.

    +

    Tests before the 220 SMTP server greeting

    The postscreen_greet_wait parameter specifies a short time @@ -754,7 +812,7 @@ For example:

     /etc/postfix/main.cf:
    -    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
    +    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
     
    @@ -763,7 +821,7 @@ For example: 

    secret.zen.spamhaus.org zen.spamhaus.org
    -

    The texthash: format is similar to hash: except that there is +

    The texthash: format is similar to hash: except that there is no need to run postmap(1) 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.

    diff --git a/postfix/html/pgsql_table.5.html b/postfix/html/pgsql_table.5.html index 8ec3d2d02..14c337a0d 100644 --- a/postfix/html/pgsql_table.5.html +++ b/postfix/html/pgsql_table.5.html @@ -88,7 +88,7 @@ PGSQL_TABLE(5) PGSQL_TABLE(5) hosts The hosts that Postfix will try to connect to and query from. Specify unix: for UNIX-domain sockets, inet: 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- diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 16469b44d..891ea211d 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -666,7 +666,7 @@ $local_header_rewrite_cli

    To get the behavior before Postfix version 2.2, specify -"local_header_rewrite_clients = static:all".

    +"local_header_rewrite_clients = static:all".

    Example: @@ -830,7 +830,7 @@ $local_header_rewrite_cli

    To get the behavior before Postfix version 2.2, specify -"local_header_rewrite_clients = static:all".

    +"local_header_rewrite_clients = static:all".

    @@ -867,7 +867,7 @@ $local_header_rewrite_cli

    To get the behavior before Postfix version 2.2, specify -"local_header_rewrite_clients = static:all".

    +"local_header_rewrite_clients = static:all".

    @@ -893,7 +893,7 @@ This feature is available in Postfix 2.1 and later.
    authorized_flush_users -(default: static:anyone)
    +(default: static:anyone)

    List of users who are authorized to flush the queue. @@ -927,7 +927,7 @@ This feature is available in Postfix 2.2 and later.

    authorized_mailq_users -(default: static:anyone)
    +(default: static:anyone)

    List of users who are authorized to view the queue. @@ -961,7 +961,7 @@ This feature is available in Postfix 2.2 and later.

    authorized_submit_users -(default: static:anyone)
    +(default: static:anyone)

    List of users who are authorized to submit mail with the sendmail(1) @@ -992,7 +992,7 @@ Example:

    -authorized_submit_users = !www, static:all
    +authorized_submit_users = !www, static:all
     

    @@ -1331,7 +1331,7 @@ $local_header_rewrite_cli

    To get the behavior before Postfix version 2.2, specify -"local_header_rewrite_clients = static:all".

    +"local_header_rewrite_clients = static:all".

    Examples: @@ -4042,7 +4042,7 @@ configuration parameter. See there for details.

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

    -

    This feature is available in Postfix 2.8 and later.

    +

    This feature is available in Postfix 2.9 and later.

    @@ -4885,7 +4885,7 @@ header addresses.

    -local_header_rewrite_clients = static:all
    +local_header_rewrite_clients = static:all
     
    @@ -5530,7 +5530,7 @@ $local_header_rewrite_cli

    To get the behavior before Postfix version 2.2, specify -"local_header_rewrite_clients = static:all".

    +"local_header_rewrite_clients = static:all".

    Example: @@ -6988,16 +6988,16 @@ when it rejects mail. When no mapping is found, the actual DNSBL domain will be used.

    For maximal stability it is best to use a file that is read -into memory such as pcre:, regexp: or texthash: (texthash: is similar +into memory such as pcre:, regexp: or texthash: (texthash: is similar to hash:, except a) there is no need to run postmap(1) before the -file can be used, and b) texthash: does not detect changes after +file can be used, and b) texthash: does not detect changes after the file is read).

    Example:

     /etc/postfix/main.cf:
    -    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
    +    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
     
    @@ -7451,6 +7451,35 @@ one-letter suffix that specifies the time unit).  Time units: s
     

    This feature is available in Postfix 2.8.

    + + +
    postscreen_whitelist_interfaces +(default: static:all)
    + +

    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.

    + +

    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.

    + +

    Example:

    + +
    +/etc/postfix/main.cf:
    +    # Don't whitelist connections to the backup IP address.
    +    postscreen_whitelist_interfaces = !168.100.189.8, static:all
    +
    + +

    This feature is available in Postfix 2.9 and later.

    + +
    prepend_delivered_header @@ -9924,7 +9953,7 @@ Examples:
     smtp_sasl_mechanism_filter = plain, login
     smtp_sasl_mechanism_filter = /etc/postfix/smtp_mechs
    -smtp_sasl_mechanism_filter = !gssapi, !login, static:rest
    +smtp_sasl_mechanism_filter = !gssapi, !login, static:rest
     
    @@ -14914,7 +14943,7 @@ $local_header_rewrite_cli

    To get the behavior before Postfix version 2.2, specify -"local_header_rewrite_clients = static:all".

    +"local_header_rewrite_clients = static:all".

    Example: diff --git a/postfix/html/postscreen.8.html b/postfix/html/postscreen.8.html index 7a656ac26..3d5716590 100644 --- a/postfix/html/postscreen.8.html +++ b/postfix/html/postscreen.8.html @@ -156,6 +156,22 @@ POSTSCREEN(8) POSTSCREEN(8) client is permanently blacklisted with the postscreen_access_list parameter. +MAIL EXCHANGER POLICY TESTS + When a remote SMTP client is not on the permanent access + list, 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. + + postscreen_whitelist_interfaces (static:all) + 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. + BEFORE-GREETING TESTS These tests are executed before the remote SMTP client receives the "220 servername" greeting. If no tests remain diff --git a/postfix/man/man5/pgsql_table.5 b/postfix/man/man5/pgsql_table.5 index 3c99dc7e6..ce072f2fc 100644 --- a/postfix/man/man5/pgsql_table.5 +++ b/postfix/man/man5/pgsql_table.5 @@ -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 diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 600f530f1..b07bbd1df 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -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 diff --git a/postfix/man/man8/postscreen.8 b/postfix/man/man8/postscreen.8 index 97ee76847..e2f73bcae 100644 --- a/postfix/man/man8/postscreen.8 +++ b/postfix/man/man8/postscreen.8 @@ -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 diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 416dd95d7..387d95255 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -962,6 +962,7 @@ while (<>) { s;\bpostscreen_expansion_filter\b;$&;g; s;\bpostscreen_reject_footer\b;$&;g; s;\bpostscreen_command_filter\b;$&;g; + s;\bpostscreen_whitelist_interfaces\b;$&;g; s;\btlsproxy_watchdog_timeout\b;$&;g; s;\btlsproxy_enforce_tls\b;$&;g; @@ -1051,7 +1052,9 @@ while (<>) { s/\b(ldap):/$1<\/a>:/g; s/\b(regexp):/$1<\/a>:/g; s/\b(sqlite):/$1<\/a>:/g; + s/\b(static):/$1<\/a>:/g; s/\b(tcp):/$1<\/a>:/g; + s/\b(texthash):/$1<\/a>:/g; # Do nice links for smtp:host:port etc. diff --git a/postfix/proto/POSTSCREEN_README.html b/postfix/proto/POSTSCREEN_README.html index 287da6439..0d31c7d46 100644 --- a/postfix/proto/POSTSCREEN_README.html +++ b/postfix/proto/POSTSCREEN_README.html @@ -150,6 +150,8 @@ handling of known clients.

  • Temporary whitelist test +
  • MX Policy test +

    Permanent white/blacklist test

    @@ -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 permanent blacklist or whitelist.

    +that 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 @@ -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.

    +

    MX Policy test

    + +

    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: +

    + +
    +    CONNECT from [address]:port to [168.100.189.8]:25
    +    WHITELIST VETO [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.

    +

    Tests before the 220 SMTP server greeting

    The postscreen_greet_wait parameter specifies a short time diff --git a/postfix/proto/pgsql_table b/postfix/proto/pgsql_table index c62190e65..fcb86ab15 100644 --- a/postfix/proto/pgsql_table +++ b/postfix/proto/pgsql_table @@ -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 # diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 210f787fb..023051d99 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -14054,4 +14054,29 @@ Postfix releases, the behavior is as if this parameter is set to

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

    -

    This feature is available in Postfix 2.8 and later.

    +

    This feature is available in Postfix 2.9 and later.

    + +%PARAM postscreen_whitelist_interfaces static:all + +

    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.

    + +

    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.

    + +

    Example:

    + +
    +/etc/postfix/main.cf:
    +    # Don't whitelist connections to the backup IP address.
    +    postscreen_whitelist_interfaces = !168.100.189.8, static:all
    +
    + +

    This feature is available in Postfix 2.9 and later.

    diff --git a/postfix/src/global/cfg_parser.c b/postfix/src/global/cfg_parser.c index 5495c2994..73c81d025 100644 --- a/postfix/src/global/cfg_parser.c +++ b/postfix/src/global/cfg_parser.c @@ -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); } diff --git a/postfix/src/global/cfg_parser.h b/postfix/src/global/cfg_parser.h index 40544a24a..c3f8d9b3c 100644 --- a/postfix/src/global/cfg_parser.h +++ b/postfix/src/global/cfg_parser.h @@ -12,15 +12,20 @@ .nf /* - * External interface. + * Utility library. */ +#include + /* + * 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 diff --git a/postfix/src/global/dict_ldap.c b/postfix/src/global/dict_ldap.c index f9df3c597..57be7dfec 100644 --- a/postfix/src/global/dict_ldap.c +++ b/postfix/src/global/dict_ldap.c @@ -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)); } diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c index 27663ad1e..8425cd72a 100644 --- a/postfix/src/global/dict_mysql.c +++ b/postfix/src/global/dict_mysql.c @@ -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)); } diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c index 9899cf4fc..c4c0e5fb8 100644 --- a/postfix/src/global/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -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 */ diff --git a/postfix/src/global/dict_sqlite.c b/postfix/src/global/dict_sqlite.c index c349930e3..697442592 100644 --- a/postfix/src/global/dict_sqlite.c +++ b/postfix/src/global/dict_sqlite.c @@ -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)); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 2403c437b..169a9100a 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -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; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 467671c23..782fff818 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20110219" +#define MAIL_RELEASE_DATE "20110228" #define MAIL_VERSION_NUMBER "2.9" #ifdef SNAPSHOT diff --git a/postfix/src/local/alias.c b/postfix/src/local/alias.c index 0fc0b9fbf..966a5dcb0 100644 --- a/postfix/src/local/alias.c +++ b/postfix/src/local/alias.c @@ -100,27 +100,6 @@ #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"); diff --git a/postfix/src/postscreen/postscreen.c b/postfix/src/postscreen/postscreen.c index 4a41f7ff3..4776a274d 100644 --- a/postfix/src/postscreen/postscreen.c +++ b/postfix/src/postscreen/postscreen.c @@ -139,6 +139,21 @@ /* .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[] = { diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h index afea976ff..7a82b3697 100644 --- a/postfix/src/postscreen/postscreen.h +++ b/postfix/src/postscreen/postscreen.h @@ -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) diff --git a/postfix/src/postscreen/postscreen_state.c b/postfix/src/postscreen/postscreen_state.c index fe189ffa2..51c3bf0c9 100644 --- a/postfix/src/postscreen/postscreen_state.c +++ b/postfix/src/postscreen/postscreen_state.c @@ -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, diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c index af3e06abb..60750cc2d 100644 --- a/postfix/src/smtpstone/smtp-sink.c +++ b/postfix/src/smtpstone/smtp-sink.c @@ -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); } diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index 10b2a3f6d..af41c7663 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -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 */ diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 9829d28ef..fb9efb5fc 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -23,6 +23,18 @@ #include #include + /* + * 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); diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c index 9c08a9071..0f97d1b6e 100644 --- a/postfix/src/util/dict_alloc.c +++ b/postfix/src/util/dict_alloc.c @@ -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; } diff --git a/postfix/src/util/dict_cdb.c b/postfix/src/util/dict_cdb.c index 05e6743f9..36437abc3 100644 --- a/postfix/src/util/dict_cdb.c +++ b/postfix/src/util/dict_cdb.c @@ -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); /* diff --git a/postfix/src/util/dict_cidr.c b/postfix/src/util/dict_cidr.c index 6f12f9f87..82480119d 100644 --- a/postfix/src/util/dict_cidr.c +++ b/postfix/src/util/dict_cidr.c @@ -32,6 +32,7 @@ /* System library. */ #include +#include #include #include #include @@ -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); diff --git a/postfix/src/util/dict_db.c b/postfix/src/util/dict_db.c index c827b8d20..7fb00985a 100644 --- a/postfix/src/util/dict_db.c +++ b/postfix/src/util/dict_db.c @@ -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 diff --git a/postfix/src/util/dict_dbm.c b/postfix/src/util/dict_dbm.c index 3603e44de..0a0a256a1 100644 --- a/postfix/src/util/dict_dbm.c +++ b/postfix/src/util/dict_dbm.c @@ -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 diff --git a/postfix/src/util/dict_env.c b/postfix/src/util/dict_env.c index b1a2a01df..bc15d6c92 100644 --- a/postfix/src/util/dict_env.c +++ b/postfix/src/util/dict_env.c @@ -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)); } diff --git a/postfix/src/util/dict_ht.c b/postfix/src/util/dict_ht.c index 657f1fc38..0eff0810a 100644 --- a/postfix/src/util/dict_ht.c +++ b/postfix/src/util/dict_ht.c @@ -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); } diff --git a/postfix/src/util/dict_ni.c b/postfix/src/util/dict_ni.c index e9bbd0f6e..569188c0c 100644 --- a/postfix/src/util/dict_ni.c +++ b/postfix/src/util/dict_ni.c @@ -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)); } diff --git a/postfix/src/util/dict_nis.c b/postfix/src/util/dict_nis.c index 552f550b1..27a20a54e 100644 --- a/postfix/src/util/dict_nis.c +++ b/postfix/src/util/dict_nis.c @@ -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)); } diff --git a/postfix/src/util/dict_nisplus.c b/postfix/src/util/dict_nisplus.c index 52ca213f4..c10fa45b9 100644 --- a/postfix/src/util/dict_nisplus.c +++ b/postfix/src/util/dict_nisplus.c @@ -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 diff --git a/postfix/src/util/dict_pcre.c b/postfix/src/util/dict_pcre.c index 2f1f3906d..f7a692153 100644 --- a/postfix/src/util/dict_pcre.c +++ b/postfix/src/util/dict_pcre.c @@ -35,6 +35,7 @@ /* System library. */ +#include #include /* sprintf() prototype */ #include #include @@ -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); diff --git a/postfix/src/util/dict_regexp.c b/postfix/src/util/dict_regexp.c index f3cb1f9ba..c88d2bd55 100644 --- a/postfix/src/util/dict_regexp.c +++ b/postfix/src/util/dict_regexp.c @@ -39,6 +39,7 @@ #ifdef HAS_POSIX_REGEXP +#include #include #include #include @@ -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); diff --git a/postfix/src/util/dict_sdbm.c b/postfix/src/util/dict_sdbm.c index 85e6092f9..c6a374914 100644 --- a/postfix/src/util/dict_sdbm.c +++ b/postfix/src/util/dict_sdbm.c @@ -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 diff --git a/postfix/src/util/dict_static.c b/postfix/src/util/dict_static.c index 346e166b9..7c9cabcb6 100644 --- a/postfix/src/util/dict_static.c +++ b/postfix/src/util/dict_static.c @@ -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)); } diff --git a/postfix/src/util/dict_thash.c b/postfix/src/util/dict_thash.c index c6fb98184..6379d2a80 100644 --- a/postfix/src/util/dict_thash.c +++ b/postfix/src/util/dict_thash.c @@ -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)); } diff --git a/postfix/src/util/dict_unix.c b/postfix/src/util/dict_unix.c index 8ba6188f8..66baba8eb 100644 --- a/postfix/src/util/dict_unix.c +++ b/postfix/src/util/dict_unix.c @@ -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)); } diff --git a/postfix/src/util/host_port.c b/postfix/src/util/host_port.c index bfcad8c09..7cc932479 100644 --- a/postfix/src/util/host_port.c +++ b/postfix/src/util/host_port.c @@ -95,22 +95,41 @@ #include + /* + * Point-fix workaround. The libutil library should be email agnostic, but + * we can't rip up the library APIs in the stable releases. + */ +#include +#ifdef STRCASECMP_IN_STRINGS_H +#include +#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; } diff --git a/postfix/src/util/host_port.in b/postfix/src/util/host_port.in index 324892fe0..160822218 100644 --- a/postfix/src/util/host_port.in +++ b/postfix/src/util/host_port.in @@ -10,3 +10,7 @@ hhh:1pp [hh.] hh. 999 +[::1] +[ipv6:::1] +[ipv6:127.0.0.1] +[ipv6:example.com] diff --git a/postfix/src/util/host_port.ref b/postfix/src/util/host_port.ref index 28c518f63..1d79745fa 100644 --- a/postfix/src/util/host_port.ref +++ b/postfix/src/util/host_port.ref @@ -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] diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index f3d0eaa5b..90eb075d6 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -111,7 +111,8 @@ #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 diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 2abd0516c..fdb1b2101 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -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); }