diff --git a/postfix/HISTORY b/postfix/HISTORY index ae0e4217f..4cdfb467c 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -8783,7 +8783,8 @@ Apologies for any names omitted. Feature: XCLIENT support to override the SMTP server's client information for logging and/or access control. This replaces the short-lived XADDR and XLOGINFO extensions. - Remotely based on code by Victor Duchovni. Files: + Remotely based on code by Victor Duchovni. See FILTER_README + and SMTPD_PROXY_README for usage details. Files: smtpd/{smtpd,smtpd_check,smtpd_proxy,smtpd_xclient}.c smtp/smtp_smtp_proto.c, *qmgr/qmgr_message.c, global/deliver_request.c. @@ -8795,6 +8796,14 @@ Apologies for any names omitted. post-install script gives the user a reminder. Files: conf/postfix-files, conf/post-install. +20031203 + + Support for SMTPD access map actions (FILTER, REDIRECT, + HOLD or DISCARD) that are delegated to the cleanup server, + but can trigger before the first valid recipient address + is accepted (and thus, before a cleanup server connection + is available). Files: smtpd/{smtpd,smtpd_state,smtpd_check}.c. + Open problems: High: when virtual aliasing is turned off after content diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 93446e973..9203c376c 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -22,11 +22,42 @@ snapshot release). Patches change the patchlevel and the release date. Snapshots change only the release date, unless they include the same bugfixes as a patch release. +Incompatible changes with Postfix snapshot 2.0.16-20031203 +========================================================== + +Many SMTPD reject logfile entries now show NOQUEUE instead of a +queue ID. This is because Postfix no longer creates queue file +before the SMTP server has received a valid recipient. + +The experimental XADDR and XLOGINFO extensions to SMTP are now +replaced by XCLIENT. + +Major changes with Postfix snapshot 2.0.16-20031203 +=================================================== + +The XCLIENT extension to SMTP replaces the short-lived XADDR and +XLOGINFO extensions. Details are given in the XCLIENT_README file. + +XCLIENT supports the following features: + +- SMTPD access rule testing. Send "xclient override client_name=xxx +client_addr=yyy" in SMTP sessions and pretend that you are sending +mail as the specified client. + +- Remote client information forwarding through a content filter to +improve logging by down-stream mail software. Send "xclient forward +client_name=xxx client_addr=yyy client_proto=aaa client_helo=bbb" +to specify the original client information that should be logged +and stored with the next MAIL FROM transaction. + Incompatible changes with Postfix snapshot 2.0.16-20031111 ========================================================== The demo greylist policy server is now case insensitive. +The demo greylist policy server now uses BTREE files which greatly +improves stability. + Major changes with Postfix snapshot 2.0.16-20031111 =================================================== diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index d81a0e6dd..73de6a5f4 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20031202" +#define MAIL_RELEASE_DATE "20031203" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index 7161b3fff..6014a9f25 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -30,7 +30,7 @@ update: ../../libexec/$(PROG) ../../libexec/$(PROG): $(PROG) cp $(PROG) ../../libexec -SMTPD_CHECK_OBJ = smtpd_state.o smtpd_peer.o +SMTPD_CHECK_OBJ = smtpd_state.o smtpd_peer.o smtpd_xclient.o smtpd_token: smtpd_token.c $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index e361def16..f80e2c39b 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -1072,9 +1072,6 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) /* * Check the queue file space, if applicable. */ -#define USE_SMTPD_PROXY(state) \ - (SMTPD_STAND_ALONE(state) == 0 && *var_smtpd_proxy_filt) - if (!USE_SMTPD_PROXY(state)) { if ((err = smtpd_check_size(state, state->msg_size)) != 0) { smtpd_chat_reply(state, "%s", err); @@ -1122,6 +1119,19 @@ static void mail_reset(SMTPD_STATE *state) myfree(state->verp_delims); state->verp_delims = 0; } + if (state->proxy_mail) { + myfree(state->proxy_mail); + state->proxy_mail = 0; + } + if (state->saved_filter) { + myfree(state->saved_filter); + state->saved_filter = 0; + } + if (state->saved_redirect) { + myfree(state->saved_redirect); + state->saved_redirect = 0; + } + state->saved_flags = 0; #ifdef USE_SASL_AUTH if (var_smtpd_sasl_enable) smtpd_sasl_mail_reset(state); @@ -1136,10 +1146,6 @@ static void mail_reset(SMTPD_STATE *state) (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE, "QUIT"); smtpd_proxy_close(state); } - if (state->proxy_mail) { - myfree(state->proxy_mail); - state->proxy_mail = 0; - } if (state->xclient.used) smtpd_xclient_reset(state); } @@ -1309,6 +1315,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) } /* + * Flush out any access table actions that are delegated to the cleanup + * server, and that may trigger before we accept the first valid + * recipient. + * * Terminate the message envelope segment. Start the message content * segment, and prepend our own Received: header. If there is only one * recipient, list the recipient address. @@ -1316,8 +1326,15 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) * Suppress our own Received: header in the unlikely case that we are an * intermediate proxy. */ - if (state->cleanup) + if (state->cleanup) { + if (state->saved_filter) + rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", state->saved_filter); + if (state->saved_redirect) + rec_fprintf(state->cleanup, REC_TYPE_RDR, "%s", state->saved_redirect); + if (state->saved_flags) + rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d", state->saved_flags); rec_fputs(state->cleanup, REC_TYPE_MESG, ""); + } if (!state->proxy || state->xclient.used == 0) { out_fprintf(out_stream, REC_TYPE_NORM, "Received: from %s (%s [%s])", diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 9ea347364..b6bbe5709 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -75,7 +75,7 @@ typedef struct SMTPD_STATE { char *access_denied; ARGV *history; char *reason; - char *sender; + char *sender; char *encoding; /* owned by mail_cmd() */ char *verp_delims; /* owned by mail_cmd() */ char *recipient; @@ -106,6 +106,9 @@ typedef struct SMTPD_STATE { int defer_if_permit_helo; /* force permit into warning */ int defer_if_permit_sender; /* force permit into warning */ int discard; /* discard message */ + char *saved_filter; /* postponed filter action */ + char *saved_redirect; /* postponed redirect action */ + int saved_flags; /* postponed hold/discard */ VSTRING *expand_buf; /* scratch space for $name expansion */ VSTREAM *proxy; /* proxy handle */ VSTRING *proxy_buffer; /* proxy query/reply buffer */ @@ -131,6 +134,13 @@ extern void smtpd_state_reset(SMTPD_STATE *); #define SMTPD_STAND_ALONE(state) \ (state->client == VSTREAM_IN && getuid() != var_owner_uid) + /* + * If running as proxy front-end, disable actions that require communication + * with the cleanup server. + */ +#define USE_SMTPD_PROXY(state) \ + (SMTPD_STAND_ALONE(state) == 0 && *var_smtpd_proxy_filt) + /* * SMTPD peer information lookup. */ diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index d22146e83..22da5f045 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -451,6 +451,7 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient); */ #define STR vstring_str #define CONST_STR(x) ((const char *) vstring_str(x)) +#define UPDATE_STRING(ptr,val) { if (ptr) myfree(ptr); ptr = mystrdup(val); } /* * If some decision can't be made due to a temporary error, then change @@ -1737,31 +1738,46 @@ static int reject_unverified_address(SMTPD_STATE *state, const char *addr, return (rqst_status); } -/* warn_skip_access_action - FILTER etc. action in unsupported context */ +/* can_delegate_action - can we delegate this to the cleanup server */ -static void warn_skip_access_action(const char *table, const char *action, - const char *reply_class) +#ifndef TEST + +static int can_delegate_action(SMTPD_STATE *state, const char *table, + const char *action, const char *reply_class) { /* - * Warn only about FILTER/HOLD/etc. access table actions that appear in - * restrictions where they will always be ignored. + * If we're not using the cleanup server, then there is no way that we + * can support actions such as FILTER or HOLD that are delegated to the + * cleanup server. */ - if (strcmp(reply_class, SMTPD_NAME_CLIENT) == 0 - || strcmp(reply_class, SMTPD_NAME_HELO) == 0 - || strcmp(reply_class, SMTPD_NAME_SENDER) == 0) { - if (var_smtpd_delay_reject == 0) - msg_warn("access table %s: with %s=%s, " - "action %s is always skipped in %s restrictions", - table, VAR_SMTPD_DELAY_REJECT, CONFIG_BOOL_NO, - action, reply_class); - } else { - msg_warn("access table %s: action %s is always " - "skipped in %s restrictions", - table, action, reply_class); + if (USE_SMTPD_PROXY(state)) { + msg_warn("access table %s: with %s specified, action %s is unavailable", + table, VAR_SMTPD_PROXY_FILT, action); + return (0); } + + /* + * If delay_reject=no, then client and helo restrictions take effect + * immediately, outside any particular mail transaction context. For + * example, rejecting HELO does not affect subsequent mail deliveries. + * Thus, if delay_reject=no, client and helo actions such as FILTER or + * HOLD also should not affect subsequent mail deliveries. Hmm... + */ + if (var_smtpd_delay_reject == 0 + && (strcmp(reply_class, SMTPD_NAME_CLIENT) == 0 + || strcmp(reply_class, SMTPD_NAME_HELO) == 0)) { + msg_warn("access table %s: with %s=%s, " + "action %s is always skipped in %s restrictions", + table, VAR_SMTPD_DELAY_REJECT, CONFIG_BOOL_NO, + action, reply_class); + return (0); + } + return (1); } +#endif + /* check_table_result - translate table lookup result into pass/reject */ static int check_table_result(SMTPD_STATE *state, const char *table, @@ -1814,10 +1830,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table, */ if (STREQUAL(value, "FILTER", cmd_len)) { #ifndef TEST - if (state->dest == 0) { - warn_skip_access_action(table, "FILTER", reply_class); + if (can_delegate_action(state, table, "FILTER", reply_class) == 0) return (SMTPD_CHECK_DUNNO); - } #endif if (*cmd_text == 0) { msg_warn("access map %s entry \"%s\" has FILTER entry without value", @@ -1832,7 +1846,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table, 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); + UPDATE_STRING(state->saved_filter, cmd_text); #endif return (SMTPD_CHECK_DUNNO); } @@ -1844,17 +1858,14 @@ static int check_table_result(SMTPD_STATE *state, const char *table, */ if (STREQUAL(value, "HOLD", cmd_len)) { #ifndef TEST - if (state->dest == 0) { - warn_skip_access_action(table, "HOLD", reply_class); + if (can_delegate_action(state, table, "HOLD", reply_class) == 0) return (SMTPD_CHECK_DUNNO); - } #endif 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); + state->saved_flags |= CLEANUP_FLAG_HOLD; #endif return (SMTPD_CHECK_DUNNO); } @@ -1864,17 +1875,14 @@ static int check_table_result(SMTPD_STATE *state, const char *table, */ if (STREQUAL(value, "DISCARD", cmd_len)) { #ifndef TEST - if (state->dest == 0) { - warn_skip_access_action(table, "DISCARD", reply_class); + if (can_delegate_action(state, table, "DISCARD", reply_class) == 0) return (SMTPD_CHECK_DUNNO); - } #endif 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); + state->saved_flags |= CLEANUP_FLAG_DISCARD; state->discard = 1; #endif return (SMTPD_CHECK_OK); @@ -1886,10 +1894,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table, */ if (STREQUAL(value, "REDIRECT", cmd_len)) { #ifndef TEST - if (state->dest == 0) { - warn_skip_access_action(table, "REDIRECT", reply_class); + if (can_delegate_action(state, table, "REDIRECT", reply_class) == 0) return (SMTPD_CHECK_DUNNO); - } #endif if (strchr(cmd_text, '@') == 0) { msg_warn("access map %s entry \"%s\" requires user@domain target", @@ -1900,7 +1906,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table, 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); + UPDATE_STRING(state->saved_redirect, cmd_text); #endif return (SMTPD_CHECK_DUNNO); } @@ -4307,8 +4313,6 @@ int main(int argc, char **argv) */ case 4: case 3: -#define UPDATE_STRING(ptr,val) { if (ptr) myfree(ptr); ptr = mystrdup(val); } - if (strcasecmp(args->argv[0], "client") == 0) { state.where = "CONNECT"; UPDATE_STRING(state.name, args->argv[1]); diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index fb7ffa0b4..1071d05b8 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -104,6 +104,9 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream) state->proxy_buffer = 0; state->proxy_mail = 0; state->proxy_features = 0; + state->saved_filter = 0; + state->saved_redirect = 0; + state->saved_flags = 0; #ifdef USE_SASL_AUTH if (SMTPD_STAND_ALONE(state))