From 6ac89aab2c7de8f2a54e4b38b1de988a8c6dca4f Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Sun, 20 May 2001 00:00:00 -0500 Subject: [PATCH] snapshot-20010520 --- postfix/HISTORY | 42 +++++++++++++ postfix/conf/main.cf | 6 +- postfix/conf/sample-misc.cf | 6 +- postfix/html/faq.html | 5 +- postfix/html/sendmail.1.html | 16 ++--- postfix/man/man1/sendmail.1 | 13 ++-- postfix/src/bounce/bounce_notify_service.c | 9 +-- postfix/src/cleanup/cleanup_message.c | 51 +++++++++++++++ postfix/src/global/Makefile.in | 8 +-- postfix/src/global/is_header.c | 7 +++ postfix/src/global/mail_params.c | 6 ++ postfix/src/global/mail_params.h | 11 ++++ postfix/src/global/mail_queue.c | 7 +++ postfix/src/global/mail_version.h | 2 +- postfix/src/global/post_mail.c | 21 +++---- postfix/src/global/post_mail.h | 5 +- postfix/src/global/smtp_stream.c | 3 + postfix/src/global/tok822.h | 3 +- postfix/src/global/tok822_node.c | 2 +- postfix/src/global/tok822_parse.c | 64 ++++++++----------- postfix/src/global/tok822_parse.ref | 14 ++--- postfix/src/lmtp/lmtp.c | 4 -- postfix/src/lmtp/lmtp_chat.c | 2 +- postfix/src/postsuper/Makefile.in | 2 + postfix/src/postsuper/postsuper.c | 38 +++++++---- postfix/src/sendmail/sendmail.c | 13 ++-- postfix/src/smtp/smtp.c | 4 -- postfix/src/smtp/smtp_addr.c | 18 ++++++ postfix/src/smtp/smtp_chat.c | 2 +- postfix/src/smtp/smtp_proto.c | 14 ++++- postfix/src/smtpd/smtpd.c | 73 +++++++++++++++++++--- postfix/src/smtpd/smtpd_chat.c | 2 +- postfix/src/smtpd/smtpd_check.c | 27 ++++++++ 33 files changed, 365 insertions(+), 135 deletions(-) diff --git a/postfix/HISTORY b/postfix/HISTORY index 408259629..8182a3e17 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -5125,3 +5125,45 @@ Apologies for any names omitted. Code cleanup: in order to make postsuper -d more usable, the showq command was extended to safely list the possibly world-writable maildrop directory. File: showq/showq.c. + +20010505 + + RFC 2821 feature: an SMTP server must accept a recipient + address of "postmaster" without domain name. File: + smtpd/smtpd_check.c. + + RFC 2821 recommendation: reply with 503 to commands sent + after 554 greeting. File: smtpd/smtpd.c. + + RFC 2821 recommendation: if VRFY is enabled, list it in + the EHLO response. + +20010507 + + Bugfix: with soft_bounce=yes, the SMTP server would log + 5xx replies even though it would send 4xx replies to the + client (Phil Howard, ipal.net). File: smtpd/smtpd_check.c. + +20010515 + + Compatibility: Microsoft sends "AUTH=MBS_BASIC LOGIN". + Updated the parsing code in smtp/smtp_proto.c. Problem + reported by Ralf Tessmann, Godot GmbH. + +20010520 + + Standard: deleted the "via" portion from Received: headers + generated by Postfix bounce or other notification processes. + File: global/post_mail.c. + + Robustness: eliminated stack-based recursion from the RFC + 822 address parser. File: global/tok822_parse.c. + + Standard: annotated the source code with comments based on + RFC 2821 and 2822. Not all the changes make sense. + + Cleanup: moved ownership of the debug_peer parameters from + the applications to the library, so that a Postfix shared + library does not suffer from undefined references. Files: + smtp/smtp.c, lmtp/lmtp.c, smtpd/smtpd.c, global/mail_params.c. + LaMont Jones, for Debian. diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 034060318..1612b1d45 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -98,8 +98,10 @@ mail_owner = postfix #inet_interfaces = $myhostname, localhost # The mydestination parameter specifies the list of domains that this -# machine considers itself the final destination for. That does not -# include domains that are hosted on this machine. Those domains are +# machine considers itself the final destination for. That includes +# Sendmail-style virtual domains hosted on this machine. +# +# Do not include Postfix-style virtual domains - those domains are # specified elsewhere (see sample-virtual.cf, and sample-transport.cf). # # The default is $myhostname + localhost.$mydomain. On a mail domain diff --git a/postfix/conf/sample-misc.cf b/postfix/conf/sample-misc.cf index 6de986725..16ca0d37f 100644 --- a/postfix/conf/sample-misc.cf +++ b/postfix/conf/sample-misc.cf @@ -165,7 +165,11 @@ max_idle = 100s max_use = 100 # The mydestination parameter specifies the list of domains that this -# machine considers itself the final destination for. +# machine considers itself the final destination for. That includes +# Sendmail-style virtual domains hosted on this machine. +# +# Do not include Postfix-style virtual domains - those domains are +# specified elsewhere (see sample-virtual.cf, and sample-transport.cf). # # The default is $myhostname + localhost.$mydomain. On a mail domain # gateway, you should also include $mydomain. Do not specify the diff --git a/postfix/html/faq.html b/postfix/html/faq.html index 0f2764aca..47f4d9ec5 100644 --- a/postfix/html/faq.html +++ b/postfix/html/faq.html @@ -1366,8 +1366,9 @@ record, and that this one PTR record needs a matching A record. Some people read the RFCs such that one IP address can have multiple PTR records, but that makes PTR records even less useful than they already are. And in any case, having multiple names per IP address -would only worsen the problem of finding out the "official name" -of a machine's IP address. +only worsens the problem of finding out the SMTP client hostname. + +

Help! Postfix is an open relay

diff --git a/postfix/html/sendmail.1.html b/postfix/html/sendmail.1.html index 43b48ba14..704fae308 100644 --- a/postfix/html/sendmail.1.html +++ b/postfix/html/sendmail.1.html @@ -43,14 +43,14 @@ SENDMAIL(1) SENDMAIL(1) daemon. newaliases - Initialize the alias database. If no alias database - type is specified, the program uses the type speci- - fied in the database_type configuration parameter; - if no input file is specified, the program pro- - cesses the file(s) specified with the - alias_database configuration parameter. This mode - of operation is implemented by running the postal- - ias(1) command. + Initialize the alias database. If no input file is + specified (with the -oA option, see below), the + program processes the file(s) specified with the + alias_database configuration parameter. If no + alias database type is specified, the program uses + the type specified with the database_type configu- + ration parameter. This mode of operation is imple- + mented by running the
postalias(1) command. Note: it may take a minute or so before an alias database update becomes visible. Use the postfix diff --git a/postfix/man/man1/sendmail.1 b/postfix/man/man1/sendmail.1 index 51fee8715..b7dade23f 100644 --- a/postfix/man/man1/sendmail.1 +++ b/postfix/man/man1/sendmail.1 @@ -40,12 +40,13 @@ be delivered. If mail could not be delivered upon the last attempt, the reason for failure is shown. This mode of operation is implemented by connecting to the \fBshowq\fR(8) daemon. .IP \fBnewaliases\fR -Initialize the alias database. If no alias database type is -specified, the program uses the type specified in the -\fBdatabase_type\fR configuration parameter; if no input file -is specified, the program processes the file(s) specified with the -\fBalias_database\fR configuration parameter. This mode of operation -is implemented by running the \fBpostalias\fR(1) command. +Initialize the alias database. If no input file is specified (with +the \fB-oA\fR option, see below), the program processes the file(s) +specified with the \fBalias_database\fR configuration parameter. +If no alias database type is specified, the program uses the type +specified with the \fBdatabase_type\fR configuration parameter. +This mode of operation is implemented by running the \fBpostalias\fR(1) +command. .sp Note: it may take a minute or so before an alias database update becomes visible. Use the \fBpostfix reload\fR command to eliminate diff --git a/postfix/src/bounce/bounce_notify_service.c b/postfix/src/bounce/bounce_notify_service.c index 35e10a45c..284b6468e 100644 --- a/postfix/src/bounce/bounce_notify_service.c +++ b/postfix/src/bounce/bounce_notify_service.c @@ -138,8 +138,7 @@ int bounce_notify_service(char *service, char *queue_name, postmaster = flush ? var_2bounce_rcpt : var_delay_rcpt; if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(), postmaster, - NULL_CLEANUP_FLAGS, - "BOUNCE")) != 0) { + NULL_CLEANUP_FLAGS)) != 0) { /* * Double bounce to Postmaster. This is the last opportunity @@ -163,8 +162,7 @@ int bounce_notify_service(char *service, char *queue_name, */ else { if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient, - NULL_CLEANUP_FLAGS, - "BOUNCE")) != 0) { + NULL_CLEANUP_FLAGS)) != 0) { /* * Send the bounce message header, some boilerplate text that @@ -203,8 +201,7 @@ int bounce_notify_service(char *service, char *queue_name, postmaster = flush ? var_bounce_rcpt : var_delay_rcpt; if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(), postmaster, - NULL_CLEANUP_FLAGS, - "BOUNCE")) != 0) { + NULL_CLEANUP_FLAGS)) != 0) { if (bounce_header(bounce, bounce_info, postmaster) == 0 && bounce_diagnostic_log(bounce, bounce_info) == 0 && bounce_header_dsn(bounce, bounce_info) == 0 diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c index 20a4c1cf5..2c84ff60c 100644 --- a/postfix/src/cleanup/cleanup_message.c +++ b/postfix/src/cleanup/cleanup_message.c @@ -287,6 +287,29 @@ static void cleanup_header(CLEANUP_STATE *state) * we should do with this header: delete, count, rewrite. Note that we * should examine headers even when they will be deleted from the output, * because the addresses in those headers might be needed elsewhere. + * + * XXX 2821: Return-path breakage. + * + * RFC 821 specifies: When the receiver-SMTP makes the "final delivery" of a + * message it inserts at the beginning of the mail data a return path + * line. The return path line preserves the information in the + * from the MAIL command. Here, final delivery means the + * message leaves the SMTP world. Normally, this would mean it has been + * delivered to the destination user, but in some cases it may be further + * processed and transmitted by another mail system. + * + * And that is what Postfix implements. Delivery agents prepend + * Return-Path:. In order to avoid cluttering up the message with + * possibly inconsistent Return-Path: information (the sender can change + * as the result of mail forwarding or mailing list delivery), Postfix + * removes any existing Return-Path: headers. + * + * RFC 2821 Section 4.4 specifies: A message-originating SMTP system + * SHOULD NOT send a message that already contains a Return-path header. + * SMTP servers performing a relay function MUST NOT inspect the message + * data, and especially not to the extent needed to determine if + * Return-path headers are present. SMTP servers making final delivery + * MAY remove Return-path headers before adding their own. */ else { state->headers_seen |= (1 << hdr_opts->type); @@ -324,6 +347,11 @@ static void cleanup_missing_headers(CLEANUP_STATE *state) /* * Add a missing (Resent-)Message-Id: header. The message ID gives the * time in GMT units, plus the local queue ID. + * + * XXX Message-Id is not a required message header (RFC 822 and RFC 2822). + * + * XXX It is the queue ID non-inode bits that prevent messages from getting + * the same Message-Id within the same second. */ if ((state->headers_seen & (1 << (state->resent[0] ? HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) { @@ -365,6 +393,29 @@ static void cleanup_missing_headers(CLEANUP_STATE *state) CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2); } + /* + * XXX 2821: Appendix B: The return address in the MAIL command SHOULD, + * if possible, be derived from the system's identity for the submitting + * (local) user, and the "From:" header field otherwise. If there is a + * system identity available, it SHOULD also be copied to the Sender + * header field if it is different from the address in the From header + * field. (Any Sender field that was already there SHOULD be removed.) + * Similar wording appears in RFC 2822 section 3.6.2. + * + * Postfix presently does not insert a Sender: header if envelope and From: + * address differ. Older Postfix versions assumed that the envelope + * sender address specifies the system identity and inserted Sender: + * whenever envelope and From: differed. This was wrong with relayed + * mail, and was often not even desirable with original submissions. + * + * XXX 2822 Section 3.6.2, as well as RFC 822 Section 4.1: FROM headers can + * contain multiple addresses. If this is the case, then a Sender: header + * must be provided with a single address. + * + * Postfix does not count the number of addresses in a From: header + * (although doing so is trivial, once the address is parsed). + */ + /* * Add a missing destination header. */ diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index c7f777ef4..053ea4e92 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -12,7 +12,7 @@ SRCS = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \ mail_scan_dir.c mail_stream.c mail_task.c mail_trigger.c maps.c \ mark_corrupt.c mkmap_db.c mkmap_dbm.c mkmap_open.c mynetworks.c \ mypwd.c namadr_list.c off_cvt.c opened.c own_inet_addr.c \ - peer_name.c pipe_command.c post_mail.c quote_821_local.c \ + pipe_command.c post_mail.c quote_821_local.c \ quote_822_local.c rec_streamlf.c rec_type.c recipient_list.c \ record.c remove.c resolve_clnt.c resolve_local.c rewrite_clnt.c \ sent.c smtp_stream.c split_addr.c string_list.c sys_exits.c \ @@ -32,7 +32,7 @@ OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \ mail_scan_dir.o mail_stream.o mail_task.o mail_trigger.o maps.o \ mark_corrupt.o mkmap_db.o mkmap_dbm.o mkmap_open.o mynetworks.o \ mypwd.o namadr_list.o off_cvt.o opened.o own_inet_addr.o \ - peer_name.o pipe_command.o post_mail.o quote_821_local.o \ + pipe_command.o post_mail.o quote_821_local.o \ quote_822_local.o rec_streamlf.o rec_type.o recipient_list.o \ record.o remove.o resolve_clnt.o resolve_local.o rewrite_clnt.o \ sent.o smtp_stream.o split_addr.o string_list.o sys_exits.o \ @@ -49,7 +49,7 @@ HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \ mail_proto.h mail_queue.h mail_run.h mail_scan_dir.h mail_stream.h \ mail_task.h mail_version.h maps.h mark_corrupt.h mkmap.h \ mynetworks.h mypwd.h namadr_list.h off_cvt.h opened.h \ - own_inet_addr.h peer_name.h pipe_command.h post_mail.h \ + own_inet_addr.h pipe_command.h post_mail.h \ quote_821_local.h quote_822_local.h rec_streamlf.h rec_type.h \ recipient_list.h record.h resolve_clnt.h resolve_local.h \ rewrite_clnt.h sent.h smtp_stream.h split_addr.h string_list.h \ @@ -65,7 +65,7 @@ INCL = LIB = libglobal.a TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \ mail_addr_map mail_date maps mynetworks mypwd namadr_list \ - off_cvt peer_name quote_822_local rec2stream recdump resolve_clnt \ + off_cvt quote_822_local rec2stream recdump resolve_clnt \ resolve_local rewrite_clnt stream2rec string_list tok822_parse \ quote_821_local mail_conf_time diff --git a/postfix/src/global/is_header.c b/postfix/src/global/is_header.c index 0cd3e93c2..5ed88fadd 100644 --- a/postfix/src/global/is_header.c +++ b/postfix/src/global/is_header.c @@ -41,6 +41,13 @@ int is_header(const char *str) const char *cp; int c; + /* + * XXX RFC 2822 Section 4.5.2, Obsolete header fields: whitespace may + * appear between header label and ":" (see: RFC 822, Section 3.4.2.). + * + * The code below allows no such whitespace. This has never been a problem, + * and therefore we're not inclined to add code for it. + */ for (cp = str; (c = *(unsigned char *) cp) != 0; cp++) { if (c == ':') return (cp > str); diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 82db3bd84..830e9b1d9 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -65,6 +65,8 @@ /* /* char *var_import_environ; /* char *var_export_environ; +/* char *var_debug_peer_list; +/* int var_debug_peer_level; /* /* void mail_params_init() /* DESCRIPTION @@ -181,6 +183,8 @@ char *var_mynetworks_style; char *var_import_environ; char *var_export_environ; +char *var_debug_peer_list; +int var_debug_peer_level; /* check_myhostname - lookup hostname and validate */ @@ -298,6 +302,7 @@ void mail_params_init() VAR_IMPORT_ENVIRON, DEF_IMPORT_ENVIRON, &var_import_environ, 0, 0, VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 0, 0, VAR_MYNETWORKS_STYLE, DEF_MYNETWORKS_STYLE, &var_mynetworks_style, 1, 0, + VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0, 0, }; static CONFIG_STR_FN_TABLE function_str_defaults_2[] = { @@ -312,6 +317,7 @@ void mail_params_init() VAR_HASH_QUEUE_DEPTH, DEF_HASH_QUEUE_DEPTH, &var_hash_queue_depth, 1, 0, VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0, VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0, + VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, 0, }; static CONFIG_TIME_TABLE time_defaults[] = { diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index caea36103..452fc443c 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -224,6 +224,10 @@ extern char *var_always_bcc; /* * What to put in the To: header when no recipients were disclosed. + * + * XXX 2822: When no recipient headers remain, a system should insert a Bcc: + * header without additional information. That is not so great given that + * MTAs routinely strip Bcc: headers from message headers. */ #define VAR_RCPT_WITHELD "undisclosed_recipients_header" #define DEF_RCPT_WITHELD "To: undisclosed-recipients:;" @@ -634,6 +638,9 @@ extern int var_hash_queue_depth; * determines how many recipient addresses the SMTP client sends along with * each message. Unfortunately, some mailers misbehave and disconnect (smap) * when given more recipients than they are willing to handle. + * + * XXX 2821: A mail system is supposed to use EHLO instead of HELO, and to fall + * back to HELO if EHLO is not supported. */ #define VAR_BESTMX_TRANSP "best_mx_transport" #define DEF_BESTMX_TRANSP "" @@ -688,7 +695,11 @@ extern bool var_ign_mx_lookup_err; extern bool var_skip_quit_resp; #define VAR_SMTP_ALWAYS_EHLO "smtp_always_send_ehlo" +#ifdef RFC821_SYNTAX #define DEF_SMTP_ALWAYS_EHLO 0 +#else +#define DEF_SMTP_ALWAYS_EHLO 1 +#endif extern bool var_smtp_always_ehlo; #define VAR_SMTP_NEVER_EHLO "smtp_never_send_ehlo" diff --git a/postfix/src/global/mail_queue.c b/postfix/src/global/mail_queue.c index 5fc18640e..d58bade6c 100644 --- a/postfix/src/global/mail_queue.c +++ b/postfix/src/global/mail_queue.c @@ -378,6 +378,13 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode) file_id = get_file_id(fd); GETTIMEOFDAY(&tv); + /* + * XXX Some systems seem to have clocks that correlate with process + * scheduling or something. Unfortunately, we cannot add random + * quantities to the time, because the non-inode part of a queue ID must + * not repeat within the same second. The queue ID is the sole thing that + * prevents multiple messages from getting the same Message-ID value. + */ for (count = 0;; count++) { vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id); mail_queue_path(path_buf, queue_name, STR(id_buf)); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index bb1b805ad..1d8829a49 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20010502" +#define DEF_MAIL_VERSION "Snapshot-20010520" extern char *var_mail_version; /* LICENSE diff --git a/postfix/src/global/post_mail.c b/postfix/src/global/post_mail.c index c3673243f..91f5a608e 100644 --- a/postfix/src/global/post_mail.c +++ b/postfix/src/global/post_mail.c @@ -6,17 +6,15 @@ /* SYNOPSIS /* #include /* -/* VSTREAM *post_mail_fopen(sender, recipient, flags, via) +/* VSTREAM *post_mail_fopen(sender, recipient, flags) /* const char *sender; /* const char *recipient; /* int flags; -/* const char *via; /* -/* VSTREAM *post_mail_fopen_nowait(sender, recipient, flags, via) +/* VSTREAM *post_mail_fopen_nowait(sender, recipient, flags) /* const char *sender; /* const char *recipient; /* int flags; -/* const char *via; /* /* int post_mail_fprintf(stream, format, ...) /* VSTREAM *stream; @@ -132,7 +130,7 @@ /* post_mail_init - initial negotiations */ static void post_mail_init(VSTREAM *stream, const char *sender, - const char *recipient, int flags, const char *via) + const char *recipient, int flags) { VSTRING *id = vstring_alloc(100); long now = time((time_t *) 0); @@ -158,8 +156,8 @@ static void post_mail_init(VSTREAM *stream, const char *sender, * Do the Received: and Date: header lines. This allows us to shave a few * cycles by using the expensive date conversion result for both. */ - post_mail_fprintf(stream, "Received: by %s (%s) via %s", - var_myhostname, var_mail_name, via); + post_mail_fprintf(stream, "Received: by %s (%s)", + var_myhostname, var_mail_name); post_mail_fprintf(stream, "\tid %s; %s", vstring_str(id), date); post_mail_fprintf(stream, "Date: %s", date); vstring_free(id); @@ -167,26 +165,25 @@ static void post_mail_init(VSTREAM *stream, const char *sender, /* post_mail_fopen - prepare for posting a message */ -VSTREAM *post_mail_fopen(const char *sender, const char *recipient, - int flags, const char *via) +VSTREAM *post_mail_fopen(const char *sender, const char *recipient, int flags) { VSTREAM *stream; stream = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP); - post_mail_init(stream, sender, recipient, flags, via); + post_mail_init(stream, sender, recipient, flags); return (stream); } /* post_mail_fopen_nowait - prepare for posting a message */ VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient, - int flags, const char *via) + int flags) { VSTREAM *stream; if ((stream = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP, BLOCKING)) != 0) - post_mail_init(stream, sender, recipient, flags, via); + post_mail_init(stream, sender, recipient, flags); return (stream); } diff --git a/postfix/src/global/post_mail.h b/postfix/src/global/post_mail.h index 8fa5a80bf..264afc77b 100644 --- a/postfix/src/global/post_mail.h +++ b/postfix/src/global/post_mail.h @@ -24,9 +24,8 @@ /* * External interface. */ -extern VSTREAM *post_mail_fopen(const char *, const char *, int, const char *); -extern VSTREAM *post_mail_fopen_nowait(const char *, const char *, - int, const char *); +extern VSTREAM *post_mail_fopen(const char *, const char *, int); +extern VSTREAM *post_mail_fopen_nowait(const char *, const char *, int); extern int PRINTFLIKE(2, 3) post_mail_fprintf(VSTREAM *, const char *,...); extern int post_mail_fputs(VSTREAM *, const char *); extern int post_mail_buffer(VSTREAM *, const char *, int); diff --git a/postfix/src/global/smtp_stream.c b/postfix/src/global/smtp_stream.c index 606ffb65f..b919cfaa8 100644 --- a/postfix/src/global/smtp_stream.c +++ b/postfix/src/global/smtp_stream.c @@ -200,6 +200,9 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound) * Allow for partial long lines (we will read the remainder later) and * allow for lines ending in bare LF. The idea is to be liberal in what * we accept, strict in what we send. + * + * XXX 2821: Section 4.1.1.4 says that an SMTP server must not recognize + * bare LF as record terminator. */ smtp_timeout_reset(stream); last_char = (bound == 0 ? vstring_get(vp, stream) : diff --git a/postfix/src/global/tok822.h b/postfix/src/global/tok822.h index c0dd10f5c..316dfb198 100644 --- a/postfix/src/global/tok822.h +++ b/postfix/src/global/tok822.h @@ -45,8 +45,7 @@ typedef struct TOK822 { #define TOK822_DOMLIT 259 /* stuff between [] not nesting */ #define TOK822_ADDR 260 /* actually a token group */ #define TOK822_STARTGRP 261 /* start of named group */ -#define TOK822_COMMENT_TEXT 262 /* comment text */ -#define TOK822_MAXTOK 262 +#define TOK822_MAXTOK 261 /* * tok822_node.c diff --git a/postfix/src/global/tok822_node.c b/postfix/src/global/tok822_node.c index 797deb342..d39bf15b9 100644 --- a/postfix/src/global/tok822_node.c +++ b/postfix/src/global/tok822_node.c @@ -57,7 +57,7 @@ TOK822 *tok822_alloc(int type, const char *strval) TOK822 *tp; #define CONTAINER_TOKEN(x) \ - ((x) == TOK822_ADDR || (x) == TOK822_COMMENT || (x) == TOK822_STARTGRP) + ((x) == TOK822_ADDR || (x) == TOK822_STARTGRP) tp = (TOK822 *) mymalloc(sizeof(*tp)); tp->type = type; diff --git a/postfix/src/global/tok822_parse.c b/postfix/src/global/tok822_parse.c index 7fd66830a..4bb54dc5d 100644 --- a/postfix/src/global/tok822_parse.c +++ b/postfix/src/global/tok822_parse.c @@ -192,12 +192,7 @@ VSTRING *tok822_internalize(VSTRING *vp, TOK822 *tree, int flags) tok822_internalize(vp, tp->head, TOK822_STR_NONE); break; case TOK822_COMMENT: - VSTRING_ADDCH(vp, '('); - tok822_internalize(vp, tp->head, TOK822_STR_NONE); - VSTRING_ADDCH(vp, ')'); - break; case TOK822_ATOM: - case TOK822_COMMENT_TEXT: case TOK822_QSTRING: vstring_strcat(vp, vstring_str(tp->vstr)); break; @@ -244,15 +239,8 @@ VSTRING *tok822_externalize(VSTRING *vp, TOK822 *tree, int flags) tok822_externalize(vp, tp->head, TOK822_STR_NONE); break; case TOK822_ATOM: - vstring_strcat(vp, vstring_str(tp->vstr)); - break; case TOK822_COMMENT: - VSTRING_ADDCH(vp, '('); - tok822_externalize(vp, tp->head, TOK822_STR_NONE); - VSTRING_ADDCH(vp, ')'); - break; - case TOK822_COMMENT_TEXT: - tok822_copy_quoted(vp, vstring_str(tp->vstr), "()\\"); + vstring_strcat(vp, vstring_str(tp->vstr)); break; case TOK822_QSTRING: VSTRING_ADDCH(vp, '"'); @@ -321,6 +309,13 @@ TOK822 *tok822_scan(const char *str, TOK822 **tailp) TOK822 *tp; int ch; + /* + * XXX 2822 new feature: Section 4.1 allows "." to appear in a phrase (to + * allow for forms such as: Johnny B. Goode . I cannot + * handle that at the tokenizer level - it is not context sensitive. And + * to fix this at the parser level requires radical changes to preserve + * white space as part of the token stream. Thanks a lot, people. + */ while ((ch = *(unsigned char *) str++) != 0) { if (ISSPACE(ch)) continue; @@ -466,36 +461,32 @@ static void tok822_quote_atom(TOK822 *tp) static const char *tok822_comment(TOK822 *tp, const char *str) { - TOK822 *tc = 0; + int level = 1; int ch; -#define COMMENT_TEXT_TOKEN(t) ((t) && (t)->type == TOK822_COMMENT_TEXT) - -#define APPEND_NEW_TOKEN(tp, type, strval) \ - tok822_sub_append(tp, tok822_alloc(type, strval)) + /* + * XXX We cheat by storing comments in their external form. Otherwise it + * would be a royal pain to preserve \ before (. That would require a + * recursive parser, which could consume unreasonable amounts of memory. + */ + VSTRING_ADDCH(tp->vstr, '('); while ((ch = *(unsigned char *) str) != 0) { + VSTRING_ADDCH(tp->vstr, ch); str++; if (ch == '(') { /* comments can nest! */ - if (COMMENT_TEXT_TOKEN(tc)) - VSTRING_TERMINATE(tc->vstr); - tc = APPEND_NEW_TOKEN(tp, TOK822_COMMENT, (char *) 0); - str = tok822_comment(tc, str); + level++; } else if (ch == ')') { - break; - } else { - if (ch == '\\') { - if ((ch = *(unsigned char *) str) == 0) - break; - str++; - } - if (!COMMENT_TEXT_TOKEN(tc)) - tc = APPEND_NEW_TOKEN(tp, TOK822_COMMENT_TEXT, (char *) 0); - VSTRING_ADDCH(tc->vstr, ch); + if (--level == 0) + break; + } else if (ch == '\\') { + if ((ch = *(unsigned char *) str) == 0) + break; + VSTRING_ADDCH(tp->vstr, ch); + str++; } } - if (COMMENT_TEXT_TOKEN(tc)) - VSTRING_TERMINATE(tc->vstr); + VSTRING_TERMINATE(tp->vstr); return (str); } @@ -554,14 +545,11 @@ static void tok822_print(TOK822 *list, int indent) } else if (tp->type == TOK822_ADDR) { vstream_printf("%*s %s\n", indent, "", "address"); tok822_print(tp->head, indent + 2); - } else if (tp->type == TOK822_COMMENT) { - vstream_printf("%*s %s\n", indent, "", "comment"); - tok822_print(tp->head, indent + 2); } else if (tp->type == TOK822_STARTGRP) { vstream_printf("%*s %s\n", indent, "", "group \":\""); } else { vstream_printf("%*s %s \"%s\"\n", indent, "", - tp->type == TOK822_COMMENT_TEXT ? "text" : + tp->type == TOK822_COMMENT ? "comment" : tp->type == TOK822_ATOM ? "atom" : tp->type == TOK822_QSTRING ? "quoted string" : tp->type == TOK822_DOMLIT ? "domain literal" : diff --git a/postfix/src/global/tok822_parse.ref b/postfix/src/global/tok822_parse.ref index dc35bff57..169957ac4 100644 --- a/postfix/src/global/tok822_parse.ref +++ b/postfix/src/global/tok822_parse.ref @@ -86,8 +86,7 @@ Parse tree: OP "," address atom "venema" - comment - text ""wietse " + comment "("wietse )" OP "," address quoted string ")" @@ -135,11 +134,7 @@ Parse tree: OP "." atom "org" OP ")" - comment - text " " - comment - text ""wietse " - text " venema"" + comment "( ("wietse ) venema")" Internalized: wietse venema@porcupine.org) ( ("wietse ) venema") @@ -272,9 +267,8 @@ Parse tree: atom "wietse" OP "@" atom "foo" - comment - text "wietse - venema" + comment "(wietse + venema)" Internalized: wietse@foo (wietse diff --git a/postfix/src/lmtp/lmtp.c b/postfix/src/lmtp/lmtp.c index 49b89f403..af74d8dd0 100644 --- a/postfix/src/lmtp/lmtp.c +++ b/postfix/src/lmtp/lmtp.c @@ -269,8 +269,6 @@ int var_lmtp_data0_tmout; int var_lmtp_data1_tmout; int var_lmtp_data2_tmout; int var_lmtp_quit_tmout; -char *var_debug_peer_list; -int var_debug_peer_level; int var_lmtp_cache_conn; int var_lmtp_skip_quit_resp; char *var_notify_classes; @@ -505,7 +503,6 @@ static void pre_accept(char *unused_name, char **unused_argv) int main(int argc, char **argv) { static CONFIG_STR_TABLE str_table[] = { - VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0, VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0, VAR_LMTP_SASL_PASSWD, DEF_LMTP_SASL_PASSWD, &var_lmtp_sasl_passwd, 0, 0, @@ -514,7 +511,6 @@ int main(int argc, char **argv) }; static CONFIG_INT_TABLE int_table[] = { VAR_LMTP_TCP_PORT, DEF_LMTP_TCP_PORT, &var_lmtp_tcp_port, 0, 0, - VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, 0, }; static CONFIG_TIME_TABLE time_table[] = { diff --git a/postfix/src/lmtp/lmtp_chat.c b/postfix/src/lmtp/lmtp_chat.c index 19b37cc03..40f594716 100644 --- a/postfix/src/lmtp/lmtp_chat.c +++ b/postfix/src/lmtp/lmtp_chat.c @@ -268,7 +268,7 @@ void lmtp_chat_notify(LMTP_STATE *state) notice = post_mail_fopen_nowait(mail_addr_double_bounce(), var_error_rcpt, - NULL_CLEANUP_FLAGS, "NOTICE"); + NULL_CLEANUP_FLAGS); if (notice == 0) { msg_warn("postmaster notify: %m"); return; diff --git a/postfix/src/postsuper/Makefile.in b/postfix/src/postsuper/Makefile.in index 3ff67a4d3..c42db2a89 100644 --- a/postfix/src/postsuper/Makefile.in +++ b/postfix/src/postsuper/Makefile.in @@ -66,7 +66,9 @@ postsuper.o: ../../include/vstring.h postsuper.o: ../../include/safe.h postsuper.o: ../../include/set_ugid.h postsuper.o: ../../include/argv.h +postsuper.o: ../../include/vstring_vstream.h postsuper.o: ../../include/mail_task.h postsuper.o: ../../include/mail_conf.h postsuper.o: ../../include/mail_params.h postsuper.o: ../../include/mail_queue.h +postsuper.o: ../../include/mail_open_ok.h diff --git a/postfix/src/postsuper/postsuper.c b/postfix/src/postsuper/postsuper.c index f4fd84517..d7c071691 100644 --- a/postfix/src/postsuper/postsuper.c +++ b/postfix/src/postsuper/postsuper.c @@ -170,29 +170,43 @@ static int delete_one(const char *queue_id) MAIL_QUEUE_ACTIVE, /* foolproof but adequate */ 0, }; + const char *log_queue_names[] = { + MAIL_QUEUE_BOUNCE, + MAIL_QUEUE_DEFER, + 0, + }; struct stat st; - const char **cpp; - const char *path; + const char **msg_qpp; + const char **log_qpp; + const char *msg_path; + VSTRING *log_path_buf = vstring_alloc(100); int found = 0; /* - * Do not delete defer or bounce logfiles, because we could lose a race - * and delete a defer/bounce logfile from a message that reuses the queue - * ID. + * Delete defer or bounce logfiles before deleting the corresponding + * message file, and only if the message file exists. This minimizes but + * does not eliminate a race condition with queue ID reuse which results + * in deleting the wrong files. */ - for (cpp = msg_queue_names; *cpp != 0; cpp++) { - if (!mail_open_ok(*cpp, queue_id, &st, &path)) { + for (msg_qpp = msg_queue_names; *msg_qpp != 0; msg_qpp++) { + if (!mail_open_ok(*msg_qpp, queue_id, &st, &msg_path)) continue; - } else if (unlink(path) == 0) { + for (log_qpp = log_queue_names; *log_qpp != 0; log_qpp++) + (void) mail_queue_path(log_path_buf, *log_qpp, queue_id); + if (unlink(STR(log_path_buf)) < 0 && errno != ENOENT) + msg_warn("remove file %s: %m", STR(log_path_buf)); + if (unlink(msg_path) == 0) { found = 1; - msg_info("removed file %s", path); + msg_info("removed file %s", msg_path); break; - } else if (errno != ENOENT) { - msg_warn("remove file %s: %m", path); + } + if (errno != ENOENT) { + msg_warn("remove file %s: %m", msg_path); } else if (msg_verbose) { - msg_info("remove file %s: %m", path); + msg_info("remove file %s: %m", msg_path); } } + vstring_free(log_path_buf); return (found); } diff --git a/postfix/src/sendmail/sendmail.c b/postfix/src/sendmail/sendmail.c index 978ff14b0..f3324d2f3 100644 --- a/postfix/src/sendmail/sendmail.c +++ b/postfix/src/sendmail/sendmail.c @@ -34,12 +34,13 @@ /* the reason for failure is shown. This mode of operation is implemented /* by connecting to the \fBshowq\fR(8) daemon. /* .IP \fBnewaliases\fR -/* Initialize the alias database. If no alias database type is -/* specified, the program uses the type specified in the -/* \fBdatabase_type\fR configuration parameter; if no input file -/* is specified, the program processes the file(s) specified with the -/* \fBalias_database\fR configuration parameter. This mode of operation -/* is implemented by running the \fBpostalias\fR(1) command. +/* Initialize the alias database. If no input file is specified (with +/* the \fB-oA\fR option, see below), the program processes the file(s) +/* specified with the \fBalias_database\fR configuration parameter. +/* If no alias database type is specified, the program uses the type +/* specified with the \fBdatabase_type\fR configuration parameter. +/* This mode of operation is implemented by running the \fBpostalias\fR(1) +/* command. /* .sp /* Note: it may take a minute or so before an alias database update /* becomes visible. Use the \fBpostfix reload\fR command to eliminate diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 2adb776f8..ab541acce 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -234,8 +234,6 @@ int var_smtp_data1_tmout; int var_smtp_data2_tmout; int var_smtp_quit_tmout; char *var_inet_interfaces; -char *var_debug_peer_list; -int var_debug_peer_level; char *var_notify_classes; int var_smtp_skip_4xx_greeting; int var_smtp_skip_5xx_greeting; @@ -395,7 +393,6 @@ static void pre_exit(void) int main(int argc, char **argv) { static CONFIG_STR_TABLE str_table[] = { - VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0, VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, VAR_FALLBACK_RELAY, DEF_FALLBACK_RELAY, &var_fallback_relay, 0, 0, VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0, @@ -417,7 +414,6 @@ int main(int argc, char **argv) 0, }; static CONFIG_INT_TABLE int_table[] = { - VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index 223e70db5..f04fcacbd 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -207,6 +207,11 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why) /* * As long as we are able to look up any host address, we ignore problems * with DNS lookups. + * + * XXX 2821: update smtp_errno (0->FAIL upon unrecoverable lookup error, + * any->RETRY upon temporary lookup error) so that we can correctly + * handle the case of no resolvable MX host. Currently this is always + * treated as a soft error. RFC 2821 wants a more precise response. */ for (rr = mx_names; rr; rr = rr->next) { if (rr->type != T_MX) @@ -320,6 +325,19 @@ DNS_RR *smtp_domain_addr(char *name, VSTRING *why, int *found_myself) * as we're looking up all the hosts, it would be better to look up the * least preferred host first, so that DNS lookup error messages make * more sense. + * + * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX + * hosts, whereas multiple A records per hostname must be used in the + * order as received. They make the bogus assumption that a hostname with + * multiple A records corresponds to one machine with multiple network + * interfaces. + * + * XXX 2821: Postfix recognizes the local machine by looking for its own IP + * address in the list of mail exchangers. RFC 2821 says one has to look + * at the mail exchanger hostname as well, making the bogus assumption + * that an IP address is listed only under one hostname. However, looking + * at hostnames provides a partial solution for MX hosts behind a NAT + * gateway. */ switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why)) { default: diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c index 62c0de64f..d33a90e4e 100644 --- a/postfix/src/smtp/smtp_chat.c +++ b/postfix/src/smtp/smtp_chat.c @@ -252,7 +252,7 @@ void smtp_chat_notify(SMTP_STATE *state) notice = post_mail_fopen_nowait(mail_addr_double_bounce(), var_error_rcpt, - NULL_CLEANUP_FLAGS, "NOTICE"); + NULL_CLEANUP_FLAGS); if (notice == 0) { msg_warn("postmaster notify: %m"); return; diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index c9b0a54e8..230c7a07a 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -213,10 +213,13 @@ int smtp_helo(SMTP_STATE *state) * overflow detection, ignore the message size limit advertised by the * SMTP server. Otherwise, we might do the wrong thing when the server * advertises a really huge message size limit. + * + * XXX Allow for "code (SP|-) ehlo-keyword (SP|=) ehlo-param...", because + * MicroSoft implemented AUTH based on an old draft. */ lines = resp->str; while ((words = mystrtok(&lines, "\n")) != 0) { - if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) { + if (mystrtok(&words, "- =") && (word = mystrtok(&words, " \t")) != 0) { if (strcasecmp(word, "8BITMIME") == 0) state->features |= SMTP_FEATURE_8BITMIME; else if (strcasecmp(word, "PIPELINING") == 0) @@ -226,8 +229,6 @@ int smtp_helo(SMTP_STATE *state) #ifdef USE_SASL_AUTH else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) smtp_sasl_helo_auth(state, words); - else if (var_smtp_sasl_enable && strncasecmp(word, "AUTH=", 5) == 0) - smtp_sasl_helo_auth(state, word + 5); #endif else if (strcasecmp(word, var_myhostname) == 0) { msg_warn("host %s replied to HELO/EHLO with my own hostname %s", @@ -480,9 +481,16 @@ int smtp_xfer(SMTP_STATE *state) * rejected, ignore RCPT TO responses: all recipients are * dead already. When all recipients are rejected the * receiver may apply a course correction. + * + * XXX 2821: Section 4.5.3.1 says that a 552 RCPT TO reply + * must be treated as if the server replied with 452. */ case SMTP_STATE_RCPT: if (!mail_from_rejected) { +#ifndef RFC821_SYNTAX + if (resp->code == 552) + resp->code = 452; +#endif if (resp->code / 100 == 2) { ++nrcpt; } else { diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 48f8c82e8..1fc3877e3 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -311,8 +311,6 @@ int var_smtpd_soft_erlim; int var_smtpd_hard_erlim; int var_queue_minfree; /* XXX use off_t */ char *var_smtpd_banner; -char *var_debug_peer_list; -int var_debug_peer_level; char *var_notify_classes; char *var_client_checks; char *var_helo_checks; @@ -416,6 +414,11 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) { char *err; + /* + * XXX 2821 new feature: Section 4.1.4 specifies that a server must clear + * all buffers and reset the state exactly as if a RSET command had been + * issued. + */ if (argc < 2) { state->error_mask |= MAIL_ERROR_PROTOCOL; smtpd_chat_reply(state, "501 Syntax: EHLO hostname"); @@ -423,7 +426,7 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) } if (state->helo_name != 0) helo_reset(state); -#if 0 +#ifndef RFC821_SYNTAX mail_reset(state); rcpt_reset(state); #endif @@ -444,6 +447,8 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) (unsigned long) var_message_limit); /* XXX */ else smtpd_chat_reply(state, "250-SIZE"); + if (var_disable_vrfy_cmd == 0) + smtpd_chat_reply(state, "250-VRFY"); smtpd_chat_reply(state, "250-ETRN"); #ifdef USE_SASL_AUTH if (var_smtpd_sasl_enable) { @@ -471,6 +476,18 @@ static void mail_open_stream(SMTPD_STATE *state) { char *postdrop_command; + /* + * XXX 2821: An SMTP server is not allowed to "clean up" mail except in + * the case of original submissions. Presently, Postfix always runs all + * mail through the cleanup server. + * + * We could approximate the RFC as follows: Postfix rewrites mail if it + * comes from a source that we are willing to relay for. This way, we + * avoid rewriting most mail that comes from elsewhere. However, that + * requires moving functionality away from the cleanup daemon elsewhere, + * such as virtual address expansion, and header/body pattern matching. + */ + /* * If running from the master or from inetd, connect to the cleanup * service. @@ -613,6 +630,12 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) /* * Sanity checks. XXX Ignore bad SIZE= values until we can reliably and * portably detect overflows while converting from string to off_t. + * + * XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a + * command in which invalid character codes have been employed, and for + * which there are no other reasons for rejection, MUST reject that + * command with a 501 response. So much for the principle of "be liberal + * in what you accept, be strict in what you send". */ if (var_helo_required && state->helo_name == 0) { state->error_mask |= MAIL_ERROR_POLICY; @@ -746,6 +769,12 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) /* * Sanity checks. + * + * XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a + * command in which invalid character codes have been employed, and for + * which there are no other reasons for rejection, MUST reject that + * command with a 501 response. So much for the principle of "be liberal + * in what you accept, be strict in what you send". */ if (state->cleanup == 0) { state->error_mask |= MAIL_ERROR_PROTOCOL; @@ -1023,6 +1052,15 @@ static int rset_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) { + /* + * XXX 2821 incompatibility: Section 4.1.1.9 says that NOOP can have a + * parameter string which is to be ignored. NOOP instructions with + * parameters? Go figure. + * + * RFC 2821 violates RFC 821, which says that NOOP takes no parameters. + */ +#ifdef RFC821_SYNTAX + /* * Sanity checks. */ @@ -1031,6 +1069,7 @@ static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) smtpd_chat_reply(state, "501 Syntax: NOOP"); return (-1); } +#endif smtpd_chat_reply(state, "250 Ok"); return (0); } @@ -1051,6 +1090,17 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) * address forms. Therefore we must parse out the address, or we must * stop doing recipient restriction checks and lose the opportunity to * say "user unknown" at the SMTP port. + * + * XXX 2821 incompatibility and brain damage: Section 4.5.1 requires that + * VRFY is implemented. RFC 821 specifies that VRFY is optional. It gets + * even worse: section 3.5.3 says that a 502 (command recognized but not + * implemented) reply is not fully compliant. + * + * Thus, an RFC 2821 compliant implementation cannot refuse to supply + * information in reply to VRFY queries. That is simply bogus. The only + * reply we could supply is a generic 252 reply. This causes spammers to + * add tons of bogus addresses to their mailing lists (spam harvesting by + * trying out large lists of potential recipient names with VRFY). */ #define SLOPPY 0 @@ -1076,7 +1126,17 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) smtpd_chat_reply(state, "%s", err); return (-1); } - smtpd_chat_reply(state, "252 <%s>", argv[1].strval); + + /* + * XXX 2821 new feature: Section 3.5.1 requires that the VRFY response is + * either "full name " or "user@domain". Postfix replies + * with the address that was provided by the client, whether or not it is + * in fully qualified domain form or not. + * + * Reply code 250 is reserved for the case where the address is verified; + * reply code 252 should be used when no definitive certainty exists. + */ + smtpd_chat_reply(state, "252 %s", argv[1].strval); return (0); } @@ -1256,7 +1316,8 @@ static void smtpd_proto(SMTPD_STATE *state) continue; } if (state->access_denied && cmdp->action != quit_cmd) { - smtpd_chat_reply(state, "%s", state->access_denied); + smtpd_chat_reply(state, "503 Error: access denied for %s", + state->namaddr); /* RFC 2821 Sec 3.1 */ state->error_count++; continue; } @@ -1445,7 +1506,6 @@ int main(int argc, char **argv) VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0, VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 0, VAR_QUEUE_MINFREE, DEF_QUEUE_MINFREE, &var_queue_minfree, 0, 0, - VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, 0, 0, VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, 0, 0, VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code, 0, 0, @@ -1475,7 +1535,6 @@ int main(int argc, char **argv) }; static CONFIG_STR_TABLE str_table[] = { VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0, - VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0, VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, VAR_CLIENT_CHECKS, DEF_CLIENT_CHECKS, &var_client_checks, 0, 0, VAR_HELO_CHECKS, DEF_HELO_CHECKS, &var_helo_checks, 0, 0, diff --git a/postfix/src/smtpd/smtpd_chat.c b/postfix/src/smtpd/smtpd_chat.c index 2d1f87726..13dd756a3 100644 --- a/postfix/src/smtpd/smtpd_chat.c +++ b/postfix/src/smtpd/smtpd_chat.c @@ -207,7 +207,7 @@ void smtpd_chat_notify(SMTPD_STATE *state) notice = post_mail_fopen_nowait(mail_addr_double_bounce(), var_error_rcpt, - NULL_CLEANUP_FLAGS, "NOTICE"); + NULL_CLEANUP_FLAGS); if (notice == 0) { msg_warn("postmaster notify: %m"); return; diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 656a652ba..397f00cf8 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -551,6 +551,26 @@ 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 + * 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. + * + * Implementing the soft_bounce safety net in the SMTP server reply output + * routine has the advantage that it covers all 5xx replies, including + * SMTP protocol or syntax errors, which makes soft_bounce great for + * non-destructive tests (especially by people who are paranoid about + * losing mail). + * + * We could eliminate the code duplication and implement the soft_bounce + * safety net only in the code below. But then the safety net would cover + * the UCE restrictions only. This would be at odds with the documentation + * which says soft_bounce changes all 5xx replies into 4xx ones. + */ + if (var_soft_bounce && STR(error_text)[0] == '5') + STR(error_text)[0] = '4'; + /* * Log what is happening. When the sysadmin discards policy violation * postmaster notices, this may be the only trace left that service was @@ -1898,6 +1918,13 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) if (recipient == 0) return (0); + /* + * XXX 2821: Section 3.6 requires that "postmaster" be accepted even when + * specified without a fully qualified domain name. + */ + if (strcasecmp(recipient, "postmaster") == 0) + return (0); + /* * Minor kluge so that we can delegate work to the generic routine and so * that we can syslog the recipient with the reject messages.