Robustness: the Postfix SMTP server will no longer receive (and discard) an unlimited amount of text while receiving a long SMTP command line. Problem introduced: Postfix 2.9, date: 20110205; reported by Michael Wollner (Ibonok). Under high load conditions, the amount of text was already limited by a 10-second deadline to receive an SMTP command. Robustness: with the above change the Postfix SMTP client will no longer receive (and discard) an unlimited amount of text while receiving a long SMTP response line. Robustness: do not receive (and discard) unlimited amounts of data with BDAT commands. Problem introduced: Postfix 3.4, date: 20180825; found during code maintenance. File: smtpd/smtpd.c. Impact statement: ================= Postfix should not receive and discard unlimited amounts of input in SMTP command lines or BDAT chunks, but fixing that will not fundamentally change the situation. By design, any SMTP client can force a server to receive (and discard) an unlimited amount of text. For example, an attacker can repeatedly send messages that are a little under the server's message size limit and abort each transaction a before reaching the message end. When sending a message with the "DATA" command, an attacker would disconnect instead of sending .; and when sending a message with the "BDAT" command, an attacker would send "RSET" instead of "BDAT LAST". To mitigate such abuse, Postfix can rate-limit the number of message transactions from the same IP address or address range (see smtpd_client_message_rate_limit and *prefix_length parameters). Such a defense is ineffective when faced with a distributed attack (botnet); for that, postscreen combined with an IP reputation service (DNSBL) may be more effective. diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' --no-dereference -r -ur /var/tmp/postfix-3.12-20260516/src/global/smtp_stream.c ./src/global/smtp_stream.c --- /var/tmp/postfix-3.12-20260516/src/global/smtp_stream.c 2024-01-12 13:39:59.000000000 -0500 +++ ./src/global/smtp_stream.c 2026-05-30 10:42:49.773119451 -0400 @@ -457,8 +457,12 @@ && vstream_feof(stream) == 0 && vstream_ferror(stream) == 0) while ((next_char = VSTREAM_GETC(stream)) != VSTREAM_EOF && next_char != '\n') - /* void */ ; - + if (--bound <= 0) { + msg_warn("disabling input from %s", VSTREAM_PATH(stream)); + vstream_fpurge(stream, VSTREAM_PURGE_READ); + shutdown(vstream_fileno(stream), SHUT_RD); + break; + } return (last_char); } diff '--exclude=man' '--exclude=html' '--exclude=README_FILES' '--exclude=INSTALL' --no-dereference -r -ur --new-file /var/tmp/postfix-3.12-20260516/src/smtpd/smtpd.c ./src/smtpd/smtpd.c --- /var/tmp/postfix-3.12-20260516/src/smtpd/smtpd.c 2026-05-16 10:40:15.000000000 -0400 +++ ./src/smtpd/smtpd.c 2026-05-31 10:25:30.802926140 -0400 @@ -4035,6 +4035,21 @@ off_t len; /* + * Skip inputs below 1.5 times the message size limit, staying in sync + * with the remote SMTP client. Otherwise, force a negative chunk_size + * value to disable reading and discarding input here, and to force a + * "lost connection" condition upon a later read operation. + */ + if (ENFORCING_SIZE_LIMIT(var_message_limit) + && state->act_size / 1.5 > var_message_limit - chunk_size / 1.5) { + chunk_size = -1; + } else if (state->act_size > OFF_T_MAX - chunk_size) { + state->act_size = OFF_T_MAX; + } else { + state->act_size += chunk_size; + } + + /* * Read and discard content from the remote SMTP client. TODO: drop the * connection in case of overload. */ @@ -4052,6 +4067,16 @@ va_end(ap); /* + * Force a "lost connection" condition upon the next read operation. + */ + if (chunk_size < 0) { + msg_warn("%s: too much BDAT content -- disabling further input from %s", + state->queue_id ? state->queue_id : "NOQUEUE", + state->namaddr); + shutdown(vstream_fileno(state->client), SHUT_RD); + } + + /* * Reset state, or drop subsequent BDAT payloads until BDAT LAST or RSET. */ if (final_chunk)