From: Wietse Venema Date: Thu, 30 May 2013 13:57:35 +0000 (-0400) Subject: postfix-2.11-20130530-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2F20130405-nonprod;p=thirdparty%2Fpostfix.git postfix-2.11-20130530-nonprod --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 88f1aa130..bd70a8bcb 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -196,6 +196,7 @@ -TNAME_CODE -TNAME_MASK -TNBBIO +-TOPTIONS -TPC_DBMS_INFO -TPC_EVAL_CTX -TPC_MASTER_ENT @@ -289,15 +290,16 @@ -TSSL -TSSL_CTX -TSSL_SESSION +-TSTATE -TSTRING_LIST -TSTRING_TABLE -TSYS_EXITS_DETAIL -TTLSMGR_SCACHE -TTLSP_STATE -TTLS_APPL_STATE +-TTLS_CERTS -TTLS_CLIENT_INIT_PROPS -TTLS_CLIENT_START_PROPS --TTLS_CERTS -TTLS_DANE -TTLS_PKEYS -TTLS_PRNG_SEED_INFO @@ -349,6 +351,3 @@ -Tsize_t -Tssize_t -Ttime_t --TRESPONSE --TSTATE --TOPTIONS diff --git a/postfix/HISTORY b/postfix/HISTORY index acea1db13..e608f5a92 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -18408,8 +18408,27 @@ Apologies for any names omitted. Documentation: in smtpd.c, the comment that justifies the 454 reply for "TLS unavailable" cited the wrong RFC. +20130404 + + Human factors: warning when a main.cf parameter has multiple + entries with different values. File: util/dict.c. + 20130405 + Feature: the recipient_delimiter parameter can now specify + a set of characters. A user name is now separated from its + address extension by the first character that matches the + recipient_delimiter set. Files: proto/postconf.proto, + src/global/mail_addr_find.c, src/global/mail_params.c, + src/global/split_addr.c, src/global/split_addr.h, + src/global/strip_addr.c, src/global/strip_addr.h, + src/global/strip_addr.ref, src/local/bounce_workaround.c, + src/local/local.c, src/local/local_expand.c, src/local/recipient.c, + src/local/resolve.c, src/oqmgr/qmgr_message.c, src/pipe/pipe.c, + src/qmgr/qmgr_message.c, src/smtpd/smtpd.c, + src/smtpd/smtpd_check.c, src/trivial-rewrite/transport.c, + src/trivial-rewrite/trivial-rewrite.c. + Feature: support for trust anchors, i.e. CA certificates or public keys that will be used instead of conventional root certificates, and revised fingerprint support. This @@ -18423,6 +18442,21 @@ Apologies for any names omitted. tls/tls_fprint.c, tls/tls_misc.c, tls/tls_verify.c, util/argv.c, util/argv.h. +20130409 + + Documentation: pointers to other actions under "ACCEPT + ACTIONS" and "REJECT ACTIONS". File: proto/access. + +20130410 + + Cleanup: more uniform permutation in dns_rr() by Victor + Duchovni & Son. File: dns/dns_rr.c. + +20130411 + + Documentation: clarified text about result formats. Files: + proto/canonical, proto/virtual. + 20130414 Cleanup: the SMTP client connection management code now @@ -18515,6 +18549,17 @@ Apologies for any names omitted. posttls-finger/tlsmgrmem.c, posttls-finger/tlsmgrmem.h, tls/tls.h, tls/tls_misc.c. +20130423 + + Bugfix (introduced: Postfix 2.0): when myhostname is not + listed in mydestination, the trivial-rewrite resolver may + log "do not list in both mydestination + and ". The fix is + to re-resolve a domain-less address after adding $myhostname + as the surrogate domain, so that it pops out with the right + address-class label. Problem reported by Quanah Gibson-Mount. + File: trivial-rewrite/resolve.c. + 20130425 Non-production fixes: revert to using proxies (sender, @@ -18558,8 +18603,42 @@ Apologies for any names omitted. smtp/smtp.c, smtp/smtp.h, smtp/smtp_params.c, smtp/smtp_tls_policy.c, tls/tls.h, tls/tls_level.c. +20130512 + + Feature: allow an SMTP client to skip postscreen(8) tests + before or after the 220 greeting, based on its DNSBL score. + Suggested by Rob McGee (/dev/rob0). Files: mantools/postlink, + proto/postconf.proto, global/mail_params.h, + postscreen/postscreen.c, postscreen/postscreen.h, + postscreen/postscreen_early.c, postscreen/postscreen_state.c, + postscreen/postscreen_tests.c. + +20130513 + + Bugfix (introduced: 20130512): postscreen logged no "PASS + NEW" event when the pregreet tests were turned off and the + postscreen_dnsbl_whitelist_treshold feature was turned on. + Reported by Rob McGee (/dev/rob0). Files: postscreen/postscreen.h, + postscreen/postscreen_early.c. + + Bugfix (introduced: 20130512): postscreen panic because the + logic for dnsbl result retrieval was changed. Reported by + Noel Jones. File: postscreen/postscreen_early.c. + +20130517 + + Cleanup: just like the postscreen DNS block test will use + partial scores when some DNS lookup result is unavailable, + the postscreen_dnsbl_whitelist_treshold feature will now + use partial scores instead of ignoring them. File: + postscreen/postscreen_early.c. + 20130518 + Bugfix (introduced: 1997): memory leak after error while + forwarding mail through the cleanup server. Viktor found + one, Wietse eliminated the rest. File: local/forward.c. + Feature: posttls-finger protocol and cipher grade selection options. Leave protocol debug flags active across reconnects, only suppress redundant logging of the certificate details. @@ -18579,4 +18658,3 @@ Apologies for any names omitted. Dukhovni. Files: proto/TLS_README.html, proto/postconf.proto, src/global/mail_params.h, src/tls/tls.h, src/tls/tls_client.c, src/tls/tls_misc.c. - diff --git a/postfix/README_FILES/DATABASE_README b/postfix/README_FILES/DATABASE_README index a9fe9b06a..6ba7772fd 100644 --- a/postfix/README_FILES/DATABASE_README +++ b/postfix/README_FILES/DATABASE_README @@ -99,9 +99,9 @@ performance. read/write access conflicts and gives the new data to Postfix once that data is available. - * If you change a regexp: or pcre: file then Postfix may or may not pick up - the file changes immediately. This is because a Postfix process reads the - entire file into memory once and never examines the file again. + * If you change a regexp:, pcre:, cidr: or texthash: file then Postfix may + not pick up the file changes immediately. This is because a Postfix process + reads the entire file into memory once and never examines the file again. o If the file is used by a short-running process such as smtpd(8), cleanup(8) or local(8), there is no need to execute "postfix reload" diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 765651348..4ca9e12dc 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -14,6 +14,30 @@ specifies the release date of a stable release or snapshot release. If you upgrade from Postfix 2.9 or earlier, read RELEASE_NOTES-2.10 before proceeding. +Major changes with snapshot 20130512 +==================================== + +Allow an SMTP client to skip postscreen(8) tests based on its +postscreen_dnsbl_sites score. + +Specify a negative "postscreen_dnsbl_whitelist_threshold" to enable +this feature. When a client passes the threshold value without +having failed other tests, all pending or disabled tests are flagged +as completed. + +Major changes with snapshot 20130405 +==================================== + +The recipient_delimiter parameter can now specify a set of characters. +A user name is now separated from its address extension by the first +character that matches the recipient_delimiter set. + +For example, specify "recipient_delimiter = +-" to support both the +Postfix-style "+" and the qmail-style "-" extension delimiter. + +As before, this implementation recognizes one delimiter character +per email address, and one address extension per email address. + Major changes with snapshot 20130319 ==================================== diff --git a/postfix/WISHLIST b/postfix/WISHLIST index a870849aa..dc17013b0 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -2,40 +2,18 @@ Wish list: Things to do before the stable release: + Spell-check, double-word check, and HTML validator check. + Remove this file from the stable release. Things to do after the stable release: - Spellcheck and double-word check. - - Fix a false cache-sharing problem. After the SASL handshake, - the connection cache client does not store SASL credentials - in the destination properties; it stores them in the endpoint - label only. When the connection cache client reuses the - connection with smtp_reuse_nexthop(), it does not restore - the SASL credentials. When it saves the connection afterwards, - it creates a new endpoint label without SASL credentials, - so the authenticated connection can now be used for unrelated - deliveries. + Discourage the use of "after 220" tests in POSTSCREEN_README + and the documentation of individual parameter settings. Begin code revision, after DANE support stabilizes. This should be one pass that changes only names and no code. - Run new source files through ccformat. If I do it now, - almost every block of code or comments is changed. Having - different formatting styles in the same project is PROBLEMATIC. - There is a reason why ccformat is included with source code. - - Embed all statement-like macros in do { ... } while (0). - This is especially necessary with macros that contain an - "if" statement, or that contain multiple statements. - - Spell-check, double-word check, and HTML validator check. - - Use make(1) and cc(1) to convert the C++ like templates - into debuggable source code, such that each statement has - its own distinct line number (what a revolutionary idea). - All source code must specify its original author and license statement. Some code modules specify Lutz Jaenicke as the original author and fall under his liberal license. @@ -47,13 +25,6 @@ Wish list: Wietse as the original author, and Lutz Jaenicke's license, which is wrong. - Generally, macro and function names should make a program - more clear, not merely reduce the number of programmer - keystrokes; similar considerations hold for variable names - and constants. Avoid 1-letter names except "for i=1 to - some_bound". Instead of bare numbers use named constants - in function argument lists. - Code clarity: replace obscure macro/function names: for example SMTP_X(XXX) -> VAR_SMTP(XXX), as the purpose is to choose between VAR_SMTP_XXX or VAR_LMTP_XXX; replace @@ -62,13 +33,6 @@ Wish list: contents of a buffer with the specified length). Replace r with res_opt, ditto for other 1-letter names. - Unnecessary complexity: the SMTP_SESSION "tls" field is - mandatory (always allocated) therefore the content can be - a permanent part of the SMTP_SESSION structure, just like - SASL-related information. This avoids silly indirections - all over the code, as well as awkward smtp_tls_sess_alloc() - error semantics. - We have smtp_host_lookup, smtp_dns_resolver_options, and now smtp_dns_support_level. Of these, smtp_dns_resolver_options is orthogonal but the rest has overlap. diff --git a/postfix/conf/access b/postfix/conf/access index 9df9991a4..cadc57d65 100644 --- a/postfix/conf/access +++ b/postfix/conf/access @@ -178,6 +178,8 @@ # mat is generated by address-based relay authoriza- # tion schemes such as pop-before-smtp. # +# For other accept actions, see "OTHER ACTIONS" below. +# # REJECT ACTIONS # Postfix version 2.3 and later support enhanced status # codes as defined in RFC 3463. When no code is specified @@ -248,6 +250,8 @@ # # This feature is available in Postfix 2.1 and later. # +# For other reject actions, see "OTHER ACTIONS" below. +# # OTHER ACTIONS # restriction... # Apply the named UCE restriction(s) (permit, reject, diff --git a/postfix/conf/canonical b/postfix/conf/canonical index 720db18cc..fc0c821ad 100644 --- a/postfix/conf/canonical +++ b/postfix/conf/canonical @@ -66,9 +66,9 @@ # TABLE FORMAT # The input format for the postmap(1) command is as follows: # -# pattern result +# pattern address # When pattern matches a mail address, replace it by -# the corresponding result. +# the corresponding address. # # blank lines and comments # Empty lines and whitespace-only lines are ignored, diff --git a/postfix/conf/virtual b/postfix/conf/virtual index 3be6ab985..9f4b3d770 100644 --- a/postfix/conf/virtual +++ b/postfix/conf/virtual @@ -65,9 +65,9 @@ # TABLE FORMAT # The input format for the postmap(1) command is as follows: # -# pattern result +# pattern address, address, ... # When pattern matches a mail address, replace it by -# the corresponding result. +# the corresponding address. # # blank lines and comments # Empty lines and whitespace-only lines are ignored, diff --git a/postfix/html/DATABASE_README.html b/postfix/html/DATABASE_README.html index 775db1445..68606057a 100644 --- a/postfix/html/DATABASE_README.html +++ b/postfix/html/DATABASE_README.html @@ -156,7 +156,8 @@ SQL, there is no need to execute "postfix reload". The LDAP, NIS or SQL server takes care of read/write access conflicts and gives the new data to Postfix once that data is available.

-
  • If you change a regexp: or pcre: file then Postfix may or +

  • If you change a regexp:, pcre:, cidr: or texthash: file +then Postfix may not pick up the file changes immediately. This is because a Postfix process reads the entire file into memory once and never examines the file again.

    diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index 0696fcdbf..d191bd561 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -184,6 +184,8 @@ ACCESS(5) ACCESS(5) mat is generated by address-based relay authoriza- tion schemes such as pop-before-smtp. + For other accept actions, see "OTHER ACTIONS" below. + REJECT ACTIONS Postfix version 2.3 and later support enhanced status codes as defined in RFC 3463. When no code is specified @@ -254,6 +256,8 @@ ACCESS(5) ACCESS(5) This feature is available in Postfix 2.1 and later. + For other reject actions, see "OTHER ACTIONS" below. + OTHER ACTIONS restriction... Apply the named UCE restriction(s) (permit, reject, diff --git a/postfix/html/canonical.5.html b/postfix/html/canonical.5.html index c97f910ac..58c522cef 100644 --- a/postfix/html/canonical.5.html +++ b/postfix/html/canonical.5.html @@ -52,7 +52,7 @@ CANONICAL(5) CANONICAL(5) remote_header_rewrite_domain configuration parameter spec- ifies a non-empty value. To get the behavior before Post- fix 2.2, specify "local_header_rewrite_clients = - static:all". + static:all". Typically, one would use the canonical(5) table to replace login names by Firstname.Lastname, or to clean up @@ -72,9 +72,9 @@ CANONICAL(5) CANONICAL(5) TABLE FORMAT The input format for the postmap(1) command is as follows: - pattern result + pattern address When pattern matches a mail address, replace it by - the corresponding result. + the corresponding address. blank lines and comments Empty lines and whitespace-only lines are ignored, diff --git a/postfix/html/cidr_table.5.html b/postfix/html/cidr_table.5.html index 09689d910..18cb8206a 100644 --- a/postfix/html/cidr_table.5.html +++ b/postfix/html/cidr_table.5.html @@ -43,6 +43,10 @@ CIDR_TABLE(5) CIDR_TABLE(5) address is a sequence of three to eight hexadecimal octet pairs separated by ":". + The network_mask is the number of high-order bits + in the network_address that the search string must + match. + Before comparisons are made, lookup keys and table entries are converted from string to binary. There- fore table entries will be matched regardless of diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index e2cfed120..ee4dff4de 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -608,8 +608,8 @@ LOCAL(8) LOCAL(8) tory. recipient_delimiter (empty) - The separator between user names and address exten- - sions (user+foo). + The set of characters that can separate a user name + from its address extension (user+foo). require_home_directory (no) Require that a local(8) recipient's home directory diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index 94be20a98..afe2b5a2e 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -481,8 +481,8 @@ PIPE(8) PIPE(8) tory. recipient_delimiter (empty) - The separator between user names and address exten- - sions (user+foo). + The set of characters that can separate a user name + from its address extension (user+foo). syslog_facility (mail) The syslog facility of Postfix logging. diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index d5363a1d0..0660da30b 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -1451,7 +1451,9 @@ with the character set that is specified with the
    $recipient_delimiter
    -
    The system-wide recipient address extension delimiter.
    +
    The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier).
    ${name?value}
    @@ -3254,7 +3256,9 @@ filtered with the character set that is specified with the
    $recipient_delimiter
    -
    The system-wide recipient address extension delimiter.
    +
    The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier).
    ${name?value}
    @@ -5319,7 +5323,9 @@ The following $name expansions are done on $recipient_delimiter -
    The system-wide recipient address extension delimiter.
    +
    The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier).
    $shell
    @@ -7410,6 +7416,25 @@ one-letter suffix that specifies the time unit). Time units: s

    This feature is available in Postfix 2.8.

    + + +
    postscreen_dnsbl_whitelist_threshold +(default: 0)
    + +

    Allow a remote SMTP client to skip "before" and "after 220 +greeting" protocol tests, based on its combined DNSBL score as +defined with the postscreen_dnsbl_sites parameter.

    + +

    Specify a negative value to enable this feature. When a client +passes the postscreen_dnsbl_whitelist_threshold without having +failed other tests, all pending or disabled tests are flagged as +completed with a time-to-live value equal to postscreen_dnsbl_ttl. +When a test was already completed, its time-to-live value is updated +if it was less than postscreen_dnsbl_ttl.

    + +

    This feature is available in Postfix 2.11.

    + +
    postscreen_enforce_tls @@ -8509,22 +8534,53 @@ Example:
    recipient_delimiter (default: empty)
    -

    -The separator between user names and address extensions (user+foo). -See canonical(5), local(8), relocated(5) and virtual(5) for the -effects this has on aliases, canonical, virtual, relocated and -on .forward file lookups. Basically, the software tries user+foo -and .forward+foo before trying user and .forward. +

    The set of characters that can separate a user name from its +address extension (user+foo). See canonical(5), local(8), relocated(5) +and virtual(5) for the effects this has on aliases, canonical, +virtual, and relocated lookups. Basically, the software tries +user+foo and .forward+foo before trying user and .forward.

    + +

    This implementation recognizes one delimiter character per email +address, and one address extension per email address.

    + +

    When the recipient_delimiter set contains multiple characters +(Postfix 2.11 and later), a user name is separated from its address +extension by the first character that matches the recipient_delimiter +set.

    + +

    When used in forward_path, ${recipient_delimiter} is replaced +with the recipient delimiter that was found in the recipient email +address (Postfix 2.11 and later), or it is replaced with the main.cf +recipient_delimiter parameter value (Postfix 2.10 and earlier).

    +

    The recipient_delimiter is not applied to the mailer-daemon +address, the postmaster address, or the double-bounce address. With +the default "owner_request_special = yes" setting, the recipient_delimiter +is also not applied to addresses with the special "owner-" prefix +or the special "-request" suffix.

    +

    -Example: +Examples:

    +# Handle Postfix-style extensions.
     recipient_delimiter = +
     
    +
    +# Handle both Postfix and qmail extensions (Postfix 2.11 and later).
    +recipient_delimiters = +-
    +
    + +
    +# Use .forward for mail without address extension, and for mail with
    +# an unrecognized address extension.
    +forward_path = $home/.forward${recipient_delimiter}${extension},
    +    $home/.forward
    +
    +
    diff --git a/postfix/html/postscreen.8.html b/postfix/html/postscreen.8.html index 12f9b02f1..f746a4650 100644 --- a/postfix/html/postscreen.8.html +++ b/postfix/html/postscreen.8.html @@ -245,25 +245,33 @@ POSTSCREEN(8) POSTSCREEN(8) The internal service that postscreen(8) hands off allowed connections to. + Available in Postfix version 2.11 and later: + + postscreen_dnsbl_whitelist_threshold (0) + Allow a remote SMTP client to skip "before" and + "after 220 greeting" protocol tests, based on its + combined DNSBL score as defined with the + postscreen_dnsbl_sites parameter. + AFTER-GREETING TESTS - These tests are executed after the remote SMTP client + These tests are executed after the remote SMTP client receives the "220 servername" greeting. If a client passes - all tests during this phase, it will receive a 4XX - response to RCPT TO commands until the client hangs up. + all tests during this phase, it will receive a 4XX + response to RCPT TO commands until the client hangs up. After this, the client will be allowed to talk directly to a Postfix SMTP server process. postscreen_bare_newline_action (ignore) - The action that postscreen(8) takes when a remote - SMTP client sends a bare newline character, that + The action that postscreen(8) takes when a remote + SMTP client sends a bare newline character, that is, a newline not preceded by carriage return. postscreen_bare_newline_enable (no) - Enable "bare newline" SMTP protocol tests in the + Enable "bare newline" SMTP protocol tests in the postscreen(8) server. postscreen_disable_vrfy_command ($disable_vrfy_command) - Disable the SMTP VRFY command in the postscreen(8) + Disable the SMTP VRFY command in the postscreen(8) daemon. postscreen_forbidden_commands ($smtpd_forbidden_commands) @@ -271,159 +279,159 @@ POSTSCREEN(8) POSTSCREEN(8) siders in violation of the SMTP protocol. postscreen_helo_required ($smtpd_helo_required) - Require that a remote SMTP client sends HELO or + Require that a remote SMTP client sends HELO or EHLO before commencing a MAIL transaction. postscreen_non_smtp_command_action (drop) - The action that postscreen(8) takes when a remote - SMTP client sends non-SMTP commands as specified + The action that postscreen(8) takes when a remote + SMTP client sends non-SMTP commands as specified with the postscreen_forbidden_commands parameter. postscreen_non_smtp_command_enable (no) - Enable "non-SMTP command" tests in the + Enable "non-SMTP command" tests in the postscreen(8) server. postscreen_pipelining_action (enforce) - The action that postscreen(8) takes when a remote - SMTP client sends multiple commands instead of - sending one command and waiting for the server to + The action that postscreen(8) takes when a remote + SMTP client sends multiple commands instead of + sending one command and waiting for the server to respond. postscreen_pipelining_enable (no) - Enable "pipelining" SMTP protocol tests in the + Enable "pipelining" SMTP protocol tests in the postscreen(8) server. CACHE CONTROLS postscreen_cache_cleanup_interval (12h) - The amount of time between postscreen(8) cache + The amount of time between postscreen(8) cache cleanup runs. postscreen_cache_map (btree:$data_direc- tory/postscreen_cache) - Persistent storage for the postscreen(8) server + Persistent storage for the postscreen(8) server decisions. postscreen_cache_retention_time (7d) The amount of time that postscreen(8) will cache an - expired temporary whitelist entry before it is + expired temporary whitelist entry before it is removed. postscreen_bare_newline_ttl (30d) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful "bare newline" SMTP proto- col test. postscreen_dnsbl_ttl (1h) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful DNS blocklist test. postscreen_greet_ttl (1d) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful PREGREET test. postscreen_non_smtp_command_ttl (30d) - The amount of time that postscreen(8) will use the - result from a successful "non_smtp_command" SMTP + The amount of time that postscreen(8) will use the + result from a successful "non_smtp_command" SMTP protocol test. postscreen_pipelining_ttl (30d) - The amount of time that postscreen(8) will use the + The amount of time that postscreen(8) will use the result from a successful "pipelining" SMTP protocol test. RESOURCE CONTROLS line_length_limit (2048) - Upon input, long lines are chopped up into pieces - of at most this length; upon delivery, long lines + Upon input, long lines are chopped up into pieces + of at most this length; upon delivery, long lines are reconstructed. postscreen_client_connection_count_limit ($smtpd_client_connection_count_limit) - How many simultaneous connections any remote SMTP - client is allowed to have with the postscreen(8) + How many simultaneous connections any remote SMTP + client is allowed to have with the postscreen(8) daemon. postscreen_command_count_limit (20) - The limit on the total number of commands per SMTP - session for postscreen(8)'s built-in SMTP protocol + The limit on the total number of commands per SMTP + session for postscreen(8)'s built-in SMTP protocol engine. postscreen_command_time_limit (${stress?10}${stress:300}s) - The time limit to read an entire command line with + The time limit to read an entire command line with postscreen(8)'s built-in SMTP protocol engine. postscreen_post_queue_limit ($default_process_limit) - The number of clients that can be waiting for ser- + The number of clients that can be waiting for ser- vice from a real Postfix SMTP server process. postscreen_pre_queue_limit ($default_process_limit) - The number of non-whitelisted clients that can be - waiting for a decision whether they will receive + The number of non-whitelisted clients that can be + waiting for a decision whether they will receive service from a real Postfix SMTP server process. postscreen_watchdog_timeout (10s) - How much time a postscreen(8) process may take to - respond to a remote SMTP client command or to per- + How much time a postscreen(8) process may take to + respond to a remote SMTP client command or to per- form a cache operation before it is terminated by a built-in watchdog timer. STARTTLS CONTROLS postscreen_tls_security_level ($smtpd_tls_security_level) - The SMTP TLS security level for the postscreen(8) - server; when a non-empty value is specified, this + The SMTP TLS security level for the postscreen(8) + server; when a non-empty value is specified, this overrides the obsolete parameters postscreen_use_tls and postscreen_enforce_tls. tlsproxy_service_name (tlsproxy) - The name of the tlsproxy(8) service entry in mas- + The name of the tlsproxy(8) service entry in mas- ter.cf. OBSOLETE STARTTLS SUPPORT CONTROLS - These parameters are supported for compatibility with + These parameters are supported for compatibility with smtpd(8) legacy parameters. postscreen_use_tls ($smtpd_use_tls) - Opportunistic TLS: announce STARTTLS support to + Opportunistic TLS: announce STARTTLS support to remote SMTP clients, but do not require that clients use TLS encryption. postscreen_enforce_tls ($smtpd_enforce_tls) - Mandatory TLS: announce STARTTLS support to remote - SMTP clients, and require that clients use TLS + Mandatory TLS: announce STARTTLS support to remote + SMTP clients, and require that clients use TLS encryption. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. delay_logging_resolution_limit (2) - The maximal number of digits after the decimal + The maximal number of digits after the decimal point when logging sub-second delay values. command_directory (see 'postconf -d' output) - The location of all postfix administrative com- + The location of all postfix administrative com- mands. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for an incoming connection + The maximum amount of time that an idle Postfix + daemon process waits for an incoming connection before terminating voluntarily. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". SEE ALSO @@ -436,14 +444,14 @@ POSTSCREEN(8) POSTSCREEN(8) POSTSCREEN_README, Postfix Postscreen Howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY This service was introduced with Postfix version 2.8. - Many ideas in postscreen(8) were explored in earlier work - by Michael Tokarev, in OpenBSD spamd, and in MailChannels + Many ideas in postscreen(8) were explored in earlier work + by Michael Tokarev, in OpenBSD spamd, and in MailChannels Traffic Control. AUTHOR(S) diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index dc934bb90..4b87f2212 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -102,11 +102,6 @@ SMTPD(8) SMTPD(8) Available in Postfix version 2.1 and later: - resolve_null_domain (no) - Resolve an address that ends in the "@" null domain - as if the local hostname were specified, instead of - rejecting the address as invalid. - smtpd_reject_unlisted_sender (no) Request that the Postfix SMTP server rejects mail from unknown sender addresses, even when no @@ -1293,8 +1288,8 @@ SMTPD(8) SMTPD(8) tory. recipient_delimiter (empty) - The separator between user names and address exten- - sions (user+foo). + The set of characters that can separate a user name + from its address extension (user+foo). smtpd_banner ($myhostname ESMTP $mail_name) The text that follows the 220 status code in the diff --git a/postfix/html/trivial-rewrite.8.html b/postfix/html/trivial-rewrite.8.html index 6da7aced5..e417e9d08 100644 --- a/postfix/html/trivial-rewrite.8.html +++ b/postfix/html/trivial-rewrite.8.html @@ -97,11 +97,15 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) Resolve a recipient address safely instead of cor- rectly, by looking inside quotes. + Available with Postfix version 2.1 and later: + resolve_null_domain (no) Resolve an address that ends in the "@" null domain as if the local hostname were specified, instead of rejecting the address as invalid. + Available with Postfix version 2.3 and later: + resolve_numeric_domain (no) Resolve "user@ipaddress" as "user@[ipaddress]", instead of rejecting the address as invalid. @@ -133,8 +137,8 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) information. recipient_delimiter (empty) - The separator between user names and address exten- - sions (user+foo). + The set of characters that can separate a user name + from its address extension (user+foo). swap_bangpath (yes) Enable the rewriting of "site!user" into diff --git a/postfix/html/virtual.5.html b/postfix/html/virtual.5.html index 5306885f2..e27077d0e 100644 --- a/postfix/html/virtual.5.html +++ b/postfix/html/virtual.5.html @@ -71,9 +71,9 @@ VIRTUAL(5) VIRTUAL(5) TABLE FORMAT The input format for the postmap(1) command is as follows: - pattern result + pattern address, address, ... When pattern matches a mail address, replace it by - the corresponding result. + the corresponding address. blank lines and comments Empty lines and whitespace-only lines are ignored, diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index 64d678ad5..c1f45ae58 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -172,6 +172,8 @@ Accept the address etc. that matches the pattern. An all-numerical result is treated as OK. This format is generated by address-based relay authorization schemes such as pop-before-smtp. +.PP +For other accept actions, see "OTHER ACTIONS" below. .SH "REJECT ACTIONS" .na .nf @@ -237,6 +239,8 @@ response message. Prior to Postfix 2.6, the SMTP reply code is 450. .sp This feature is available in Postfix 2.1 and later. +.PP +For other reject actions, see "OTHER ACTIONS" below. .SH "OTHER ACTIONS" .na .nf diff --git a/postfix/man/man5/canonical.5 b/postfix/man/man5/canonical.5 index 1bf8d53eb..edf2a306f 100644 --- a/postfix/man/man5/canonical.5 +++ b/postfix/man/man5/canonical.5 @@ -73,9 +73,9 @@ lookup fields can match both upper and lower case. .ad .fi The input format for the \fBpostmap\fR(1) command is as follows: -.IP "\fIpattern result\fR" +.IP "\fIpattern address\fR" When \fIpattern\fR matches a mail address, replace it by the -corresponding \fIresult\fR. +corresponding \fIaddress\fR. .IP "blank lines and comments" Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'. diff --git a/postfix/man/man5/cidr_table.5 b/postfix/man/man5/cidr_table.5 index f25ab45cd..5619b2b09 100644 --- a/postfix/man/man5/cidr_table.5 +++ b/postfix/man/man5/cidr_table.5 @@ -43,6 +43,9 @@ An IPv4 network address is a sequence of four decimal octets separated by ".", and an IPv6 network address is a sequence of three to eight hexadecimal octet pairs separated by ":". +The \fInetwork_mask\fR is the number of high-order bits in +the \fInetwork_address\fR that the search string must match. + Before comparisons are made, lookup keys and table entries are converted from string to binary. Therefore table entries will be matched regardless of redundant zero characters. diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index d073fc9ca..d6fa80b15 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -835,7 +835,9 @@ The recipient domain. The entire recipient localpart. .br .IP "\fB$recipient_delimiter\fR" -The system-wide recipient address extension delimiter. +The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier). .br .IP "\fB${name?value}\fR" Expands to \fIvalue\fR when \fI$name\fR is non-empty. @@ -1919,7 +1921,9 @@ The recipient domain. The entire recipient localpart. .br .IP "\fB$recipient_delimiter\fR" -The system-wide recipient address extension delimiter. +The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier). .br .IP "\fB${name?value}\fR" Expands to \fIvalue\fR when \fI$name\fR is non-empty. @@ -3073,7 +3077,9 @@ The entire recipient address localpart. The full recipient address. .br .IP "\fB$recipient_delimiter\fR" -The system-wide recipient address extension delimiter. +The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier). .br .IP "\fB$shell\fR" The recipient's login shell. @@ -4458,6 +4464,19 @@ 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_dnsbl_whitelist_threshold (default: 0) +Allow a remote SMTP client to skip "before" and "after 220 +greeting" protocol tests, based on its combined DNSBL score as +defined with the postscreen_dnsbl_sites parameter. +.PP +Specify a negative value to enable this feature. When a client +passes the postscreen_dnsbl_whitelist_threshold without having +failed other tests, all pending or disabled tests are flagged as +completed with a time-to-live value equal to postscreen_dnsbl_ttl. +When a test was already completed, its time-to-live value is updated +if it was less than postscreen_dnsbl_ttl. +.PP +This feature is available in Postfix 2.11. .SH postscreen_enforce_tls (default: $smtpd_enforce_tls) Mandatory TLS: announce STARTTLS support to remote SMTP clients, and require that clients use TLS encryption. See smtpd_postscreen_enforce_tls @@ -5100,21 +5119,61 @@ recipient_canonical_maps = hash:/etc/postfix/recipient_canonical .ad .ft R .SH recipient_delimiter (default: empty) -The separator between user names and address extensions (user+foo). -See \fBcanonical\fR(5), \fBlocal\fR(8), \fBrelocated\fR(5) and \fBvirtual\fR(5) for the -effects this has on aliases, canonical, virtual, relocated and -on .forward file lookups. Basically, the software tries user+foo -and .forward+foo before trying user and .forward. +The set of characters that can separate a user name from its +address extension (user+foo). See \fBcanonical\fR(5), \fBlocal\fR(8), \fBrelocated\fR(5) +and \fBvirtual\fR(5) for the effects this has on aliases, canonical, +virtual, and relocated lookups. Basically, the software tries +user+foo and .forward+foo before trying user and .forward. +.PP +This implementation recognizes one delimiter character per email +address, and one address extension per email address. +.PP +When the recipient_delimiter set contains multiple characters +(Postfix 2.11 and later), a user name is separated from its address +extension by the first character that matches the recipient_delimiter +set. +.PP +When used in forward_path, ${recipient_delimiter} is replaced +with the recipient delimiter that was found in the recipient email +address (Postfix 2.11 and later), or it is replaced with the main.cf +recipient_delimiter parameter value (Postfix 2.10 and earlier). +.PP +The recipient_delimiter is not applied to the mailer-daemon +address, the postmaster address, or the double-bounce address. With +the default "owner_request_special = yes" setting, the recipient_delimiter +is also not applied to addresses with the special "owner-" prefix +or the special "-request" suffix. .PP -Example: +Examples: .PP .nf .na .ft C +# Handle Postfix-style extensions. recipient_delimiter = + .fi .ad .ft R +.PP +.nf +.na +.ft C +# Handle both Postfix and qmail extensions (Postfix 2.11 and later). +recipient_delimiters = +- +.fi +.ad +.ft R +.PP +.nf +.na +.ft C +# Use .forward for mail without address extension, and for mail with +# an unrecognized address extension. +forward_path = $home/.forward${recipient_delimiter}${extension}, + $home/.forward +.fi +.ad +.ft R .SH reject_code (default: 554) The numerical Postfix SMTP server response code when a remote SMTP client request is rejected by the "reject" restriction. diff --git a/postfix/man/man5/virtual.5 b/postfix/man/man5/virtual.5 index da139f8a1..a8c80ecd4 100644 --- a/postfix/man/man5/virtual.5 +++ b/postfix/man/man5/virtual.5 @@ -71,9 +71,9 @@ lookup fields can match both upper and lower case. .ad .fi The input format for the \fBpostmap\fR(1) command is as follows: -.IP "\fIpattern result\fR" +.IP "\fIpattern address, address, ...\fR" When \fIpattern\fR matches a mail address, replace it by the -corresponding \fIresult\fR. +corresponding \fIaddress\fR. .IP "blank lines and comments" Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'. diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index 5b6bc81b4..b872ce6fe 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -575,7 +575,8 @@ key to the lookup result. .IP "\fBqueue_directory (see 'postconf -d' output)\fR" The location of the Postfix top-level queue directory. .IP "\fBrecipient_delimiter (empty)\fR" -The separator between user names and address extensions (user+foo). +The set of characters that can separate a user name from its +address extension (user+foo). .IP "\fBrequire_home_directory (no)\fR" Require that a \fBlocal\fR(8) recipient's home directory exists before mail delivery is attempted. diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8 index ec088ac6b..a24a5ee48 100644 --- a/postfix/man/man8/pipe.8 +++ b/postfix/man/man8/pipe.8 @@ -412,7 +412,8 @@ The process name of a Postfix command or daemon process. .IP "\fBqueue_directory (see 'postconf -d' output)\fR" The location of the Postfix top-level queue directory. .IP "\fBrecipient_delimiter (empty)\fR" -The separator between user names and address extensions (user+foo). +The set of characters that can separate a user name from its +address extension (user+foo). .IP "\fBsyslog_facility (mail)\fR" The syslog facility of Postfix logging. .IP "\fBsyslog_name (see 'postconf -d' output)\fR" diff --git a/postfix/man/man8/postscreen.8 b/postfix/man/man8/postscreen.8 index 821748de8..2aa1aacb2 100644 --- a/postfix/man/man8/postscreen.8 +++ b/postfix/man/man8/postscreen.8 @@ -242,6 +242,12 @@ up to 6 seconds otherwise). .IP "\fBsmtpd_service_name (smtpd)\fR" The internal service that \fBpostscreen\fR(8) hands off allowed connections to. +.PP +Available in Postfix version 2.11 and later: +.IP "\fBpostscreen_dnsbl_whitelist_threshold (0)\fR" +Allow a remote SMTP client to skip "before" and "after 220 +greeting" protocol tests, based on its combined DNSBL score as +defined with the postscreen_dnsbl_sites parameter. .SH "AFTER-GREETING TESTS" .na .nf diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index d6c8bce00..22e7b6603 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -106,10 +106,6 @@ commands are enclosed with <>, and that those addresses do not contain RFC 822 style comments or phrases. .PP Available in Postfix version 2.1 and later: -.IP "\fBresolve_null_domain (no)\fR" -Resolve an address that ends in the "@" null domain as if the -local hostname were specified, instead of rejecting the address as -invalid. .IP "\fBsmtpd_reject_unlisted_sender (no)\fR" Request that the Postfix SMTP server rejects mail from unknown sender addresses, even when no explicit reject_unlisted_sender @@ -1012,7 +1008,8 @@ The process name of a Postfix command or daemon process. .IP "\fBqueue_directory (see 'postconf -d' output)\fR" The location of the Postfix top-level queue directory. .IP "\fBrecipient_delimiter (empty)\fR" -The separator between user names and address extensions (user+foo). +The set of characters that can separate a user name from its +address extension (user+foo). .IP "\fBsmtpd_banner ($myhostname ESMTP $mail_name)\fR" The text that follows the 220 status code in the SMTP greeting banner. diff --git a/postfix/man/man8/trivial-rewrite.8 b/postfix/man/man8/trivial-rewrite.8 index ca85bd36b..ff0b25a99 100644 --- a/postfix/man/man8/trivial-rewrite.8 +++ b/postfix/man/man8/trivial-rewrite.8 @@ -101,10 +101,14 @@ The text below provides only a parameter summary. See .IP "\fBresolve_dequoted_address (yes)\fR" Resolve a recipient address safely instead of correctly, by looking inside quotes. +.PP +Available with Postfix version 2.1 and later: .IP "\fBresolve_null_domain (no)\fR" Resolve an address that ends in the "@" null domain as if the local hostname were specified, instead of rejecting the address as invalid. +.PP +Available with Postfix version 2.3 and later: .IP "\fBresolve_numeric_domain (no)\fR" Resolve "user@ipaddress" as "user@[ipaddress]", instead of rejecting the address as invalid. @@ -130,7 +134,8 @@ addresses without domain information. With locally submitted mail, append the string ".$mydomain" to addresses that have no ".domain" information. .IP "\fBrecipient_delimiter (empty)\fR" -The separator between user names and address extensions (user+foo). +The set of characters that can separate a user name from its +address extension (user+foo). .IP "\fBswap_bangpath (yes)\fR" Enable the rewriting of "site!user" into "user@site". .PP diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 3edfdf06f..baeb2afba 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -968,6 +968,7 @@ while (<>) { s;\bpostscreen_dnsbl_reply_map\b;$&;g; s;\bpostscreen_dnsbl_sites\b;$&;g; s;\bpostscreen_dnsbl_thresh[-]*\n* *[]*old\b;$&;g; + s;\bpostscreen_dnsbl_whitelist_thresh[-]*\n* *[]*old\b;$&;g; s;\bpostscreen_dnsbl_action\b;$&;g; s;\bpostscreen_dnsbl_ttl\b;$&;g; s;\bpostscreen_for[-]*\n*[ ]*bidden_commands\b;$&;g; diff --git a/postfix/proto/DATABASE_README.html b/postfix/proto/DATABASE_README.html index 591cf0d02..af0c2de95 100644 --- a/postfix/proto/DATABASE_README.html +++ b/postfix/proto/DATABASE_README.html @@ -156,7 +156,8 @@ SQL, there is no need to execute "postfix reload". The LDAP, NIS or SQL server takes care of read/write access conflicts and gives the new data to Postfix once that data is available.

    -
  • If you change a regexp: or pcre: file then Postfix may or +

  • If you change a regexp:, pcre:, cidr: or texthash: file +then Postfix may not pick up the file changes immediately. This is because a Postfix process reads the entire file into memory once and never examines the file again.

    diff --git a/postfix/proto/access b/postfix/proto/access index 403e080d1..12b56a378 100644 --- a/postfix/proto/access +++ b/postfix/proto/access @@ -154,6 +154,8 @@ # An all-numerical result is treated as OK. This format is # generated by address-based relay authorization schemes # such as pop-before-smtp. +# .PP +# For other accept actions, see "OTHER ACTIONS" below. # REJECT ACTIONS # .ad # .fi @@ -217,6 +219,8 @@ # Prior to Postfix 2.6, the SMTP reply code is 450. # .sp # This feature is available in Postfix 2.1 and later. +# .PP +# For other reject actions, see "OTHER ACTIONS" below. # OTHER ACTIONS # .ad # .fi diff --git a/postfix/proto/canonical b/postfix/proto/canonical index cdda91818..6fb534816 100644 --- a/postfix/proto/canonical +++ b/postfix/proto/canonical @@ -63,9 +63,9 @@ # .ad # .fi # The input format for the \fBpostmap\fR(1) command is as follows: -# .IP "\fIpattern result\fR" +# .IP "\fIpattern address\fR" # When \fIpattern\fR matches a mail address, replace it by the -# corresponding \fIresult\fR. +# corresponding \fIaddress\fR. # .IP "blank lines and comments" # Empty lines and whitespace-only lines are ignored, as # are lines whose first non-whitespace character is a `#'. diff --git a/postfix/proto/cidr_table b/postfix/proto/cidr_table index 4a24d0531..0f1706ad1 100644 --- a/postfix/proto/cidr_table +++ b/postfix/proto/cidr_table @@ -35,6 +35,9 @@ # separated by ".", and an IPv6 network address is a sequence # of three to eight hexadecimal octet pairs separated by ":". # +# The \fInetwork_mask\fR is the number of high-order bits in +# the \fInetwork_address\fR that the search string must match. +# # Before comparisons are made, lookup keys and table entries # are converted from string to binary. Therefore table entries # will be matched regardless of redundant zero characters. diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 1359d1b7c..81c09725a 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -1609,7 +1609,9 @@ execution_directory_expansion_filter parameter.

    $recipient_delimiter
    -
    The system-wide recipient address extension delimiter.
    +
    The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier).
    ${name?value}
    @@ -1670,7 +1672,9 @@ forward_expansion_filter parameter.

    $recipient_delimiter
    -
    The system-wide recipient address extension delimiter.
    +
    The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier).
    ${name?value}
    @@ -2404,7 +2408,9 @@ The following $name expansions are done on luser_relay:
    $recipient_delimiter
    -
    The system-wide recipient address extension delimiter.
    +
    The address extension delimiter that was found in the recipient +address (Postfix 2.11 and later), or the system-wide recipient +address extension delimiter (Postfix 2.10 and earlier).
    $shell
    @@ -3495,24 +3501,55 @@ Example: recipient_canonical_maps = hash:/etc/postfix/recipient_canonical -%PARAM recipient_delimiter +%PARAM recipient_delimiter -

    -The separator between user names and address extensions (user+foo). -See canonical(5), local(8), relocated(5) and virtual(5) for the -effects this has on aliases, canonical, virtual, relocated and -on .forward file lookups. Basically, the software tries user+foo -and .forward+foo before trying user and .forward. +

    The set of characters that can separate a user name from its +address extension (user+foo). See canonical(5), local(8), relocated(5) +and virtual(5) for the effects this has on aliases, canonical, +virtual, and relocated lookups. Basically, the software tries +user+foo and .forward+foo before trying user and .forward.

    + +

    This implementation recognizes one delimiter character per email +address, and one address extension per email address.

    + +

    When the recipient_delimiter set contains multiple characters +(Postfix 2.11 and later), a user name is separated from its address +extension by the first character that matches the recipient_delimiter +set.

    + +

    When used in forward_path, ${recipient_delimiter} is replaced +with the recipient delimiter that was found in the recipient email +address (Postfix 2.11 and later), or it is replaced with the main.cf +recipient_delimiter parameter value (Postfix 2.10 and earlier).

    +

    The recipient_delimiter is not applied to the mailer-daemon +address, the postmaster address, or the double-bounce address. With +the default "owner_request_special = yes" setting, the recipient_delimiter +is also not applied to addresses with the special "owner-" prefix +or the special "-request" suffix.

    +

    -Example: +Examples:

    - +
    +# Handle Postfix-style extensions.
     recipient_delimiter = +
     
    +
    +# Handle both Postfix and qmail extensions (Postfix 2.11 and later).
    +recipient_delimiters = +-
    +
    + +
    +# Use .forward for mail without address extension, and for mail with
    +# an unrecognized address extension.
    +forward_path = $home/.forward${recipient_delimiter}${extension},
    +    $home/.forward
    +
    + %PARAM reject_code 554

    @@ -13816,6 +13853,21 @@ parameter.

    This feature is available in Postfix 2.8.

    +%PARAM postscreen_dnsbl_whitelist_threshold 0 + +

    Allow a remote SMTP client to skip "before" and "after 220 +greeting" protocol tests, based on its combined DNSBL score as +defined with the postscreen_dnsbl_sites parameter.

    + +

    Specify a negative value to enable this feature. When a client +passes the postscreen_dnsbl_whitelist_threshold without having +failed other tests, all pending or disabled tests are flagged as +completed with a time-to-live value equal to postscreen_dnsbl_ttl. +When a test was already completed, its time-to-live value is updated +if it was less than postscreen_dnsbl_ttl.

    + +

    This feature is available in Postfix 2.11.

    + %PARAM postscreen_command_count_limit 20

    The limit on the total number of commands per SMTP session for diff --git a/postfix/proto/virtual b/postfix/proto/virtual index 8a2d28f60..fd98e2d48 100644 --- a/postfix/proto/virtual +++ b/postfix/proto/virtual @@ -61,9 +61,9 @@ # .ad # .fi # The input format for the \fBpostmap\fR(1) command is as follows: -# .IP "\fIpattern result\fR" +# .IP "\fIpattern address, address, ...\fR" # When \fIpattern\fR matches a mail address, replace it by the -# corresponding \fIresult\fR. +# corresponding \fIaddress\fR. # .IP "blank lines and comments" # Empty lines and whitespace-only lines are ignored, as # are lines whose first non-whitespace character is a `#'. diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index 870a78f75..4ae4eff6e 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -303,10 +303,12 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list) rr_array[len] = rr; /* - * Shuffle resource records. + * Shuffle resource records. Every element has an equal chance of landing + * in slot 0. After that every remaining element has an equal chance of + * landing in slot 1, ... This is exactly n! states for n! permutations. */ - for (i = 0; i < len; i++) { - r = myrand() % len; + for (i = 0; i < len - 1; i++) { + r = i + (myrand() % (len - i)); /* Victor&Son */ rr = rr_array[i]; rr_array[i] = rr_array[r]; rr_array[r] = rr; diff --git a/postfix/src/global/mail_addr_find.c b/postfix/src/global/mail_addr_find.c index ed8caab6d..e21dd3600 100644 --- a/postfix/src/global/mail_addr_find.c +++ b/postfix/src/global/mail_addr_find.c @@ -109,7 +109,7 @@ const char *mail_addr_find(MAPS *path, const char *address, char **extp) if (*var_rcpt_delim == 0) { bare_key = saved_ext = 0; } else { - bare_key = strip_addr(full_key, &saved_ext, *var_rcpt_delim); + bare_key = strip_addr(full_key, &saved_ext, var_rcpt_delim); } /* diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 94ea155d7..5bc95c659 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -557,7 +557,7 @@ void mail_params_init() VAR_MAIL_VERSION, DEF_MAIL_VERSION, &var_mail_version, 1, 0, VAR_DB_TYPE, DEF_DB_TYPE, &var_db_type, 1, 0, VAR_HASH_QUEUE_NAMES, DEF_HASH_QUEUE_NAMES, &var_hash_queue_names, 1, 0, - VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 1, + VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 0, VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 0, 0, VAR_FFLUSH_DOMAINS, DEF_FFLUSH_DOMAINS, &var_fflush_domains, 0, 0, VAR_EXPORT_ENVIRON, DEF_EXPORT_ENVIRON, &var_export_environ, 0, 0, diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index c40702f13..ef78537ba 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3374,6 +3374,10 @@ extern char *var_psc_dnsbl_sites; #define DEF_PSC_DNSBL_THRESH 1 extern int var_psc_dnsbl_thresh; +#define VAR_PSC_DNSBL_WTHRESH "postscreen_dnsbl_whitelist_threshold" +#define DEF_PSC_DNSBL_WTHRESH 0 +extern int var_psc_dnsbl_wthresh; + #define VAR_PSC_DNSBL_ENABLE "postscreen_dnsbl_enable" #define DEF_PSC_DNSBL_ENABLE 0 extern char *var_psc_dnsbl_enable; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index ed6467d86..c29863155 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,19 +20,19 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20130518" +#define MAIL_RELEASE_DATE "20130530" #define MAIL_VERSION_NUMBER "2.11" #ifdef SNAPSHOT -# define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE +#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE #else -# define MAIL_VERSION_DATE "" +#define MAIL_VERSION_DATE "" #endif #ifdef NONPROD -# define MAIL_VERSION_PROD "-nonprod" +#define MAIL_VERSION_PROD "-nonprod" #else -# define MAIL_VERSION_PROD "" +#define MAIL_VERSION_PROD "" #endif #define VAR_MAIL_VERSION "mail_version" diff --git a/postfix/src/global/resolve_clnt.c b/postfix/src/global/resolve_clnt.c index bbd1b9b3a..5b28772c1 100644 --- a/postfix/src/global/resolve_clnt.c +++ b/postfix/src/global/resolve_clnt.c @@ -386,8 +386,11 @@ int main(int argc, char **argv) VSTRING *buffer = vstring_alloc(1); while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { - if ((addr = split_at(STR(buffer), ' ')) == 0 || *STR(buffer) == 0) - msg_fatal("need as input: class address"); + addr = split_at(STR(buffer), ' '); + if (*STR(buffer) == 0) + msg_fatal("need as input: class [address]"); + if (addr == 0) + addr = ""; resolve(STR(buffer), addr, &reply); } vstring_free(buffer); diff --git a/postfix/src/global/split_addr.c b/postfix/src/global/split_addr.c index 279ad4fb6..7e7cc27c8 100644 --- a/postfix/src/global/split_addr.c +++ b/postfix/src/global/split_addr.c @@ -6,12 +6,12 @@ /* SYNOPSIS /* #include /* -/* char *split_addr(localpart, delimiter) +/* char *split_addr(localpart, delimiter_set) /* char *localpart; -/* int delimiter; +/* const char *delimiter_set; /* DESCRIPTION /* split_addr() null-terminates \fIlocalpart\fR at the first -/* occurrence of the \fIdelimiter\fR character found, and +/* occurrence of the \fIdelimiter\fR character(s) found, and /* returns a pointer to the remainder. /* /* Reserved addresses are not split: postmaster, mailer-daemon, @@ -50,7 +50,7 @@ /* split_addr - split address with extreme prejudice */ -char *split_addr(char *localpart, int delimiter) +char *split_addr(char *localpart, const char *delimiter_set) { int len; @@ -67,7 +67,7 @@ char *split_addr(char *localpart, int delimiter) /* * Backwards compatibility: don't split owner-foo or foo-request. */ - if (delimiter == '-' && var_ownreq_special != 0) { + if (strchr(delimiter_set, '-') != 0 && var_ownreq_special != 0) { if (strncasecmp(localpart, "owner-", 6) == 0) return (0); if ((len = strlen(localpart) - 8) > 0 @@ -79,5 +79,10 @@ char *split_addr(char *localpart, int delimiter) * Safe to split this address. Do not split the address if the result * would have a null localpart. */ - return (delimiter == *localpart ? 0 : split_at(localpart, delimiter)); + if ((len = strcspn(localpart, delimiter_set)) == 0 || localpart[len] == 0) { + return (0); + } else { + localpart[len] = 0; + return (localpart + len + 1); + } } diff --git a/postfix/src/global/split_addr.h b/postfix/src/global/split_addr.h index 1c87fb99a..fa89faeae 100644 --- a/postfix/src/global/split_addr.h +++ b/postfix/src/global/split_addr.h @@ -13,7 +13,7 @@ /* External interface. */ -extern char *split_addr(char *, int); +extern char *split_addr(char *, const char *); /* LICENSE /* .ad diff --git a/postfix/src/global/strip_addr.c b/postfix/src/global/strip_addr.c index 803db87f8..6792d35f3 100644 --- a/postfix/src/global/strip_addr.c +++ b/postfix/src/global/strip_addr.c @@ -6,10 +6,10 @@ /* SYNOPSIS /* #include /* -/* char *strip_addr(address, extension, delimiter) +/* char *strip_addr(address, extension, delimiter_set) /* const char *address; /* char **extension; -/* int delimiter; +/* const char *delimiter_set; /* DESCRIPTION /* strip_addr() takes an address and either returns a null /* pointer when the address contains no address extension, @@ -25,8 +25,8 @@ /* that had to be chopped off. /* The copy includes the recipient address delimiter. /* The caller is expected to pass the copy to myfree(). -/* .IP delimiter -/* Recipient address delimiter. +/* .IP delimiter_set +/* Set of recipient address delimiter characters. /* SEE ALSO /* split_addr(3) strip extension from localpart /* LICENSE @@ -56,7 +56,7 @@ /* strip_addr - strip extension from address */ -char *strip_addr(const char *full, char **extension, int delimiter) +char *strip_addr(const char *full, char **extension, const char *delimiter_set) { char *ratsign; char *extent; @@ -66,16 +66,16 @@ char *strip_addr(const char *full, char **extension, int delimiter) /* * A quick test to eliminate inputs without delimiter anywhere. */ - if (delimiter == 0 || strchr(full, delimiter) == 0) { + if (*delimiter_set == 0 || full[strcspn(full, delimiter_set)] == 0) { stripped = saved_ext = 0; } else { stripped = mystrdup(full); if ((ratsign = strrchr(stripped, '@')) != 0) *ratsign = 0; - if ((extent = split_addr(stripped, delimiter)) != 0) { + if ((extent = split_addr(stripped, delimiter_set)) != 0) { extent -= 1; if (extension) { - *extent = delimiter; + *extent = full[strlen(stripped)]; saved_ext = mystrdup(extent); *extent = 0; } else @@ -105,17 +105,19 @@ int main(int unused_argc, char **unused_argv) { char *extension; char *stripped; - int delim = '-'; + char* delim = "+-"; + +#define NO_DELIM "" /* * Incredible. This function takes only three arguments, and the tests * already take more lines of code than the code being tested. */ - stripped = strip_addr("foo", (char **) 0, 0); + stripped = strip_addr("foo", (char **) 0, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 1"); - stripped = strip_addr("foo", &extension, 0); + stripped = strip_addr("foo", &extension, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 2"); if (extension != 0) @@ -131,11 +133,11 @@ int main(int unused_argc, char **unused_argv) if (extension != 0) msg_panic("strip_addr botch 6"); - stripped = strip_addr("foo@bar", (char **) 0, 0); + stripped = strip_addr("foo@bar", (char **) 0, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 7"); - stripped = strip_addr("foo@bar", &extension, 0); + stripped = strip_addr("foo@bar", &extension, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 8"); if (extension != 0) @@ -151,11 +153,11 @@ int main(int unused_argc, char **unused_argv) if (extension != 0) msg_panic("strip_addr botch 12"); - stripped = strip_addr("foo-ext", (char **) 0, 0); + stripped = strip_addr("foo-ext", (char **) 0, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 13"); - stripped = strip_addr("foo-ext", &extension, 0); + stripped = strip_addr("foo-ext", &extension, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 14"); if (extension != 0) @@ -178,11 +180,11 @@ int main(int unused_argc, char **unused_argv) myfree(stripped); myfree(extension); - stripped = strip_addr("foo-ext@bar", (char **) 0, 0); + stripped = strip_addr("foo-ext@bar", (char **) 0, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 19"); - stripped = strip_addr("foo-ext@bar", &extension, 0); + stripped = strip_addr("foo-ext@bar", &extension, NO_DELIM); if (stripped != 0) msg_panic("strip_addr botch 20"); if (extension != 0) @@ -205,6 +207,16 @@ int main(int unused_argc, char **unused_argv) myfree(stripped); myfree(extension); + stripped = strip_addr("foo+ext@bar", &extension, delim); + if (stripped == 0) + msg_panic("strip_addr botch 25"); + if (extension == 0) + msg_panic("strip_addr botch 26"); + msg_info("wanted: foo+ext@bar -> %s %s", "foo@bar", "+ext"); + msg_info("strip_addr foo+ext@bar -> %s %s", stripped, extension); + myfree(stripped); + myfree(extension); + return (0); } diff --git a/postfix/src/global/strip_addr.h b/postfix/src/global/strip_addr.h index e99c6fdf8..19530e1ba 100644 --- a/postfix/src/global/strip_addr.h +++ b/postfix/src/global/strip_addr.h @@ -13,7 +13,7 @@ /* External interface. */ -extern char *strip_addr(const char *, char **, int); +extern char *strip_addr(const char *, char **, const char *); /* LICENSE /* .ad diff --git a/postfix/src/global/strip_addr.ref b/postfix/src/global/strip_addr.ref index 3068918ca..af218e5c0 100644 --- a/postfix/src/global/strip_addr.ref +++ b/postfix/src/global/strip_addr.ref @@ -6,3 +6,5 @@ unknown: wanted: foo-ext@bar -> foo@bar unknown: strip_addr foo-ext@bar -> foo@bar unknown: wanted: foo-ext@bar -> foo@bar -ext unknown: strip_addr foo-ext@bar -> foo@bar -ext +unknown: wanted: foo+ext@bar -> foo@bar +ext +unknown: strip_addr foo+ext@bar -> foo@bar +ext diff --git a/postfix/src/local/bounce_workaround.c b/postfix/src/local/bounce_workaround.c index 8d1670c80..e7211ec55 100644 --- a/postfix/src/local/bounce_workaround.c +++ b/postfix/src/local/bounce_workaround.c @@ -109,7 +109,7 @@ int bounce_workaround(LOCAL_STATE state) if (alias_maps->error == 0 && owner_expansion == 0 && (stripped_recipient = strip_addr(state.msg_attr.rcpt.address, (char **) 0, - *var_rcpt_delim)) != 0) { + var_rcpt_delim)) != 0) { myfree(owner_alias); FIND_OWNER(owner_alias, owner_expansion, stripped_recipient); myfree(stripped_recipient); diff --git a/postfix/src/local/forward.c b/postfix/src/local/forward.c index 6ebe74f18..fb7da4f2e 100644 --- a/postfix/src/local/forward.c +++ b/postfix/src/local/forward.c @@ -118,6 +118,11 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender) FORWARD_INFO *info; VSTREAM *cleanup; +#define FORWARD_OPEN_RETURN(res) do { \ + vstring_free(buffer); \ + return (res); \ + } while (0) + /* * Contact the cleanup service and save the new mail queue id. Request * that the cleanup service bounces bad messages to the sender so that we @@ -129,13 +134,13 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender) */ cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING); if (cleanup == 0) - return (0); + FORWARD_OPEN_RETURN(0); close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC); if (attr_scan(cleanup, ATTR_FLAG_STRICT, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, buffer, ATTR_TYPE_END) != 1) { vstream_fclose(cleanup); - return (0); + FORWARD_OPEN_RETURN(0); } info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO)); info->cleanup = cleanup; @@ -190,8 +195,7 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender) PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident); PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context); - vstring_free(buffer); - return (info); + FORWARD_OPEN_RETURN(info); } /* forward_append - append recipient to message envelope */ diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c index cec231263..32da88bca 100644 --- a/postfix/src/local/local.c +++ b/postfix/src/local/local.c @@ -531,7 +531,8 @@ /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" /* The location of the Postfix top-level queue directory. /* .IP "\fBrecipient_delimiter (empty)\fR" -/* The separator between user names and address extensions (user+foo). +/* The set of characters that can separate a user name from its +/* address extension (user+foo). /* .IP "\fBrequire_home_directory (no)\fR" /* Require that a \fBlocal\fR(8) recipient's home directory exists /* before mail delivery is attempted. diff --git a/postfix/src/local/local_expand.c b/postfix/src/local/local_expand.c index db5121ee8..612e68094 100644 --- a/postfix/src/local/local_expand.c +++ b/postfix/src/local/local_expand.c @@ -113,6 +113,7 @@ typedef struct { static const char *local_expand_lookup(const char *name, int mode, char *ptr) { LOCAL_EXP *local = (LOCAL_EXP *) ptr; + static char rcpt_delim[2]; #define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0) @@ -135,7 +136,10 @@ static const char *local_expand_lookup(const char *name, int mode, char *ptr) local->status |= LOCAL_EXP_EXTENSION_MATCHED; return (local->state->msg_attr.extension); } else if (STREQ(name, "recipient_delimiter")) { - return (*var_rcpt_delim ? var_rcpt_delim : 0); + rcpt_delim[0] = + local->state->msg_attr.local[strlen(local->state->msg_attr.user)]; + rcpt_delim[1] = 0; + return (rcpt_delim[0] ? rcpt_delim : 0); #if 0 } else if (STREQ(name, "client_hostname")) { return (local->state->msg_attr.request->client_name); diff --git a/postfix/src/local/recipient.c b/postfix/src/local/recipient.c index 192144493..8017942e5 100644 --- a/postfix/src/local/recipient.c +++ b/postfix/src/local/recipient.c @@ -270,7 +270,7 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) state.msg_attr.user = mystrdup(state.msg_attr.local); if (*var_rcpt_delim) { state.msg_attr.extension = - split_addr(state.msg_attr.user, *var_rcpt_delim); + split_addr(state.msg_attr.user, var_rcpt_delim); if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) { msg_warn("%s: address with illegal extension: %s", state.msg_attr.queue_id, state.msg_attr.local); diff --git a/postfix/src/local/resolve.c b/postfix/src/local/resolve.c index 89c330325..a6aa9d035 100644 --- a/postfix/src/local/resolve.c +++ b/postfix/src/local/resolve.c @@ -90,6 +90,7 @@ int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr int status; ssize_t ext_len; char *ratsign; + int rcpt_delim; /* * Make verbose logging easier to understand. @@ -130,8 +131,9 @@ int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr * Splice in the optional unmatched address extension. */ if (state.msg_attr.unmatched) { + rcpt_delim = state.msg_attr.local[strlen(state.msg_attr.user)]; if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { - VSTRING_ADDCH(reply.recipient, *var_rcpt_delim); + VSTRING_ADDCH(reply.recipient, rcpt_delim); vstring_strcat(reply.recipient, state.msg_attr.unmatched); } else { ext_len = strlen(state.msg_attr.unmatched); @@ -139,7 +141,7 @@ int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) msg_panic("%s: recipient @ botch", myname); memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1); - *ratsign = *var_rcpt_delim; + *ratsign = rcpt_delim; memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len); VSTRING_SKIP(reply.recipient); } diff --git a/postfix/src/oqmgr/qmgr_message.c b/postfix/src/oqmgr/qmgr_message.c index 71955d4f3..e2f923741 100644 --- a/postfix/src/oqmgr/qmgr_message.c +++ b/postfix/src/oqmgr/qmgr_message.c @@ -1175,7 +1175,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) : strlen(STR(reply.recipient))); vstring_strncpy(queue_name, STR(reply.recipient), len); /* Remove the address extension from the recipient localpart. */ - if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim)) + if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim)) vstring_truncate(queue_name, strlen(STR(queue_name))); /* Assume the recipient domain is equivalent to nexthop. */ vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop)); diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index 73e9978bb..b6329f02f 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -390,7 +390,8 @@ /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" /* The location of the Postfix top-level queue directory. /* .IP "\fBrecipient_delimiter (empty)\fR" -/* The separator between user names and address extensions (user+foo). +/* The set of characters that can separate a user name from its +/* address extension (user+foo). /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" @@ -717,7 +718,7 @@ static ARGV *expand_argv(const char *service, char **argv, msg_warn("no @ in recipient address: %s", rcpt_list->info[i].address); if (*var_rcpt_delim) - split_addr(STR(buf), *var_rcpt_delim); + split_addr(STR(buf), var_rcpt_delim); if (*STR(buf) == 0) continue; dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf)); @@ -735,7 +736,7 @@ static ARGV *expand_argv(const char *service, char **argv, msg_warn("no @ in recipient address: %s", rcpt_list->info[i].address); if (*var_rcpt_delim == 0 - || (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0) + || (ext = split_addr(STR(buf), var_rcpt_delim)) == 0) ext = ""; /* insert null arg */ dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext); } diff --git a/postfix/src/postscreen/Makefile.in b/postfix/src/postscreen/Makefile.in index 4d2f8265a..9c29a3d55 100644 --- a/postfix/src/postscreen/Makefile.in +++ b/postfix/src/postscreen/Makefile.in @@ -385,6 +385,7 @@ postscreen_tests.o: ../../include/match_list.h postscreen_tests.o: ../../include/msg.h postscreen_tests.o: ../../include/myaddrinfo.h postscreen_tests.o: ../../include/myflock.h +postscreen_tests.o: ../../include/name_code.h postscreen_tests.o: ../../include/server_acl.h postscreen_tests.o: ../../include/string_list.h postscreen_tests.o: ../../include/sys_defs.h diff --git a/postfix/src/postscreen/postscreen.c b/postfix/src/postscreen/postscreen.c index 0149b59ad..deff9abfa 100644 --- a/postfix/src/postscreen/postscreen.c +++ b/postfix/src/postscreen/postscreen.c @@ -214,6 +214,12 @@ /* .IP "\fBsmtpd_service_name (smtpd)\fR" /* The internal service that \fBpostscreen\fR(8) hands off allowed /* connections to. +/* .PP +/* Available in Postfix version 2.11 and later: +/* .IP "\fBpostscreen_dnsbl_whitelist_threshold (0)\fR" +/* Allow a remote SMTP client to skip "before" and "after 220 +/* greeting" protocol tests, based on its combined DNSBL score as +/* defined with the postscreen_dnsbl_sites parameter. /* AFTER-GREETING TESTS /* .ad /* .fi @@ -465,6 +471,7 @@ int var_psc_pregr_ttl; char *var_psc_dnsbl_sites; char *var_psc_dnsbl_reply; int var_psc_dnsbl_thresh; +int var_psc_dnsbl_wthresh; char *var_psc_dnsbl_action; int var_psc_dnsbl_ttl; @@ -1095,6 +1102,7 @@ int main(int argc, char **argv) static const CONFIG_INT_TABLE int_table[] = { VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0, VAR_PSC_DNSBL_THRESH, DEF_PSC_DNSBL_THRESH, &var_psc_dnsbl_thresh, 0, 0, + VAR_PSC_DNSBL_WTHRESH, DEF_PSC_DNSBL_WTHRESH, &var_psc_dnsbl_wthresh, 0, 0, VAR_PSC_CMD_COUNT, DEF_PSC_CMD_COUNT, &var_psc_cmd_count, 1, 0, VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0, 0, diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h index 3fe0a9b6b..ebc048a02 100644 --- a/postfix/src/postscreen/postscreen.h +++ b/postfix/src/postscreen/postscreen.h @@ -35,6 +35,25 @@ */ #define PSC_READ_BUF_SIZE 1024 + /* + * Numeric indices and symbolic names for tests whose time stamps and status + * flags can be accessed by numeric index. + */ +#define PSC_TINDX_PREGR 0 /* pregreet */ +#define PSC_TINDX_DNSBL 1 /* dnsbl */ +#define PSC_TINDX_PIPEL 2 /* pipelining */ +#define PSC_TINDX_NSMTP 3 /* non-smtp command */ +#define PSC_TINDX_BARLF 4 /* bare newline */ +#define PSC_TINDX_COUNT 5 /* number of tests */ + +#define PSC_TNAME_PREGR "pregreet" +#define PSC_TNAME_DNSBL "dnsbl" +#define PSC_TNAME_PIPEL "pipelining" +#define PSC_TNAME_NSMTP "non-smtp command" +#define PSC_TNAME_BARLF "bare newline" + +#define PSC_TINDX_BYTNAME(tname) (PSC_TINDX_ ## tname) + /* * Per-session state. */ @@ -53,16 +72,11 @@ typedef struct { /* Test context. */ struct timeval start_time; /* start of current test */ const char *test_name; /* name of current test */ - /* Before-handshake tests. */ - time_t pregr_stamp; /* pregreet expiration time */ - time_t dnsbl_stamp; /* dnsbl expiration time */ + time_t expire_time[PSC_TINDX_COUNT]; /* per-test expiration */ VSTRING *dnsbl_reply; /* dnsbl reject text */ + int dnsbl_score; /* saved DNSBL score */ + const char *dnsbl_name; /* DNSBL name with largest weight */ int dnsbl_index; /* dnsbl request index */ - time_t penal_stamp; /* penalty expiration time */ - /* Built-in SMTP protocol engine. */ - time_t pipel_stamp; /* pipelining expiration time */ - time_t nsmtp_stamp; /* non-smtp command expiration time */ - time_t barlf_stamp; /* bare newline expiration time */ const char *rcpt_reply; /* how to reject recipients */ int command_count; /* error + junk command count */ const char *protocol; /* SMTP or ESMTP */ @@ -76,10 +90,26 @@ typedef struct { const char *where; /* SMTP protocol state */ } PSC_STATE; + /* + * Emulate legacy ad-hoc variables on top of indexable time stamps. This + * avoids massive scar tissue during initial feature development. + */ +#define pregr_stamp expire_time[PSC_TINDX_PREGR] +#define dnsbl_stamp expire_time[PSC_TINDX_DNSBL] +#define pipel_stamp expire_time[PSC_TINDX_PIPEL] +#define nsmtp_stamp expire_time[PSC_TINDX_NSMTP] +#define barlf_stamp expire_time[PSC_TINDX_BARLF] + + /* + * Special expiration time values. + */ #define PSC_TIME_STAMP_NEW (0) /* test was never passed */ #define PSC_TIME_STAMP_DISABLED (1) /* never passed but disabled */ #define PSC_TIME_STAMP_INVALID (-1) /* must not be cached */ + /* + * Status flags. + */ #define PSC_STATE_FLAG_NOFORWARD (1<<0) /* don't forward this session */ #define PSC_STATE_FLAG_USING_TLS (1<<1) /* using the TLS proxy */ #define PSC_STATE_FLAG_UNUSED2 (1<<2) /* use me! */ @@ -88,8 +118,11 @@ typedef struct { #define PSC_STATE_FLAG_HANGUP (1<<5) /* NOT a test failure */ #define PSC_STATE_FLAG_SMTPD_X21 (1<<6) /* hang up after command */ #define PSC_STATE_FLAG_WLIST_FAIL (1<<7) /* do not whitelist */ +#define PSC_STATE_FLAG_TEST_BASE (8) /* start of indexable flags */ /* + * Tests whose flags and expiration time can be accessed by numerical index. + * * Important: every MUMBLE_TODO flag must have a MUMBLE_PASS flag, such that * MUMBLE_PASS == PSC_STATE_FLAGS_TODO_TO_PASS(MUMBLE_TODO). * @@ -107,39 +140,74 @@ typedef struct { * the result was ignored. MUMBLE_FAIL, on the other hand, is always final. * We use MUMBLE_SKIP to indicate that a decision was either "fail" or * forced "pass". + * + * The difference between DONE and SKIP is in the beholder's eye. These flags + * share the same bit. */ #define PSC_STATE_FLAGS_TODO_TO_PASS(todo_flags) ((todo_flags) >> 1) #define PSC_STATE_FLAGS_TODO_TO_DONE(todo_flags) ((todo_flags) << 1) -#define PSC_STATE_FLAG_PENAL_UPDATE (1<<6) /* save new penalty */ -#define PSC_STATE_FLAG_PENAL_FAIL (1<<7) /* penalty is active */ - -#define PSC_STATE_FLAG_PREGR_FAIL (1<<8) /* failed pregreet test */ -#define PSC_STATE_FLAG_PREGR_PASS (1<<9) /* passed pregreet test */ -#define PSC_STATE_FLAG_PREGR_TODO (1<<10) /* pregreet test expired */ -#define PSC_STATE_FLAG_PREGR_DONE (1<<11) /* decision is final */ - -#define PSC_STATE_FLAG_DNSBL_FAIL (1<<12) /* failed DNSBL test */ -#define PSC_STATE_FLAG_DNSBL_PASS (1<<13) /* passed DNSBL test */ -#define PSC_STATE_FLAG_DNSBL_TODO (1<<14) /* DNSBL test expired */ -#define PSC_STATE_FLAG_DNSBL_DONE (1<<15) /* decision is final */ - - /* Room here for one more after-handshake test. */ +#define PSC_STATE_FLAG_SHIFT_FAIL (0) /* failed test */ +#define PSC_STATE_FLAG_SHIFT_PASS (1) /* passed test */ +#define PSC_STATE_FLAG_SHIFT_TODO (2) /* expired test */ +#define PSC_STATE_FLAG_SHIFT_DONE (3) /* decision is final */ +#define PSC_STATE_FLAG_SHIFT_SKIP (3) /* action is already logged */ +#define PSC_STATE_FLAG_SHIFT_STRIDE (4) /* nr of flags per test */ -#define PSC_STATE_FLAG_PIPEL_FAIL (1<<20) /* failed pipelining test */ -#define PSC_STATE_FLAG_PIPEL_PASS (1<<21) /* passed pipelining test */ -#define PSC_STATE_FLAG_PIPEL_TODO (1<<22) /* pipelining test expired */ -#define PSC_STATE_FLAG_PIPEL_SKIP (1<<23) /* action is already logged */ +#define PSC_STATE_FLAG_SHIFT_BYFNAME(fname) (PSC_STATE_FLAG_SHIFT_ ## fname) -#define PSC_STATE_FLAG_NSMTP_FAIL (1<<24) /* failed non-SMTP test */ -#define PSC_STATE_FLAG_NSMTP_PASS (1<<25) /* passed non-SMTP test */ -#define PSC_STATE_FLAG_NSMTP_TODO (1<<26) /* non-SMTP test expired */ -#define PSC_STATE_FLAG_NSMTP_SKIP (1<<27) /* action is already logged */ + /* + * Indexable per-test flags. These are used for DNS whitelisting multiple + * tests, without needing per-test ad-hoc code. + */ +#define PSC_STATE_FLAG_BYTINDX_FNAME(tindx, fname) \ + (1U << (PSC_STATE_FLAG_TEST_BASE \ + + PSC_STATE_FLAG_SHIFT_STRIDE * (tindx) \ + + PSC_STATE_FLAG_SHIFT_BYFNAME(fname))) + +#define PSC_STATE_FLAG_BYTINDX_FAIL(tindx) \ + PSC_STATE_FLAG_BYTINDX_FNAME((tindx), FAIL) +#define PSC_STATE_FLAG_BYTINDX_PASS(tindx) \ + PSC_STATE_FLAG_BYTINDX_FNAME((tindx), PASS) +#define PSC_STATE_FLAG_BYTINDX_TODO(tindx) \ + PSC_STATE_FLAG_BYTINDX_FNAME((tindx), TODO) +#define PSC_STATE_FLAG_BYTINDX_DONE(tindx) \ + PSC_STATE_FLAG_BYTINDX_FNAME((tindx), DONE) +#define PSC_STATE_FLAG_BYTINDX_SKIP(tindx) \ + PSC_STATE_FLAG_BYTINDX_FNAME((tindx), SKIP) -#define PSC_STATE_FLAG_BARLF_FAIL (1<<28) /* failed bare newline test */ -#define PSC_STATE_FLAG_BARLF_PASS (1<<29) /* passed bare newline test */ -#define PSC_STATE_FLAG_BARLF_TODO (1<<30) /* bare newline test expired */ -#define PSC_STATE_FLAG_BARLF_SKIP (1<<31) /* action is already logged */ + /* + * Flags with distinct names. These are used in the per-test ad-hoc code. + */ +#define PSC_STATE_FLAG_BYTNAME_FNAME(tname, fname) \ + (1U << (PSC_STATE_FLAG_TEST_BASE \ + + PSC_STATE_FLAG_SHIFT_STRIDE * PSC_TINDX_BYTNAME(tname) \ + + PSC_STATE_FLAG_SHIFT_BYFNAME(fname))) + +#define PSC_STATE_FLAG_PREGR_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, FAIL) +#define PSC_STATE_FLAG_PREGR_PASS PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, PASS) +#define PSC_STATE_FLAG_PREGR_TODO PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, TODO) +#define PSC_STATE_FLAG_PREGR_DONE PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, DONE) + +#define PSC_STATE_FLAG_DNSBL_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, FAIL) +#define PSC_STATE_FLAG_DNSBL_PASS PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, PASS) +#define PSC_STATE_FLAG_DNSBL_TODO PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, TODO) +#define PSC_STATE_FLAG_DNSBL_DONE PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, DONE) + +#define PSC_STATE_FLAG_PIPEL_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, FAIL) +#define PSC_STATE_FLAG_PIPEL_PASS PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, PASS) +#define PSC_STATE_FLAG_PIPEL_TODO PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, TODO) +#define PSC_STATE_FLAG_PIPEL_SKIP PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, SKIP) + +#define PSC_STATE_FLAG_NSMTP_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, FAIL) +#define PSC_STATE_FLAG_NSMTP_PASS PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, PASS) +#define PSC_STATE_FLAG_NSMTP_TODO PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, TODO) +#define PSC_STATE_FLAG_NSMTP_SKIP PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, SKIP) + +#define PSC_STATE_FLAG_BARLF_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, FAIL) +#define PSC_STATE_FLAG_BARLF_PASS PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, PASS) +#define PSC_STATE_FLAG_BARLF_TODO PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, TODO) +#define PSC_STATE_FLAG_BARLF_SKIP PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, SKIP) /* * Aggregates for individual tests. @@ -155,6 +223,8 @@ typedef struct { #define PSC_STATE_MASK_BARLF_TODO_FAIL \ (PSC_STATE_FLAG_BARLF_TODO | PSC_STATE_FLAG_BARLF_FAIL) +#define PSC_STATE_MASK_PREGR_TODO_DONE \ + (PSC_STATE_FLAG_PREGR_TODO | PSC_STATE_FLAG_PREGR_DONE) #define PSC_STATE_MASK_PIPEL_TODO_SKIP \ (PSC_STATE_FLAG_PIPEL_TODO | PSC_STATE_FLAG_PIPEL_SKIP) #define PSC_STATE_MASK_NSMTP_TODO_SKIP \ @@ -162,6 +232,9 @@ typedef struct { #define PSC_STATE_MASK_BARLF_TODO_SKIP \ (PSC_STATE_FLAG_BARLF_TODO | PSC_STATE_FLAG_BARLF_SKIP) +#define PSC_STATE_MASK_PREGR_FAIL_DONE \ + (PSC_STATE_FLAG_PREGR_FAIL | PSC_STATE_FLAG_PREGR_DONE) + #define PSC_STATE_MASK_PIPEL_TODO_PASS_FAIL \ (PSC_STATE_MASK_PIPEL_TODO_FAIL | PSC_STATE_FLAG_PIPEL_PASS) #define PSC_STATE_MASK_NSMTP_TODO_PASS_FAIL \ @@ -195,7 +268,7 @@ typedef struct { * Super-aggregates for all tests combined. */ #define PSC_STATE_MASK_ANY_FAIL \ - (PSC_STATE_FLAG_BLIST_FAIL | PSC_STATE_FLAG_PENAL_FAIL | \ + (PSC_STATE_FLAG_BLIST_FAIL | \ PSC_STATE_MASK_EARLY_FAIL | PSC_STATE_MASK_SMTPD_FAIL | \ PSC_STATE_FLAG_WLIST_FAIL) @@ -209,7 +282,7 @@ typedef struct { (PSC_STATE_MASK_ANY_TODO | PSC_STATE_MASK_ANY_FAIL) #define PSC_STATE_MASK_ANY_UPDATE \ - (PSC_STATE_MASK_ANY_PASS | PSC_STATE_FLAG_PENAL_UPDATE) + (PSC_STATE_MASK_ANY_PASS) /* * Meta-commands for state->where that reflect the initial command processor @@ -414,12 +487,12 @@ extern int psc_dnsbl_request(const char *, void (*) (int, char *), char *); * postscreen_tests.c */ #define PSC_INIT_TESTS(dst) do { \ + time_t *_it_stamp_p; \ (dst)->flags = 0; \ - (dst)->pregr_stamp = PSC_TIME_STAMP_INVALID; \ - (dst)->dnsbl_stamp = PSC_TIME_STAMP_INVALID; \ - (dst)->pipel_stamp = PSC_TIME_STAMP_INVALID; \ - (dst)->barlf_stamp = PSC_TIME_STAMP_INVALID; \ - (dst)->penal_stamp = PSC_TIME_STAMP_INVALID; \ + for (_it_stamp_p = (dst)->expire_time; \ + _it_stamp_p < (dst)->expire_time + PSC_TINDX_COUNT; \ + _it_stamp_p++) \ + *_it_stamp_p = PSC_TIME_STAMP_INVALID; \ } while (0) #define PSC_BEGIN_TESTS(state, name) do { \ (state)->test_name = (name); \ @@ -430,6 +503,7 @@ extern void psc_parse_tests(PSC_STATE *, const char *, time_t); extern char *psc_print_tests(VSTRING *, PSC_STATE *); extern char *psc_print_grey_key(VSTRING *, const char *, const char *, const char *, const char *); +extern const char *psc_test_name(int); #define PSC_MIN(x, y) ((x) < (y) ? (x) : (y)) #define PSC_MAX(x, y) ((x) > (y) ? (x) : (y)) diff --git a/postfix/src/postscreen/postscreen_early.c b/postfix/src/postscreen/postscreen_early.c index cd77f5afd..a540703d9 100644 --- a/postfix/src/postscreen/postscreen_early.c +++ b/postfix/src/postscreen/postscreen_early.c @@ -31,6 +31,7 @@ #include #include +#include /* Utility library. */ @@ -50,6 +51,48 @@ static char *psc_teaser_greeting; static VSTRING *psc_escape_buf; +/* psc_whitelist_non_dnsbl - whitelist pending non-dnsbl tests */ + +static void psc_whitelist_non_dnsbl(PSC_STATE *state) +{ + time_t now; + int tindx; + + /* + * If no tests failed (we can't undo those), and if the whitelist + * threshold is met, flag non-dnsbl tests that are pending or disabled as + * successfully completed, and set their expiration times equal to the + * DNSBL expiration time, except for tests that would expire later. + * + * Why flag disabled tests as passed? When a disabled test is turned on, + * postscreen should not apply that test to clients that are already + * whitelisted based on their combined DNSBL score. + */ + if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0 + && state->dnsbl_score < var_psc_dnsbl_thresh + && var_psc_dnsbl_wthresh < 0 + && state->dnsbl_score <= var_psc_dnsbl_wthresh) { + now = event_time(); + for (tindx = 0; tindx < PSC_TINDX_COUNT; tindx++) { + if (tindx == PSC_TINDX_DNSBL) + continue; + if ((state->flags & PSC_STATE_FLAG_BYTINDX_TODO(tindx)) + && !(state->flags & PSC_STATE_FLAG_BYTINDX_PASS(tindx))) { + if (msg_verbose) + msg_info("skip %s test for [%s]:%s", + psc_test_name(tindx), PSC_CLIENT_ADDR_PORT(state)); + /* Wrong for deep protocol tests, but we disable those. */ + state->flags |= PSC_STATE_FLAG_BYTINDX_DONE(tindx); + /* This also disables pending deep protocol tests. */ + state->flags |= PSC_STATE_FLAG_BYTINDX_PASS(tindx); + } + /* Update expiration even if the test was completed or disabled. */ + if (state->expire_time[tindx] < now + var_psc_dnsbl_ttl) + state->expire_time[tindx] = now + var_psc_dnsbl_ttl; + } + } +} + /* psc_early_event - handle pre-greet, EOF, and DNSBL results. */ static void psc_early_event(int event, char *context) @@ -58,9 +101,7 @@ static void psc_early_event(int event, char *context) PSC_STATE *state = (PSC_STATE *) context; char read_buf[PSC_READ_BUF_SIZE]; int read_count; - int dnsbl_score; DELTA_TIME elapsed; - const char *dnsbl_name; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s", @@ -83,15 +124,16 @@ static void psc_early_event(int event, char *context) switch (event) { /* - * We reached the end of the early tests time limit. + * We either reached the end of the early tests time limit, or all + * early tests completed before the pregreet timer would go off. */ case EVENT_TIME: /* * Check if the SMTP client spoke before its turn. */ - if ((state->flags & PSC_STATE_MASK_PREGR_TODO_FAIL) - == PSC_STATE_FLAG_PREGR_TODO) { + if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0 + && (state->flags & PSC_STATE_MASK_PREGR_FAIL_DONE) == 0) { state->pregr_stamp = event_time() + var_psc_pregr_ttl; PSC_PASS_SESSION_STATE(state, "pregreet test", PSC_STATE_FLAG_PREGR_PASS); @@ -103,35 +145,47 @@ static void psc_early_event(int event, char *context) } /* + * Collect the DNSBL score, and whitelist other tests if applicable. + * Note: this score will be partial when some DNS lookup did not + * complete before the pregreet timer expired. + * * If the client is DNS blocklisted, drop the connection, send the * client to a dummy protocol engine, or continue to the next test. */ #define PSC_DNSBL_FORMAT \ "%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n" +#define NO_DNSBL_SCORE INT_MAX if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) { - dnsbl_score = - psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name, - state->dnsbl_index); - if (dnsbl_score < var_psc_dnsbl_thresh) { + if (state->dnsbl_score == NO_DNSBL_SCORE) { + state->dnsbl_score = + psc_dnsbl_retrieve(state->smtp_client_addr, + &state->dnsbl_name, + state->dnsbl_index); + if (var_psc_dnsbl_wthresh < 0) + psc_whitelist_non_dnsbl(state); + } + if (state->dnsbl_score < var_psc_dnsbl_thresh) { state->dnsbl_stamp = event_time() + var_psc_dnsbl_ttl; PSC_PASS_SESSION_STATE(state, "dnsbl test", PSC_STATE_FLAG_DNSBL_PASS); } else { msg_info("DNSBL rank %d for [%s]:%s", - dnsbl_score, PSC_CLIENT_ADDR_PORT(state)); + state->dnsbl_score, PSC_CLIENT_ADDR_PORT(state)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL); switch (psc_dnsbl_action) { case PSC_ACT_DROP: state->dnsbl_reply = vstring_sprintf(vstring_alloc(100), PSC_DNSBL_FORMAT, "521", - state->smtp_client_addr, dnsbl_name); + state->smtp_client_addr, + state->dnsbl_name); PSC_DROP_SESSION_STATE(state, STR(state->dnsbl_reply)); return; case PSC_ACT_ENFORCE: state->dnsbl_reply = vstring_sprintf(vstring_alloc(100), PSC_DNSBL_FORMAT, "550", - state->smtp_client_addr, dnsbl_name); + state->smtp_client_addr, + state->dnsbl_name); PSC_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply)); break; case PSC_ACT_IGNORE: @@ -150,7 +204,9 @@ static void psc_early_event(int event, char *context) * Pass the connection to a real SMTP server, or enter the dummy * engine for deep tests. */ - if (state->flags & (PSC_STATE_FLAG_NOFORWARD | PSC_STATE_MASK_SMTPD_TODO)) + if ((state->flags & PSC_STATE_FLAG_NOFORWARD) != 0 + || ((state->flags & PSC_STATE_MASK_SMTPD_PASS) + != PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_SMTPD_TODO))) psc_smtpd_tests(state); else psc_conclude(state); @@ -165,8 +221,10 @@ static void psc_early_event(int event, char *context) if ((read_count = recv(vstream_fileno(state->smtp_client_stream), read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) { /* Avoid memory leak. */ - if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) - (void) psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name, + if (state->dnsbl_score == NO_DNSBL_SCORE + && (state->flags & PSC_STATE_FLAG_DNSBL_TODO)) + (void) psc_dnsbl_retrieve(state->smtp_client_addr, + &state->dnsbl_name, state->dnsbl_index); /* XXX Wait for DNS replies to come in. */ psc_hangup_event(state); @@ -181,8 +239,10 @@ static void psc_early_event(int event, char *context) switch (psc_pregr_action) { case PSC_ACT_DROP: /* Avoid memory leak. */ - if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) - (void) psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name, + if (state->dnsbl_score == NO_DNSBL_SCORE + && (state->flags & PSC_STATE_FLAG_DNSBL_TODO)) + (void) psc_dnsbl_retrieve(state->smtp_client_addr, + &state->dnsbl_name, state->dnsbl_index); PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n"); return; @@ -230,6 +290,15 @@ static void psc_early_dnsbl_event(int unused_event, char *context) if (msg_verbose) msg_info("%s: notify [%s]:%s", myname, PSC_CLIENT_ADDR_PORT(state)); + /* + * Collect the DNSBL score, and whitelist other tests if applicable. + */ + state->dnsbl_score = + psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, + state->dnsbl_index); + if (var_psc_dnsbl_wthresh < 0) + psc_whitelist_non_dnsbl(state); + /* * Terminate the greet delay if we're just waiting for DNSBL lookup to * complete. Don't call psc_early_event directly, that would result in a @@ -272,6 +341,7 @@ void psc_early_tests(PSC_STATE *state) (char *) state); else state->dnsbl_index = -1; + state->dnsbl_score = NO_DNSBL_SCORE; /* * Wait for the client to respond or for DNS lookup to complete. diff --git a/postfix/src/postscreen/postscreen_state.c b/postfix/src/postscreen/postscreen_state.c index 5b97ce032..c7472cc62 100644 --- a/postfix/src/postscreen/postscreen_state.c +++ b/postfix/src/postscreen/postscreen_state.c @@ -268,9 +268,6 @@ const char *psc_print_state_flags(int flags, const char *context) /* unused */ "WLIST_FAIL", PSC_STATE_FLAG_WLIST_FAIL, - "PENAL_UPDATE", PSC_STATE_FLAG_PENAL_UPDATE, - "PENAL_FAIL", PSC_STATE_FLAG_PENAL_FAIL, - "PREGR_FAIL", PSC_STATE_FLAG_PREGR_FAIL, "PREGR_PASS", PSC_STATE_FLAG_PREGR_PASS, "PREGR_TODO", PSC_STATE_FLAG_PREGR_TODO, diff --git a/postfix/src/postscreen/postscreen_tests.c b/postfix/src/postscreen/postscreen_tests.c index 57bfd6999..17b7a3926 100644 --- a/postfix/src/postscreen/postscreen_tests.c +++ b/postfix/src/postscreen/postscreen_tests.c @@ -27,6 +27,9 @@ /* const char *helo; /* const char *sender; /* const char *rcpt; +/* +/* const char *psc_test_name(tindx) +/* int tindx; /* DESCRIPTION /* The functions in this module overwrite the per-test expiration /* time stamps and all flags bits. Some functions are implemented @@ -54,6 +57,9 @@ /* This may modify the time stamps for disabled tests. /* /* psc_print_grey_key() prints a greylist lookup key. +/* +/* psc_test_name() returns the name for the specified text +/* index. /* LICENSE /* .ad /* .fi @@ -69,10 +75,12 @@ #include #include /* sscanf */ +#include /* strtoul */ /* Utility library. */ #include +#include /* Global library. */ @@ -132,7 +140,6 @@ void psc_new_tests(PSC_STATE *state) state->pipel_stamp = PSC_TIME_STAMP_NEW; state->nsmtp_stamp = PSC_TIME_STAMP_NEW; state->barlf_stamp = PSC_TIME_STAMP_NEW; - state->penal_stamp = PSC_TIME_STAMP_NEW; /* * Don't flag disabled tests as "todo", because there would be no way to @@ -156,17 +163,10 @@ void psc_parse_tests(PSC_STATE *state, const char *stamp_str, time_t time_value) { - unsigned long pregr_stamp; - unsigned long dnsbl_stamp; - unsigned long pipel_stamp; - unsigned long nsmtp_stamp; - unsigned long barlf_stamp; - unsigned long penal_stamp; - -#ifdef NONPROD - time_t penalty_left; - -#endif + const char *start = stamp_str; + char *cp; + time_t *time_stamps = state->expire_time; + time_t *sp; /* * We don't know what tests have expired or have never passed. @@ -182,37 +182,19 @@ void psc_parse_tests(PSC_STATE *state, * enabled tests, but the remote SMTP client has not yet passed all those * tests. */ - switch (sscanf(stamp_str, "%lu;%lu;%lu;%lu;%lu;%lu", - &pregr_stamp, &dnsbl_stamp, &pipel_stamp, &nsmtp_stamp, - &barlf_stamp, &penal_stamp)) { - case 0: - pregr_stamp = PSC_TIME_STAMP_DISABLED; - case 1: - dnsbl_stamp = PSC_TIME_STAMP_DISABLED; - case 2: - pipel_stamp = PSC_TIME_STAMP_DISABLED; - case 3: - nsmtp_stamp = PSC_TIME_STAMP_DISABLED; - case 4: - barlf_stamp = PSC_TIME_STAMP_DISABLED; - case 5: - penal_stamp = PSC_TIME_STAMP_DISABLED; - default: - break; + for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) { + *sp = strtoul(start, &cp, 10); + if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE) + *sp = PSC_TIME_STAMP_DISABLED; + if (*sp == PSC_TIME_STAMP_NEW) + state->flags |= PSC_STATE_FLAG_NEW; + if (msg_verbose) + msg_info("%s -> %lu", start, (unsigned long) *sp); + if (*cp == ';') + start = cp + 1; + else + start = cp; } - state->pregr_stamp = pregr_stamp; - state->dnsbl_stamp = dnsbl_stamp; - state->pipel_stamp = pipel_stamp; - state->nsmtp_stamp = nsmtp_stamp; - state->barlf_stamp = barlf_stamp; - state->penal_stamp = penal_stamp; - - if (pregr_stamp == PSC_TIME_STAMP_NEW - || dnsbl_stamp == PSC_TIME_STAMP_NEW - || pipel_stamp == PSC_TIME_STAMP_NEW - || nsmtp_stamp == PSC_TIME_STAMP_NEW - || barlf_stamp == PSC_TIME_STAMP_NEW) - state->flags |= PSC_STATE_FLAG_NEW; /* * Don't flag disabled tests as "todo", because there would be no way to @@ -270,41 +252,6 @@ void psc_parse_tests(PSC_STATE *state, state->flags |= PSC_STATE_FLAG_DNSBL_TODO; } #endif - - /* - * Apply unexpired penalty for past behavior. - * - * XXX Before we can drop connections, change this function to return - * success/fail, to inform the caller that the state object no longer - * exists. - */ -#ifdef NONPROD - if ((penalty_left = state->penal_stamp - event_time()) > 0) { - msg_info("PENALTY %ld for %s", - (long) penalty_left, state->smtp_client_addr); - PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); -#if 0 - switch (psc_penal_action) { - case PSC_ACT_DROP: - PSC_DROP_SESSION_STATE(state, - "421 4.3.2 Service currently unavailable\r\n"); - break; - case PSC_ACT_ENFORCE: -#endif - PSC_ENFORCE_SESSION_STATE(state, - "450 4.3.2 Service currently unavailable\r\n"); -#if 0 - break; - case PSC_ACT_IGNORE: - PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); - break; - default: - msg_panic("%s: unknown penalty action value %d", - myname, psc_penal_action); - } -#endif - } -#endif /* NONPROD */ } /* psc_print_tests - print postscreen cache record */ @@ -319,25 +266,6 @@ char *psc_print_tests(VSTRING *buf, PSC_STATE *state) if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0) msg_panic("%s: attempt to save a no-update record", myname); - /* - * Don't record a client as "passed" while subject to penalty. Be sure to - * produce correct PASS OLD/NEW logging. - * - * XXX This needs to be refined - we should not reset the result of tests - * that were passed in previous sessions, otherwise a client may never - * pass a multi-stage test such as greylisting. One solution is to keep - * the original and updated time stamps around, and to save an updated - * time stamp only when the corresponding "pass" flag is raised. - */ -#ifdef NONPROD - if (state->flags & PSC_STATE_FLAG_PENAL_FAIL) { - state->pregr_stamp = state->dnsbl_stamp = state->pipel_stamp = - state->nsmtp_stamp = state->barlf_stamp = - ((state->flags & PSC_STATE_FLAG_NEW) ? - PSC_TIME_STAMP_NEW : PSC_TIME_STAMP_DISABLED); - } -#endif - /* * Give disabled tests a dummy time stamp so that we don't log a client * with "pass new" when some disabled test becomes enabled at some later @@ -354,13 +282,12 @@ char *psc_print_tests(VSTRING *buf, PSC_STATE *state) if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW) state->barlf_stamp = PSC_TIME_STAMP_DISABLED; - vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu;%lu", + vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu", (unsigned long) state->pregr_stamp, (unsigned long) state->dnsbl_stamp, (unsigned long) state->pipel_stamp, (unsigned long) state->nsmtp_stamp, - (unsigned long) state->barlf_stamp, - (unsigned long) state->penal_stamp); + (unsigned long) state->barlf_stamp); return (STR(buf)); } @@ -373,3 +300,23 @@ char *psc_print_grey_key(VSTRING *buf, const char *client, return (STR(vstring_sprintf(buf, "%s/%s/%s/%s", client, helo, sender, rcpt))); } + +/* psc_test_name - map test index to symbolic name */ + +const char *psc_test_name(int tindx) +{ + const char *myname = "psc_test_name"; + const NAME_CODE test_name_map[] = { + PSC_TNAME_PREGR, PSC_TINDX_PREGR, + PSC_TNAME_DNSBL, PSC_TINDX_DNSBL, + PSC_TNAME_PIPEL, PSC_TINDX_PIPEL, + PSC_TNAME_NSMTP, PSC_TINDX_NSMTP, + PSC_TNAME_BARLF, PSC_TINDX_BARLF, + 0, -1, + }; + const char *result; + + if ((result = str_name_code(test_name_map, tindx)) == 0) + msg_panic("%s: bad index %d", myname, tindx); + return (result); +} diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index fffad281e..3ca448ac3 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -425,8 +425,8 @@ typedef struct STATE { TLS_SESS_STATE *tls_context; /* Session TLS context */ TLS_DANE *dane; /* DANE TLSA validation structure */ TLS_DANE *ddane; /* DANE TLSA from DNS */ - char *grade; /* Minimum cipher grade */ - char *protocols; /* Protocol inclusion/exclusion */ + char *grade; /* Minimum cipher grade */ + char *protocols; /* Protocol inclusion/exclusion */ #endif OPTIONS options; /* JCL */ } STATE; diff --git a/postfix/src/posttls-finger/tlsmgrmem.c b/postfix/src/posttls-finger/tlsmgrmem.c index 413fcfbc4..d6b09d87c 100644 --- a/postfix/src/posttls-finger/tlsmgrmem.c +++ b/postfix/src/posttls-finger/tlsmgrmem.c @@ -140,4 +140,3 @@ int tls_mgr_delete(const char *unused_type, const char *key) } #endif - diff --git a/postfix/src/posttls-finger/tlsmgrmem.h b/postfix/src/posttls-finger/tlsmgrmem.h index be2946b14..706b206b7 100644 --- a/postfix/src/posttls-finger/tlsmgrmem.h +++ b/postfix/src/posttls-finger/tlsmgrmem.h @@ -26,4 +26,3 @@ extern void tlsmgrmem_flush(void); /* /* Viktor Dukhovni /*--*/ - diff --git a/postfix/src/qmgr/qmgr_message.c b/postfix/src/qmgr/qmgr_message.c index 576fb2d6f..e6bbaf65c 100644 --- a/postfix/src/qmgr/qmgr_message.c +++ b/postfix/src/qmgr/qmgr_message.c @@ -1234,7 +1234,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) : strlen(STR(reply.recipient))); vstring_strncpy(queue_name, STR(reply.recipient), len); /* Remove the address extension from the recipient localpart. */ - if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim)) + if (*var_rcpt_delim && split_addr(STR(queue_name), var_rcpt_delim)) vstring_truncate(queue_name, strlen(STR(queue_name))); /* Assume the recipient domain is equivalent to nexthop. */ vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop)); diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 1f219e187..ce2b31d26 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -90,10 +90,6 @@ /* not contain RFC 822 style comments or phrases. /* .PP /* Available in Postfix version 2.1 and later: -/* .IP "\fBresolve_null_domain (no)\fR" -/* Resolve an address that ends in the "@" null domain as if the -/* local hostname were specified, instead of rejecting the address as -/* invalid. /* .IP "\fBsmtpd_reject_unlisted_sender (no)\fR" /* Request that the Postfix SMTP server rejects mail from unknown /* sender addresses, even when no explicit reject_unlisted_sender @@ -956,7 +952,8 @@ /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" /* The location of the Postfix top-level queue directory. /* .IP "\fBrecipient_delimiter (empty)\fR" -/* The separator between user names and address extensions (user+foo). +/* The set of characters that can separate a user name from its +/* address extension (user+foo). /* .IP "\fBsmtpd_banner ($myhostname ESMTP $mail_name)\fR" /* The text that follows the 220 status code in the SMTP greeting /* banner. diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index e5b165bf1..a27dc7012 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -2866,7 +2866,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, if (*var_rcpt_delim == 0) { bare_addr = 0; } else { - bare_addr = strip_addr(addr, (char **) 0, *var_rcpt_delim); + bare_addr = strip_addr(addr, (char **) 0, var_rcpt_delim); } #define CHECK_MAIL_ACCESS_RETURN(x) \ diff --git a/postfix/src/smtpd/smtpd_proxy.c b/postfix/src/smtpd/smtpd_proxy.c index f77885436..aec804974 100644 --- a/postfix/src/smtpd/smtpd_proxy.c +++ b/postfix/src/smtpd/smtpd_proxy.c @@ -30,9 +30,6 @@ /* int expect; /* const char *format; /* -/* void smtpd_proxy_disconnect(state) -/* SMTPD_STATE *state; -/* /* void smtpd_proxy_free(state) /* SMTPD_STATE *state; /* @@ -92,10 +89,6 @@ /* In case of error, proxy->cmd() updates the state->error_mask /* and state->err fields. /* -/* smtpd_proxy_disconnect() disconnects from a proxy server. -/* The last proxy server reply or error description remains -/* available via the proxy->buffer field. -/* /* smtpd_proxy_free() destroys a proxy server handle and resets /* the state->proxy field. /* diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c index d961199bf..e9c94daf8 100644 --- a/postfix/src/tls/tls_dane.c +++ b/postfix/src/tls/tls_dane.c @@ -158,7 +158,7 @@ #include #include #include -#include /* event_time() */ +#include /* event_time() */ #include #include @@ -201,22 +201,22 @@ static CTABLE *dane_cache; /* tls_dane_verbose - enable/disable verbose logging */ -void tls_dane_verbose(int on) +void tls_dane_verbose(int on) { dane_verbose = on; } /* tls_dane_avail - check for availability of dane required digests */ -int tls_dane_avail(void) +int tls_dane_avail(void) { -#ifdef TLSEXT_MAXLEN_host_name /* DANE mandates client SNI. */ +#ifdef TLSEXT_MAXLEN_host_name /* DANE mandates client SNI. */ static int avail = -1; const EVP_MD *sha256md; const EVP_MD *sha512md; static NAME_MASK ta_dgsts[] = { - TLS_DANE_CC, TLS_DANE_ENABLE_CC, - TLS_DANE_TAA, TLS_DANE_ENABLE_TAA, + TLS_DANE_CC, TLS_DANE_ENABLE_CC, + TLS_DANE_TAA, TLS_DANE_ENABLE_TAA, 0, }; @@ -239,13 +239,13 @@ int tls_dane_avail(void) return (avail = 1); #else - return (0); + return (0); #endif } /* tls_dane_flush - flush the cache */ -void tls_dane_flush(void) +void tls_dane_flush(void) { if (dane_cache) ctable_free(dane_cache); @@ -256,7 +256,7 @@ void tls_dane_flush(void) TLS_DANE *tls_dane_alloc(int flags) { - TLS_DANE *dane = (TLS_DANE *)mymalloc(sizeof(*dane)); + TLS_DANE *dane = (TLS_DANE *) mymalloc(sizeof(*dane)); dane->ta = 0; dane->ee = 0; @@ -270,7 +270,7 @@ TLS_DANE *tls_dane_alloc(int flags) static void ta_cert_insert(TLS_DANE *d, X509 *x) { - TLS_CERTS *new = (TLS_CERTS *)mymalloc(sizeof(*new)); + TLS_CERTS *new = (TLS_CERTS *) mymalloc(sizeof(*new)); CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); new->cert = x; @@ -286,13 +286,13 @@ static void free_ta_certs(TLS_DANE *d) for (head = d->certs; head; head = next) { next = head->next; X509_free(head->cert); - myfree((char *)head); + myfree((char *) head); } } static void ta_pkey_insert(TLS_DANE *d, EVP_PKEY *k) { - TLS_PKEYS *new = (TLS_PKEYS *)mymalloc(sizeof(*new)); + TLS_PKEYS *new = (TLS_PKEYS *) mymalloc(sizeof(*new)); CRYPTO_add(&k->references, 1, CRYPTO_LOCK_EVP_PKEY); new->pkey = k; @@ -308,7 +308,7 @@ static void free_ta_pkeys(TLS_DANE *d) for (head = d->pkeys; head; head = next) { next = head->next; EVP_PKEY_free(head->pkey); - myfree((char *)head); + myfree((char *) head); } } @@ -320,12 +320,12 @@ static void tlsa_free(TLS_TLSA *tlsa) argv_free(tlsa->certs); if (tlsa->pkeys) argv_free(tlsa->pkeys); - myfree((char *)tlsa); + myfree((char *) tlsa); } /* tls_dane_free - free a TLS_DANE structure */ -void tls_dane_free(TLS_DANE *dane) +void tls_dane_free(TLS_DANE *dane) { TLS_TLSA *tlsa; TLS_TLSA *next; @@ -347,14 +347,14 @@ void tls_dane_free(TLS_DANE *dane) free_ta_certs(dane); free_ta_pkeys(dane); - myfree((char *)dane); + myfree((char *) dane); } /* dane_free - ctable style */ static void dane_free(void *dane, void *unused_context) { - tls_dane_free((TLS_DANE *)dane); + tls_dane_free((TLS_DANE *) dane); } /* tlsa_sort - sort digests for a single certusage */ @@ -373,6 +373,7 @@ static void tlsa_sort(TLS_TLSA *tlsa) TLS_DANE *tls_dane_final(TLS_DANE *dane) { + /* * We only sort the trust anchors, see tls_serverid_digest(). */ @@ -390,8 +391,8 @@ static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg) /* * Correct computation of the session cache serverid requires a TLSA - * digest list that is sorted by algorithm name. Below we maintain - * the sort order (by algorithm name canonicalized to lowercase). + * digest list that is sorted by algorithm name. Below we maintain the + * sort order (by algorithm name canonicalized to lowercase). */ for (; *tlsap; tlsap = &(*tlsap)->next) { int cmp = strcasecmp(mdalg, (*tlsap)->mdalg); @@ -402,7 +403,7 @@ static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg) break; } - new = (TLS_TLSA *)mymalloc(sizeof(*new)); + new = (TLS_TLSA *) mymalloc(sizeof(*new)); new->mdalg = lowercase(mystrdup(mdalg)); new->certs = 0; new->pkeys = 0; @@ -414,8 +415,8 @@ static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg) /* tls_dane_split - split and append digests */ -void tls_dane_split(TLS_DANE *dane, int certusage, int selector, - const char *mdalg, const char *digest, const char *delim) +void tls_dane_split(TLS_DANE *dane, int certusage, int selector, + const char *mdalg, const char *digest, const char *delim) { TLS_TLSA **tlsap; TLS_TLSA *tlsa; @@ -450,7 +451,7 @@ void tls_dane_split(TLS_DANE *dane, int certusage, int selector, /* dane_add - add a digest entry */ static void dane_add(TLS_DANE *dane, int certusage, int selector, - const char *mdalg, char *digest) + const char *mdalg, char *digest) { TLS_TLSA **tlsap; TLS_TLSA *tlsa; @@ -462,11 +463,11 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector, switch (certusage) { case DNS_TLSA_USAGE_CA_CONSTRAINT: case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION: - certusage = TLS_DANE_TA; /* Collapse 0/2 -> 2 */ + certusage = TLS_DANE_TA; /* Collapse 0/2 -> 2 */ break; case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT: case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE: - certusage = TLS_DANE_EE; /* Collapse 1/3 -> 3 */ + certusage = TLS_DANE_EE; /* Collapse 1/3 -> 3 */ break; } @@ -498,18 +499,18 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) uint8_t mtype; int mlen; const unsigned char *p; - X509 *x = 0; /* OpenSSL tries to re-use *x if x!=0 */ - EVP_PKEY *k = 0; /* OpenSSL tries to re-use *k if k!=0 */ + X509 *x = 0; /* OpenSSL tries to re-use *x if x!=0 */ + EVP_PKEY *k = 0; /* OpenSSL tries to re-use *k if k!=0 */ if (rr == 0) msg_panic("null TLSA rr"); - for (/* nop */; rr; rr = rr->next) { + for ( /* nop */ ; rr; rr = rr->next) { const char *mdalg = 0; int mdlen; char *digest; int same = (strcasecmp(rr->rname, rr->qname) == 0); - uint8_t *ip = (uint8_t *)rr->data; + uint8_t *ip = (uint8_t *) rr->data; #define rcname(rr) (same ? "" : rr->qname) #define rarrow(rr) (same ? "" : " -> ") @@ -521,10 +522,9 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) /* Skip malformed */ if ((mlen = rr->data_len - 3) < 0) { msg_warn("truncated length %u RR: %s%s%s IN TLSA ...", - (unsigned)rr->data_len, rcname(rr), rarrow(rr), rr->rname); + (unsigned) rr->data_len, rcname(rr), rarrow(rr), rr->rname); continue; } - switch (usage = *ip++) { default: msg_warn("unsupported certificate usage %u in RR: " @@ -594,11 +594,11 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) break; } dane_add(dane, usage, selector, mdalg, - digest = tls_digest_encode((unsigned char *)ip, mdlen)); + digest = tls_digest_encode((unsigned char *) ip, mdlen)); break; case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED: - p = (unsigned char *)ip; + p = (unsigned char *) ip; /* Validate the cert or public key via d2i_mumble() */ switch (selector) { @@ -610,13 +610,14 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) usage, selector, mtype); continue; } + /* * When a full trust-anchor certificate is published via DNS, * we may need to use it to validate the server trust chain. - * Store it away for later use. We collapse certificate usage - * 0/2 because MTAs don't stock a complete list of the usual - * browser-trusted CAs. Thus, here (and in the public key - * case below) we treat the usages identically. + * Store it away for later use. We collapse certificate + * usage 0/2 because MTAs don't stock a complete list of the + * usual browser-trusted CAs. Thus, here (and in the public + * key case below) we treat the usages identically. */ switch (usage) { case DNS_TLSA_USAGE_CA_CONSTRAINT: @@ -644,12 +645,13 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) EVP_PKEY_free(k); break; } + /* - * The cert or key was valid, just digest the raw object, - * and encode the digest value. We choose SHA256. + * The cert or key was valid, just digest the raw object, and + * encode the digest value. We choose SHA256. */ dane_add(dane, usage, selector, sha256, - digest = tls_data_fprint((char *)ip, mlen, sha256)); + digest = tls_data_fprint((char *) ip, mlen, sha256)); break; } if (msg_verbose || dane_verbose) @@ -705,13 +707,13 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx) break; } - return ((void *)tls_dane_final(dane)); + return ((void *) tls_dane_final(dane)); } /* tls_dane_resolve - cached map: (host, proto, port) -> TLS_DANE */ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, - unsigned port) + unsigned port) { static VSTRING *qname; TLS_DANE *dane; @@ -725,9 +727,9 @@ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, if (qname == 0) qname = vstring_alloc(64); vstring_sprintf(qname, "_%u._%s.%s", ntohs(port), proto, host); - dane = (TLS_DANE *)ctable_locate(dane_cache, STR(qname)); + dane = (TLS_DANE *) ctable_locate(dane_cache, STR(qname)); if (timecmp(event_time(), dane->expires) > 0) - dane = (TLS_DANE *)ctable_refresh(dane_cache, STR(qname)); + dane = (TLS_DANE *) ctable_refresh(dane_cache, STR(qname)); if (dane->flags & TLS_DANE_FLAG_ERROR) return (0); @@ -738,7 +740,7 @@ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, /* tls_dane_load_trustfile - load trust anchor certs or keys from file */ -int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) +int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) { BIO *bp; char *name = 0; @@ -746,22 +748,22 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) unsigned char *data = 0; long len; int tacount; - char *errtype = 0; /* if error: cert or pkey? */ + char *errtype = 0; /* if error: cert or pkey? */ /* nop */ if (tafile == 0 || *tafile == 0) return (1); /* - * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio, calls - * PEM_read_bio() and then frees the bio. It is just as easy to open a - * BIO as a stdio file, so we use BIOs and call PEM_read_bio() directly. + * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio, + * calls PEM_read_bio() and then frees the bio. It is just as easy to + * open a BIO as a stdio file, so we use BIOs and call PEM_read_bio() + * directly. */ if ((bp = BIO_new_file(tafile, "r")) == NULL) { msg_warn("error opening trust anchor file: %s: %m", tafile); return (0); } - /* Don't report old news */ ERR_clear_error(); @@ -779,7 +781,7 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) if (cert && (p - data) == len) { selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE; - digest = tls_data_fprint((char *)data, len, sha256); + digest = tls_data_fprint((char *) data, len, sha256); dane_add(dane, usage, selector, sha256, digest); myfree(digest); ta_cert_insert(dane, cert); @@ -792,7 +794,7 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) if (pkey && (p - data) == len) { selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO; - digest = tls_data_fprint((char *)data, len, sha256); + digest = tls_data_fprint((char *) data, len, sha256); dane_add(dane, usage, selector, sha256, digest); myfree(digest); ta_pkey_insert(dane, pkey); @@ -817,13 +819,11 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) tafile, errtype); return (0); } - if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) { /* Reached end of PEM file */ ERR_clear_error(); return (tacount > 0); } - /* Some other PEM read error */ tls_print_errors(); return (0); diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index 93b5fb1d5..86f47759b 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -950,11 +950,12 @@ long tls_bug_bits(void) } /* - * Allow users to set options not in SSL_OP_ALL, and not already - * managed via other Postfix parameters. + * Allow users to set options not in SSL_OP_ALL, and not already managed + * via other Postfix parameters. */ if (*var_tls_ssl_options) { - long enable; + long enable; + enable = long_name_mask_opt(VAR_TLS_SSL_OPTIONS, ssl_op_tweaks, var_tls_ssl_options, NAME_MASK_ANY_CASE | NAME_MASK_NUMBER | NAME_MASK_WARN); diff --git a/postfix/src/trivial-rewrite/resolve.c b/postfix/src/trivial-rewrite/resolve.c index c0b6d9964..d9a709e30 100644 --- a/postfix/src/trivial-rewrite/resolve.c +++ b/postfix/src/trivial-rewrite/resolve.c @@ -324,9 +324,18 @@ static void resolve_addr(RES_CONTEXT *rp, char *sender, char *addr, tok822_free(tree->head); tree->head = 0; } - /* XXX must be localpart only, not user@domain form. */ - if (tree->head == 0) + /* XXX Re-resolve the surrogate, in case already in user@domain form. */ + if (tree->head == 0) { tree->head = tok822_scan(var_empty_addr, &tree->tail); + continue; + } + + /* XXX Re-resolve with @$myhostname for backwards compatibility. */ + if (domain == 0 && saved_domain == 0) { + tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); + tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0)); + continue; + } /* * We're done. There are no domains left to strip off the address, diff --git a/postfix/src/trivial-rewrite/transport.c b/postfix/src/trivial-rewrite/transport.c index 29720541c..61937ae42 100644 --- a/postfix/src/trivial-rewrite/transport.c +++ b/postfix/src/trivial-rewrite/transport.c @@ -286,7 +286,7 @@ int transport_lookup(TRANSPORT_INFO *tp, const char *addr, * partial lookup keys with regular expressions. */ if ((stripped_addr = strip_addr(addr, DISCARD_EXTENSION, - *var_rcpt_delim)) != 0) { + var_rcpt_delim)) != 0) { found = find_transport_entry(tp, stripped_addr, rcpt_domain, PARTIAL, channel, nexthop); diff --git a/postfix/src/trivial-rewrite/trivial-rewrite.c b/postfix/src/trivial-rewrite/trivial-rewrite.c index b8cc438c2..c5dbe0663 100644 --- a/postfix/src/trivial-rewrite/trivial-rewrite.c +++ b/postfix/src/trivial-rewrite/trivial-rewrite.c @@ -83,10 +83,14 @@ /* .IP "\fBresolve_dequoted_address (yes)\fR" /* Resolve a recipient address safely instead of correctly, by /* looking inside quotes. +/* .PP +/* Available with Postfix version 2.1 and later: /* .IP "\fBresolve_null_domain (no)\fR" /* Resolve an address that ends in the "@" null domain as if the /* local hostname were specified, instead of rejecting the address as /* invalid. +/* .PP +/* Available with Postfix version 2.3 and later: /* .IP "\fBresolve_numeric_domain (no)\fR" /* Resolve "user@ipaddress" as "user@[ipaddress]", instead of /* rejecting the address as invalid. @@ -110,7 +114,8 @@ /* With locally submitted mail, append the string ".$mydomain" to /* addresses that have no ".domain" information. /* .IP "\fBrecipient_delimiter (empty)\fR" -/* The separator between user names and address extensions (user+foo). +/* The set of characters that can separate a user name from its +/* address extension (user+foo). /* .IP "\fBswap_bangpath (yes)\fR" /* Enable the rewriting of "site!user" into "user@site". /* .PP diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index 35bb56db5..5b2eec3eb 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -156,8 +156,9 @@ ARGV *argv_alloc(ssize_t len) static int argv_cmp(const void *e1, const void *e2) { - const char *s1 = *(const char **)e1; - const char *s2 = *(const char **)e2; + const char *s1 = *(const char **) e1; + const char *s2 = *(const char **) e2; + return strcmp(s1, s2); } diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index 8d77883f5..3c4a9b1b8 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -431,6 +431,7 @@ void dict_load_fp(const char *dict_name, VSTREAM *fp) VSTRING *buf; char *member; char *val; + const char *old; int old_lineno; int lineno; const char *err; @@ -455,6 +456,10 @@ void dict_load_fp(const char *dict_name, VSTREAM *fp) err, STR(buf)); if (msg_verbose > 1) msg_info("%s: %s = %s", myname, member, val); + if ((old = dict->lookup(dict, member)) != 0 + && strcmp(old, val) != 0) + msg_warn("%s, line %d: overriding earlier entry: %s=%s", + VSTREAM_PATH(fp), lineno, member, old); if (dict->update(dict, member, val) != 0) msg_fatal("%s, line %d: unable to update %s:%s", VSTREAM_PATH(fp), lineno, dict->type, dict->name); diff --git a/postfix/src/util/poll_fd.c b/postfix/src/util/poll_fd.c index e8c7ed21f..80cd0f67d 100644 --- a/postfix/src/util/poll_fd.c +++ b/postfix/src/util/poll_fd.c @@ -27,8 +27,8 @@ /* int true_res; /* int false_res; /* DESCRIPTION -/* The functions in this module are macros that provide a -/* convenient interface to poll_fd(). +/* The read*() and write*() functions in this module are macros +/* that provide a convenient interface to poll_fd(). /* /* readable() asks the kernel if the specified file descriptor /* is readable, i.e. a read operation would not block. @@ -72,8 +72,8 @@ /* it is false. They never return an error indication. /* /* read_wait() and write_wait() return zero when the requested -/* POLL_FD_READ or POLL_FD_WRITE condition is true, -1 with -/* errno set to ETIMEDOUT when it is false. +/* POLL_FD_READ or POLL_FD_WRITE condition is true, -1 (with +/* errno set to ETIMEDOUT) when it is false. /* /* poll_fd() returns true_res when the requested POLL_FD_READ /* or POLL_FD_WRITE condition is true, false_res when it is