diff -ur --new-file /var/tmp/postfix-3.5.19/src/global/mail_params.h ./src/global/mail_params.h --- /var/tmp/postfix-3.5.19/src/global/mail_params.h 2022-03-22 17:30:42.000000000 -0400 +++ ./src/global/mail_params.h 2023-06-05 17:44:55.000000000 -0400 @@ -2381,6 +2381,10 @@ #define DEF_SMTPD_PEERNAME_LOOKUP 1 extern bool var_smtpd_peername_lookup; +#define VAR_SMTPD_FORBID_UNAUTH_PIPE "smtpd_forbid_unauth_pipelining" +#define DEF_SMTPD_FORBID_UNAUTH_PIPE 1 +extern bool var_smtpd_forbid_unauth_pipe; + /* * Heuristic to reject unknown local recipients at the SMTP port. */ diff -ur --new-file /var/tmp/postfix-3.5.19/src/smtpd/smtpd.c ./src/smtpd/smtpd.c --- /var/tmp/postfix-3.5.19/src/smtpd/smtpd.c 2021-11-15 08:42:43.000000000 -0500 +++ ./src/smtpd/smtpd.c 2023-06-05 16:34:00.000000000 -0400 @@ -751,6 +758,11 @@ /* The maximal number of AUTH commands that any client is allowed to /* send to this service per time unit, regardless of whether or not /* Postfix actually accepts those commands. +/* .PP +/* Available in Postfix 3.9, 3.8.1, 3.7.6, 3.6.10, 3.5.20 and later: +/* .IP "\fBsmtpd_forbid_unauth_pipelining (Postfix >= 3.9: yes)\fR" +/* Disconnect remote SMTP clients that violate RFC 2920 (or 5321) +/* command pipelining constraints. /* TARPIT CONTROLS /* .ad /* .fi @@ -1436,6 +1448,7 @@ char *var_milt_unk_macros; char *var_milt_macro_deflts; bool var_smtpd_client_port_log; +bool var_smtpd_forbid_unauth_pipe; char *var_stress; char *var_reject_tmpf_act; @@ -5363,6 +5376,32 @@ static STRING_LIST *smtpd_noop_cmds; static STRING_LIST *smtpd_forbid_cmds; +/* smtpd_flag_ill_pipelining - flag pipelining protocol violation */ + +static int smtpd_flag_ill_pipelining(SMTPD_STATE *state) +{ + + /* + * This code will not return after I/O error, timeout, or EOF. VSTREAM + * exceptions must be enabled in advance with smtp_stream_setup(). + */ + if (vstream_peek(state->client) == 0 + && peekfd(vstream_fileno(state->client)) > 0) + (void) vstream_ungetc(state->client, smtp_fgetc(state->client)); + if (vstream_peek(state->client) > 0) { + if (state->expand_buf == 0) + state->expand_buf = vstring_alloc(100); + escape(state->expand_buf, vstream_peek_data(state->client), + vstream_peek(state->client) < 100 ? + vstream_peek(state->client) : 100); + msg_info("improper command pipelining after %s from %s: %s", + state->where, state->namaddr, STR(state->expand_buf)); + state->flags |= SMTPD_FLAG_ILL_PIPELINING; + return (1); + } + return (0); +} + /* smtpd_proto - talk the SMTP protocol */ static void smtpd_proto(SMTPD_STATE *state) @@ -5502,6 +5541,21 @@ #endif /* + * If the client spoke before the server sends the initial greeting, + * raise a flag and log the content of the protocol violation. This + * check MUST NOT apply to TLS wrappermode connections. + */ + if (SMTPD_STAND_ALONE(state) == 0 + && vstream_context(state->client) == 0 /* not postscreen */ + && (state->flags & SMTPD_FLAG_ILL_PIPELINING) == 0 + && smtpd_flag_ill_pipelining(state) + && var_smtpd_forbid_unauth_pipe) { + smtpd_chat_reply(state, + "554 5.5.0 Error: SMTP protocol synchronization"); + break; + } + + /* * XXX The client connection count/rate control must be consistent in * its use of client address information in connect and disconnect * events. For now we exclude xclient authorized hosts from @@ -5728,16 +5782,11 @@ && (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0 || (cmdp->flags & SMTPD_CMD_FLAG_LAST)) && (state->flags & SMTPD_FLAG_ILL_PIPELINING) == 0 - && (vstream_peek(state->client) > 0 - || peekfd(vstream_fileno(state->client)) > 0)) { - if (state->expand_buf == 0) - state->expand_buf = vstring_alloc(100); - escape(state->expand_buf, vstream_peek_data(state->client), - vstream_peek(state->client) < 100 ? - vstream_peek(state->client) : 100); - msg_info("improper command pipelining after %s from %s: %s", - cmdp->name, state->namaddr, STR(state->expand_buf)); - state->flags |= SMTPD_FLAG_ILL_PIPELINING; + && smtpd_flag_ill_pipelining(state) + && var_smtpd_forbid_unauth_pipe) { + smtpd_chat_reply(state, + "554 5.5.0 Error: SMTP protocol synchronization"); + break; } if (cmdp->action(state, argc, argv) != 0) state->error_count++; @@ -6400,6 +6449,7 @@ VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup, VAR_SMTPD_DELAY_OPEN, DEF_SMTPD_DELAY_OPEN, &var_smtpd_delay_open, VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log, + VAR_SMTPD_FORBID_UNAUTH_PIPE, DEF_SMTPD_FORBID_UNAUTH_PIPE, &var_smtpd_forbid_unauth_pipe, 0, }; static const CONFIG_NBOOL_TABLE nbool_table[] = {