]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.7-20030317
authorWietse Venema <wietse@porcupine.org>
Mon, 17 Mar 2003 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:28:46 +0000 (06:28 +0000)
21 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/conf/master.cf
postfix/html/proxymap.8.html
postfix/html/uce.html
postfix/man/man8/proxymap.8
postfix/src/global/Makefile.in
postfix/src/global/mail_version.h
postfix/src/global/maps.c
postfix/src/global/tok822_parse.c
postfix/src/master/mail_flow.c
postfix/src/postsuper/postsuper.c
postfix/src/proxymap/proxymap.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check.h
postfix/src/smtpd/smtpd_check.in4
postfix/src/smtpd/smtpd_check.ref4
postfix/src/smtpd/smtpd_check_access
postfix/src/smtpd/smtpd_state.c

index 132e9b83f99610f21450d3331097e5c451047e59..7e8ce4ccf87743d06b869a063405c5e344919fac 100644 (file)
 -TSINK_STATE
 -TSMTPD_CMD
 -TSMTPD_DEFER
+-TSMTPD_MSG_ACTION
 -TSMTPD_RBL_EXPAND_CONTEXT
 -TSMTPD_RBL_STATE
 -TSMTPD_STATE
index c25921a282b0f6d26e643610b4c95333991718ae..23f5446064e6bd9c1ed7b4050e2bad8721a928a0 100644 (file)
@@ -7895,6 +7895,24 @@ Apologies for any names omitted.
        systems against exploitation of the remote buffer overflow
        vulnerability described in CERT advisory CA-2003-07.
 
+20030311-17
+
+       Bugfix: the access map actions HOLD, DISCARD, FILTER and
+       REDIRECT were broken with smtpd_delay_reject=no.  This
+       required re-architecting of the actions code. Files:
+       smtpd/smtpd.[hc], smtpd/smtpd_check.c, smtpd/smtpd_state.c.
+
+20030315
+
+       Bugfix: the postsuper manual page documented support for
+       the -c command line option, but it was not implemented.
+       File: postsuper/postsuper.c.
+
+       Bugfix: the Postfix 2.0 recipient map checking code broke
+       the VRFY command, causing it to reply with status code 252
+       for non-existent addresses. This required re-architecting
+       the recipient table lookup code. File:  smtpd/smtpd_check.c.
+
 Open problems:
 
        Med: make qmgr recipient bounce/defer activity asynchronous
index 2c8d34fc2f8dd2c1b1de2f31114596d1990855e6..a8f9737215f0fc53646ae1c4f8b9ecfc2b7bf275 100644 (file)
@@ -26,6 +26,8 @@
 # directory (pathname is controlled by the queue_directory configuration
 # variable in the main.cf file). Presently, all Postfix daemons can run
 # chrooted, except for the pipe, virtual and local delivery daemons.
+# The proxymap server can run chrooted, but doing so defeats most of
+# the purpose of having that service in the first place.
 # The files in the examples/chroot-setup subdirectory describe how
 # to set up a Postfix chroot environment for your type of machine.
 #
index 4ea05c0960300511f3bdb7489fad7bf2513e03b3..0cff6e6a62551ca229fffdb87d151689e713bd8d 100644 (file)
@@ -88,7 +88,9 @@ PROXYMAP(8)                                           PROXYMAP(8)
        The  proxymap  server  opens only tables that are approved
        via the <b>proxy</b><i>_</i><b>read</b><i>_</i><b>maps</b> configuration parameter, does  not
        talk  to  users,  and  can  run  at  fixed  low privilege,
-       chrooted or not.
+       chrooted or not.  However,  running  the  proxymap  server
+       chrooted  severely  limits  usability, because it can open
+       only chrooted tables.
 
        The proxymap server is not a trusted daemon  process,  and
        must  not be used to look up sensitive information such as
index 9ca51eb41b8de2df62104a407052df436d2f8664..6d0a25c7949460cadb6f8608951d803045546bbb 100644 (file)
@@ -880,7 +880,7 @@ and the address contains no sender-specified routing
 <li>Postfix is the final destination:  any destination that matches
 <a href="basic.html#mydestination">$mydestination</a>, <a
 href="basic.html#inet_interfaces">$inet_interfaces</a>, <a
-href="virtual.5.html">$virtual_alias_domains</a>, or
+href="virtual.5.html">$virtual_alias_domains</a>, or <a
 href="virtual.8.html">$virtual_mailbox_domains</a>.
 
 </ul>
index a801de872740a253ec51bddcfda5b0526aed3add..73efb71428b0b606b2b72dbb7077e9af20507fd1 100644 (file)
@@ -88,6 +88,8 @@ of idle time.
 The proxymap server opens only tables that are approved via the
 \fBproxy_read_maps\fR configuration parameter, does not talk to
 users, and can run at fixed low privilege, chrooted or not.
+However, running the proxymap server chrooted severely limits
+usability, because it can open only chrooted tables.
 
 The proxymap server is not a trusted daemon process, and must
 not be used to look up sensitive information such as user or
index cd4ef60aff7973bebd268c271d4462122f9432cf..fb9f9f638f564c15e6571e95c06d5c60aff2fccb 100644 (file)
@@ -1251,6 +1251,7 @@ tok822_parse.o: ../../include/sys_defs.h
 tok822_parse.o: ../../include/vstring.h
 tok822_parse.o: ../../include/vbuf.h
 tok822_parse.o: ../../include/msg.h
+tok822_parse.o: ../../include/stringops.h
 tok822_parse.o: lex_822.h
 tok822_parse.o: quote_822_local.h
 tok822_parse.o: quote_flags.h
index 2da4fcccd01575cb170ac0bd51b311576815fb68..4dc8dae25dcd98df0b456ee691f4a77c2ddfabc2 100644 (file)
   * 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      "20030305"
+#define MAIL_RELEASE_DATE      "20030317"
 
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "2.0.6-" MAIL_RELEASE_DATE
+#define DEF_MAIL_VERSION       "2.0.7-" MAIL_RELEASE_DATE
 extern char *var_mail_version;
 
  /*
index fc7dedc32a224a6fdadbdebe1632c4169582929e..ae345e3ea9376de1ac34dddbdf4998fe76a7a215 100644 (file)
@@ -172,14 +172,15 @@ const char *maps_find(MAPS *maps, const char *name, int flags)
            continue;
        if ((expansion = dict_get(dict, name)) != 0) {
            if (msg_verbose)
-               msg_info("%s: %s: %s = %s", myname, *map_name, name, expansion);
+               msg_info("%s: %s: %s: %s = %s", myname, maps->title,
+                        *map_name, name, expansion);
            return (expansion);
        } else if (dict_errno != 0) {
            break;
        }
     }
     if (msg_verbose)
-       msg_info("%s: %s: %s", myname, name, dict_errno ?
+       msg_info("%s: %s: %s: %s", myname, maps->title, name, dict_errno ?
                 "search aborted" : "not found");
     return (0);
 }
index fe554bf7ae2f8015287b5cc72e1c665cdf8348cd..4866ac5ad3db2dd5a5d763dc7e05436ca5c8e5fa 100644 (file)
 
 #include <vstring.h>
 #include <msg.h>
+#include <stringops.h>
 
 /* Global library. */
 
@@ -250,7 +251,7 @@ static void strip_address(VSTRING *vp, int start, TOK822 *addr)
      * Emit plain <address>. Discard any comments or phrases.
      */
     msg_warn("stripping too many comments from address: %.100s...",
-            vstring_str(vp) + start);
+            printable(vstring_str(vp) + start, '?'));
     vstring_truncate(vp, start);
     VSTRING_ADDCH(vp, '<');
     if (addr) {
@@ -263,7 +264,6 @@ static void strip_address(VSTRING *vp, int start, TOK822 *addr)
     VSTRING_ADDCH(vp, '>');
 }
 
-
 /* tok822_externalize - token tree to string, external form */
 
 VSTRING *tok822_externalize(VSTRING *vp, TOK822 *tree, int flags)
index efb59885409a7ee61ed460102142645035a6bdf8..383ba5816c5a9e454d1765c0d1b812e6c934e9ee 100644 (file)
@@ -47,6 +47,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <string.h>
 
 /* Utility library. */
 
index 3247a2c0015b07238f5db28e8284317704897aeb..a5c4ded8651825a23c44d4ef4b7cd6984741972e 100644 (file)
@@ -987,16 +987,13 @@ int     main(int argc, char **argv)
            msg_fatal("open /dev/null: %m");
 
     /*
-     * Process environment options as early as we can. We might be called
-     * from a set-uid (set-gid) program, so be careful with importing
-     * environment variables.
+     * Process this environment option as early as we can, to aid debugging.
      */
     if (safe_getenv(CONF_ENV_VERB))
        msg_verbose = 1;
 
     /*
-     * Initialize. Set up logging, read the global configuration file and
-     * extract configuration information.
+     * Initialize logging.
      */
     if ((slash = strrchr(argv[0], '/')) != 0)
        argv[0] = slash + 1;
@@ -1004,47 +1001,37 @@ int     main(int argc, char **argv)
     msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY);
     set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
 
-    mail_conf_read();
-    if (chdir(var_queue_dir))
-       msg_fatal("chdir %s: %m", var_queue_dir);
-
     /*
-     * Be sure to log a warning if we do not finish structural repair. Maybe
-     * we should have an fsck-style "clean" flag so Postfix will not start
-     * with a broken queue.
-     */
-    signal(SIGHUP, interrupted);
-    signal(SIGINT, interrupted);
-    signal(SIGQUIT, interrupted);
-    signal(SIGTERM, interrupted);
-    msg_cleanup(fatal_exit);
-
-    /*
-     * All file/directory updates must be done as the mail system owner. This
-     * is because Postfix daemons manipulate the queue with those same
-     * privileges, so directories must be created with the right ownership.
-     * 
-     * Running as a non-root user is also required for security reasons. When
-     * the Postfix queue hierarchy is compromised, an attacker could trick us
-     * into entering other file hierarchies and afflicting damage. Running as
-     * a non-root user limits the damage to the already compromised mail
-     * owner.
+     * Disallow unsafe practices, and refuse to run set-uid (or as the child
+     * of a set-uid process). Whenever a privileged wrapper program is
+     * needed, it must properly sanitize the real/effective/saved UID/GID,
+     * the secondary groups, the process environment, and so on. Otherwise,
+     * accidents can happen. If not with Postfix, then with other software.
      */
+    if (unsafe() != 0)
+       msg_fatal("this postfix command must not run as a set-uid process");
     if (getuid())
        msg_fatal("use of this command is reserved for the superuser");
-    set_ugid(var_owner_uid, var_owner_gid);
 
     /*
      * Parse JCL.
      */
-    while ((c = GETOPT(argc, argv, "d:h:H:pr:sv")) > 0) {
+    while ((c = GETOPT(argc, argv, "c:d:h:H:pr:sv")) > 0) {
        switch (c) {
        default:
-           msg_fatal("usage: %s [-d queue_id (delete)] "
+           msg_fatal("usage: %s "
+                     "[-c config_dir] "
+                     "[-d queue_id (delete)] "
                      "[-h queue_id (hold)] [-H queue_id (un-hold)] "
                      "[-p (purge temporary files)] [-r queue_id (requeue)] "
                      "[-s (structure fix)] [-v (verbose)] "
                      "[queue...]", argv[0]);
+       case 'c':
+           if (*optarg != '/')
+               msg_fatal("-c requires absolute pathname");
+           if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
+               msg_fatal("setenv: %m");
+           break;
        case 'd':
            if (delete_names == 0)
                delete_names = argv_alloc(1);
@@ -1085,6 +1072,42 @@ int     main(int argc, char **argv)
        }
     }
 
+    /*
+     * Read the global configuration file and extract configuration
+     * information. The -c command option can override the default
+     * configuration directory location.
+     */
+    mail_conf_read();
+    if (chdir(var_queue_dir))
+       msg_fatal("chdir %s: %m", var_queue_dir);
+
+    /*
+     * All file/directory updates must be done as the mail system owner. This
+     * is because Postfix daemons manipulate the queue with those same
+     * privileges, so directories must be created with the right ownership.
+     * 
+     * Running as a non-root user is also required for security reasons. When
+     * the Postfix queue hierarchy is compromised, an attacker could trick us
+     * into entering other file hierarchies and afflicting damage. Running as
+     * a non-root user limits the damage to the already compromised mail
+     * owner.
+     */
+    set_ugid(var_owner_uid, var_owner_gid);
+
+    /*
+     * Be sure to log a warning if we do not finish structural repair. Maybe
+     * we should have an fsck-style "clean" flag so Postfix will not start
+     * with a broken queue.
+     * 
+     * Set up signal handlers after permanently dropping super-user privileges,
+     * so that signal handlers will always run with the correct privileges.
+     */
+    signal(SIGHUP, interrupted);
+    signal(SIGINT, interrupted);
+    signal(SIGQUIT, interrupted);
+    signal(SIGTERM, interrupted);
+    msg_cleanup(fatal_exit);
+
     /*
      * Sanity checks.
      */
index 618d585959e89ead5588b8459efb2fc00da76375..22d070f3aed7a25a89ead3cbfdab7c16c0b9d514 100644 (file)
@@ -78,6 +78,8 @@
 /*     The proxymap server opens only tables that are approved via the
 /*     \fBproxy_read_maps\fR configuration parameter, does not talk to
 /*     users, and can run at fixed low privilege, chrooted or not.
+/*     However, running the proxymap server chrooted severely limits
+/*     usability, because it can open only chrooted tables.
 /*
 /*     The proxymap server is not a trusted daemon process, and must
 /*     not be used to look up sensitive information such as user or
index 24aae3d1d148def2eb57a515a53c120bab8417ca..e75bb0d473232c4fa0a83344b24ce886d76c37b7 100644 (file)
@@ -495,6 +495,8 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "501 Syntax: HELO hostname");
        return (-1);
     }
+    if (state->helo_name != 0)
+       helo_reset(state);
     if (argc > 2)
        collapse_args(argc - 1, argv + 1);
     if (SMTPD_STAND_ALONE(state) == 0
@@ -503,8 +505,6 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
-    if (state->helo_name != 0)
-       helo_reset(state);
     chat_reset(state, var_smtpd_hist_thrsh);
     mail_reset(state);
     rcpt_reset(state);
@@ -531,6 +531,8 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "501 Syntax: EHLO hostname");
        return (-1);
     }
+    if (state->helo_name != 0)
+       helo_reset(state);
     if (argc > 2)
        collapse_args(argc - 1, argv + 1);
     if (SMTPD_STAND_ALONE(state) == 0
@@ -539,8 +541,6 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
-    if (state->helo_name != 0)
-       helo_reset(state);
     chat_reset(state, var_smtpd_hist_thrsh);
     mail_reset(state);
     rcpt_reset(state);
@@ -576,6 +576,26 @@ static void helo_reset(SMTPD_STATE *state)
     if (state->helo_name)
        myfree(state->helo_name);
     state->helo_name = 0;
+
+    /*
+     * With smtpd_delay_reject=yes, smtpd_helo_restrictions is evaluated at
+     * RCPT TO time, and may be evaluated multiple times per message.
+     * Per-message smtpd_helo_restrictions side effects (HOLD, DISCARD, etc.)
+     * accumulate from one evaluation to the next, and may also depend on
+     * client, sender or recipient information. Therefore, any per-message
+     * helo restriction side effects need to be reset at the end of a mail
+     * delivery transaction.
+     * 
+     * With smtpd_delay_reject=no, smtpd_helo_restrictions is evaluated when
+     * HELO/EHLO is issued, and its per-message side effects change only when
+     * another HELO/EHLO command is issued. Therefore, any per-message helo
+     * restriction side effects must be reset before smtpd_helo_restrictions
+     * is evaluated.
+     */
+    if (var_smtpd_delay_reject == 0) {
+       SMTPD_MSG_ACT_FREE(state->action_helo);
+       SMTPD_MSG_ACT_ZERO(state->action_helo);
+    }
 }
 
 /* mail_open_stream - open mail destination */
@@ -903,6 +923,20 @@ static void mail_reset(SMTPD_STATE *state)
     if (var_smtpd_sasl_enable)
        smtpd_sasl_mail_reset(state);
 #endif
+
+    /*
+     * With smtpd_delay_reject=yes, smtpd_sender_restrictions is evaluated at
+     * RCPT TO time, and may be evaluated multiple times per message.
+     * Per-message smtpd_sender_restrictions side effects (HOLD, DISCARD,
+     * etc.) accumulate from one evaluation to the next, and may also depend
+     * on client, helo or recipient information.
+     * 
+     * The action_mailrcpt member accumulates both sender and recipient side
+     * effects. It needs to be reset after each mail delivery, whether or not
+     * it is actually completed.
+     */
+    SMTPD_MSG_ACT_FREE(state->action_mailrcpt);
+    SMTPD_MSG_ACT_ZERO(state->action_mailrcpt);
 }
 
 /* rcpt_cmd - process RCPT TO command */
@@ -961,10 +995,6 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
            smtpd_chat_reply(state, "%s", err);
            return (-1);
        }
-       if ((err = smtpd_check_rcptmap(state, argv[2].strval)) != 0) {
-           smtpd_chat_reply(state, "%s", err);
-           return (-1);
-       }
     }
 
     /*
@@ -987,6 +1017,26 @@ static void rcpt_reset(SMTPD_STATE *state)
        state->recipient = 0;
     }
     state->rcpt_count = 0;
+
+    /*
+     * With smtpd_delay_reject=yes, smtpd_{client,helo}_restrictions are
+     * evaluated at RCPT TO time. They may be evaluated multiple times per
+     * message delivery. Per-message {client,helo} restriction side effects
+     * (HOLD, DISCARD, etc.) accumulate from one evaluation to the next, and
+     * the result may also depend on sender or recipient information.
+     * Therefore, per-message {client,helo} restriction side effects need to
+     * be reset between deliveries.
+     * 
+     * With smtpd_delay_reject=no, smtpd_{client,helo}_restrictions are
+     * evaluated once and take effect over multiple deliveries. Therefore,
+     * their per-message side effects must not be reset between deliveries.
+     */
+    if (var_smtpd_delay_reject) {
+       SMTPD_MSG_ACT_FREE(state->action_client);
+       SMTPD_MSG_ACT_ZERO(state->action_client);
+       SMTPD_MSG_ACT_FREE(state->action_helo);
+       SMTPD_MSG_ACT_ZERO(state->action_helo);
+    }
 }
 
 /* data_cmd - process DATA command */
@@ -1001,6 +1051,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
     int     first = 1;
     VSTRING *why = 0;
     int     saved_err;
+    char   *act_value;
 
     /*
      * Sanity checks. With ESMTP command pipelining the client can send DATA
@@ -1033,6 +1084,18 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      */
     if (*var_always_bcc)
        rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc);
+    if (SMTPD_MSG_ACT_FLAGS(state) & SMTPD_MSG_ACT_DISCARD) {
+       rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+                   CLEANUP_FLAG_DISCARD);
+    } else {
+       if (SMTPD_MSG_ACT_FLAGS(state) & SMTPD_MSG_ACT_HOLD)
+           rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
+                       CLEANUP_FLAG_HOLD);
+       if ((act_value = SMTPD_MSG_ACT_VALUE(state, filter)) != 0)
+           rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", act_value);
+       if ((act_value = SMTPD_MSG_ACT_VALUE(state, redirect)) != 0)
+           rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s", act_value);
+    }
     rec_fputs(state->cleanup, REC_TYPE_MESG, "");
     rec_fprintf(state->cleanup, REC_TYPE_NORM,
                "Received: from %s (%s [%s])",
@@ -1269,7 +1332,7 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        return (-1);
     }
     if (SMTPD_STAND_ALONE(state) == 0
-       && (err = smtpd_check_rcptmap(state, argv[1].strval)) != 0) {
+       && (err = smtpd_check_vrfy(state, argv[1].strval)) != 0) {
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
@@ -1609,7 +1672,7 @@ static void smtpd_service(VSTREAM *stream, char *unused_service, char **argv)
 static void pre_accept(char *unused_name, char **unused_argv)
 {
     const char *table;
+
     if ((table = dict_changed_name()) != 0) {
        msg_info("table %s has changed -- restarting", table);
        exit(0);
index 8be9efef126085a6ebf93b2ad7ba71577c7508bb..be0ad3421faaa1f19f4f04984fe3088cc97afda6 100644 (file)
@@ -45,6 +45,43 @@ typedef struct SMTPD_DEFER {
     int     class;                     /* error notification class */
 } SMTPD_DEFER;
 
+ /*
+  * Actions that affect all recipients of a given message. That is, we don't
+  * "undo" results from one recipient when evaluating the next recipient.
+  */
+typedef struct SMTPD_MSG_ACTION {
+    int     flags;                     /* see below */
+    char   *filter;                    /* filter destination */
+    char   *redirect;                  /* redirect destination */
+} SMTPD_MSG_ACTION;
+
+#define SMTPD_MSG_ACT_HOLD     (1<<0)  /* place message on hold */
+#define SMTPD_MSG_ACT_DISCARD  (1<<1)  /* discard message */
+#define SMTPD_MSG_ACT_FILTER   (1<<2)  /* filter message */
+#define SMTPD_MSG_ACT_REDIRECT (1<<3)  /* redirect message */
+
+ /*
+  * Short-hand for when to stop searching restriction lists.
+  */
+#define SMTPD_MSG_ACT_FINAL            (SMTPD_MSG_ACT_DISCARD)
+
+#define SMTPD_MSG_ACT_ZERO(a) do { \
+       (a).flags = 0; \
+       (a).filter = 0; \
+       (a).redirect = 0; \
+    } while (0)
+
+#define SMTPD_MSG_ACT_FREE(a) do { \
+       if ((a).filter) myfree((a).filter); \
+       if ((a).redirect) myfree((a).redirect); \
+    } while (0)
+
+#define SMTPD_MSG_ACT_COPY(d, s) do { \
+       SMTPD_MSG_ACT_FREE(d); \
+       (d).filter = ((s).filter ? mystrdup((s).filter) : 0); \
+       (d).redirect = ((s).redirect ? mystrdup((s).redirect) : 0); \
+    } while (0)
+
 typedef struct SMTPD_STATE {
     int     err;
     VSTREAM *client;
@@ -86,6 +123,10 @@ typedef struct SMTPD_STATE {
     VSTRING *sasl_encoded;
     VSTRING *sasl_decoded;
 #endif
+    SMTPD_MSG_ACTION *action;          /* action from access map */
+    SMTPD_MSG_ACTION action_client;    /* action after connect */
+    SMTPD_MSG_ACTION action_helo;      /* action after helo/ehlo */
+    SMTPD_MSG_ACTION action_mailrcpt;  /* action after mail from/rcpt to */
     int     rcptmap_checked;
     int     warn_if_reject;            /* force reject into warning */
     SMTPD_DEFER defer_if_reject;       /* force reject into deferral */
@@ -96,6 +137,15 @@ typedef struct SMTPD_STATE {
     VSTRING *expand_buf;               /* scratch space for $name expansion */
 } SMTPD_STATE;
 
+#define SMTPD_MSG_ACT_FLAGS(s) \
+    ((s)->action_client.flags | (s)->action_helo.flags \
+       | (s)->action_mailrcpt.flags)
+
+#define SMTPD_MSG_ACT_VALUE(s,m) \
+    ((s)->action_mailrcpt.m ? (s)->action_mailrcpt.m : \
+       (s)->action_helo.m ? (s)->action_helo.m : \
+           (s)->action_client.m)
+
 extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
 extern void smtpd_state_reset(SMTPD_STATE *);
 
index 1db47f3d965b46f2cb884093eb39d01e2f0d2265..4b030da860475c5c6deafc8c0e67be6761f9a10a 100644 (file)
@@ -24,7 +24,7 @@
 /*     SMTPD_STATE *state;
 /*     char    *recipient;
 /*
-/*     char    *smtpd_check_rcptmap(state, recipient)
+/*     char    *smtpd_check_vrfy(state, recipient)
 /*     SMTPD_STATE *state;
 /*     char    *recipient;
 /*
 /* .IP smtpd_recipient_restrictions
 /*     Restrictions on the recipient address that is sent with the RCPT
 /*     TO command.
+/* .IP local_recipient_maps
+/*     Tables of user names (not addresses) that exist in $mydestination.
+/*     Mail for local users not in these tables is rejected.
 /* .PP
-/*     smtpd_check_rcptmap() validates the recipient address provided
-/*     with an RCPT TO request and sets the rcptmap_checked flag.
-/*     Relevant configuration parameters:
-/* .IP local_recipients_map
+/*     smtpd_check_vrfy() validates the recipient address provided
+/*     with a VRFY request. Relevant configuration parameters:
+/* .IP local_recipient_maps
 /*     Tables of user names (not addresses) that exist in $mydestination.
 /*     Mail for local users not in these tables is rejected.
 /* .PP
@@ -1751,6 +1753,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
      * mind, and reject/discard the message for other reasons.
      */
     if (STREQUAL(value, "FILTER", cmd_len)) {
+       if (state->action == 0)
+           return (SMTPD_CHECK_DUNNO);
        if (*cmd_text == 0) {
            msg_warn("access map %s entry \"%s\" has FILTER entry without value",
                     table, datum);
@@ -1763,9 +1767,10 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
            vstring_sprintf(error_text, "<%s>: %s triggers FILTER %s",
                            reply_name, reply_class, cmd_text);
            log_whatsup(state, "filter", STR(error_text));
-#ifndef TEST
-           rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", cmd_text);
-#endif
+           state->action->flags |= SMTPD_MSG_ACT_FILTER;
+           if (state->action->filter)
+               myfree(state->action->filter);
+           state->action->filter = mystrdup(cmd_text);
            return (SMTPD_CHECK_DUNNO);
        }
     }
@@ -1775,30 +1780,25 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
      * reject/discard the message for other reasons.
      */
     if (STREQUAL(value, "HOLD", cmd_len)) {
+       if (state->action == 0)
+           return (SMTPD_CHECK_DUNNO);
        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",
-                   CLEANUP_FLAG_HOLD);
-#endif
+       state->action->flags |= SMTPD_MSG_ACT_HOLD;
        return (SMTPD_CHECK_DUNNO);
     }
 
     /*
      * DISCARD means silently discard and claim successful delivery.
-     * 
-     * XXX Set some global flag that disables all further restrictions.
-     * Triggering a "reject" or "hold" action after "discard" is silly.
      */
     if (STREQUAL(value, "DISCARD", cmd_len)) {
+       if (state->action == 0)
+           return (SMTPD_CHECK_DUNNO);
        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",
-                   CLEANUP_FLAG_DISCARD);
-#endif
+       state->action->flags |= SMTPD_MSG_ACT_DISCARD;
        return (SMTPD_CHECK_OK);
     }
 
@@ -1807,6 +1807,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
      * change our mind, and reject/discard the message for other reasons.
      */
     if (STREQUAL(value, "REDIRECT", cmd_len)) {
+       if (state->action == 0)
+           return (SMTPD_CHECK_DUNNO);
        if (strchr(cmd_text, '@') == 0) {
            msg_warn("access map %s entry \"%s\" requires user@domain target",
                     table, datum);
@@ -1815,9 +1817,10 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
            vstring_sprintf(error_text, "<%s>: %s triggers REDIRECT %s",
                            reply_name, reply_class, cmd_text);
            log_whatsup(state, "redirect", STR(error_text));
-#ifndef TEST
-           rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s", cmd_text);
-#endif
+           state->action->flags |= SMTPD_MSG_ACT_REDIRECT;
+           if (state->action->redirect)
+               myfree(state->action->redirect);
+           state->action->redirect = mystrdup(cmd_text);
            return (SMTPD_CHECK_DUNNO);
        }
     }
@@ -2646,6 +2649,9 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
 
     for (cpp = restrictions->argv; (name = *cpp) != 0; cpp++) {
 
+       if (state->action && SMTPD_MSG_ACT_FLAGS(state) & SMTPD_MSG_ACT_FINAL)
+           break;
+
        if (msg_verbose)
            msg_info("%s: name=%s", myname, name);
 
@@ -2966,6 +2972,20 @@ char   *smtpd_check_client(SMTPD_STATE *state)
      */
     state->defer_if_permit.active = 0;
 
+    /*
+     * With smtpd_delay_reject=yes, smtpd_client_restrictions is evaluated at
+     * RCPT TO time, and may be evaluated multiple times. Per-message side
+     * effects (HOLD, DISCARD, etc.) accumulate from one evaluation to the
+     * next, and may also depend on helo, sender or recipient information.
+     * 
+     * With smtpd_delay_reject=no, smtpd_{client,helo}_restrictions are
+     * evaluated immediately, and HELO may be given multiple times. The same
+     * {client,helo} per-message side effects (HOLD, DISCARD, etc.) apply to
+     * multiple deliveries. Therefore, we need separate per-message side
+     * effect storage for client, helo, and for sender+recipients.
+     */
+    state->action = &state->action_client;
+
     /*
      * Apply restrictions in the order as specified.
      */
@@ -3020,6 +3040,20 @@ char   *smtpd_check_helo(SMTPD_STATE *state, char *helohost)
      */
     state->defer_if_permit.active = state->defer_if_permit_client;
 
+    /*
+     * With smtpd_delay_reject=yes, smtpd_helo_restrictions is evaluated at
+     * RCPT TO time, and may be evaluated multiple times. Per-message side
+     * effects (HOLD, DISCARD, etc.) accumulate from one evaluation to the
+     * next, and may also depend on sender or recipient information.
+     * 
+     * With smtpd_delay_reject=no, smtpd_{client,helo}_restrictions are
+     * evaluated immediately, and HELO may be given multiple times. The same
+     * {client,helo} per-message side effects (HOLD, DISCARD, etc.) apply to
+     * multiple deliveries. Therefore, we need separate per-message side
+     * effect storage for client, helo, and for sender+recipients.
+     */
+    state->action = &state->action_helo;
+
     /*
      * Apply restrictions in the order as specified.
      */
@@ -3065,6 +3099,14 @@ char   *smtpd_check_mail(SMTPD_STATE *state, char *sender)
     state->defer_if_permit.active = state->defer_if_permit_client
        | state->defer_if_permit_helo;
 
+    /*
+     * With smtpd_delay_reject=yes, smtpd_sender_restrictions is evaluated at
+     * RCPT TO time, and may be evaluated multiple times. Per-message side
+     * effects (HOLD, DISCARD, etc.) accumulate from one evaluation to the
+     * next, and may also depend on client, helo or recipient information.
+     */
+    state->action = &state->action_mailrcpt;
+
     /*
      * Apply restrictions in the order as specified.
      */
@@ -3152,6 +3194,13 @@ char   *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
      */
     state->defer_if_permit.active = state->defer_if_permit_sender;
 
+    /*
+     * Per-message side effects (HOLD, DISCARD, etc.) accumulate from one
+     * recipient to the next, and may also depend on client, helo or sender
+     * information.
+     */
+    state->action = &state->action_mailrcpt;
+
     /*
      * Apply restrictions in the order as specified.
      */
@@ -3169,6 +3218,14 @@ char   *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
        status = smtpd_check_reject(state, state->defer_if_permit.class,
                                  "%s", STR(state->defer_if_permit.reason));
 
+    /*
+     * If the "check_recipient_maps" restriction was not applied, and if mail
+     * is not being rejected or discarded, validate the recipient here.
+     */
+    if (status == 0 && state->rcptmap_checked == 0
+       && (SMTPD_MSG_ACT_FLAGS(state) & SMTPD_MSG_ACT_FINAL) == 0)
+       status = check_rcpt_maps(state, recipient);
+
     SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
 }
 
@@ -3213,6 +3270,11 @@ char   *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
     state->defer_if_permit.active = state->defer_if_permit_client
        | state->defer_if_permit_helo;
 
+    /*
+     * HOLD, DISCARD, FILTER, etc. are meaningless.
+     */
+    state->action = 0;
+
     /*
      * Apply restrictions in the order as specified.
      */
@@ -3233,11 +3295,11 @@ char   *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
     SMTPD_CHECK_ETRN_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
 }
 
-/* smtpd_check_rcptmap - permit if recipient address matches lookup table */
+/* smtpd_check_vrfy - permit if recipient address matches lookup table */
 
-char   *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
+char   *smtpd_check_vrfy(SMTPD_STATE *state, char *recipient)
 {
-    char   *myname = "smtpd_check_rcptmap";
+    char   *myname = "smtpd_check_vrfy";
     int     status;
 
     if (msg_verbose)
@@ -3246,6 +3308,7 @@ char   *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
     /*
      * Return here in case of serious trouble.
      */
+    SMTPD_CHECK_RESET();
     if ((status = setjmp(smtpd_check_buf)) == 0)
        status = check_rcpt_maps(state, recipient);
 
@@ -3258,14 +3321,6 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
 {
     const RESOLVE_REPLY *reply;
 
-    /*
-     * Duplicate suppression. There's an implicit check_recipient_maps
-     * restriction at the end of all recipient restrictions.
-     */
-    if (state->rcptmap_checked == 1)
-       return (0);
-    state->rcptmap_checked = 1;
-
     /*
      * Resolve the address.
      */
@@ -3622,6 +3677,7 @@ int     var_local_rcpt_code;
 int     var_relay_rcpt_code;
 int     var_virt_mailbox_code;
 int     var_virt_alias_code;
+int     var_show_unk_rcpt_table;
 
 static INT_TABLE int_table[] = {
     "msg_verbose", 0, &msg_verbose,
@@ -3644,6 +3700,7 @@ static INT_TABLE int_table[] = {
     VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code,
     VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code,
     VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code,
+    VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
     0,
 };
 
@@ -3902,6 +3959,8 @@ int     main(int argc, char **argv)
                state.namaddr = concatenate(state.name, "[", state.addr,
                                            "]", (char *) 0);
                resp = smtpd_check_client(&state);
+                SMTPD_MSG_ACT_FREE(state.action_client);
+                SMTPD_MSG_ACT_ZERO(state.action_client);
            }
            break;
 
@@ -4013,17 +4072,22 @@ int     main(int argc, char **argv)
            if (strcasecmp(args->argv[0], "helo") == 0) {
                state.where = "HELO";
                resp = smtpd_check_helo(&state, args->argv[1]);
+                SMTPD_MSG_ACT_FREE(state.action_helo);
+                SMTPD_MSG_ACT_ZERO(state.action_helo);
                UPDATE_STRING(state.helo_name, args->argv[1]);
            } else if (strcasecmp(args->argv[0], "mail") == 0) {
                state.where = "MAIL";
                TRIM_ADDR(args->argv[1], addr);
                UPDATE_STRING(state.sender, addr);
                resp = smtpd_check_mail(&state, addr);
+                SMTPD_MSG_ACT_FREE(state.action_mailrcpt);
+                SMTPD_MSG_ACT_ZERO(state.action_mailrcpt);
            } else if (strcasecmp(args->argv[0], "rcpt") == 0) {
                state.where = "RCPT";
                TRIM_ADDR(args->argv[1], addr);
-               (resp = smtpd_check_rcpt(&state, addr))
-                   || (resp = smtpd_check_rcptmap(&state, addr));
+               resp = smtpd_check_rcpt(&state, addr);
+                SMTPD_MSG_ACT_FREE(state.action_mailrcpt);
+                SMTPD_MSG_ACT_ZERO(state.action_mailrcpt);
            }
            break;
 
index 60348c40d9a3db247a0a325f3d0b549112c05101..e05b80a7da0af0f7b430f7ee5c57ef576d59ead0 100644 (file)
@@ -16,7 +16,7 @@ extern void smtpd_check_init(void);
 extern char *smtpd_check_client(SMTPD_STATE *);
 extern char *smtpd_check_helo(SMTPD_STATE *, char *);
 extern char *smtpd_check_mail(SMTPD_STATE *, char *);
-extern char *smtpd_check_rcptmap(SMTPD_STATE *, char *);
+extern char *smtpd_check_vrfy(SMTPD_STATE *, char *);
 extern char *smtpd_check_size(SMTPD_STATE *, off_t);
 extern char *smtpd_check_rcpt(SMTPD_STATE *, char *);
 extern char *smtpd_check_etrn(SMTPD_STATE *, char *);
index 024bb77ffea9ad9d898a138a8e462ee13d54823a..abffa1ff0ea673aa06c59cf36d7e3f5f349e4611 100644 (file)
@@ -11,6 +11,7 @@ sender_restrictions hash:./smtpd_check_access
 mail rejecttext@bad.domain
 mail filter@filter.domain
 mail filtertext@filter.domain
+mail filtertexttext@filter.domain
 mail hold@hold.domain
 mail holdtext@hold.domain
 mail discard@hold.domain
index e4760017777aac0bb5b5324e3f389fd7d54b8d7f..a73b5a1fc70e4e657e7b9c9d01192babb19cb1dd 100644 (file)
@@ -14,10 +14,13 @@ OK
 ./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
+./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
+./smtpd_check: warning: access map hash:./smtpd_check_access entry "filtertext@filter.domain" requires transport:destination
+OK
+>>> mail filtertexttext@filter.domain
+./smtpd_check: <queue id>: filter: MAIL from localhost[127.0.0.1]: <filtertexttext@filter.domain>: Sender address triggers FILTER text:text; from=<filtertexttext@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
index 41405ef4f6bc203b30830b40f6c807f7a7d07595..bfcb3d35f4b17ebb3ada02b3937d772c20c4e7ed 100644 (file)
@@ -50,6 +50,7 @@ dsn.rfc-ignorant.org  $rbl_code client=$client
 rejecttext@bad.domain  reject text
 filter@filter.domain   filter
 filtertext@filter.domain       filter text
+filtertexttext@filter.domain   filter text:text
 hold@hold.domain       hold
 holdtext@hold.domain   hold text
 discard@hold.domain    discard
index 53ef2758085bbe62707ee5ae1baf7ed1dc378661..d3694839fbbbdc4a9036dc1c52712f6110127c21 100644 (file)
@@ -92,6 +92,9 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream)
     state->recursion = 0;
     state->msg_size = 0;
     state->junk_cmds = 0;
+    SMTPD_MSG_ACT_ZERO(state->action_client);
+    SMTPD_MSG_ACT_ZERO(state->action_helo);
+    SMTPD_MSG_ACT_ZERO(state->action_mailrcpt);
     state->defer_if_permit_client = 0;
     state->defer_if_permit_helo = 0;
     state->defer_if_permit_sender = 0;
@@ -136,6 +139,9 @@ void    smtpd_state_reset(SMTPD_STATE *state)
        vstring_free(state->defer_if_reject.reason);
     if (state->expand_buf)
        vstring_free(state->expand_buf);
+    SMTPD_MSG_ACT_FREE(state->action_client);
+    SMTPD_MSG_ACT_FREE(state->action_helo);
+    SMTPD_MSG_ACT_FREE(state->action_mailrcpt);
 
 #ifdef USE_SASL_AUTH
     if (var_smtpd_sasl_enable)