diff --git a/postfix/HISTORY b/postfix/HISTORY index 6e0561a75..71df8695f 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -17020,3 +17020,15 @@ Apologies for any names omitted. EAI Future-proofing: don't apply strict_mime_encoding_domain checks to unknown message subtypes such as message/global*. File: global/mime_state.c. + +20111025 + + Bugfix (introduced: Postfix 2.8): postscreen sent non-compliant + SMTP responses (220- followed by 421) when it could not + hand off a connection to a real smtpd process, causing + undefined behavior in the remote SMTP client. The fix + redirects the client to the dummy SMTP engine which sends + the 421 reply at the first legitimate opportunity. Problem + reported by Ralf Hildebrandt. Files: postscreen/postscreen_send.c, + postscreen/postscreen_smtpd.c, postscreen/postscreen.h. + diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index ab6759329..75dfe5cb1 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20111024" +#define MAIL_RELEASE_DATE "20111025" #define MAIL_VERSION_NUMBER "2.9" #ifdef SNAPSHOT diff --git a/postfix/src/milter/milter.c b/postfix/src/milter/milter.c index f4c64aff7..f912add48 100644 --- a/postfix/src/milter/milter.c +++ b/postfix/src/milter/milter.c @@ -635,7 +635,7 @@ int milter_send(MILTERS *milters, VSTREAM *stream) (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count); if (msg_verbose) - msg_info("send %d milters"); + msg_info("send %d milters", count); /* * XXX Optimization: don't send or receive further information when there diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h index b378aaad4..3a190f7c3 100644 --- a/postfix/src/postscreen/postscreen.h +++ b/postfix/src/postscreen/postscreen.h @@ -81,7 +81,7 @@ typedef struct { #define PSC_STATE_FLAG_NEW (1<<3) /* some test was never passed */ #define PSC_STATE_FLAG_BLIST_FAIL (1<<4) /* blacklisted */ #define PSC_STATE_FLAG_HANGUP (1<<5) /* NOT a test failure */ -/* unused */ +#define PSC_STATE_FLAG_SMTPD_421 (1<<6) /* hang up after command */ #define PSC_STATE_FLAG_WLIST_FAIL (1<<7) /* do not whitelist */ /* @@ -435,6 +435,12 @@ extern void psc_smtpd_tests(PSC_STATE *); extern void psc_smtpd_init(void); extern void psc_smtpd_pre_jail_init(void); +#define PSC_SMTPD_421(state, reply) do { \ + (state)->flags |= PSC_STATE_FLAG_SMTPD_421; \ + (state)->final_reply = (reply); \ + psc_smtpd_tests(state); \ + } while (0) + /* * postscreen_misc.c */ diff --git a/postfix/src/postscreen/postscreen_send.c b/postfix/src/postscreen/postscreen_send.c index f68a255d7..bb111965e 100644 --- a/postfix/src/postscreen/postscreen_send.c +++ b/postfix/src/postscreen/postscreen_send.c @@ -35,7 +35,10 @@ /* work is finished including postscreen cache updates. /* /* In case of an immediate error, psc_send_socket() sends a 421 -/* reply to the remote SMTP client and closes the connection. +/* reply to the remote SMTP client and closes the connection +/* if no partial SMTP greeting was sent. Otherwise, it redirects +/* the SMTP client to the dummy protocol engine which sends +/* 421 at the first legitimate opportunity and hangs up. /* LICENSE /* .ad /* .fi @@ -182,22 +185,35 @@ void psc_send_socket(PSC_STATE *state) * suspicious. Alternatively, we could send attributes along with the * socket with client reputation information, making everything even more * Postfix-specific. + * + * If the operation fails after the partial SMTP handshake was sent, + * redirect the client to the dummy SMTP engine, which finishes the + * partial SMTP handshake sends the bad news after the first client + * command. */ if ((server_fd = PASS_CONNECT(psc_smtpd_service_name, NON_BLOCKING, PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); - PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); - psc_free_session_state(state); + if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { + PSC_SMTPD_421(state, "421 4.3.2 No system resources\r\n"); + } else { + PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); + psc_free_session_state(state); + } return; } - PSC_ADD_SERVER_STATE(state, server_fd); - if (LOCAL_SEND_FD(state->smtp_server_fd, + if (LOCAL_SEND_FD(server_fd, vstream_fileno(state->smtp_client_stream)) < 0) { msg_warn("cannot pass connection to service %s: %m", psc_smtpd_service_name); - PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); - psc_free_session_state(state); + (void) close(server_fd); + if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { + PSC_SMTPD_421(state, "421 4.3.2 No system resources\r\n"); + } else { + PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); + psc_free_session_state(state); + } return; } else { @@ -209,6 +225,7 @@ void psc_send_socket(PSC_STATE *state) #if 0 PSC_DEL_CLIENT_STATE(state); #endif + PSC_ADD_SERVER_STATE(state, server_fd); PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, (char *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); return; diff --git a/postfix/src/postscreen/postscreen_smtpd.c b/postfix/src/postscreen/postscreen_smtpd.c index 062d19bff..67db377f5 100644 --- a/postfix/src/postscreen/postscreen_smtpd.c +++ b/postfix/src/postscreen/postscreen_smtpd.c @@ -12,6 +12,10 @@ /* /* void psc_smtpd_tests(state) /* PSC_STATE *state; +/* +/* void PSC_SMTPD_421(state, final_reply) +/* PSC_STATE *state; +/* const char *final_reply; /* DESCRIPTION /* psc_smtpd_pre_jail_init() performs one-time per-process /* initialization during the "before chroot" execution phase. @@ -22,6 +26,12 @@ /* protocol tests and for collecting helo/sender/recipient /* information. /* +/* PSC_SMTPD_421() redirects the SMTP client to the dummy SMTP +/* protocol engine, completes the SMTP protocol handshake, +/* sends the specified final reply after the first non-QUIT +/* client command, and hangs up without doing any protocol +/* tests. The final reply must end in . +/* /* Unlike the Postfix SMTP server, this engine does not announce /* PIPELINING support. This exposes spambots that pipeline /* their commands anyway. Like the Postfix SMTP server, this @@ -904,6 +914,12 @@ static void psc_smtpd_read_event(int event, char *context) if (strcasecmp(command, cmdp->name) == 0) break; + if ((state->flags & PSC_STATE_FLAG_SMTPD_421) + && cmdp->action != psc_quit_cmd) { + PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event, + state->final_reply); + return; + } /* Non-SMTP command test. */ if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_SKIP) == PSC_STATE_FLAG_NSMTP_TODO && cmdp->name == 0 @@ -1088,8 +1104,10 @@ void psc_smtpd_tests(PSC_STATE *state) * * XXX Make "opportunistically" configurable for each test. */ - state->flags |= (PSC_STATE_FLAG_PIPEL_TODO | PSC_STATE_FLAG_NSMTP_TODO | \ - PSC_STATE_FLAG_BARLF_TODO); + if ((state->flags & PSC_STATE_FLAG_SMTPD_421) == 0) + state->flags |= (PSC_STATE_FLAG_PIPEL_TODO | \ + PSC_STATE_FLAG_NSMTP_TODO | \ + PSC_STATE_FLAG_BARLF_TODO); /* * Send no SMTP banner to pregreeting clients. This eliminates a lot of