diff --git a/postfix/.indent.pro b/postfix/.indent.pro
index 132e9b83f..7e8ce4ccf 100644
--- a/postfix/.indent.pro
+++ b/postfix/.indent.pro
@@ -136,6 +136,7 @@
-TSINK_STATE
-TSMTPD_CMD
-TSMTPD_DEFER
+-TSMTPD_MSG_ACTION
-TSMTPD_RBL_EXPAND_CONTEXT
-TSMTPD_RBL_STATE
-TSMTPD_STATE
diff --git a/postfix/HISTORY b/postfix/HISTORY
index c25921a28..23f544606 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -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
diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf
index 2c8d34fc2..a8f973721 100644
--- a/postfix/conf/master.cf
+++ b/postfix/conf/master.cf
@@ -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.
#
diff --git a/postfix/html/proxymap.8.html b/postfix/html/proxymap.8.html
index 4ea05c096..0cff6e6a6 100644
--- a/postfix/html/proxymap.8.html
+++ b/postfix/html/proxymap.8.html
@@ -88,7 +88,9 @@ PROXYMAP(8) PROXYMAP(8)
The proxymap server opens only tables that are approved
via the proxy_read_maps 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
diff --git a/postfix/html/uce.html b/postfix/html/uce.html
index 9ca51eb41..6d0a25c79 100644
--- a/postfix/html/uce.html
+++ b/postfix/html/uce.html
@@ -880,7 +880,7 @@ and the address contains no sender-specified routing
Postfix is the final destination: any destination that matches
$mydestination, $inet_interfaces, $virtual_alias_domains, or
+href="virtual.5.html">$virtual_alias_domains, or $virtual_mailbox_domains.
diff --git a/postfix/man/man8/proxymap.8 b/postfix/man/man8/proxymap.8
index a801de872..73efb7142 100644
--- a/postfix/man/man8/proxymap.8
+++ b/postfix/man/man8/proxymap.8
@@ -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
diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in
index cd4ef60af..fb9f9f638 100644
--- a/postfix/src/global/Makefile.in
+++ b/postfix/src/global/Makefile.in
@@ -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
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 2da4fcccd..4dc8dae25 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,10 +20,10 @@
* 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;
/*
diff --git a/postfix/src/global/maps.c b/postfix/src/global/maps.c
index fc7dedc32..ae345e3ea 100644
--- a/postfix/src/global/maps.c
+++ b/postfix/src/global/maps.c
@@ -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);
}
diff --git a/postfix/src/global/tok822_parse.c b/postfix/src/global/tok822_parse.c
index fe554bf7a..4866ac5ad 100644
--- a/postfix/src/global/tok822_parse.c
+++ b/postfix/src/global/tok822_parse.c
@@ -126,6 +126,7 @@
#include
#include
+#include
/* Global library. */
@@ -250,7 +251,7 @@ static void strip_address(VSTRING *vp, int start, TOK822 *addr)
* Emit plain . 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)
diff --git a/postfix/src/master/mail_flow.c b/postfix/src/master/mail_flow.c
index efb598854..383ba5816 100644
--- a/postfix/src/master/mail_flow.c
+++ b/postfix/src/master/mail_flow.c
@@ -47,6 +47,7 @@
#include
#include
#include
+#include
/* Utility library. */
diff --git a/postfix/src/postsuper/postsuper.c b/postfix/src/postsuper/postsuper.c
index 3247a2c00..a5c4ded86 100644
--- a/postfix/src/postsuper/postsuper.c
+++ b/postfix/src/postsuper/postsuper.c
@@ -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.
*/
diff --git a/postfix/src/proxymap/proxymap.c b/postfix/src/proxymap/proxymap.c
index 618d58595..22d070f3a 100644
--- a/postfix/src/proxymap/proxymap.c
+++ b/postfix/src/proxymap/proxymap.c
@@ -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
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 24aae3d1d..e75bb0d47 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -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);
diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h
index 8be9efef1..be0ad3421 100644
--- a/postfix/src/smtpd/smtpd.h
+++ b/postfix/src/smtpd/smtpd.h
@@ -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 *);
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 1db47f3d9..4b030da86 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -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;
/*
@@ -202,11 +202,13 @@
/* .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;
diff --git a/postfix/src/smtpd/smtpd_check.h b/postfix/src/smtpd/smtpd_check.h
index 60348c40d..e05b80a7d 100644
--- a/postfix/src/smtpd/smtpd_check.h
+++ b/postfix/src/smtpd/smtpd_check.h
@@ -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 *);
diff --git a/postfix/src/smtpd/smtpd_check.in4 b/postfix/src/smtpd/smtpd_check.in4
index 024bb77ff..abffa1ff0 100644
--- a/postfix/src/smtpd/smtpd_check.in4
+++ b/postfix/src/smtpd/smtpd_check.in4
@@ -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
diff --git a/postfix/src/smtpd/smtpd_check.ref4 b/postfix/src/smtpd/smtpd_check.ref4
index e47600177..a73b5a1fc 100644
--- a/postfix/src/smtpd/smtpd_check.ref4
+++ b/postfix/src/smtpd/smtpd_check.ref4
@@ -14,10 +14,13 @@ OK
./smtpd_check: : reject: MAIL from localhost[127.0.0.1]: 554 : Sender address rejected: text; from= proto=SMTP
554 : Sender address rejected: text
>>> mail filter@filter.domain
-./smtpd_check: warning: access map hash:./smtpd_check_access entry filter@filter.domain has FILTER entry without value
+./smtpd_check: warning: access map hash:./smtpd_check_access entry "filter@filter.domain" has FILTER entry without value
OK
>>> mail filtertext@filter.domain
-./smtpd_check: : filter: MAIL from localhost[127.0.0.1]: : Sender address triggers FILTER text; from= proto=SMTP
+./smtpd_check: warning: access map hash:./smtpd_check_access entry "filtertext@filter.domain" requires transport:destination
+OK
+>>> mail filtertexttext@filter.domain
+./smtpd_check: : filter: MAIL from localhost[127.0.0.1]: : Sender address triggers FILTER text:text; from= proto=SMTP
OK
>>> mail hold@hold.domain
./smtpd_check: : hold: MAIL from localhost[127.0.0.1]: : Sender address triggers HOLD action; from= proto=SMTP
diff --git a/postfix/src/smtpd/smtpd_check_access b/postfix/src/smtpd/smtpd_check_access
index 41405ef4f..bfcb3d35f 100644
--- a/postfix/src/smtpd/smtpd_check_access
+++ b/postfix/src/smtpd/smtpd_check_access
@@ -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
diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c
index 53ef27580..d3694839f 100644
--- a/postfix/src/smtpd/smtpd_state.c
+++ b/postfix/src/smtpd/smtpd_state.c
@@ -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)