2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

postfix-2.0.7-20030317

This commit is contained in:
Wietse Venema 2003-03-17 00:00:00 -05:00 committed by Viktor Dukhovni
parent bab3a35113
commit e57de0df68
21 changed files with 329 additions and 88 deletions

1
postfix/.indent.pro vendored
View File

@ -136,6 +136,7 @@
-TSINK_STATE
-TSMTPD_CMD
-TSMTPD_DEFER
-TSMTPD_MSG_ACTION
-TSMTPD_RBL_EXPAND_CONTEXT
-TSMTPD_RBL_STATE
-TSMTPD_STATE

View 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

View 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.
#

View 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

View 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>

View 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

View 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

View File

@ -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;
/*

View 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);
}

View File

@ -126,6 +126,7 @@
#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)

View File

@ -47,6 +47,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/* Utility library. */

View 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.
*/

View 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

View 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);

View 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 *);

View 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;
/*
@ -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;

View 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 *);

View 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

View 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

View 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

View 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)