The most recent patch contains the following extensions, modifications:
${name?text}, $(name?text} Conditional expansion. If the named attribute is defined, the expansion is the given text with another iteration of macro expansions. Otherwise, the expansion is empty. ${name:text}, $(name:text) Conditional expansion. If the named attribute is undefined, the expansion is the given text with another iteration of macro expansions. Otherwise, the expansion is empty.The trinary (test ? if_true : if_false) conditional macro expansion was unimplemented.
This patch extends Postfix with the trinary conditional macro expansion.
However, BEWARE! BEWARE! BEWARE! INCOMPATIBILITY!
In order to use the intuitive ${name?text1:text2} or $(name?text1:text2} forms, the inverted test operator ':' has been replaced with '!'. Thus the full list of the new conditional macro expansions are:
${name?text}, $(name?text} Conditional expansion. If the named attribute is defined, the expansion is the given text with another iteration of macro expansions. Otherwise, the expansion is empty. ${name?text1:text2}, $(name?text1:text2} Conditional expansion. If the named attribute is defined, the expansion is the given text1 with another iteration of macro expansions. Otherwise, the expansion is the given text2 with another iteration of macro expansions. Embedded pure ':' characters in text1 must be escaped as '\:'. ${name!text}, $(name!text) Conditional expansion. If the named attribute is undefined, the expansion is the given text with another iteration of macro expansions. Otherwise, the expansion is empty. ${name!text1:text2}, $(name!text1:text2} Conditional expansion. If the named attribute is undefined, the expansion is the given text1 with another iteration of macro expansions. Otherwise, the expansion is the given text2 with another iteration of macro expansions. Embedded pure ':' characters in text1 must be escaped as '\:'.Please note that in the unpatched Postfix literal $name in text had to be escaped as $$name. Now for the sake of consistency, it must be escaped as \$name.
Examples:
foo = x bar = y ${foo?foo defined:foo not defined} foo defined ${foo?\$foo\: "$foo":\$foo is emtpy} $foo: "x" ${foo?foo defined, bar ${bar?ditto:doesn't}:foo not defined} foo defined, bar ditto
$client_name client hostname (or "unknown") $client_address client address $client client hostname [client address] $helo announced helo name $sender sender E-mail address (sender@from.somewhere) $sender_name the username part of the sender E-mail address (sender) $sender_domain the domain part of the sender E-mail address (from.somewhere) $recipient recipient E-mail address (recip@to.somewhere) $recipient_name the username part of the recipient E-mail address (recip) $recipient_domain the domain part of the recipient E-mail address (to.somewhere) $rbl_txt DNS TXT lookup result for the client in the current RBL/RHSBL domain (see example below) $rbl_ip the lookup result for the client in the current RBL/RHSBL domain. If the client is not in the domain, it's value is "0.0.0.0".
key any one-word Postfix restriction including user defined restriction classes value anything which is valid on the RHS in mapfiles (OK|RELAY|REJECT|[45]xx text|list of UCE restrictions)The "lookup" in the map file happens as follows:
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = restrictions:/etc/postfix/unknown_client >>> from /etc/postfix/unknown_client <<< reject_unknown_client 450 Cannot find your hostname, [$client_address]. Ask your system manager to fix your domain name registration.
key domain name value anything which is valid on the RHS in mapfiles (OK|RELAY|REJECT|[45]xx text|list of UCE restrictions)In the case of rbl maptype:
The max_rhsbl_subdomain (default 5) parameter controls maximally from which subdomain to start the lookup, i.e. in the case a.b.c.d.e.com, the lookup is started at b.c.d.e.com and continues to e.com.
Example for rbl map and $rbl_txt macro usage:
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = rbl:/etc/postfix/rbl_domain >>> from /etc/postfix/rbl_domain <<< relays.orbs.org 554 Service unavailable; [$client_address] blocked using relays.orbs.org. ${rbl_txt?$rbl_txt:See http://www.orbs.org/ for details.}Example for rhsbl map usage:
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = check_sender_access rhsbl:/etc/postfix/rhsbl_sender_domain, rhsbl:/etc/postfix/rhsbl_recip_domainor
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = check_access \$sender rhsbl:/etc/postfix/rhsbl_sender_domain, check_access \$recipient rhsbl:/etc/postfix/rhsbl_recip_domain >>> from /etc/postfix/rbl_sender_domain <<< in.dnsbl.org 554 Service unavailable; $sender_domain blocked using in.dnsbl.org. >>> from /etc/postfix/rbl_recip_domain <<< in.dnsbl.org 554 Service unavailable; $recipient_domain blocked using in.dnsbl.org.Please note, rbl/rhsbl maps don't perform DNS TXT record lookups. The $rbl_txt macro triggers the DNS TXT lookup in the current RBL/RHSBL domain. Additionally, until a new DNS A record isn't looked up by rbl/rhsbl map, $rbl_txt preserves the result of the last DNS TXT record lookup.
key netblock in CIDR notation (or plain IP address as individual node address) value anything which is valid on the RHS in mapfiles (OK|RELAY|REJECT|[45]xx text|list of UCE restrictions)
>>> from /etc/postfix/main.cf <<< rbl_domain = relays.orbs.org 554 Service unavailable; [\$client_address] blocked using relays.orbs.org. \${rbl_txt?\$rbl_txt:See http://www.orbs.org/ for details.} smtpd_recipient_restrictions = rbl:inline:rbl_domain[Don't forget to escape macro expansions like in the example above.]
check_access parameter maptype:mapfilewhere the parameter may contain macro expressions from above.
At evaluating it, first the macros in the parameter are expanded, then the result is looked up in the specified mapfile according to the maptype.
The goal at the first version of this patch was to make UCE restrictions as configurable as possible. Let it be possible for the users to define (setup) individual restrictions based on different local spammer databases, DNS checkings, RBL-style databases.
The biggest complain we received after installing the patched system was: "But how could I let in E-mail messages from foo address despite my restriction settings?"
With the parametrized access check we can make possible for the users to set up individual exceptions against their individual UCE settings.
The example from our mail gateways:
>>> /etc/postfix/main.cf <<< # The definition of the "atomic" restriction classes smtpd_restriction_classes = class_ip, class_sender, class_dns, class_kfki, class_cern, class_rbl, class_dul, class_orbs, class_rss class_ip = check_client_access hash:/etc/postfix/ip_address # ... class_rss = rbl:/etc/postfix/rbl_domain_rss # We have per site default restriction, so the users must have # an excplicite 'permit' at the end of their restrictions. smtpd_recipient_restrictions = reject_non_fqdn_sender, reject_non_fqdn_recipient, permit_mynetworks, reject_unauth_destination, check_access \${sender}|\$recipient hash:/etc/postfix/user_exceptions, check_recipient_access hash:/etc/postfix/user_restrictions, class_ip, class_sender, class_dns, class_kfki, class_cern, class_rbl, class_dul, class_orbs, class_rss >>> /etc/postfix/user_exceptions <<< innocent1@bad.domain|one_user@our.domain OK innocent2@another.domain|second_user@our.domain OK >>> /etc/postfix/user_restrictions <<< abuse@kfki.hu permit foo@kfki.hu class_ip, class_sender, class_kfki, class_rbl, permit bar@kfki.hu class_ip, class_sender, class_kfki, permit
Enjoy it!
József Kadlecsik