From: Wietse Venema Date: Thu, 5 Sep 2002 05:00:00 +0000 (-0500) Subject: postfix-1.1.11-20020905 X-Git-Tag: v2.0.0~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3807468deb52e3b06674b4da76157118b2d82979;p=thirdparty%2Fpostfix.git postfix-1.1.11-20020905 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 0f9bb2644..4c60e4c34 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -119,6 +119,7 @@ -TSINK_COMMAND -TSINK_STATE -TSMTPD_CMD +-TSMTPD_DEFER -TSMTPD_STATE -TSMTPD_TOKEN -TSMTP_ADDR diff --git a/postfix/HISTORY b/postfix/HISTORY index 4f496021f..6d46badad 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -6888,14 +6888,40 @@ Apologies for any names omitted. Feature: "FILTER transport:nexthop" is now also available in SMTPD access tables. +20020901 + + Cleanup: postfix-install no longer installs all the manual + pages under $POSTFIXSOURCE/man, so we can generate manual + pages for smtp-sink etc. File: man/Makefile.in. + +20020903 + + Bugfix: the rmail script should have been updated when + Postfix sendmail was changed to recognize `.' as the end + of input. Problem fix by Christian Kratzer, cksoft.de. + File: auxiliary/rmail/rmail. + +20020904 + + Bugfix: qmail compatibility: qmqpd should support any + character at the end of the VERP prefix in prefix@host-@[]. + Based on a patch by LaMont Jones, HP. + +20020905 + + Feature: "smtpd_data_restrictions = reject_unauth_pipelining" + blocks mail from SMTP clients that send message content + before Postfix has replied to the DATA command. File: + smtpd/smtpd.c, smtpd/smtpd_check.c. + + Bugfix: the LDAP client dumped core in verbose mode. + Reported by Will Day and others. File: util/dict_ldap.c. + Open problems: Low: smtpd should log queue ID with reject/warn/hold/discard actions. - Medium: should permit_mx_backup defer delivery if DNS has - some error of some kind? - Low: revise other local delivery agent duplicate filters. Low: all table lookups should consistently use internalized diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 47bdd6777..3d27318b1 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -12,20 +12,28 @@ 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. -Major changes with Postfix snapshot 1.1.11-20020823 +Major changes with Postfix snapshot 1.1.11-20020XXX =================================================== More sophisticated handling of UCE-related DNS lookup errors. These cause Postfix to not give up so easily, so that some deliveries will not have to be deferred after all. This affects the following -restrictions: permit_mx_backup (defer the request if any subsequent -restriction would cause the request to be rejected, accept the -request if any subsequent restriction would cause the request to -be accepted); reject_unknown_hostname, reject_unknown_sender_domain -and reject_unknown_recipient_domain (defer the request if any -subsequent restriction would cause the request to be accepted, -reject the request if any subsequent restriction would cause the -request to be rejected). +restrictions: + +- permit_mx_backup (defer the request if a subsequent restriction +would cause the request to be rejected, accept the request if a +subsequent restriction would cause the request to be accepted +anyway); + +- reject_unknown_hostname, reject_unknown_sender_domain and +reject_unknown_recipient_domain (defer the request if a subsequent +restriction would cause the request to be accepted, reject the +request if a subsequent restriction would cause the request to be +rejected anyway). + +Specify "smtpd_data_restrictions = reject_unauth_pipelining" to +block mail from SMTP clients that send message content before +Postfix has replied to the DATA command. Incompatible changes with Postfix snapshot 1.1.11-20020819 ========================================================== diff --git a/postfix/auxiliary/rmail/rmail b/postfix/auxiliary/rmail/rmail index eb35bf1c2..ab1573c5f 100755 --- a/postfix/auxiliary/rmail/rmail +++ b/postfix/auxiliary/rmail/rmail @@ -10,4 +10,4 @@ case "$from" in *) from="$from@$relay";; esac -exec $SENDMAIL -f "$from" -- "$@" +exec $SENDMAIL -i -f "$from" -- "$@" diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 96dbad7a1..14e79e9d8 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -254,6 +254,11 @@ SMTPD(8) SMTPD(8) Restrict what domain names can be used in ETRN com- mands, and what clients may issue ETRN commands. + smtpd_data_restrictions + Restrictions on the DATA command. Currently, the + only restriction that makes sense here is + reject_unauth_pipelining. + allow_untrusted_routing Allow untrusted clients to specify addresses with sender-specified routing. Enabling this opens up diff --git a/postfix/man/Makefile.in b/postfix/man/Makefile.in index f1461025a..279950201 100644 --- a/postfix/man/Makefile.in +++ b/postfix/man/Makefile.in @@ -12,8 +12,10 @@ COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ man1/postqueue.1 man1/postsuper.1 CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \ man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5 +TOOLS = man1/smtp-sink.1 man1/smtp-source.1 man1/qmqp-sink.1 \ + man1/qmqp-source.1 -update: $(DAEMONS) $(COMMANDS) $(CONFIG) +update: $(DAEMONS) $(COMMANDS) $(CONFIG) $(TOOLS) Makefile: Makefile.in (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../src/makedefs; cat $?) >$@ @@ -151,3 +153,15 @@ man5/transport.5: ../proto/transport man5/virtual.5: ../proto/virtual ../mantools/srctoman - $? >$@ + +man1/smtp-sink.1: ../src/smtpstone/smtp-sink.c + ../mantools/srctoman $? >$@ + +man1/smtp-source.1: ../src/smtpstone/smtp-source.c + ../mantools/srctoman $? >$@ + +man1/qmqp-sink.1: ../src/smtpstone/qmqp-sink.c + ../mantools/srctoman $? >$@ + +man1/qmqp-source.1: ../src/smtpstone/qmqp-source.c + ../mantools/srctoman $? >$@ diff --git a/postfix/man/man1/qmqp-sink.1 b/postfix/man/man1/qmqp-sink.1 new file mode 100644 index 000000000..6bd71a778 --- /dev/null +++ b/postfix/man/man1/qmqp-sink.1 @@ -0,0 +1,52 @@ +.TH QMQP-SINK 8 +.ad +.fi +.SH NAME +qmqp-sink +\- +multi-threaded QMQP test server +.SH SYNOPSIS +.na +.nf +.fi +\fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR] +[\fBinet:\fR][\fIhost\fR]:\fIport\fR \fIbacklog\fR + +\fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR] +\fBunix:\fR\fIpathname\fR \fIbacklog\fR +.SH DESCRIPTION +.ad +.fi +\fIqmqp-sink\fR listens on the named host (or address) and port. +It receives messages from the network and throws them away. +The purpose is to measure QMQP client performance, not protocol +compliance. +Connections can be accepted on IPV4 endpoints or UNIX-domain sockets. +IPV4 is the default. +This program is the complement of the \fIqmqp-source\fR program. +.IP \fB-c\fR +Display a running counter that is updated whenever a delivery +is completed. +.IP \fB-v\fR +Increase verbosity. Specify \fB-v -v\fR to see some of the QMQP +conversation. +.IP "\fB-x \fItime\fR +Terminate after \fItime\fR seconds. This is to facilitate memory +leak testing. +.SH SEE ALSO +.na +.nf +qmqp-source, QMQP test message generator +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/man/man1/qmqp-source.1 b/postfix/man/man1/qmqp-source.1 new file mode 100644 index 000000000..db13a5431 --- /dev/null +++ b/postfix/man/man1/qmqp-source.1 @@ -0,0 +1,66 @@ +.TH QMQP-SOURCE 8 +.ad +.fi +.SH NAME +qmqp-source +\- +multi-threaded QMQP test generator +.SH SYNOPSIS +.na +.nf +.fi +\fBqmqp-source\fR [\fIoptions\fR] [\fBinet:\fR]\fIhost\fR[:\fIport\fR] + +\fBqmqp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR +.SH DESCRIPTION +.ad +.fi +qmqp-source connects to the named host and TCP port (default 628) +and sends one or more messages to it, either sequentially +or in parallel. The program speaks the QMQP protocol. +Connections can be made to UNIX-domain and IPV4 servers. +IPV4 is the default. + +Options: +.IP -c +Display a running counter that is incremented each time +a delivery completes. +.IP "-C count" +When a host sends RESET instead of SYN|ACK, try \fIcount\fR times +before giving up. The default count is 1. Specify a larger count in +order to work around a problem with TCP/IP stacks that send RESET +when the listen queue is full. +.IP "-f from" +Use the specified sender address (default: ). +.IP "-l length" +Send \fIlength\fR bytes as message payload. The length +includes the message headers. +.IP "-m message_count" +Send the specified number of messages (default: 1). +.IP "-r recipient_count" +Send the specified number of recipients per transaction (default: 1). +Recipient names are generated by prepending a number to the +recipient address. +.IP "-s session_count" +Run the specified number of QMQP sessions in parallel (default: 1). +.IP "-t to" +Use the specified recipient address (default: ). +.IP "-R interval" +Wait for a random period of time 0 <= n <= interval between messages. +Suspending one thread does not affect other delivery threads. +.IP "-w interval" +Wait a fixed time between messages. +Suspending one thread does not affect other delivery threads. +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/man/man1/smtp-sink.1 b/postfix/man/man1/smtp-sink.1 new file mode 100644 index 000000000..3335f076f --- /dev/null +++ b/postfix/man/man1/smtp-sink.1 @@ -0,0 +1,82 @@ +.TH SMTP-SINK 8 +.ad +.fi +.SH NAME +smtp-sink +\- +multi-threaded SMTP/LMTP test server +.SH SYNOPSIS +.na +.nf +.fi +\fBsmtp-sink\fR [\fIoptions\fR] [\fBinet:\fR][\fIhost\fR]:\fIport\fR +\fIbacklog\fR + +\fBsmtp-sink\fR [\fIoptions\fR] \fBunix:\fR\fIpathname\fR \fIbacklog\fR +.SH DESCRIPTION +.ad +.fi +\fIsmtp-sink\fR listens on the named host (or address) and port. +It takes SMTP messages from the network and throws them away. +The purpose is to measure SMTP client performance, not protocol +compliance. +Connections can be accepted on IPV4 endpoints or UNIX-domain sockets. +IPV4 is the default. +This program is the complement of the \fIsmtp-source\fR program. + +Arguments: +.IP \fB-c\fR +Display a running counter that is updated whenever an SMTP +QUIT command is executed. +.IP \fB-e\fR +Disable ESMTP support. +.IP \fB-h\fI hostname\fR +Use \fIhostname\fR in the SMTP greeting, in the HELO response, +and in the EHLO response. The default hostname is "smtp-sink". +.IP \fB-L\fR +Enable LMTP rather than SMTP. +.IP "\fB-n \fIcount\fR" +Terminate after \fIcount\fR sessions. This is for testing purposes. +.IP \fB-p\fR +Disable ESMTP command pipelining. +.IP \fB-P\fR +Change the server greeting so that it appears to come through +a CISCO PIX system. +.IP "\fB-s \fIcommand,command,...\fR" +Log the named commands to syslogd. +Examples of commands that can be logged are HELO, EHLO, LHLO, MAIL, +RCPT, VRFY, RSET, NOOP, and QUIT. Separate command names by white +space or commas, and use quotes to protect white space from the +shell. Command names are case-insensitive. +.IP \fB-v\fR +Show the SMTP conversations. +.IP "\fB-w \fIdelay\fR" +Wait \fIdelay\fR seconds before responding to a DATA command. +.IP \fB-8\fR +Disable 8BITMIME support. +.IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR +Listen on network interface \fIhost\fR (default: any interface) +TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be +specified in numeric or symbolic form. +.IP \fBunix:\fR\fIpathname\fR +Listen on the UNIX-domain socket at \fIpathname\fR. +.IP \fIbacklog\fR +The maximum length the queue of pending connections, +as defined by the listen(2) call. +.SH SEE ALSO +.na +.nf +smtp-source, SMTP/LMTP test message generator +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/man/man1/smtp-source.1 b/postfix/man/man1/smtp-source.1 new file mode 100644 index 000000000..0bd3f7a32 --- /dev/null +++ b/postfix/man/man1/smtp-source.1 @@ -0,0 +1,85 @@ +.TH SMTP-SOURCE 8 +.ad +.fi +.SH NAME +smtp-source +\- +multi-threaded SMTP test generator +.SH SYNOPSIS +.na +.nf +.fi +\fBsmtp-source\fR [\fIoptions\fR] [\fBinet:\fR]\fIhost\fR[:\fIport\fR] + +\fBsmtp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR +.SH DESCRIPTION +.ad +.fi +smtp-source connects to the named \fIhost\fR and TCP \fIport\fR +(default: port 25) +and sends one or more messages to it, either sequentially +or in parallel. The program speaks either SMTP (default) or +LMTP. Connections can be made to UNIX-domain and IPV4 servers. +IPV4 is the default. + +Arguments: +.IP \fB-c\fR +Display a running counter that is incremented each time +an SMTP DATA command completes. +.IP "\fB-C \fIcount\fR" +When a host sends RESET instead of SYN|ACK, try \fIcount\fR times +before giving up. The default count is 1. Specify a larger count in +order to work around a problem with TCP/IP stacks that send RESET +when the listen queue is full. +.IP \fB-d\fR +Don't disconnect after sending a message; send the next +message over the same connection. +.IP "\fB-f \fIfrom\fR" +Use the specified sender address (default: ). +.IP \fB-o\fR +Old mode: don't send HELO, and don't send message headers. +.IP "\fB-l \fIlength\fR" +Send \fIlength\fR bytes as message payload. The length does not +include message headers. +.IP \fB-L\fR +Speak LMTP rather than SMTP. +.IP "\fB-m \fImessage_count\fR" +Send the specified number of messages (default: 1). +.IP "\fB-r \fIrecipient_count\fR" +Send the specified number of recipients per transaction (default: 1). +Recipient names are generated by prepending a number to the +recipient address. +.IP "\fB-s \fIsession_count\fR" +Run the specified number of SMTP sessions in parallel (default: 1). +.IP "\fB-S \fIsubject\fR" +Send mail with the named subject line (default: none). +.IP "\fB-t \fIto\fR" +Use the specified recipient address (default: ). +.IP "\fB-R \fIinterval\fR" +Wait for a random period of time 0 <= n <= interval between messages. +Suspending one thread does not affect other delivery threads. +.IP "\fB-w \fIinterval\fR" +Wait a fixed time between messages. +Suspending one thread does not affect other delivery threads. +.IP [\fBinet:\fR]\fIhost\fR[:\fIport\fR] +Connect via TCP to \fIhost\fR port \fIport\fR. The default +port is \fBsmtp\fR. +.IP \fBunix:\fIpathname\fR +Connect to the UNIX-domain socket at \fIpathname\fR. +.SH BUGS +.ad +.fi +No SMTP command pipelining support. +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 6b02a0e3b..3fbdab426 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -212,6 +212,9 @@ Restrict what recipient addresses are allowed in \fBRCPT TO\fR commands. .IP \fBsmtpd_etrn_restrictions\fR Restrict what domain names can be used in \fBETRN\fR commands, and what clients may issue \fBETRN\fR commands. +.IP \fBsmtpd_data_restrictions\fR +Restrictions on the \fBDATA\fR command. Currently, the only restriction +that makes sense here is \fBreject_unauth_pipelining\fR. .IP \fBallow_untrusted_routing\fR Allow untrusted clients to specify addresses with sender-specified routing. Enabling this opens up nasty relay loopholes involving diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 71f78bd20..25fa510de 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1116,6 +1116,10 @@ extern char *var_rcpt_checks; #define DEF_ETRN_CHECKS "" extern char *var_etrn_checks; +#define VAR_DATA_CHECKS "smtpd_data_restrictions" +#define DEF_DATA_CHECKS "" +extern char *var_data_checks; + #define VAR_REST_CLASSES "smtpd_restriction_classes" #define DEF_REST_CLASSES "" extern char *var_rest_classes; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index af88095e0..4373cc45a 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 "20020827" +#define MAIL_RELEASE_DATE "20020905" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "1.1.11-" MAIL_RELEASE_DATE diff --git a/postfix/src/nqmgr/qmgr.c b/postfix/src/nqmgr/qmgr.c index 57f65cac4..0d1603d75 100644 --- a/postfix/src/nqmgr/qmgr.c +++ b/postfix/src/nqmgr/qmgr.c @@ -553,7 +553,7 @@ int main(int argc, char **argv) VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0, VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0, VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0, - VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 8640000, + VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 0, 8640000, VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0, VAR_QMGR_CLOG_WARN_TIME, DEF_QMGR_CLOG_WARN_TIME, &var_qmgr_clog_warn_time, 0, 0, 0, diff --git a/postfix/src/nqmgr/qmgr_active.c b/postfix/src/nqmgr/qmgr_active.c index b4b7f9c48..dd0044b4c 100644 --- a/postfix/src/nqmgr/qmgr_active.c +++ b/postfix/src/nqmgr/qmgr_active.c @@ -365,7 +365,7 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message) * daemon waits for the qmgr to accept the "new mail" trigger. */ if (message->flags) { - if (event_time() > message->arrival_time + var_max_queue_time) { + if (event_time() >= message->arrival_time + var_max_queue_time) { msg_info("%s: from=<%s>, status=expired, returned to sender", message->queue_id, message->sender); if (message->verp_delims == 0 || var_verp_bounce_off) diff --git a/postfix/src/qmgr/qmgr.c b/postfix/src/qmgr/qmgr.c index 87d75dd82..d09a64650 100644 --- a/postfix/src/qmgr/qmgr.c +++ b/postfix/src/qmgr/qmgr.c @@ -505,7 +505,7 @@ int main(int argc, char **argv) VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0, VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0, VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0, - VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 8640000, + VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 0, 8640000, VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0, VAR_QMGR_CLOG_WARN_TIME, DEF_QMGR_CLOG_WARN_TIME, &var_qmgr_clog_warn_time, 0, 0, 0, diff --git a/postfix/src/qmgr/qmgr_active.c b/postfix/src/qmgr/qmgr_active.c index b4b7f9c48..dd0044b4c 100644 --- a/postfix/src/qmgr/qmgr_active.c +++ b/postfix/src/qmgr/qmgr_active.c @@ -365,7 +365,7 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message) * daemon waits for the qmgr to accept the "new mail" trigger. */ if (message->flags) { - if (event_time() > message->arrival_time + var_max_queue_time) { + if (event_time() >= message->arrival_time + var_max_queue_time) { msg_info("%s: from=<%s>, status=expired, returned to sender", message->queue_id, message->sender); if (message->verp_delims == 0 || var_verp_bounce_off) diff --git a/postfix/src/qmqpd/qmqpd.c b/postfix/src/qmqpd/qmqpd.c index dbcfcb4f0..ff331c355 100644 --- a/postfix/src/qmqpd/qmqpd.c +++ b/postfix/src/qmqpd/qmqpd.c @@ -217,23 +217,27 @@ static void qmqpd_copy_sender(QMQPD_STATE *state) char *end_prefix; char *end_origin; int verp_requested; + static char verp_chars[] = "-="; /* - * If the sender address looks like prefix-@origin-@[], then request + * If the sender address looks like prefix@origin-@[], then request * variable envelope return path delivery, with an envelope sender - * address of prefix@origin, and with VERP delimiters of - and =. This + * address of prefi@origin, and with VERP delimiters of x and =. This * way, the recipients will see envelope sender addresses that look like: - * prefix-user=domain@origin. + * prefixuser=domain@origin. */ state->where = "receiving sender address"; netstring_get(state->client, state->buf, var_line_limit); VSTRING_TERMINATE(state->buf); - verp_requested = ((end_prefix = strstr(STR(state->buf), "-@")) != 0 - && (end_origin = strstr(end_prefix + 2, "-@")) != 0 - && strncmp(end_origin + 2, "[]", 2) == 0 - && vstring_end(state->buf) == end_origin + 4); + verp_requested = + ((end_origin = vstring_end(state->buf) - 4) > STR(state->buf) + && strcmp(end_origin, "-@[]") == 0 + && (end_prefix = strchr(STR(state->buf), '@')) != 0 /* XXX */ + && --end_prefix < end_origin - 2 /* non-null origin */ + && end_prefix > STR(state->buf)); /* non-null prefix */ if (verp_requested) { - memcpy(end_prefix, end_prefix + 1, end_origin - end_prefix - 1); + verp_chars[0] = end_prefix[0]; + memmove(end_prefix, end_prefix + 1, end_origin - end_prefix - 1); vstring_truncate(state->buf, end_origin - STR(state->buf) - 1); } if (state->err == CLEANUP_STAT_OK @@ -241,7 +245,7 @@ static void qmqpd_copy_sender(QMQPD_STATE *state) state->err = CLEANUP_STAT_WRITE; if (verp_requested) if (state->err == CLEANUP_STAT_OK - && rec_put(state->cleanup, REC_TYPE_VERP, "-=", 2) < 0) + && rec_put(state->cleanup, REC_TYPE_VERP, verp_chars, 2) < 0) state->err = CLEANUP_STAT_WRITE; state->sender = mystrndup(STR(state->buf), LEN(state->buf)); } diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index ca31431ff..66f4a97d0 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -198,6 +198,9 @@ /* .IP \fBsmtpd_etrn_restrictions\fR /* Restrict what domain names can be used in \fBETRN\fR commands, /* and what clients may issue \fBETRN\fR commands. +/* .IP \fBsmtpd_data_restrictions\fR +/* Restrictions on the \fBDATA\fR command. Currently, the only restriction +/* that makes sense here is \fBreject_unauth_pipelining\fR. /* .IP \fBallow_untrusted_routing\fR /* Allow untrusted clients to specify addresses with sender-specified /* routing. Enabling this opens up nasty relay loopholes involving @@ -350,6 +353,7 @@ char *var_helo_checks; char *var_mail_checks; char *var_rcpt_checks; char *var_etrn_checks; +char *var_data_checks; int var_unk_client_code; int var_bad_name_code; int var_unk_name_code; @@ -925,6 +929,7 @@ static void rcpt_reset(SMTPD_STATE *state) static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) { + char *err; char *start; int len; int curr_rec_type; @@ -951,6 +956,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) smtpd_chat_reply(state, "501 Syntax: DATA"); return (-1); } + if (SMTPD_STAND_ALONE(state) == 0 && (err = smtpd_check_data(state)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } /* * Terminate the message envelope segment. Start the message content @@ -1609,6 +1618,7 @@ int main(int argc, char **argv) VAR_MAIL_CHECKS, DEF_MAIL_CHECKS, &var_mail_checks, 0, 0, VAR_RCPT_CHECKS, DEF_RCPT_CHECKS, &var_rcpt_checks, 0, 0, VAR_ETRN_CHECKS, DEF_ETRN_CHECKS, &var_etrn_checks, 0, 0, + VAR_DATA_CHECKS, DEF_DATA_CHECKS, &var_data_checks, 0, 0, VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0, VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0, VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0, diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 9f04b72e6..5cb9f19ec 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -39,6 +39,12 @@ * some of this has to be global anyway, so that the run-time error handler * can clean up in case of a fatal error deep down in some library routine. */ +typedef struct SMTPD_DEFER { + int active; /* is this active */ + VSTRING *reason; /* reason for deferral */ + int class; /* error notification class */ +} SMTPD_DEFER; + typedef struct SMTPD_STATE { int err; VSTREAM *client; @@ -80,11 +86,9 @@ typedef struct SMTPD_STATE { VSTRING *sasl_encoded; VSTRING *sasl_decoded; #endif - int warn_if_reject; - int defer_if_reject; /* force reject into deferral */ - int defer_if_permit; /* force permit into deferral */ - VSTRING *defer_reason; /* reason why we force deferral */ - int defer_class; /* forced deferral error class */ + int warn_if_reject; /* force reject into warning */ + SMTPD_DEFER defer_if_reject; /* force reject into deferral */ + SMTPD_DEFER defer_if_permit; /* force permit into deferral */ } SMTPD_STATE; extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *); diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 6dd9a206e..a426ab4ac 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -31,6 +31,9 @@ /* char *smtpd_check_etrn(state, destination) /* SMTPD_STATE *state; /* char *destination; +/* +/* char *smtpd_check_data(state) +/* SMTPD_STATE *state; /* DESCRIPTION /* This module implements additional checks on SMTP client requests. /* A client request is validated in the context of the session state. @@ -145,6 +148,8 @@ /* .IP reject_unauth_pipelining /* Reject the request when the client has already sent the next request /* without being told that the server implements SMTP command pipelining. +/* Reject the DATA command when the client sends message content before +/* Postfix has sent the DATA command reply. /* .IP permit_mx_backup /* Allow the request when all primary MX hosts for the recipient /* are in the networks specified with the $permit_mx_backup_networks @@ -359,6 +364,7 @@ static ARGV *helo_restrctions; static ARGV *mail_restrctions; static ARGV *rcpt_restrctions; static ARGV *etrn_restrctions; +static ARGV *data_restrctions; static HTABLE *smtpd_rest_classes; @@ -375,6 +381,7 @@ static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, con #define SMTPD_NAME_SENDER "Sender address" #define SMTPD_NAME_RECIPIENT "Recipient address" #define SMTPD_NAME_ETRN "Etrn command" +#define SMTPD_NAME_DATA "Data command" /* * YASLM. @@ -384,11 +391,28 @@ static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, con /* * If some decision can't be made due to a temporary error, then change - * other decisions in to deferrals. + * other decisions into deferrals. + * + * XXX Deferrals can be postponed only with restrictions that are based on + * client-specified information: this restricts their use to parameters + * given in HELO, MAIL FROM, RCPT TO commands. + * + * XXX Deferrals must not be postponed after client hostname lookup failure. + * The reason is that the effect of access tables may depend on whether a + * client hostname is available or not. Thus, the reject_unknown_client + * restriction must defer immediately when lookup fails, otherwise incorrect + * results happen with: + * + * reject_unknown_client, hostname-based white-list, reject */ -static void PRINTFLIKE(3, 4) defer_if_reject(SMTPD_STATE *, int, const char *, ...); -static void PRINTFLIKE(3, 4) defer_if_permit(SMTPD_STATE *, int, const char *, ...); - +static void PRINTFLIKE(3, 4) defer_if(SMTPD_DEFER *, int, const char *,...); + +#define DEFER_IF_REJECT2(state, class, fmt, a1, a2) \ + defer_if(&(state)->defer_if_reject, (class), (fmt), (a1), (a2)) +#define DEFER_IF_REJECT3(state, class, fmt, a1, a2, a3) \ + defer_if(&(state)->defer_if_reject, (class), (fmt), (a1), (a2), (a3)) +#define DEFER_IF_PERMIT2(state, class, fmt, a1, a2) \ + defer_if(&(state)->defer_if_permit, (class), (fmt), (a1), (a2)) /* resolve_pagein - page in an address resolver result */ static void *resolve_pagein(const char *addr, void *unused_context) @@ -584,6 +608,7 @@ void smtpd_check_init(void) mail_restrctions = smtpd_check_parse(var_mail_checks); rcpt_restrctions = smtpd_check_parse(var_rcpt_checks); etrn_restrctions = smtpd_check_parse(var_etrn_checks); + data_restrctions = smtpd_check_parse(var_data_checks); /* * Parse the pre-defined restriction classes. @@ -656,6 +681,21 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, int warn_if_reject; const char *whatsup; + /* + * defer_if_whatever has precedence over warn_if_reject, so as to + * minimize confusion. Bummer. There goes transparency. + */ + if (state->warn_if_reject && state->defer_if_reject.active) { + state->warn_if_reject = state->defer_if_reject.active = 0; + return (smtpd_check_reject(state, state->defer_if_reject.class, + "%s", STR(state->defer_if_reject.reason))); + } + if (state->warn_if_reject && state->defer_if_permit.active) { + state->warn_if_reject = state->defer_if_permit.active = 0; + return (smtpd_check_reject(state, state->defer_if_permit.class, + "%s", STR(state->defer_if_permit.reason))); + } + /* * Do not reject mail if we were asked to warn only. However, * configuration errors cannot be converted into warnings. @@ -691,7 +731,20 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, printable(STR(error_text), ' '); /* - * XXX The code below also appears in the SMTP server reply output + * Force this rejection into deferral because of some earlier temporary + * error that may have prevented us from accepting mail, and report the + * earlier problem instead. + */ + if (!warn_if_reject && state->defer_if_reject.active && STR(error_text)[0] == '5') { + state->warn_if_reject = state->defer_if_reject.active = 0; + return (smtpd_check_reject(state, state->defer_if_reject.class, + "%s", STR(state->defer_if_reject.reason))); + } + + /* + * Soft bounce safety net. + * + * XXX The code below also appears in the Postfix SMTP server reply output * routine. It is duplicated here in order to avoid discrepancies between * the reply codes that are shown in "reject" logging and the reply codes * that are actually sent to the SMTP client. @@ -707,15 +760,8 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, * the UCE restrictions only. This would be at odds with documentation * which says soft_bounce changes all 5xx replies into 4xx ones. */ - if (STR(error_text)[0] == '5') { - if (state->defer_if_reject) { - state->defer_if_reject = 0; - return (smtpd_check_reject(state, state->defer_class, - "%s", STR(state->defer_reason))); - } - if (var_soft_bounce) - STR(error_text)[0] = '4'; - } + if (var_soft_bounce && STR(error_text)[0] == '5') + STR(error_text)[0] = '4'; /* * Log what is happening. When the sysadmin discards policy violation @@ -727,36 +773,25 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, return (warn_if_reject ? 0 : SMTPD_CHECK_REJECT); } -/* defer_if_reject - prepare to change our mind */ - -static void defer_if_reject(SMTPD_STATE *state, int error_class, - const char *fmt,...) -{ - va_list ap; - - if (state->defer_reason == 0) - state->defer_reason = vstring_alloc(10); - state->defer_class = error_class; - va_start(ap, fmt); - vstring_vsprintf(state->defer_reason, fmt, ap); - va_end(ap); - state->defer_if_reject = 1; -} - -/* defer_if_permit - prepare to change our mind */ +/* defer_if - prepare to change our mind */ -static void defer_if_permit(SMTPD_STATE *state, int error_class, - const char *fmt,...) +static void defer_if(SMTPD_DEFER *defer, int error_class, const char *fmt,...) { va_list ap; - if (state->defer_reason == 0) - state->defer_reason = vstring_alloc(10); - state->defer_class = error_class; - va_start(ap, fmt); - vstring_vsprintf(state->defer_reason, fmt, ap); - va_end(ap); - state->defer_if_permit = 1; + /* + * Keep the first reason for this type of deferral, to minimize + * confusion. + */ + if (defer->active == 0) { + defer->active = 1; + defer->class = error_class; + if (defer->reason == 0) + defer->reason = vstring_alloc(10); + va_start(ap, fmt); + vstring_vsprintf(defer->reason, fmt, ap); + va_end(ap); + } } /* reject_dict_retry - reject with temporary failure if dict lookup fails */ @@ -1032,9 +1067,9 @@ static int reject_unknown_hostname(SMTPD_STATE *state, char *name, var_unk_name_code, reply_name, reply_class)); else if (dns_status != DNS_OK) - defer_if_permit(state, MAIL_ERROR_POLICY, - "450 <%s>: %s rejected: Host not found", - reply_name, reply_class); + DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY, + "450 <%s>: %s rejected: Host not found", + reply_name, reply_class); return (SMTPD_CHECK_DUNNO); } @@ -1057,9 +1092,9 @@ static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name, var_unk_addr_code, reply_name, reply_class)); else if (dns_status != DNS_OK) - defer_if_permit(state, MAIL_ERROR_POLICY, - "450 <%s>: %s rejected: Domain not found", - reply_name, reply_class); + DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY, + "450 <%s>: %s rejected: Domain not found", + reply_name, reply_class); return (SMTPD_CHECK_DUNNO); } @@ -1169,7 +1204,8 @@ static int reject_unauth_destination(SMTPD_STATE *state, char *recipient) /* reject_unauth_pipelining - reject improper use of SMTP command pipelining */ -static int reject_unauth_pipelining(SMTPD_STATE *state) +static int reject_unauth_pipelining(SMTPD_STATE *state, + const char *reply_name, const char *reply_class) { char *myname = "reject_unauth_pipelining"; @@ -1179,16 +1215,19 @@ static int reject_unauth_pipelining(SMTPD_STATE *state) if (state->client != 0 && SMTPD_STAND_ALONE(state) == 0 && vstream_peek(state->client) > 0 - && strcasecmp(state->protocol, "ESMTP") != 0) { + && (strcasecmp(state->protocol, "ESMTP") != 0 + || strcasecmp(state->where, "DATA") == 0)) { return (smtpd_check_reject(state, MAIL_ERROR_PROTOCOL, - "503 Improper use of SMTP command pipelining")); + "503 <%s>: %s rejected: Improper use of SMTP command pipelining", + reply_name, reply_class)); } return (SMTPD_CHECK_DUNNO); } /* all_auth_mx_addr - match host addresses against permit_mx_backup_networks */ -static int all_auth_mx_addr(SMTPD_STATE *state, char *host) +static int all_auth_mx_addr(SMTPD_STATE *state, char *host, + const char *reply_name, const char *reply_class) { char *myname = "all_auth_mx_addr"; struct in_addr addr; @@ -1210,9 +1249,9 @@ static int all_auth_mx_addr(SMTPD_STATE *state, char *host) */ dns_status = dns_lookup(host, T_A, 0, &addr_list, (VSTRING *) 0, (VSTRING *) 0); if (dns_status != DNS_OK) { - defer_if_reject(state, MAIL_ERROR_POLICY, - "450 Unable to look up host %s as mail exchanger: %s", - host, dns_strerror(h_errno)); + DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY, + "450 <%s>: %s rejected: Unable to look up host %s as mail exchanger", + reply_name, reply_class, host); return (NOPE); } for (rr = addr_list; rr != 0; rr = rr->next) { @@ -1244,7 +1283,8 @@ static int all_auth_mx_addr(SMTPD_STATE *state, char *host) /* has_my_addr - see if this host name lists one of my network addresses */ -static int has_my_addr(SMTPD_STATE *state, const char *host) +static int has_my_addr(SMTPD_STATE *state, const char *host, + const char *reply_name, const char *reply_class) { char *myname = "has_my_addr"; struct in_addr addr; @@ -1261,9 +1301,9 @@ static int has_my_addr(SMTPD_STATE *state, const char *host) #define NOPE 0 if ((hp = gethostbyname(host)) == 0) { - defer_if_reject(state, MAIL_ERROR_POLICY, - "450 Unable to look up host %s as mail exchanger: %s", - host, dns_strerror(h_errno)); + DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY, + "450 <%s>: %s rejected: Unable to look up mail exchanger host %s", + reply_name, reply_class, host); return (NOPE); } if (hp->h_addrtype != AF_INET || hp->h_length != sizeof(addr)) { @@ -1286,7 +1326,8 @@ static int has_my_addr(SMTPD_STATE *state, const char *host) /* i_am_mx - is this machine listed as MX relay */ -static int i_am_mx(SMTPD_STATE *state, DNS_RR *mx_list) +static int i_am_mx(SMTPD_STATE *state, DNS_RR *mx_list, + const char *reply_name, const char *reply_class) { const char *myname = "permit_mx_backup"; DNS_RR *mx; @@ -1308,7 +1349,7 @@ static int i_am_mx(SMTPD_STATE *state, DNS_RR *mx_list) for (mx = mx_list; mx != 0; mx = mx->next) { if (msg_verbose) msg_info("%s: address lookup: %s", myname, (char *) mx->data); - if (has_my_addr(state, (char *) mx->data)) + if (has_my_addr(state, (char *) mx->data, reply_name, reply_class)) return (YUP); } @@ -1322,7 +1363,8 @@ static int i_am_mx(SMTPD_STATE *state, DNS_RR *mx_list) /* permit_mx_primary - authorize primary MX relays */ -static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list) +static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list, + const char *reply_name, const char *reply_class) { DNS_RR *mx; unsigned int best_pref; @@ -1341,7 +1383,7 @@ static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list) for (mx = mx_list; mx != 0; mx = mx->next) { if (mx->pref != best_pref) continue; - if (!all_auth_mx_addr(state, (char *) mx->data)) + if (!all_auth_mx_addr(state, (char *) mx->data, reply_name, reply_class)) return (NOPE); } @@ -1349,12 +1391,13 @@ static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list) * All IP addresses of the best MX hosts are within * permit_mx_backup_networks. */ - return (mx_list ? YUP : NOPE); + return (YUP); } /* permit_mx_backup - permit use of me as MX backup for recipient domain */ -static int permit_mx_backup(SMTPD_STATE *state, const char *recipient) +static int permit_mx_backup(SMTPD_STATE *state, const char *recipient, + const char *reply_name, const char *reply_class) { char *myname = "permit_mx_backup"; const RESOLVE_REPLY *reply; @@ -1411,18 +1454,19 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient) dns_status = dns_lookup(domain, T_MX, 0, &mx_list, (VSTRING *) 0, (VSTRING *) 0); if (dns_status == DNS_NOTFOUND) - return (has_my_addr(state, domain) ? SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO); + return (has_my_addr(state, domain, reply_name, reply_class) ? + SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO); if (dns_status != DNS_OK) { - defer_if_reject(state, MAIL_ERROR_POLICY, - "450 Unable to look up mail exchanger information: %s", - dns_strerror(h_errno)); + DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY, + "450 <%s>: %s rejected: Unable to look up mail exchanger information", + reply_name, reply_class); return (SMTPD_CHECK_DUNNO); } /* * First, see if we match any of the MX host names listed. */ - if (!i_am_mx(state, mx_list)) { + if (!i_am_mx(state, mx_list, reply_name, reply_class)) { dns_rr_free(mx_list); return (SMTPD_CHECK_DUNNO); } @@ -1431,7 +1475,8 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient) * Optionally, see if the primary MX hosts are in a restricted list of * networks. */ - if (*var_perm_mx_networks && !permit_mx_primary(state, mx_list)) { + if (*var_perm_mx_networks + && !permit_mx_primary(state, mx_list, reply_name, reply_class)) { dns_rr_free(mx_list); return (SMTPD_CHECK_DUNNO); } @@ -1569,18 +1614,24 @@ static int check_table_result(SMTPD_STATE *state, const char *table, */ #define FILTER_LEN (sizeof("FILTER") - 1) - if (strncasecmp(value, "FILTER", FILTER_LEN) == 0 - && (value[FILTER_LEN] == 0 || ISSPACE(value[FILTER_LEN]))) { - value += FILTER_LEN; - while (ISSPACE(*value)) - value++; - vstring_sprintf(error_text, "<%s>: %s triggers FILTER %s", - reply_name, reply_class, value); - log_whatsup(state, "hold", STR(error_text)); + if (strncasecmp(value, "FILTER", FILTER_LEN) == 0) { + if (value[FILTER_LEN] == 0) { + msg_warn("access map %s entry %s has FILTER entry without value", + table, datum); + return (SMTPD_CHECK_DUNNO); + } + if (ISSPACE(value[FILTER_LEN])) { + value += FILTER_LEN; + while (ISSPACE(*value)) + value++; + vstring_sprintf(error_text, "<%s>: %s triggers FILTER %s", + reply_name, reply_class, value); + log_whatsup(state, "filter", STR(error_text)); #ifndef TEST - rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", value); + rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", value); #endif - return (SMTPD_CHECK_DUNNO); + return (SMTPD_CHECK_DUNNO); + } } /* @@ -2164,7 +2215,16 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, * Spoof the is_map_command() routine, so that we do not have to make * special cases for the implicit short-hand access map notation. */ +#define NO_DEF_ACL 0 + if (strchr(name, ':') != 0) { + if (def_acl == NO_DEF_ACL) { + msg_warn("specify one of (%s, %s, %s, %s, %s) before restriction \"%s\"", + CHECK_CLIENT_ACL, CHECK_HELO_ACL, CHECK_SENDER_ACL, + CHECK_RECIP_ACL, CHECK_ETRN_ACL, name); + longjmp(smtpd_check_buf, smtpd_check_reject(state, + MAIL_ERROR_SOFTWARE, "451 Server configuration error")); + } name = def_acl; cpp -= 1; } @@ -2192,7 +2252,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, msg_warn("restriction `%s' after `%s' is ignored", cpp[1], REJECT_ALL); } else if (strcasecmp(name, REJECT_UNAUTH_PIPE) == 0) { - status = reject_unauth_pipelining(state); + status = reject_unauth_pipelining(state, reply_name, reply_class); } /* @@ -2295,7 +2355,8 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, SMTPD_NAME_RECIPIENT, def_acl); } else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) { if (state->recipient) - status = permit_mx_backup(state, state->recipient); + status = permit_mx_backup(state, state->recipient, + state->recipient, SMTPD_NAME_RECIPIENT); } else if (strcasecmp(name, PERMIT_AUTH_DEST) == 0) { if (state->recipient) status = permit_auth_destination(state, state->recipient); @@ -2361,16 +2422,25 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, if (status != 0) break; + + if (state->defer_if_permit.active && state->defer_if_reject.active) + break; } if (msg_verbose && name == 0) msg_info("%s: END", myname); state->recursion = saved_recursion; - if (status == SMTPD_CHECK_OK || status == SMTPD_CHECK_DUNNO) - if (state->defer_if_permit) - status = smtpd_check_reject(state, state->defer_class, - "%s", STR(state->defer_reason)); + /* + * Force this permission into deferral because of some earlier temporary + * error that may have prevented us from rejecting mail, and report the + * earlier problem instead. + */ + if (status == SMTPD_CHECK_OK || status == SMTPD_CHECK_DUNNO) { + if (state->defer_if_permit.active) + status = smtpd_check_reject(state, state->defer_if_permit.class, + "%s", STR(state->defer_if_permit.reason)); + } return (status); } @@ -2386,13 +2456,17 @@ char *smtpd_check_client(SMTPD_STATE *state) if (state->name == 0 || state->addr == 0) return (0); +#define SMTPD_CHECK_RESET() { \ + state->recursion = 1; \ + state->warn_if_reject = 0; \ + state->defer_if_reject.active = 0; \ + state->defer_if_permit.active = 0; \ + } + /* * Apply restrictions in the order as specified. */ - state->recursion = 1; - state->warn_if_reject = 0; - state->defer_if_reject = 0; - state->defer_if_permit = 0; + SMTPD_CHECK_RESET(); status = setjmp(smtpd_check_buf); if (status == 0 && client_restrctions->argc) status = generic_checks(state, client_restrctions, state->namaddr, @@ -2420,11 +2494,11 @@ char *smtpd_check_helo(SMTPD_STATE *state, char *helohost) */ #define SMTPD_CHECK_PUSH(backup, current, new) { \ backup = current; \ - current = mystrdup(new); \ + current = (new ? mystrdup(new) : new); \ } #define SMTPD_CHECK_POP(current, backup) { \ - myfree(current); \ + if (current) myfree(current); \ current = backup; \ } @@ -2438,10 +2512,7 @@ char *smtpd_check_helo(SMTPD_STATE *state, char *helohost) /* * Apply restrictions in the order as specified. */ - state->recursion = 1; - state->warn_if_reject = 0; - state->defer_if_reject = 0; - state->defer_if_permit = 0; + SMTPD_CHECK_RESET(); status = setjmp(smtpd_check_buf); if (status == 0 && helo_restrctions->argc) status = generic_checks(state, helo_restrctions, state->helo_name, @@ -2477,10 +2548,7 @@ char *smtpd_check_mail(SMTPD_STATE *state, char *sender) /* * Apply restrictions in the order as specified. */ - state->recursion = 1; - state->warn_if_reject = 0; - state->defer_if_reject = 0; - state->defer_if_permit = 0; + SMTPD_CHECK_RESET(); status = setjmp(smtpd_check_buf); if (status == 0 && mail_restrctions->argc) status = generic_checks(state, mail_restrctions, sender, @@ -2534,10 +2602,7 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) /* * Apply restrictions in the order as specified. */ - state->recursion = 1; - state->warn_if_reject = 0; - state->defer_if_reject = 0; - state->defer_if_permit = 0; + SMTPD_CHECK_RESET(); status = setjmp(smtpd_check_buf); if (status == 0 && rcpt_restrctions->argc) status = generic_checks(state, rcpt_restrctions, @@ -2582,10 +2647,7 @@ char *smtpd_check_etrn(SMTPD_STATE *state, char *domain) /* * Apply restrictions in the order as specified. */ - state->recursion = 1; - state->warn_if_reject = 0; - state->defer_if_reject = 0; - state->defer_if_permit = 0; + SMTPD_CHECK_RESET(); status = setjmp(smtpd_check_buf); if (status == 0 && etrn_restrctions->argc) status = generic_checks(state, etrn_restrctions, domain, @@ -2747,6 +2809,34 @@ char *smtpd_check_size(SMTPD_STATE *state, off_t size) return (0); } +/* smtpd_check_data - check DATA command */ + +char *smtpd_check_data(SMTPD_STATE *state) +{ + int status; + char *saved_recipient; + + /* + * Minor kluge so that we can delegate work to the generic routine. We + * provide no recipient information, because this restriction applies to + * all recipients alike. Picking a specific recipient would be wrong. + */ + SMTPD_CHECK_PUSH(saved_recipient, state->recipient, 0); + + /* + * Apply restrictions in the order as specified. + * + * XXX We cannot specify a default target for a bare access map. + */ + SMTPD_CHECK_RESET(); + status = setjmp(smtpd_check_buf); + if (status == 0 && data_restrctions->argc) + status = generic_checks(state, data_restrctions, + "DATA", SMTPD_NAME_DATA, NO_DEF_ACL); + + SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); +} + #ifdef TEST /* @@ -2772,6 +2862,7 @@ char *var_helo_checks = ""; char *var_mail_checks = ""; char *var_rcpt_checks = ""; char *var_etrn_checks = ""; +char *var_data_checks = ""; char *var_relay_domains = ""; char *var_mynetworks = ""; char *var_notify_classes = ""; diff --git a/postfix/src/smtpd/smtpd_check.h b/postfix/src/smtpd/smtpd_check.h index 00f923a8f..60348c40d 100644 --- a/postfix/src/smtpd/smtpd_check.h +++ b/postfix/src/smtpd/smtpd_check.h @@ -20,6 +20,7 @@ extern char *smtpd_check_rcptmap(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 *); +extern char *smtpd_check_data(SMTPD_STATE *); /* LICENSE /* .ad diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index cc07eeeaf..5139d1b1c 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -91,7 +91,8 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream) state->recursion = 0; state->msg_size = 0; state->junk_cmds = 0; - state->defer_reason = 0; + state->defer_if_reject.reason = 0; + state->defer_if_permit.reason = 0; #ifdef USE_SASL_AUTH if (SMTPD_STAND_ALONE(state)) @@ -124,8 +125,10 @@ void smtpd_state_reset(SMTPD_STATE *state) if (state->buffer) vstring_free(state->buffer); smtpd_peer_reset(state); - if (state->defer_reason) - vstring_free(state->defer_reason); + if (state->defer_if_permit.reason) + vstring_free(state->defer_if_permit.reason); + if (state->defer_if_reject.reason) + vstring_free(state->defer_if_reject.reason); #ifdef USE_SASL_AUTH if (var_smtpd_sasl_enable) diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/util/dict_ldap.c index 9eafc6613..7b2333b6b 100644 --- a/postfix/src/util/dict_ldap.c +++ b/postfix/src/util/dict_ldap.c @@ -302,10 +302,15 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) &dict_ldap->version) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to set LDAP protocol version", myname); - if (msg_verbose) - msg_warn("%s: Actual Protocol version used was %d.", - myname, ldap_get_option(dict_ldap->ld, - LDAP_OPT_PROTOCOL_VERSION, (int *) dict_ldap->version)); + if (msg_verbose) { + if (ldap_get_option(dict_ldap->ld, + LDAP_OPT_PROTOCOL_VERSION, + &dict_ldap->version) != LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to get LDAP protocol version", myname); + else + msg_warn("%s: Actual Protocol version used was %d.", + myname, dict_ldap->version); + } #endif /*