]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-1.1.11-20021031
authorWietse Venema <wietse@porcupine.org>
Thu, 31 Oct 2002 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:28:23 +0000 (06:28 +0000)
20 files changed:
postfix/HISTORY
postfix/README_FILES/LMTP_README
postfix/conf/access
postfix/conf/sample-smtpd.cf
postfix/html/access.5.html
postfix/html/uce.html
postfix/man/man5/access.5
postfix/proto/access
postfix/src/global/Makefile.in
postfix/src/global/mail_version.h
postfix/src/global/mkmap.h
postfix/src/global/mkmap_db.c
postfix/src/global/mkmap_dbm.c
postfix/src/global/mkmap_open.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check.in4 [new file with mode: 0644]
postfix/src/smtpd/smtpd_check.ref4 [new file with mode: 0644]
postfix/src/smtpd/smtpd_check_access
postfix/src/util/dict_db.c

index 5091041ec59ac98d2f2416f1f8c11bb3557cb79f..9e2c2f2f98e9d6039b4e9ebc6f893f412619ab81 100644 (file)
@@ -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.
index 06a6878eafc9a8d2099c8518f63c279aed416d56..26e922807a3038db980f7e200dca4027547204e5 100644 (file)
@@ -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,
index 3dcd6000ed005b441f4b3e9d2c9684568c7ca6e8..9810d5237974afabf1006d65f6e89c7ac34bacd2 100644 (file)
 #               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.
 # 
 #               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.
index d2c1ff794c0dba69e34476974178a7ce85cbeb78..b781213a9a09c385ff049f8d33addace455c7061 100644 (file)
@@ -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.
index aff8bac218f72a67b512a7c4f2ef2ce00c530bab..64186e76db22e06f7d7818c793407b25132ade69 100644 (file)
@@ -115,8 +115,13 @@ ACCESS(5)                                               ACCESS(5)
               Reject  the  address etc. that matches the pattern,
               and respond with the numerical code and text.
 
-       <b>REJECT</b> Reject the address etc. that matches the pattern. A
-              generic error response message is generated.
+       <b>REJECT</b>
+
+       <b>REJECT</b> <i>optional</i> <i>text...</i>
+              Reject the address etc. that matches  the  pattern.
+              Reply  with  <i>$reject_code</i> <i>optional</i> <i>text...</i> when the
+              optional text is specified, otherwise reply with  a
+              generic error response message.
 
        <b>OK</b>     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.
 
-       <b>HOLD</b>   Place  the message on the <b>hold</b> 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 <a href="postcat.1.html"><b>postcat</b>(1)</a> command,  and  can  be
-              destroyed  or  released  with the <a href="postsuper.1.html"><b>postsuper</b>(1)</a> com-
-              mand.
+       <b>DUNNO</b>  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
+       <b>HOLD</b>
+
+       <b>HOLD</b> <i>optional</i> <i>text...</i>
+              Place the message on the <b>hold</b> 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 <a href="postcat.1.html"><b>postcat</b>(1)</a> command, and  can  be  destroyed  or
+              released with the <a href="postsuper.1.html"><b>postsuper</b>(1)</a> command.
+
+              Note:  this action currently affects all recipients
               of the message.
 
        <b>DISCARD</b>
-              Claim  successful delivery and silently discard the
-              message.
+
+       <b>DISCARD</b> <i>optional</i> <i>text...</i>
+              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.
index 7856acc090a1473163d4ca87b9ba6558e48b7ae2..525f5f9264f6c3a327bfe64c5db11a05850297ed 100644 (file)
@@ -1060,7 +1060,8 @@ hash:/etc/postfix/etrn_access, reject</b>
 <dt> <i>maptype</i>:<i>mapname</i> <dd> Search the named <a
 href="access.5.html">access database</a> for the domain specified
 in the ETRN command, or its parent domains. Reject the request if
-the result is <b>REJECT</b> or "[<b>45</b>]<i>XX text</i>".  Permit
+the result is <b>REJECT</b> <i>text...</i> or "[<b>45</b>]<i>XX
+text</i>".  Permit
 the request if the result is <b>OK</b> or <b>RELAY</b> or
 all-numerical. Otherwise, treat the result as another list of UCE
 restrictions.  The <b>access_map_reject_code </b> parameter specifies
index 2fa6fac6b861d8fc77531b4e799aff7c2dae05bf..23afae49b5fc6bddcbbcba76a8375e7a447105da 100644 (file)
@@ -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"
index 75c64cd104d331efcbe5ae972565aa8532efd1c4..1fc1a6a426a806922e7b2c0c2eb76dfcb81af37d 100644 (file)
 #      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"
index ecfabc29ec8e345f89b169de3ca6d703a36e6fe2..329991927e2018aa3c19425fc1025d41a757c2c4 100644 (file)
@@ -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
index cedea784f930cfc6c4fd9ba888262468443442f0..9acb715967db0ef2d57f12f2ebd204aa6ecb9159 100644 (file)
@@ -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
index 86384575b99e61ce139b66e2736138fecab473d2..e1a443d19f910c2fd2696bf5f139f1c2292d1cb8 100644 (file)
 
  /*
   * 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);
index bb82919f54051cbf893ad6fbe2ca3cc70c46afc0..cfc07a42dbbbf6d12c3a4148d04303dd39d134e5 100644 (file)
@@ -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 <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 /* Utility library. */
 
@@ -44,6 +46,7 @@
 #include <stringops.h>
 #include <dict.h>
 #include <dict_db.h>
+#include <myflock.h>
 
 /* Global library. */
 
 #include <db.h>
 #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
index cb6664b39cfdc675cb44ea1e14064375b7434d2a..968e76ac4d76810e69907d8b5c52436fe0991d43 100644 (file)
@@ -42,6 +42,7 @@
 #include <stringops.h>
 #include <dict.h>
 #include <dict_dbm.h>
+#include <myflock.h>
 
 /* 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);
 }
 
index e2cd167176d9784809c952f1758fb09ea692db32..bf3097dd4f0726c4a1034fc6798fbceed3d51c17 100644 (file)
@@ -64,7 +64,6 @@
 #include <dict_dbm.h>
 #include <sigdelay.h>
 #include <mymalloc.h>
-#include <myflock.h>
 
 /* 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);
 }
index 1b98eb0814072f55780ad44cc1bd89677f080fb0..840ae5f4dee7538e8a943d06ff2dcdb6fb00d985 100644 (file)
@@ -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.in4 >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_acl.in >smtpd_check.tmp 2>&1
index 3b41403fc21944fdc1af3bf82c0f5fa8354d7c49..969cb6feac42897fa39fb87786b12a682f74051c 100644 (file)
 /*     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 (file)
index 0000000..024bb77
--- /dev/null
@@ -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 (file)
index 0000000..e476001
--- /dev/null
@@ -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: <queue id>: reject: MAIL from localhost[127.0.0.1]: 554 <rejecttext@bad.domain>: Sender address rejected: text; from=<rejecttext@bad.domain> proto=SMTP
+554 <rejecttext@bad.domain>: 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: <queue id>: filter: MAIL from localhost[127.0.0.1]: <filtertext@filter.domain>: Sender address triggers FILTER text; from=<filtertext@filter.domain> proto=SMTP
+OK
+>>> mail hold@hold.domain
+./smtpd_check: <queue id>: hold: MAIL from localhost[127.0.0.1]: <hold@hold.domain>: Sender address triggers HOLD action; from=<hold@hold.domain> proto=SMTP
+OK
+>>> mail holdtext@hold.domain
+./smtpd_check: <queue id>: hold: MAIL from localhost[127.0.0.1]: <holdtext@hold.domain>: Sender address text; from=<holdtext@hold.domain> proto=SMTP
+OK
+>>> mail discard@hold.domain
+./smtpd_check: <queue id>: discard: MAIL from localhost[127.0.0.1]: <discard@hold.domain>: Sender address triggers DISCARD action; from=<discard@hold.domain> proto=SMTP
+OK
+>>> mail discardtext@hold.domain
+./smtpd_check: <queue id>: discard: MAIL from localhost[127.0.0.1]: <discardtext@hold.domain>: Sender address text; from=<discardtext@hold.domain> proto=SMTP
+OK
+>>> mail dunnotext@dunno.domain
+OK
index d04bc4badffaaf301aeb6a771c61db259fe39a10..41405ef4f6bc203b30830b40f6c807f7a7d07595 100644 (file)
@@ -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
index 41eea2118458d594c5b116623c8f0ae24bac91ad..85504bcddaa7f4bd19c16655ffc610c098ef378e 100644 (file)
@@ -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)