From: Wietse Venema Date: Thu, 31 Oct 2002 05:00:00 +0000 (-0500) Subject: postfix-1.1.11-20021031 X-Git-Tag: v2.0.0~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6bc08d7f83d1cba17471b1c2931ebb4654f89674;p=thirdparty%2Fpostfix.git postfix-1.1.11-20021031 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 5091041ec..9e2c2f2f9 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -7078,7 +7078,7 @@ Apologies for any names omitted. Paranoia: defend against a very unlikely false alarm in safe_open(). -20020125 +20021025 Feature: X-Original-To: message headers with the raw original envelope recipient. @@ -7087,7 +7087,7 @@ Apologies for any names omitted. the original recipient address if it differs from the final address. -20020126 +20021026 Logging: SMTP UCE reject/warn/hold/discard logging now includes queue ID. This will break some logfile analyzers. @@ -7115,6 +7115,30 @@ Apologies for any names omitted. Workaround: DJBDNS produces a bogus A record when given a numerical hostname. File: dns/dns_lookup.c. +20021030 + + Portability: support for Berkeley DB version 4.0 but not + for Berkeley DB version 4.1 (yes, the API is different). + Postfix is now going to be paranoid about the minor version + number, too. File: util/dict_db.c. + + Documentation: updated LMTP_README file by Amos Gouaux. + +20021031 + + Bugfix: (bug introduced 20021026) log NOQUEUE when rejecting + ETRN, instead of trying to log a non-existent queue ID. + Victor Duchovni, Morgan Stanley. File: smtpd/smtpd_check.c. + + Cleanup: allow optional text after commands in SMTPD access + maps. Based on initial effort by Victor Duchovni, Morgan + Stanley. File: smtpd/smtpd_check.c. + + Portability: support for Berkeley DB version 4.1. This + version refuses to open zero-length files. This complicates + lock management and requires extra code to remove broken + files. Files: util/dict_db.c, global/mkmap*.[hc]. + Open problems: Low: revise other local delivery agent duplicate filters. diff --git a/postfix/README_FILES/LMTP_README b/postfix/README_FILES/LMTP_README index 06a6878ea..26e922807 100644 --- a/postfix/README_FILES/LMTP_README +++ b/postfix/README_FILES/LMTP_README @@ -1,5 +1,3 @@ -[This file still needs to be updated - some information is obsolete] - 1 - Postfix LMTP support ======================== @@ -10,16 +8,17 @@ delivery agent, which may run on the local host or a remote host. This protocol opens up interesting possibilities: one Postfix front end machine can drive multiple mailbox back end machines over LMTP. As the mail load increases, you add more Postfix front end systems -and more LMTP mailbox back end systems. This is the model that I -had in mind when I began drafting the design for Postfix - a scalable -architecture that allows you to keep adding SMTP servers and mailbox -servers painlessly. +and more LMTP mailbox back end systems. This is the model that +Wietse had in mind when he began drafting the design for Postfix +- a scalable architecture that allows you to keep adding SMTP +servers and mailbox servers painlessly. Such a distributed architecture needs glue to keep things together. -You can use a networked database LDAP or mysql to share the user +You can use a networked database (LDAP or mysql) to share the user database among the front end and back end systems. Use a replicated database so that no machine becomes a single point of failure for -the entire mail infrastructure. +the entire mail infrastructure. Or you can use rsync when files +are small and/or when information does not change often. Postfix LMTP support is based on a modified version of the Postfix SMTP client. The initial version was by Philip A. Prindeville of @@ -39,10 +38,9 @@ server, available from: While certainly not the only application that could make use of LMTP, it tends to be the most discussed. These examples are based -on the forthcoming Cyrus 2.0.10, at least at the time of writing. -The 2.x branch of Cyrus places greater emphasis on LMTP delivery -than the previous releases. Those using older releases of Cyrus -can find a discussion in the appendix of this document. +on Cyrus 2.1.5. The 2.x branch of Cyrus places greater emphasis on +LMTP delivery than the previous releases. Those using older releases +of Cyrus can find a discussion in the appendix of this document. There are a variety of ways LMTP delivery can be configured in Postfix. The two basic flavors are delivery over UNIX-domain @@ -67,10 +65,8 @@ text below. Both socket flavors can be specified in either the Postfix main.cf file (see section 5) or in a Postfix transport map (section 6). What is the best approach for you depends upon the arrangement of -your servers and the desired level of parallelization. +your servers. -Please be sure to study this entire document as there are trade-offs -in convenience and in performance with these different approaches. 3 - LMTP over UNIX-domain sockets ================================= @@ -97,6 +93,7 @@ NOTE: With LMTP delivery to the local machine there is no good reason to run the Postfix LMTP client chrooted. + 4 - LMTP over TCP sockets ========================= @@ -112,21 +109,28 @@ The "inet:" part can be omitted, as it is the default socket type. The destination port can be omitted as well. Currently the default TCP port number for this type of connection is 24, but this can be -customized in the "/etc/services" file. Specific examples are +customized in the /etc/services file. Specific examples are given later in this document. NOTE: - With connections over TCP sockets, later Cyrus LMTP server - implementations insist on SASL-style authentication. This means - that Postfix must be built with SASL support (see SASL_README). - The examples below show how to enable this in the Postfix LMTP - client. + With connections over TCP sockets, Cyrus 2.0.x LMTP server + implementations insisted on SASL-style authentication. This + meant that Postfix had to be built with SASL support (see + SASL_README). While newer Cyrus releases offer an option to + turn off this requirement, you must exercise great care in + both determining the approach used and the configuration of + your selection. It is imperative that you do not allow + unauthorized access to your LMTP server. The examples below + show both approaches. Some Cyrus LMTP server implementations do not allow SASL-style - authentication via plaintext passwords. You will have to jump - some extra hoops in order to enable MD5 password support, or - you will have to wait until this restriction is relaxed. + authentication via plaintext passwords over unencrypted + connections. This is not the case with 2.1.5. However, you + must realize that this could make your LMTP link vulnerable. + If your LMTP communications traverse exposed networks, you + should either use an encrypted connection or enable MD5 SASL + mechanisms. 5 - Configuring LMTP using main.cf configuration @@ -188,66 +192,171 @@ To utilize UNIX-domain sockets for the communication between Postfix and Cyrus, the corresponding configuration files should look something like this: -/etc/cyrus.conf: - - SERVICES { - ... - lmtpunix cmd="lmtpd" listen="/var/imap/socket/lmtp" prefork=1 - ... - } - -/etc/postfix/main.cf: - - mailbox_transport = lmtp:unix:/var/imap/socket/lmtp + /etc/cyrus.conf: + SERVICES { + ... + lmtpunix cmd="lmtpd" listen="/var/imap/socket/lmtp" prefork=1 + ... + } -/etc/postfix/master.cf: + /etc/postfix/main.cf: + mailbox_transport = lmtp:unix:/var/imap/socket/lmtp - lmtp unix - - n - - lmtp + /etc/postfix/master.cf: + lmtp unix - - n - - lmtp In this case, the Postfix local delivery agent expands aliases and .forward files, and delegates mailbox delivery to the Cyrus lmtpd server via the socket "/var/imap/socket/lmtp". -5.2.2 - LMTP over TCP sockets ------------------------------ +NOTE: + + Make sure that both the user id that Cyrus runs under and the + Postfix user id can access the socket "/var/imap/socket/lmtp". + While this is implied by the example above, it is often + overlooked and so warrants emphasis. + +5.2.2 - LMTP over TCP sockets (non-SASL) +---------------------------------------- For this example, suppose the following files are configured thusly: -/etc/cyrus.conf: + /etc/cyrus.conf: + SERVICES { + ... + lmtp cmd="lmtpd -a" listen="127.0.0.1:lmtp" prefork=1 + ... + } + + /etc/services: + lmtp 2003/tcp + + /etc/postfix/main.cf: + mailbox_transport = lmtp:localhost + + /etc/postfix/master.cf: + lmtp unix - - n - - lmtp + +With the above settings, the Postfix local delivery agent expands +aliases and .forward files, and delegates mailbox delivery to the +Cyrus LMTP server. Postfix makes a connection to port 2003 on the +local host, subsequently transmitting the message to the lmtpd +server managed by the Cyrus master process. The port number has +been changed in /etc/services from 24 to 2003 as that is what the +Cyrus LMTP server is now using. + +See the Cyrus lmtpd(8) man page to verify that the version you +have supports the "-a" option. + +NOTE: + + Consider this approach if and only if this particular host + does not allow direct user logins or user-controlled + processes. Otherwise, this LMTP server may be abused! + + If the Cyrus lmtp service is to listen on a network other + than the local loop-back (127.0.0.1), be sure to install + Cyrus with tcp_wrappers support. Then use tcp_wrappers + to only allow access to the "lmtp" service from trusted + hosts. Otherwise, this LMTP server may be abused! + + Section 10 contains an example using tcp_wrappers. For the + configuration above, replace "deliver" with "lmtp" in the + /etc/hosts.allow shown in that example. - SERVICES { - ... - lmtp cmd="lmtpd" listen="127.0.0.1:lmtp" prefork=0 - ... - } +5.2.3 - LMTP over TCP sockets (SASL) +------------------------------------ -/etc/services: +In the following example "cyhost.my.domain" is the name of the Cyrus +IMAP/POP server. It is important that you use this name consistently +in the Postfix configuration settings. The first approach uses the +PLAIN authentication mechanism (see the Cyrus SASL documentation.) - lmtp 24/tcp +Suppose the Cyrus server has the following files configured thusly: -/etc/postfix/main.cf: + /etc/cyrus.conf: + SERVICES { + ... + lmtp cmd="lmtpd" listen="cyhost.my.domain:lmtp" prefork=1 + ... + } - mailbox_transport = lmtp:localhost - lmtp_sasl_auth_enable = yes - lmtp_sasl_password_maps = hash:/etc/postfix/lmtp_sasl_pass + /etc/imapd.conf: + lmtp_admins: lmtpuser -/etc/postfix/lmtp_sasl_pass: - localhost.my.domain username:password + /etc/services: + lmtp 2003/tcp + +The Postfix host (may be same box) has the following configuration: -/etc/postfix/master.cf: + /etc/postfix/main.cf: + mailbox_transport = lmtp:cyhost.my.domain + lmtp_sasl_auth_enable = yes + lmtp_sasl_password_maps = hash:/etc/postfix/lmtp_sasl_pass + lmtp_sasl_security_options = noanonymous - lmtp unix - - n - - lmtp + /etc/postfix/lmtp_sasl_pass: + cyhost.my.domain lmtpuser:password + + /etc/services: + lmtp 2003/tcp + + /etc/postfix/master.cf: + lmtp unix - - n - - lmtp Instead of "hash", use the map type of your choice. Some systems use "dbm" instead. Use "postconf -m" to find out what map types are supported. +If your version of Cyrus does not support "lmtp_admins" as a +setting in imapd.conf, use "admins" instead. + With the above settings, the Postfix local delivery agent expands aliases and .forward files, and delegates mailbox delivery to the -Cyrus LMTP server. Postfix makes a connection to port 24 on the +Cyrus LMTP server. Postfix makes a connection to port 2003 on the local host, subsequently transmitting the message to the lmtpd -server managed by the Cyrus master process. +server managed by the Cyrus master process. The port number has +been changed in /etc/services from 24 to 2003 as that is what the +Cyrus LMTP server is now using. The SASL configuration on the Cyrus +server will need to accept the user "lmtpuser" using the password +specified in /etc/postfix/lmtp_sasl_pass on the Postfix host. + +If this LMTP conduit exists over an exposed network, you should +compile Postfix with MD5 (CRAM or DIGEST) password support. See +SASL_README for more details. Then configure Postfix as follows: + + /etc/postfix/main.cf: + lmtp_sasl_security_options = noanonymous, noplaintext + +On the Cyrus host you should also set: + + /etc/imapd.conf: + lmtp_allowplaintext: no + +You will need to make sure the "lmtpuser" is in the appropriate +SASL database. As an example, the following would add "lmtpuser" +to /etc/sasldb2: + + saslpasswd2 -c -u cyhost.my.domain lmtpuser + +If you encounter difficulties with "lmtpuser" not being permitted +to authenticate to the LMTP server, try the above command with the +un-qualified hostname: + + saslpasswd2 -c -u cyhost lmtpuser + +Also make sure the Cyrus user has read permission of the SASL +database, /etc/sasldb2 in the example above. + +Incidentally, it is very likely that the Cyrus server and the +Postfix server will need to use the same SASL backend databases +(e.g., auxprop or saslauthd.) Currently it is not possible to +assign different SASL backends for different Cyrus services. +Only TLS (SSL) or STARTTLS can be used in conjunction with +saslauthd. Since none of these encryption methods are available +for LMTP, if you need to encrypt your LMTP connections, you will +very likely have to use auxprop throughout. 6 - Configuring LMTP using transport map configuration @@ -264,17 +373,14 @@ to route mail for multiple domains to their respective mail retrieval (IMAP/POP) server. Example: /etc/postfix/transport: - domain1.tld lmtp1:unix:/path/name domain2.tld lmtp2:lmtp2host /etc/postfix/master.cf: - lmtp1 unix - - n - - lmtp lmtp2 unix - - n - - lmtp /etc/postfix/main.cf: - transport_maps = hash:/etc/postfix/transport For details of the Cyrus LMTP server configuration, see section 5. @@ -307,68 +413,36 @@ recipients would then see a hard link of this single instance. Depending on your user base, this can be considerable motivation to using LMTP. -However, there is a catch: the Postfix local delivery agent is -designed to deliver one recipient at a time, which in most cases -is more than adequate. So, if you wish to support single instance -message store delivery, you will have to use a virtual table to -map these users to the appropriate LMTP destination (at the time -of writing, the Postfix transport table supports only per-domain -routing, and not per-recipient routing). - -While the simplest thing to do would be to list the entire domain -in the transport map for LMTP delivery, this by-passes alias -expansion for otherwise local addresses (see section 5.1, delivery -mechanism 2). If the site is to run software via aliases, like -most Mailing List Management (MLM) software, a more complex solution -is required. A virtual table should do the trick. - -As an example, suppose we wanted to support single instance message -store delivery for the hosted (not local) domain "example.org". -The configuration files for this domain could look something like -this: +With the examples in section 5.2, we can increase the number of +recipients (to $mydestination) that can be handled at once: - /etc/postfix/virtual: - - mlist@example.org mlist@localhost - - /etc/postfix/transport: - - example.org lmtp:unix:/var/imap/socket/lmtp + /etc/postfix/main.cf: + local_destination_recipient_limit = 300 - /etc/postfix/aliases: +The 300 was arbitrarily chosen for this example. Be sure to pick a +number that is appropriate for the capabilities of your hardware. +The bigger the number, the more you can leverage the single instance +message store. However, if it is too big the LMTP server will need +too much time to deliver a message and Postfix will experience +timeout errors. Choose this value very carefully. - mlist: "|/path/to/mlm/software" +NOTE: - /etc/postfix/master.cf: + Not all local delivery agent transports can support a recipient + limit greater than 1. Be sure to check the man page of the + specific transport before attempting this with anything but the + Postfix LMTP client. - lmtp unix - - n - - lmtp +If we wish to apply this single instance message store technique +with the configuration example in section 6, the setting would be: /etc/postfix/main.cf: + lmtp1_destination_recipient_limit = 300 + lmtp2_destination_recipient_limit = 300 - mydestination = localhost, $myhostname, $mydomain - virtual_maps = hash:/etc/postfix/virtual - transport_maps = hash:/etc/postfix/transport - alias_maps = hash:/etc/postfix/aliases - alias_database = hash:/etc/postfix/aliases - - /etc/cyrus.conf: - - SERVICES { - ... - lmtpunix cmd="lmtpd" listen="/var/imap/socket/lmtp" prefork=1 - ... - } - -Breaking things down, we begin with the address "mlist@example.org", -which represents a mailing list. By placing an entry in the virtual -map to direct this mail to "mlist@localhost", we can override the -transport map that would by default route all "@example.org" mail -to a LMTP server via a UNIX-domain socket. - -To summarize, all mail that is to be processed by an alias entry -must first be diverted with a virtual table entry so that it does -not fall into the more general routing established by the transport -table. +As previously mentioned, exercise tremendous care backed by +extensive analysis of your systems before setting the recipient +limit like this. 9 - Improving connection caching performance @@ -388,7 +462,6 @@ You can prevent the LMTP client from switching between servers by configuring a separate LMTP delivery transport for each LMTP server: /etc/postfix/master.cf: - lmtp1 unix - - n - - lmtp lmtp2 unix - - n - - lmtp . . . . . . . . @@ -398,7 +471,6 @@ transport is used for all deliveries to the LMTP server #1, the mail lmtp2 transport for the LMTP server #2, and so on. /etc/postfix/transport: - foo.com lmtp1:lmtp1host bar.com lmtp2:lmtp2host @@ -406,10 +478,9 @@ mail lmtp2 transport for the LMTP server #2, and so on. 10 - Appendix: Older Cyrus versions =================================== -First of all, if you are using a Cyrus 2.x version prior to 2.0.10, -it would be good to upgrade. The previous 2.x releases were beta -releases, and numerous bug fixes and enhancements have been -incorporated into the 2.0.10 release. +First of all, if you are using a Cyrus 2.x version prior to 2.1.4, +you should really consider upgrading. There have been numerous bug +fixes and performance improvements since the early 2.0 releases. Further back, 1.6.24 was the last pre-2.x production release. (Actually, there was a 1.6.25-BETA, but it is uncertain whether this @@ -427,15 +498,12 @@ To utilize LMTP delivery with Cyrus 1.6.24, the first thing to do is configure inetd. This involves the following file edits: /etc/services: - - lmtp 24/tcp + lmtp 2003/tcp /etc/inetd.conf: - lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/cyrus/bin/deliver -e -l /etc/hosts.allow: - deliver : localhost : ALLOW deliver : ALL@ALL : DENY @@ -450,32 +518,27 @@ can do a similar job of logging and access control. Now comes the Postfix configuration. Basically, the Cyrus 2.x discussions regarding LMTP delivery over TCP are also applicable to -Cyrus 1.x, with the exception of the "/etc/cyrus.conf" file. A +Cyrus 1.x, with the exception of the /etc/cyrus.conf file. A typical Postfix configuration might look like this: /etc/postfix/master.cf: - lmtp unix - - n - - lmtp /etc/postfix/main.cf: - mailbox_transport = lmtp It is also possible to use the transport map to route mail to your Cyrus 1.6.24 LMTP server: /etc/postfix/transport: - domain1.tld lmtp1:lmtp1host domain2.tld lmtp2:lmtp2host /etc/postfix/master.cf: - lmtp1 unix - - n - - lmtp lmtp2 unix - - n - - lmtp /etc/postfix/main.cf: - transport_maps = hash:/etc/postfix/transport If you have read the discussion covering the Cyrus 2.x installation, diff --git a/postfix/conf/access b/postfix/conf/access index 3dcd6000e..9810d5237 100644 --- a/postfix/conf/access +++ b/postfix/conf/access @@ -114,8 +114,13 @@ # Reject the address etc. that matches the pattern, # and respond with the numerical code and text. # -# REJECT Reject the address etc. that matches the pattern. A -# generic error response message is generated. +# REJECT +# +# REJECT optional text... +# Reject the address etc. that matches the pattern. +# Reply with $reject_code optional text... when the +# optional text is specified, otherwise reply with a +# generic error response message. # # OK Accept the address etc. that matches the pattern. # @@ -124,19 +129,32 @@ # mat is generated by address-based relay authoriza- # tion schemes. # -# HOLD Place the message on the hold queue, where it will -# sit until someone either deletes it or releases it -# for delivery. Mail that is placed on hold can be -# examined with the postcat(1) command, and can be -# destroyed or released with the postsuper(1) com- -# mand. +# DUNNO Pretend that the lookup key was not found in this +# table, to prevents Postfix from trying substrings +# of the lookup key (such as a subdomain name, or a +# network address subnetwork). # -# Note: this action currently affects all recipients +# HOLD +# +# HOLD optional text... +# Place the message on the hold queue, where it will +# sit until someone either deletes it or releases it +# for delivery. Log the optional text if specified, +# otherwise log a generic message. +# +# Mail that is placed on hold can be examined with +# the postcat(1) command, and can be destroyed or +# released with the postsuper(1) command. +# +# Note: this action currently affects all recipients # of the message. # # DISCARD -# Claim successful delivery and silently discard the -# message. +# +# DISCARD optional text... +# Claim successful delivery and silently discard the +# message. Log the optional text if specified, oth- +# erwise log a generic message. # # Note: this action currently affects all recipients # of the message. diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf index d2c1ff794..b781213a9 100644 --- a/postfix/conf/sample-smtpd.cf +++ b/postfix/conf/sample-smtpd.cf @@ -68,8 +68,9 @@ smtpd_banner = $myhostname ESMTP $mail_name # check_client_access maptype:mapname # look up client name, parent domains, client address, # or networks obtained by stripping octets. -# Reject if result is REJECT or "[45]xx text" -# Permit if result is OK or all numerical. +# Skip this lookup table if the result is DUNNO. +# Reject the ETRN command if the result is REJECT text... or "[45]xx text" +# Permit the ETRN command if the result is OK or all numerical. # reject_rbl_client domain.tld: reject if the reverse client network # address is listed in an A record under domain.tld. # reject_rhsbl_client domain.tld: reject if the client hostname is listed @@ -201,8 +202,12 @@ mynetworks_style = subnet # check_client_access maptype:mapname # look up client name, parent domains, client address, # or networks obtained by stripping octets. -# Reject if result is REJECT or "[45]xx text" -# Permit if result is OK or all numerical. +# Skip this lookup table if the result is DUNNO. +# Reject the SMTP client if the result is REJECT text... or "[45]xx text" +# Discard the message if the result is DISCARD text... +# Hold the message in the queue if the result is HOLD text... +# Release mail "on hold" with the postsuper(1) command. +# Permit the SMTP client if the result is OK or all numerical. # reject_rbl_client domain.tld: reject if the reversed client IP address # is listed in an A record under domain.tld. # reject_rhsbl_client domain.tld: reject if the client hostname is listed @@ -243,8 +248,12 @@ smtpd_helo_required = no # reject_non_fqdn_hostname: reject HELO hostname that is not in FQDN form # check_helo_access maptype:mapname # look up HELO hostname or parent domains. -# Reject if result is REJECT or "[45]xx text" -# Permit if result is OK or all numerical. +# Skip this lookup table if the result is DUNNO. +# Reject the HELO command if the result is REJECT text... or "[45]xx text" +# Discard the message if the result is DISCARD text... +# Hold the message in the queue if the result is HOLD text... +# Release mail "on hold" with the postsuper(1) command. +# Permit the HELO command if the result is OK or all numerical. # reject: reject the request. Place this at the end of a restriction. # permit: permit the request. Place this at the end of a restriction. # warn_if_reject: next restriction logs a warning instead of rejecting. @@ -276,8 +285,12 @@ smtpd_helo_restrictions = # in an A record under domain.tld. # check_sender_access maptype:mapname # look up sender address, parent domain, or localpart@. -# Reject if result is REJECT or "[45]xx text" -# Permit if result is OK or all numerical. +# Skip this lookup table if the result is DUNNO. +# Reject the sender if the result is REJECT text... or "[45]xx text" +# Discard the message if the result is DISCARD text... +# Hold the message in the queue if the result is HOLD text... +# Release mail "on hold" with the postsuper(1) command. +# Permit the sender if the result is OK or all numerical. # reject_sender_login_mismatch: reject if $smtpd_sender_login_maps specifies # a MAIL FROM address owner, but the client is not (SASL) logged in as # that MAIL FROM address owner; or if the client is (SASL) logged in, but @@ -350,8 +363,12 @@ smtpd_sender_restrictions = # reject_unknown_recipient_domain: reject domains without A or MX record. # check_recipient_access maptype:mapname # look up recipient address, parent domain, or localpart@. -# Reject if result is REJECT or "[45]xx text" -# Permit if result is OK or all numerical. +# Skip this lookup table if the result is DUNNO. +# Reject the recipient if the result is REJECT text... or "[45]xx text" +# Discard the message if the result is DISCARD text... +# Hold the message in the queue if the result is HOLD text... +# Release mail "on hold" with the postsuper(1) command. +# Permit the recipient if the result is OK or all numerical. # reject_non_fqdn_recipient: reject recipient address that is not in FQDN form # reject: reject the request. Place this at the end of a restriction. # permit: permit the request. Place this at the end of a restriction. diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index aff8bac21..64186e76d 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -115,8 +115,13 @@ ACCESS(5) ACCESS(5) Reject the address etc. that matches the pattern, and respond with the numerical code and text. - REJECT Reject the address etc. that matches the pattern. A - generic error response message is generated. + REJECT + + REJECT optional text... + Reject the address etc. that matches the pattern. + Reply with $reject_code optional text... when the + optional text is specified, otherwise reply with a + generic error response message. OK Accept the address etc. that matches the pattern. @@ -125,19 +130,32 @@ ACCESS(5) ACCESS(5) mat is generated by address-based relay authoriza- tion schemes. - HOLD Place the message on the hold queue, where it will - sit until someone either deletes it or releases it - for delivery. Mail that is placed on hold can be - examined with the postcat(1) command, and can be - destroyed or released with the postsuper(1) com- - mand. + DUNNO Pretend that the lookup key was not found in this + table, to prevents Postfix from trying substrings + of the lookup key (such as a subdomain name, or a + network address subnetwork). - Note: this action currently affects all recipients + HOLD + + HOLD optional text... + Place the message on the hold queue, where it will + sit until someone either deletes it or releases it + for delivery. Log the optional text if specified, + otherwise log a generic message. + + Mail that is placed on hold can be examined with + the postcat(1) command, and can be destroyed or + released with the postsuper(1) command. + + Note: this action currently affects all recipients of the message. DISCARD - Claim successful delivery and silently discard the - message. + + DISCARD optional text... + Claim successful delivery and silently discard the + message. Log the optional text if specified, oth- + erwise log a generic message. Note: this action currently affects all recipients of the message. diff --git a/postfix/html/uce.html b/postfix/html/uce.html index 7856acc09..525f5f926 100644 --- a/postfix/html/uce.html +++ b/postfix/html/uce.html @@ -1060,7 +1060,8 @@ hash:/etc/postfix/etrn_access, reject
maptype:mapname
Search the named access database for the domain specified in the ETRN command, or its parent domains. Reject the request if -the result is REJECT or "[45]XX text". Permit +the result is REJECT text... or "[45]XX +text". Permit the request if the result is OK or RELAY or all-numerical. Otherwise, treat the result as another list of UCE restrictions. The access_map_reject_code parameter specifies diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index 2fa6fac6b..23afae49b 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -115,23 +115,36 @@ address is a sequence of one or more octets separated by ".". Reject the address etc. that matches the pattern, and respond with the numerical code and text. .IP \fBREJECT\fR -Reject the address etc. that matches the pattern. A generic -error response message is generated. +.IP "\fBREJECT \fIoptional text...\fR +Reject the address etc. that matches the pattern. Reply with +\fI$reject_code optional text...\fR when the optional text is +specified, otherwise reply with a generic error response message. .IP \fBOK\fR Accept the address etc. that matches the pattern. .IP \fIall-numerical\fR An all-numerical result is treated as OK. This format is generated by address-based relay authorization schemes. +.IP \fBDUNNO\fR +Pretend that the lookup key was not found in this table, to +prevents Postfix from trying substrings of the lookup key +(such as a subdomain name, or a network address subnetwork). .IP \fBHOLD\fR +.IP "\fBHOLD \fIoptional text...\fR" Place the message on the \fBhold\fR queue, where it will sit until someone either deletes it or releases it for delivery. +Log the optional text if specified, otherwise log a generic +message. + Mail that is placed on hold can be examined with the \fBpostcat\fR(1) command, and can be destroyed or released with the \fBpostsuper\fR(1) command. .sp Note: this action currently affects all recipients of the message. .IP \fBDISCARD\fR +.IP "\fBDISCARD \fIoptional text...\fR Claim successful delivery and silently discard the message. +Log the optional text if specified, otherwise log a generic +message. .sp Note: this action currently affects all recipients of the message. .IP "\fBFILTER \fItransport:destination\fR" diff --git a/postfix/proto/access b/postfix/proto/access index 75c64cd10..1fc1a6a42 100644 --- a/postfix/proto/access +++ b/postfix/proto/access @@ -99,23 +99,36 @@ # Reject the address etc. that matches the pattern, and respond with # the numerical code and text. # .IP \fBREJECT\fR -# Reject the address etc. that matches the pattern. A generic -# error response message is generated. +# .IP "\fBREJECT \fIoptional text...\fR +# Reject the address etc. that matches the pattern. Reply with +# \fI$reject_code optional text...\fR when the optional text is +# specified, otherwise reply with a generic error response message. # .IP \fBOK\fR # Accept the address etc. that matches the pattern. # .IP \fIall-numerical\fR # An all-numerical result is treated as OK. This format is # generated by address-based relay authorization schemes. +# .IP \fBDUNNO\fR +# Pretend that the lookup key was not found in this table, to +# prevents Postfix from trying substrings of the lookup key +# (such as a subdomain name, or a network address subnetwork). # .IP \fBHOLD\fR +# .IP "\fBHOLD \fIoptional text...\fR" # Place the message on the \fBhold\fR queue, where it will sit # until someone either deletes it or releases it for delivery. +# Log the optional text if specified, otherwise log a generic +# message. +# # Mail that is placed on hold can be examined with the # \fBpostcat\fR(1) command, and can be destroyed or released with # the \fBpostsuper\fR(1) command. # .sp # Note: this action currently affects all recipients of the message. # .IP \fBDISCARD\fR +# .IP "\fBDISCARD \fIoptional text...\fR # Claim successful delivery and silently discard the message. +# Log the optional text if specified, otherwise log a generic +# message. # .sp # Note: this action currently affects all recipients of the message. # .IP "\fBFILTER \fItransport:destination\fR" diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index ecfabc29e..329991927 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -886,6 +886,7 @@ mkmap_db.o: ../../include/dict.h mkmap_db.o: ../../include/vstream.h mkmap_db.o: ../../include/argv.h mkmap_db.o: ../../include/dict_db.h +mkmap_db.o: ../../include/myflock.h mkmap_db.o: mail_params.h mkmap_db.o: mkmap.h mkmap_dbm.o: mkmap_dbm.c @@ -899,6 +900,7 @@ mkmap_dbm.o: ../../include/dict.h mkmap_dbm.o: ../../include/vstream.h mkmap_dbm.o: ../../include/argv.h mkmap_dbm.o: ../../include/dict_dbm.h +mkmap_dbm.o: ../../include/myflock.h mkmap_dbm.o: mkmap.h mkmap_open.o: mkmap_open.c mkmap_open.o: ../../include/sys_defs.h @@ -911,7 +913,6 @@ mkmap_open.o: ../../include/dict_db.h mkmap_open.o: ../../include/dict_dbm.h mkmap_open.o: ../../include/sigdelay.h mkmap_open.o: ../../include/mymalloc.h -mkmap_open.o: ../../include/myflock.h mkmap_open.o: mkmap.h mynetworks.o: mynetworks.c mynetworks.o: ../../include/sys_defs.h diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index cedea784f..9acb71596 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20021029" +#define MAIL_RELEASE_DATE "20021031" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "1.1.11-" MAIL_RELEASE_DATE diff --git a/postfix/src/global/mkmap.h b/postfix/src/global/mkmap.h index 86384575b..e1a443d19 100644 --- a/postfix/src/global/mkmap.h +++ b/postfix/src/global/mkmap.h @@ -18,13 +18,16 @@ /* * A database handle is an opaque structure. The user is not supposed to - * know its implementation. + * know its implementation. We try to open and lock a file before DB/DBM + * initialization. However, if the file does not exist then we may have to + * acquire the lock after the DB/DBM initialization. */ typedef struct MKMAP { - struct DICT *(*open) (const char *, int, int); - struct DICT *dict; - char *lock_file; - int lock_fd; + struct DICT *(*open) (const char *, int, int); /* dict_xx_open() */ + struct DICT *dict; /* dict_xx_open() result */ + char *lock_file; /* lock file name */ + int lock_fd; /* locked open file, or -1 */ + void (*after_open) (struct MKMAP *); /* may be null */ } MKMAP; extern MKMAP *mkmap_open(const char *, const char *, int, int); diff --git a/postfix/src/global/mkmap_db.c b/postfix/src/global/mkmap_db.c index bb82919f5..cfc07a42d 100644 --- a/postfix/src/global/mkmap_db.c +++ b/postfix/src/global/mkmap_db.c @@ -15,9 +15,8 @@ /* This module implements support for creating DB databases. /* /* mkmap_hash_open() and mkmap_btree_open() take a file name, -/* append the ".db" suffix, and create or open the named DB -/* database. This routine is a DB-specific helper for the more -/* general mkmap_open() interface. +/* append the ".db" suffix, and do whatever initialization is +/* required before the Berkeley DB open routine is called. /* /* All errors are fatal. /* SEE ALSO @@ -36,6 +35,9 @@ /* System library. */ #include +#include +#include +#include /* Utility library. */ @@ -44,6 +46,7 @@ #include #include #include +#include /* Global library. */ @@ -60,12 +63,25 @@ #include #endif -/* mkmap_db_open - create or open database */ +/* mkmap_db_after_open - lock newly created database */ -static MKMAP *mkmap_db_open(const char *path, +static void mkmap_db_after_open(MKMAP *mkmap) +{ + if (mkmap->lock_fd < 0) { + if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) + msg_fatal("open lockfile %s: %m", mkmap->lock_file); + if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) + msg_fatal("lock %s: %m", mkmap->lock_file); + } +} + +/* mkmap_db_before_open - lock existing database */ + +static MKMAP *mkmap_db_before_open(const char *path, DICT *(*db_open) (const char *, int, int)) { MKMAP *mkmap = (MKMAP *) mymalloc(sizeof(*mkmap)); + struct stat st; /* * Override the default per-table cache size for map (re)builds. @@ -92,13 +108,47 @@ static MKMAP *mkmap_db_open(const char *path, */ mkmap->lock_file = concatenate(path, ".db", (char *) 0); mkmap->open = db_open; + mkmap->after_open = mkmap_db_after_open; /* * Unfortunately, not all systems that might support db databases do * support locking on open(), so we open the file before updating it. + * + * XXX Berkeley DB 4.1 refuses to open a zero-length file. This means we can + * open and lock only an existing file, and that we must not truncate it. + */ + if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) { + if (errno != ENOENT) + msg_fatal("open %s: %m", mkmap->lock_file); + } + + /* + * Get an exclusive lock - we're going to change the database so we can't + * have any spectators. + * + * XXX Horror. Berkeley DB 4.1 refuses to open a zero-length file. This + * means that we must examine the size while the file is locked, and that + * we must unlink a zero-length file while it is locked. Avoid a race + * condition where two processes try to open the same zero-length file + * and where the second process ends up deleting the wrong file. */ - if ((mkmap->lock_fd = open(mkmap->lock_file, O_CREAT | O_RDWR, 0644)) < 0) - msg_fatal("open %s: %m", mkmap->lock_file); + else { + if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) + msg_fatal("lock %s: %m", mkmap->lock_file); + if (fstat(mkmap->lock_fd, &st) < 0) + msg_fatal("fstat %s: %m", mkmap->lock_file); + if (st.st_size == 0) { + if (st.st_nlink > 0) { + if (unlink(mkmap->lock_file) < 0) + msg_fatal("cannot remove zero-length database file %s: %m", + mkmap->lock_file); + msg_warn("removing zero-length database file: %s", + mkmap->lock_file); + } + close(mkmap->lock_fd); + mkmap->lock_fd = -1; + } + } return (mkmap); } @@ -107,14 +157,14 @@ static MKMAP *mkmap_db_open(const char *path, MKMAP *mkmap_hash_open(const char *path) { - return (mkmap_db_open(path, dict_hash_open)); + return (mkmap_db_before_open(path, dict_hash_open)); } /* mkmap_btree_open - create or open btree DB file */ MKMAP *mkmap_btree_open(const char *path) { - return (mkmap_db_open(path, dict_btree_open)); + return (mkmap_db_before_open(path, dict_btree_open)); } #endif diff --git a/postfix/src/global/mkmap_dbm.c b/postfix/src/global/mkmap_dbm.c index cb6664b39..968e76ac4 100644 --- a/postfix/src/global/mkmap_dbm.c +++ b/postfix/src/global/mkmap_dbm.c @@ -42,6 +42,7 @@ #include #include #include +#include /* Application-specific. */ @@ -67,6 +68,7 @@ MKMAP *mkmap_dbm_open(const char *path) */ mkmap->lock_file = concatenate(path, ".dir", (char *) 0); mkmap->open = dict_dbm_open; + mkmap->after_open = 0; /* * Unfortunately, not all systems support locking on open(), so we open @@ -83,6 +85,13 @@ MKMAP *mkmap_dbm_open(const char *path) msg_warn("close %s: %m", pag_file); myfree(pag_file); + /* + * Get an exclusive lock - we're going to change the database so we can't + * have any spectators. + */ + if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) + msg_fatal("lock %s: %m", mkmap->lock_file); + return (mkmap); } diff --git a/postfix/src/global/mkmap_open.c b/postfix/src/global/mkmap_open.c index e2cd16717..bf3097dd4 100644 --- a/postfix/src/global/mkmap_open.c +++ b/postfix/src/global/mkmap_open.c @@ -64,7 +64,6 @@ #include #include #include -#include /* Global library. */ @@ -76,7 +75,7 @@ */ typedef struct { char *type; - MKMAP *(*create_or_open) (const char *); + MKMAP *(*before_open) (const char *); } MKMAP_OPEN_INFO; MKMAP_OPEN_INFO mkmap_types[] = { @@ -142,16 +141,9 @@ MKMAP *mkmap_open(const char *type, const char *path, msg_info("open %s %s", type, path); /* - * Create or open the desired map file(s). + * Do whatever before-open initialization is needed. */ - mkmap = mp->create_or_open(path); - - /* - * Get an exclusive lock - we're going to change the database so we can't - * have any spectators. - */ - if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) - msg_fatal("lock %s: %m", mkmap->lock_file); + mkmap = mp->before_open(path); /* * Delay signal delivery, so that we won't leave the database in an @@ -167,5 +159,12 @@ MKMAP *mkmap_open(const char *type, const char *path, mkmap->dict->lock_fd = -1; /* XXX just in case */ mkmap->dict->stat_fd = -1; /* XXX just in case */ mkmap->dict->flags |= DICT_FLAG_DUP_WARN; + + /* + * Do whatever post-open initialization is needed. + */ + if (mkmap->after_open) + mkmap->after_open(mkmap); + return (mkmap); } diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index 1b98eb081..840ae5f4d 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -68,7 +68,7 @@ depend: $(MAKES) @$(EXPORT) make -f Makefile.in Makefile 1>&2 tests: smtpd_check_test smtpd_check_test2 smtpd_acl_test smtpd_exp_test \ - smtpd_token_test + smtpd_token_test smtpd_check_test4 smtpd_check_test: smtpd_check smtpd_check.in smtpd_check.ref smtpd_check_access ../postmap/postmap hash:smtpd_check_access @@ -82,6 +82,12 @@ smtpd_check_test2: smtpd_check smtpd_check.in2 smtpd_check.ref2 smtpd_check_acce diff smtpd_check.ref2 smtpd_check.tmp rm -f smtpd_check.tmp smtpd_check_access.* +smtpd_check_test4: smtpd_check smtpd_check.in4 smtpd_check.ref4 smtpd_check_access + ../postmap/postmap hash:smtpd_check_access + ./smtpd_check smtpd_check.tmp 2>&1 + diff smtpd_check.ref4 smtpd_check.tmp + rm -f smtpd_check.tmp smtpd_check_access.* + smtpd_acl_test: smtpd_check smtpd_acl.in smtpd_acl.ref smtpd_check_access ../postmap/postmap hash:smtpd_check_access ./smtpd_check smtpd_check.tmp 2>&1 diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 3b41403fc..969cb6fea 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -74,9 +74,10 @@ /* Perform a lookup in the specified access table. Reject the request /* when the lookup result is REJECT or when the result begins with a /* 4xx or 5xx status code. Other numerical status codes are not -/* permitted. Allow the request otherwise. The -/* \fIaccess_map_reject_code\fR configuration parameter specifies the -/* reject status code (default: 554). +/* permitted. DUNNO suppresses less precise searches in the same map, but +/* neither allows nor denies the request. Allow the request otherwise. +/* The \fIaccess_map_reject_code\fR configuration parameter specifies +/* the reject status code (default: 554). /* .IP "check_client_access maptype:mapname" /* Look up the client host name or any of its parent domains, or /* the client address or any network obtained by stripping octets @@ -723,8 +724,8 @@ static void log_whatsup(SMTPD_STATE *state, const char *whatsup, VSTRING *buf = vstring_alloc(100); vstring_sprintf(buf, "%s: %s: %s from %s: %s;", - state->queue_id, whatsup, state->where, - state->namaddr, text); + state->queue_id ? state->queue_id : "NOQUEUE", + whatsup, state->where, state->namaddr, text); if (state->sender) vstring_sprintf_append(buf, " from=<%s>", state->sender); if (state->recipient) @@ -1657,42 +1658,51 @@ static int check_table_result(SMTPD_STATE *state, const char *table, ARGV *restrictions; jmp_buf savebuf; int status; + const char *cmd_text; + int cmd_len; + + /* + * Parse into command and text. Do not change the input. + */ + cmd_text = value + strcspn(value, " \t"); + cmd_len = cmd_text - value; + while (*cmd_text && ISSPACE(*cmd_text)) + cmd_text++; if (msg_verbose) msg_info("%s: %s %s %s", myname, table, value, datum); +#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0) + /* - * DUNNO means skip this table. + * DUNNO means skip this table. Silently ignore optional text. */ - if (strcasecmp(value, "DUNNO") == 0) + if (STREQUAL(value, "DUNNO", cmd_len)) return (SMTPD_CHECK_DUNNO); /* - * REJECT means NO. Generate a generic error response. + * REJECT means NO. Use optional text or generate a generic error + * response. */ - if (strcasecmp(value, "REJECT") == 0) + if (STREQUAL(value, "REJECT", cmd_len)) { return (smtpd_check_reject(state, MAIL_ERROR_POLICY, - "%d <%s>: %s rejected: Access denied", - var_access_map_code, reply_name, reply_class)); + "%d <%s>: %s rejected: %s", + var_access_map_code, reply_name, reply_class, + *cmd_text ? cmd_text : "Access denied")); + } /* * FILTER means deliver to content filter. But we may still change our * mind, and reject/discard the message for other reasons. */ -#define FILTER_LEN (sizeof("FILTER") - 1) - - if (strncasecmp(value, "FILTER", FILTER_LEN) == 0) { - if (value[FILTER_LEN] == 0) { + if (STREQUAL(value, "FILTER", cmd_len)) { + if (*cmd_text == 0) { msg_warn("access map %s entry %s has FILTER entry without value", table, datum); return (SMTPD_CHECK_DUNNO); - } - if (ISSPACE(value[FILTER_LEN])) { - value += FILTER_LEN; - while (ISSPACE(*value)) - value++; + } else { vstring_sprintf(error_text, "<%s>: %s triggers FILTER %s", - reply_name, reply_class, value); + reply_name, reply_class, cmd_text); log_whatsup(state, "filter", STR(error_text)); #ifndef TEST rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", value); @@ -1705,9 +1715,9 @@ static int check_table_result(SMTPD_STATE *state, const char *table, * HOLD means deliver later. But we may still change our mind, and * reject/discard the message for other reasons. */ - if (strcasecmp(value, "HOLD") == 0) { - vstring_sprintf(error_text, "<%s>: %s triggers HOLD action", - reply_name, reply_class); + if (STREQUAL(value, "HOLD", cmd_len)) { + vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class, + *cmd_text ? cmd_text : "triggers HOLD action"); log_whatsup(state, "hold", STR(error_text)); #ifndef TEST rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d", @@ -1722,9 +1732,9 @@ static int check_table_result(SMTPD_STATE *state, const char *table, * XXX Set some global flag that disables all further restrictions. * Triggering a "reject" or "hold" action after "discard" is silly. */ - if (strcasecmp(value, "DISCARD") == 0) { - vstring_sprintf(error_text, "<%s>: %s triggers DISCARD action", - reply_name, reply_class); + if (STREQUAL(value, "DISCARD", cmd_len)) { + vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class, + *cmd_text ? cmd_text : "triggers DISCARD action"); log_whatsup(state, "discard", STR(error_text)); #ifndef TEST rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d", @@ -1744,19 +1754,18 @@ static int check_table_result(SMTPD_STATE *state, const char *table, * 4xx or 5xx means NO as well. smtpd_check_reject() will validate the * response status code. */ - if (ISDIGIT(value[0]) && ISDIGIT(value[1]) && ISDIGIT(value[2])) { + if (cmd_len == 3 && *cmd_text + && ISDIGIT(value[0]) && ISDIGIT(value[1]) && ISDIGIT(value[2])) { code = atoi(value); - while (ISDIGIT(*value) || ISSPACE(*value)) - value++; return (smtpd_check_reject(state, MAIL_ERROR_POLICY, "%d <%s>: %s rejected: %s", - code, reply_name, reply_class, value)); + code, reply_name, reply_class, cmd_text)); } /* - * OK or RELAY means YES. + * OK or RELAY means YES. Ignore trailing text. */ - if (strcasecmp(value, "OK") == 0 || strcasecmp(value, "RELAY") == 0) + if (STREQUAL(value, "OK", cmd_len) || STREQUAL(value, "RELAY", cmd_len)) return (SMTPD_CHECK_OK); /* diff --git a/postfix/src/smtpd/smtpd_check.in4 b/postfix/src/smtpd/smtpd_check.in4 new file mode 100644 index 000000000..024bb77ff --- /dev/null +++ b/postfix/src/smtpd/smtpd_check.in4 @@ -0,0 +1,18 @@ +# +# Initialize. +# +#! ../bin/postmap smtpd_check_access +#msg_verbose 1 +smtpd_delay_reject 0 +# +# Test the nex access map features +# +sender_restrictions hash:./smtpd_check_access +mail rejecttext@bad.domain +mail filter@filter.domain +mail filtertext@filter.domain +mail hold@hold.domain +mail holdtext@hold.domain +mail discard@hold.domain +mail discardtext@hold.domain +mail dunnotext@dunno.domain diff --git a/postfix/src/smtpd/smtpd_check.ref4 b/postfix/src/smtpd/smtpd_check.ref4 new file mode 100644 index 000000000..e47600177 --- /dev/null +++ b/postfix/src/smtpd/smtpd_check.ref4 @@ -0,0 +1,35 @@ +>>> # +>>> # Initialize. +>>> # +>>> #! ../bin/postmap smtpd_check_access +>>> #msg_verbose 1 +>>> smtpd_delay_reject 0 +OK +>>> # +>>> # Test the nex access map features +>>> # +>>> sender_restrictions hash:./smtpd_check_access +OK +>>> mail rejecttext@bad.domain +./smtpd_check: : reject: MAIL from localhost[127.0.0.1]: 554 : Sender address rejected: text; from= proto=SMTP +554 : Sender address rejected: text +>>> mail filter@filter.domain +./smtpd_check: warning: access map hash:./smtpd_check_access entry filter@filter.domain has FILTER entry without value +OK +>>> mail filtertext@filter.domain +./smtpd_check: : filter: MAIL from localhost[127.0.0.1]: : Sender address triggers FILTER text; from= proto=SMTP +OK +>>> mail hold@hold.domain +./smtpd_check: : hold: MAIL from localhost[127.0.0.1]: : Sender address triggers HOLD action; from= proto=SMTP +OK +>>> mail holdtext@hold.domain +./smtpd_check: : hold: MAIL from localhost[127.0.0.1]: : Sender address text; from= proto=SMTP +OK +>>> mail discard@hold.domain +./smtpd_check: : discard: MAIL from localhost[127.0.0.1]: : Sender address triggers DISCARD action; from= proto=SMTP +OK +>>> mail discardtext@hold.domain +./smtpd_check: : discard: MAIL from localhost[127.0.0.1]: : Sender address text; from= proto=SMTP +OK +>>> mail dunnotext@dunno.domain +OK diff --git a/postfix/src/smtpd/smtpd_check_access b/postfix/src/smtpd/smtpd_check_access index d04bc4bad..41405ef4f 100644 --- a/postfix/src/smtpd/smtpd_check_access +++ b/postfix/src/smtpd/smtpd_check_access @@ -46,3 +46,12 @@ dsn.rfc-ignorant.org $rbl_code client=$client recipient=$recipient recipient_name=$recipient_name recipient_domain=$recipient_domain rbl_code=$rbl_code rbl_domain=$rbl_domain rbl_txt=$rbl_txt rbl_what=$rbl_what rbl_class=$rbl_class + +rejecttext@bad.domain reject text +filter@filter.domain filter +filtertext@filter.domain filter text +hold@hold.domain hold +holdtext@hold.domain hold text +discard@hold.domain discard +discardtext@hold.domain discard text +dunnotext@dunno.domain dunno text diff --git a/postfix/src/util/dict_db.c b/postfix/src/util/dict_db.c index 41eea2118..85504bcdd 100644 --- a/postfix/src/util/dict_db.c +++ b/postfix/src/util/dict_db.c @@ -466,9 +466,9 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags, int patch_version; (void) db_version(&major_version, &minor_version, &patch_version); - if (major_version != DB_VERSION_MAJOR) + if (major_version != DB_VERSION_MAJOR || minor_version != DB_VERSION_MINOR) msg_fatal("incorrect version of Berkeley DB: " - "compiled against %d.%d.%d, linked against %d.%d.%d", + "compiled against %d.%d.%d, run-time linked against %d.%d.%d", DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH, major_version, minor_version, patch_version); #endif @@ -481,12 +481,21 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags, * * Programs such as postmap/postalias use their own large-grained (in the * time domain) locks while rewriting the entire file. + * + * XXX DB version 4.1 will not open a zero-length file. This means we must + * open an existing file without O_CREAT|O_TRUNC, and that we must let + * db_open() create a non-existent file for us. */ +#define LOCK_OPEN_FLAGS(f) ((f) & ~(O_CREAT|O_TRUNC)) + if (dict_flags & DICT_FLAG_LOCK) { - if ((lock_fd = open(db_path, open_flags, 0644)) < 0) - msg_fatal("open database %s: %m", db_path); - if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) - msg_fatal("shared-lock database %s for open: %m", db_path); + if ((lock_fd = open(db_path, LOCK_OPEN_FLAGS(open_flags), 0644)) < 0) { + if (errno != ENOENT) + msg_fatal("open database %s: %m", db_path); + } else { + if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0) + msg_fatal("shared-lock database %s for open: %m", db_path); + } } /* @@ -539,12 +548,19 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags, msg_fatal("set DB cache size %d: %m", dict_db_cache_size); if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0) msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM); +#if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0) + if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0) + msg_fatal("open database %s: %m", db_path); +#elif (DB_VERSION_MAJOR == 3 || DB_VERSION_MAJOR == 4) if ((errno = db->open(db, db_path, 0, type, db_flags, 0644)) != 0) msg_fatal("open database %s: %m", db_path); +#else +#error "Unsupported Berkeley DB version" +#endif if ((errno = db->fd(db, &dbfd)) != 0) msg_fatal("get database file descriptor: %m"); #endif - if (dict_flags & DICT_FLAG_LOCK) { + if ((dict_flags & DICT_FLAG_LOCK) && lock_fd >= 0) { if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) msg_fatal("unlock database %s for open: %m", db_path); if (close(lock_fd) < 0)