Home | History | Annotate | Line # | Download | only in postscreen
      1 /*	$NetBSD: postscreen_smtpd.c,v 1.6 2025/02/25 19:15:48 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	postscreen_smtpd 3
      6 /* SUMMARY
      7 /*	postscreen built-in SMTP server engine
      8 /* SYNOPSIS
      9 /*	#include <postscreen.h>
     10 /*
     11 /*	void	psc_smtpd_pre_jail_init(void)
     12 /*
     13 /*	void	psc_smtpd_init(void)
     14 /*
     15 /*	void	psc_smtpd_tests(state)
     16 /*	PSC_STATE *state;
     17 /*
     18 /*	void	PSC_SMTPD_X21(state, final_reply)
     19 /*	PSC_STATE *state;
     20 /*	const char *final_reply;
     21 /* DESCRIPTION
     22 /*	psc_smtpd_pre_jail_init() performs one-time per-process
     23 /*	initialization during the "before chroot" execution phase.
     24 /*
     25 /*	psc_smtpd_init() performs one-time per-process initialization.
     26 /*
     27 /*	psc_smtpd_tests() starts up an SMTP server engine for deep
     28 /*	protocol tests and for collecting helo/sender/recipient
     29 /*	information.
     30 /*
     31 /*	PSC_SMTPD_X21() redirects the SMTP client to an SMTP server
     32 /*	engine, which sends the specified final reply at the first
     33 /*	legitimate opportunity without doing any protocol tests.
     34 /*
     35 /*	Unlike the Postfix SMTP server, this engine does not announce
     36 /*	PIPELINING support. This exposes spambots that pipeline
     37 /*	their commands anyway. Like the Postfix SMTP server, this
     38 /*	engine will accept input with bare newline characters. To
     39 /*	pass the "pipelining" and "bare newline" test, the client
     40 /*	has to properly speak SMTP all the way to the RCPT TO
     41 /*	command. These tests fail if the client violates the protocol
     42 /*	at any stage.
     43 /*
     44 /*	No support is announced for AUTH, XCLIENT or XFORWARD.
     45 /*	Clients that need this should be allowlisted or should talk
     46 /*	directly to the submission service.
     47 /*
     48 /*	The engine rejects RCPT TO and VRFY commands with the
     49 /*	state->rcpt_reply response which depends on program history,
     50 /*	rejects ETRN with a generic response, and closes the
     51 /*	connection after QUIT.
     52 /*
     53 /*	Since this engine defers or rejects all non-junk commands,
     54 /*	there is no point maintaining separate counters for "error"
     55 /*	commands and "junk" commands.  Instead, the engine maintains
     56 /*	a per-session command counter, and terminates the session
     57 /*	with a 421 reply when the command count exceeds the limit.
     58 /*
     59 /*	We limit the command count, as well as the total time to
     60 /*	receive a command. This limits the time per client more
     61 /*	effectively than would be possible with read() timeouts.
     62 /*
     63 /*	There is no concern about getting blocked on output.  The
     64 /*	psc_send() routine uses non-blocking output, and discards
     65 /*	output that the client is not willing to receive.
     66 /* PROTOCOL INSPECTION VERSUS CONTENT INSPECTION
     67 /*	The goal of postscreen is to keep spambots away from Postfix.
     68 /*	To recognize spambots, postscreen measures properties of
     69 /*	the client IP address and of the client SMTP protocol
     70 /*	implementation.  These client properties don't change with
     71 /*	each delivery attempt.  Therefore it is possible to make a
     72 /*	long-term decision after a single measurement.  For example,
     73 /*	allow a good client to skip the DNSBL test for 24 hours,
     74 /*	or to skip the pipelining test for one week.
     75 /*
     76 /*	If postscreen were to measure properties of message content
     77 /*	(MIME compliance, etc.) then it would measure properties
     78 /*	that may change with each delivery attempt.  Here, it would
     79 /*	be wrong to make a long-term decision after a single
     80 /*	measurement. Instead, postscreen would need to develop a
     81 /*	ranking based on the content of multiple messages from the
     82 /*	same client.
     83 /*
     84 /*	Many spambots avoid spamming the same site repeatedly.
     85 /*	Thus, postscreen must make decisions after a single
     86 /*	measurement. Message content is not a good indicator for
     87 /*	making long-term decisions after single measurements, and
     88 /*	that is why postscreen does not inspect message content.
     89 /* REJECTING RCPT TO VERSUS SENDING LIVE SOCKETS TO SMTPD(8)
     90 /*	When post-handshake protocol tests are enabled, postscreen
     91 /*	rejects the RCPT TO command from a good client, and forces
     92 /*	it to deliver mail in a later session. This is why
     93 /*	post-handshake protocol tests have a longer expiration time
     94 /*	than pre-handshake tests.
     95 /*
     96 /*	Instead, postscreen could send the network socket to smtpd(8)
     97 /*	and ship the session history (including TLS and other SMTP
     98 /*	or non-SMTP attributes) as auxiliary data. The Postfix SMTP
     99 /*	server would then use new code to replay the session history,
    100 /*	and would use existing code to validate the client, helo,
    101 /*	sender and recipient address.
    102 /*
    103 /*	Such an approach would increase the implementation and
    104 /*	maintenance effort, because:
    105 /*
    106 /*	1) New replay code would be needed in smtpd(8), such that
    107 /*	the HELO, EHLO, and MAIL command handlers can delay their
    108 /*	error responses until the RCPT TO reply.
    109 /*
    110 /*	2) postscreen(8) would have to implement more of smtpd(8)'s
    111 /*	syntax checks, to avoid confusing delayed "syntax error"
    112 /*	and other error responses syntax error responses while
    113 /*	replaying history.
    114 /*
    115 /*	3) New code would be needed in postscreen(8) and smtpd(8)
    116 /*	to send and receive the session history (including TLS and
    117 /*	other SMTP or non-SMTP attributes) as auxiliary data while
    118 /*	sending the network socket from postscreen(8) to smtpd(8).
    119 /* REJECTING RCPT TO VERSUS PROXYING LIVE SESSIONS TO SMTPD(8)
    120 /*	An alternative would be to proxy the session history to a
    121 /*	real Postfix SMTP process, presumably passing TLS and other
    122 /*	attributes via an extended XCLIENT implementation. That
    123 /*	would require all the work described in 2) above, plus
    124 /*	duplication of all the features of the smtpd(8) TLS engine,
    125 /*	plus additional XCLIENT support for a lot more attributes.
    126 /* LICENSE
    127 /* .ad
    128 /* .fi
    129 /*	The Secure Mailer license must be distributed with this software.
    130 /* AUTHOR(S)
    131 /*	Wietse Venema
    132 /*	IBM T.J. Watson Research
    133 /*	P.O. Box 704
    134 /*	Yorktown Heights, NY 10598, USA
    135 /*
    136 /*	Wietse Venema
    137 /*	Google, Inc.
    138 /*	111 8th Avenue
    139 /*	New York, NY 10011, USA
    140 /*--*/
    141 
    142 /* System library. */
    143 
    144 #include <sys_defs.h>
    145 #include <string.h>
    146 #include <ctype.h>
    147 
    148 #ifdef STRCASECMP_IN_STRINGS_H
    149 #include <strings.h>
    150 #endif
    151 
    152 /* Utility library. */
    153 
    154 #include <msg.h>
    155 #include <stringops.h>
    156 #include <mymalloc.h>
    157 #include <iostuff.h>
    158 #include <vstring.h>
    159 
    160 /* Global library. */
    161 
    162 #include <mail_params.h>
    163 #include <mail_proto.h>
    164 #include <is_header.h>
    165 #include <string_list.h>
    166 #include <maps.h>
    167 #include <ehlo_mask.h>
    168 #include <lex_822.h>
    169 #include <info_log_addr_form.h>
    170 
    171 /* TLS library. */
    172 
    173 #include <tls.h>
    174 
    175 /* Application-specific. */
    176 
    177 #include <postscreen.h>
    178 
    179  /*
    180   * Plan for future body processing. See smtp-sink.c. For now, we have no
    181   * per-session push-back except for the single-character push-back that
    182   * VSTREAM guarantees after we read one character.
    183   */
    184 #define PSC_SMTPD_HAVE_PUSH_BACK(state)	(0)
    185 #define PSC_SMTPD_PUSH_BACK_CHAR(state, ch) \
    186 	vstream_ungetc((state)->smtp_client_stream, (ch))
    187 #define PSC_SMTPD_NEXT_CHAR(state) \
    188 	VSTREAM_GETC((state)->smtp_client_stream)
    189 
    190 #define PSC_SMTPD_BUFFER_EMPTY(state) \
    191 	(!PSC_SMTPD_HAVE_PUSH_BACK(state) \
    192 	&& vstream_peek((state)->smtp_client_stream) <= 0)
    193 
    194 #define PSC_SMTPD_PEEK_DATA(state) \
    195 	vstream_peek_data((state)->smtp_client_stream)
    196 #define PSC_SMTPD_PEEK_LEN(state) \
    197 	vstream_peek((state)->smtp_client_stream)
    198 
    199  /*
    200   * Dynamic reply strings. To minimize overhead we format these once.
    201   */
    202 static char *psc_smtpd_greeting;	/* smtp banner */
    203 static char *psc_smtpd_helo_reply;	/* helo reply */
    204 static char *psc_smtpd_ehlo_reply_plain;/* multi-line ehlo reply, non-TLS */
    205 static char *psc_smtpd_ehlo_reply_tls;	/* multi-line ehlo reply, with TLS */
    206 static char *psc_smtpd_timeout_reply;	/* timeout reply */
    207 static char *psc_smtpd_421_reply;	/* generic final_reply value */
    208 
    209  /*
    210   * Forward declaration, needed by PSC_CLEAR_EVENT_REQUEST.
    211   */
    212 static void psc_smtpd_time_event(int, void *);
    213 static void psc_smtpd_read_event(int, void *);
    214 
    215  /*
    216   * Encapsulation. The STARTTLS, EHLO and AUTH command handlers temporarily
    217   * suspend SMTP command events, send an asynchronous proxy request, and
    218   * resume SMTP command events after receiving the asynchronous proxy
    219   * response (the EHLO handler must asynchronously talk to the auth server
    220   * before it can announce the SASL mechanism list; the list can depend on
    221   * the client IP address and on the presence on TLS encryption).
    222   */
    223 #define PSC_RESUME_SMTP_CMD_EVENTS(state) do { \
    224 	PSC_READ_EVENT_REQUEST2(vstream_fileno((state)->smtp_client_stream), \
    225 			       psc_smtpd_read_event, psc_smtpd_time_event, \
    226 			       (void *) (state), PSC_EFF_CMD_TIME_LIMIT); \
    227 	if (!PSC_SMTPD_BUFFER_EMPTY(state)) \
    228 	    psc_smtpd_read_event(EVENT_READ, (void *) state); \
    229     } while (0)
    230 
    231 #define PSC_SUSPEND_SMTP_CMD_EVENTS(state) \
    232     PSC_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
    233 			   psc_smtpd_time_event, (void *) (state));
    234 
    235  /*
    236   * Make control characters and other non-text visible.
    237   */
    238 #define PSC_SMTPD_ESCAPE_TEXT(dest, src, src_len, max_len) do { \
    239 	ssize_t _s_len = (src_len); \
    240 	ssize_t _m_len = (max_len); \
    241 	(void) escape((dest), (src), _s_len < _m_len ? _s_len : _m_len); \
    242     } while (0)
    243 
    244  /*
    245   * Command parser support.
    246   */
    247 #define PSC_SMTPD_NEXT_TOKEN(ptr) mystrtok(&(ptr), " ")
    248 
    249  /*
    250   * EHLO keyword filter
    251   */
    252 static MAPS *psc_ehlo_discard_maps;
    253 static int psc_ehlo_discard_mask;
    254 
    255  /*
    256   * Command editing filter.
    257   */
    258 static DICT *psc_cmd_filter;
    259 
    260  /*
    261   * Encapsulation. We must not forget turn off input/timer events when we
    262   * terminate the SMTP protocol engine.
    263   *
    264   * It would be safer to turn off input/timer events after each event, and to
    265   * turn on input/timer events again when we want more input. But experience
    266   * with the Postfix smtp-source and smtp-sink tools shows that this would
    267   * noticeably increase the run-time cost.
    268   */
    269 #define PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, event, reply) do { \
    270 	PSC_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
    271 				   (event), (void *) (state)); \
    272 	PSC_DROP_SESSION_STATE((state), (reply)); \
    273     } while (0)
    274 
    275 #define PSC_CLEAR_EVENT_HANGUP(state, event) do { \
    276 	PSC_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
    277 				   (event), (void *) (state)); \
    278 	psc_hangup_event(state); \
    279     } while (0)
    280 
    281 /* psc_helo_cmd - record HELO and respond */
    282 
    283 static int psc_helo_cmd(PSC_STATE *state, char *args)
    284 {
    285     char   *helo_name = PSC_SMTPD_NEXT_TOKEN(args);
    286 
    287     /*
    288      * smtpd(8) incompatibility: we ignore extra words; smtpd(8) saves them.
    289      */
    290     if (helo_name == 0)
    291 	return (PSC_SEND_REPLY(state, "501 Syntax: HELO hostname\r\n"));
    292 
    293     PSC_STRING_UPDATE(state->helo_name, helo_name);
    294     PSC_STRING_RESET(state->sender);
    295     /* Don't downgrade state->protocol, in case some test depends on this. */
    296     return (PSC_SEND_REPLY(state, psc_smtpd_helo_reply));
    297 }
    298 
    299 /* psc_smtpd_format_ehlo_reply - format EHLO response */
    300 
    301 static void psc_smtpd_format_ehlo_reply(VSTRING *buf, int discard_mask
    302 				   /* , const char *sasl_mechanism_list */ )
    303 {
    304     const char *myname = "psc_smtpd_format_ehlo_reply";
    305     int     saved_len = 0;
    306 
    307     if (msg_verbose)
    308 	msg_info("%s: discard_mask %s", myname, str_ehlo_mask(discard_mask));
    309 
    310 #define PSC_EHLO_APPEND(save, buf, fmt) do { \
    311 	(save) = LEN(buf); \
    312 	vstring_sprintf_append((buf), (fmt)); \
    313     } while (0)
    314 
    315 #define PSC_EHLO_APPEND1(save, buf, fmt, arg1) do { \
    316 	(save) = LEN(buf); \
    317 	vstring_sprintf_append((buf), (fmt), (arg1)); \
    318     } while (0)
    319 
    320     vstring_sprintf(psc_temp, "250-%s\r\n", var_myhostname);
    321     if ((discard_mask & EHLO_MASK_SIZE) == 0) {
    322 	if (ENFORCING_SIZE_LIMIT(var_message_limit))
    323 	    PSC_EHLO_APPEND1(saved_len, psc_temp, "250-SIZE %lu\r\n",
    324 			     (unsigned long) var_message_limit);
    325 	else
    326 	    PSC_EHLO_APPEND(saved_len, psc_temp, "250-SIZE\r\n");
    327     }
    328     if ((discard_mask & EHLO_MASK_VRFY) == 0 && var_disable_vrfy_cmd == 0)
    329 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-VRFY\r\n");
    330     if ((discard_mask & EHLO_MASK_ETRN) == 0)
    331 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-ETRN\r\n");
    332     if ((discard_mask & EHLO_MASK_STARTTLS) == 0 && var_psc_use_tls)
    333 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-STARTTLS\r\n");
    334 #ifdef TODO_SASL_AUTH
    335     if ((discard_mask & EHLO_MASK_AUTH) == 0 && sasl_mechanism_list
    336 	&& (!var_psc_tls_auth_only || (discard_mask & EHLO_MASK_STARTTLS))) {
    337 	PSC_EHLO_APPEND1(saved_len, psc_temp, "AUTH %s", sasl_mechanism_list);
    338 	if (var_broken_auth_clients)
    339 	    PSC_EHLO_APPEND1(saved_len, psc_temp, "AUTH=%s", sasl_mechanism_list);
    340     }
    341 #endif
    342     if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
    343 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-ENHANCEDSTATUSCODES\r\n");
    344     if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
    345 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-8BITMIME\r\n");
    346     if ((discard_mask & EHLO_MASK_DSN) == 0)
    347 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-DSN\r\n");
    348     /* Fix 20140708: announce SMTPUTF8. */
    349     if (var_smtputf8_enable && (discard_mask & EHLO_MASK_SMTPUTF8) == 0)
    350 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-SMTPUTF8\r\n");
    351     if ((discard_mask & EHLO_MASK_CHUNKING) == 0)
    352 	PSC_EHLO_APPEND(saved_len, psc_temp, "250-CHUNKING\r\n");
    353     STR(psc_temp)[saved_len + 3] = ' ';
    354 }
    355 
    356 /* psc_ehlo_cmd - record EHLO and respond */
    357 
    358 static int psc_ehlo_cmd(PSC_STATE *state, char *args)
    359 {
    360     char   *helo_name = PSC_SMTPD_NEXT_TOKEN(args);
    361     const char *ehlo_words;
    362     int     discard_mask;
    363     char   *reply;
    364 
    365     /*
    366      * smtpd(8) incompatibility: we ignore extra words; smtpd(8) saves them.
    367      */
    368     if (helo_name == 0)
    369 	return (PSC_SEND_REPLY(state, "501 Syntax: EHLO hostname\r\n"));
    370 
    371     PSC_STRING_UPDATE(state->helo_name, helo_name);
    372     PSC_STRING_RESET(state->sender);
    373     state->protocol = MAIL_PROTO_ESMTP;
    374 
    375     /*
    376      * smtpd(8) compatibility: dynamic reply filtering.
    377      */
    378     if (psc_ehlo_discard_maps != 0
    379 	&& (ehlo_words = psc_maps_find(psc_ehlo_discard_maps,
    380 				       state->smtp_client_addr, 0)) != 0
    381 	&& (discard_mask = ehlo_mask(ehlo_words)) != psc_ehlo_discard_mask) {
    382 	if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
    383 	    msg_info("[%s]%s: discarding EHLO keywords: %s",
    384 		  PSC_CLIENT_ADDR_PORT(state), str_ehlo_mask(discard_mask));
    385 	if (state->flags & PSC_STATE_FLAG_USING_TLS)
    386 	    discard_mask |= EHLO_MASK_STARTTLS;
    387 	psc_smtpd_format_ehlo_reply(psc_temp, discard_mask);
    388 	reply = STR(psc_temp);
    389 	state->ehlo_discard_mask = discard_mask;
    390     } else if (psc_ehlo_discard_maps && psc_ehlo_discard_maps->error) {
    391 	msg_fatal("%s lookup error for %s",
    392 		  psc_ehlo_discard_maps->title, state->smtp_client_addr);
    393     } else if (state->flags & PSC_STATE_FLAG_USING_TLS) {
    394 	reply = psc_smtpd_ehlo_reply_tls;
    395 	state->ehlo_discard_mask = psc_ehlo_discard_mask | EHLO_MASK_STARTTLS;
    396     } else {
    397 	reply = psc_smtpd_ehlo_reply_plain;
    398 	state->ehlo_discard_mask = psc_ehlo_discard_mask;
    399     }
    400     return (PSC_SEND_REPLY(state, reply));
    401 }
    402 
    403 /* psc_starttls_resume - resume the SMTP protocol after tlsproxy activation */
    404 
    405 static void psc_starttls_resume(int unused_event, void *context)
    406 {
    407     const char *myname = "psc_starttls_resume";
    408     PSC_STATE *state = (PSC_STATE *) context;
    409 
    410     /*
    411      * Reset SMTP server state if STARTTLS was successful.
    412      */
    413     if (state->flags & PSC_STATE_FLAG_USING_TLS) {
    414 	/* Purge the push-back buffer, when implemented. */
    415 	PSC_STRING_RESET(state->helo_name);
    416 	PSC_STRING_RESET(state->sender);
    417 #ifdef TODO_SASL_AUTH
    418 	/* Reset SASL AUTH state. Dovecot responses may change. */
    419 #endif
    420     }
    421 
    422     /*
    423      * Resume read/timeout events. If we still have unread input, resume the
    424      * command processor immediately.
    425      */
    426     PSC_RESUME_SMTP_CMD_EVENTS(state);
    427 }
    428 
    429 /* psc_starttls_cmd - activate the tlsproxy server */
    430 
    431 static int psc_starttls_cmd(PSC_STATE *state, char *args)
    432 {
    433     const char *myname = "psc_starttls_cmd";
    434 
    435     /*
    436      * smtpd(8) incompatibility: we can't send a 4XX reply that TLS is
    437      * unavailable when tlsproxy(8) detects the problem too late.
    438      */
    439     if (PSC_SMTPD_NEXT_TOKEN(args) != 0)
    440 	return (PSC_SEND_REPLY(state, "501 5.5.4 Syntax: STARTTLS\r\n"));
    441     if (state->flags & PSC_STATE_FLAG_USING_TLS)
    442 	return (PSC_SEND_REPLY(state,
    443 			       "554 5.5.1 Error: TLS already active\r\n"));
    444     if (var_psc_use_tls == 0 || (state->ehlo_discard_mask & EHLO_MASK_STARTTLS))
    445 	return (PSC_SEND_REPLY(state,
    446 			   "502 5.5.1 Error: command not implemented\r\n"));
    447 
    448     /*
    449      * Suspend the SMTP protocol until psc_starttls_resume() is called.
    450      */
    451     PSC_SUSPEND_SMTP_CMD_EVENTS(state);
    452     psc_starttls_open(state, psc_starttls_resume);
    453     return (0);
    454 }
    455 
    456 /* psc_extract_addr - extract MAIL/RCPT address, unquoted form */
    457 
    458 static char *psc_extract_addr(VSTRING *result, const char *string)
    459 {
    460     const unsigned char *cp = (const unsigned char *) string;
    461     char   *addr;
    462     char   *colon;
    463     int     stop_at;
    464     int     inquote = 0;
    465 
    466     /*
    467      * smtpd(8) incompatibility: we allow more invalid address forms, and we
    468      * don't validate recipients. We are not going to deliver them so we
    469      * won't have to worry about deliverability. This may have to change when
    470      * we pass the socket to a real SMTP server and replay message envelope
    471      * commands.
    472      */
    473 
    474     /* Skip SP characters. */
    475     while (*cp && *cp == ' ')
    476 	cp++;
    477 
    478     /* Choose the terminator for <addr> or bare addr. */
    479     if (*cp == '<') {
    480 	cp++;
    481 	stop_at = '>';
    482     } else {
    483 	stop_at = ' ';
    484     }
    485 
    486     /* Skip to terminator or end. */
    487     VSTRING_RESET(result);
    488     for ( /* void */ ; *cp; cp++) {
    489 	if (!inquote && *cp == stop_at)
    490 	    break;
    491 	if (*cp == '"') {
    492 	    inquote = !inquote;
    493 	} else {
    494 	    if (*cp == '\\' && *++cp == 0)
    495 		break;
    496 	    VSTRING_ADDCH(result, *cp);
    497 	}
    498     }
    499     VSTRING_TERMINATE(result);
    500 
    501     /*
    502      * smtpd(8) compatibility: truncate deprecated route address form. This
    503      * is primarily to simplify logfile analysis.
    504      */
    505     addr = STR(result);
    506     if (*addr == '@' && (colon = strchr(addr, ':')) != 0)
    507 	addr = colon + 1;
    508     return (addr);
    509 }
    510 
    511 /* psc_mail_cmd - record MAIL and respond */
    512 
    513 static int psc_mail_cmd(PSC_STATE *state, char *args)
    514 {
    515     char   *colon;
    516     char   *addr;
    517 
    518     /*
    519      * smtpd(8) incompatibility: we never reject the sender, and we ignore
    520      * additional arguments.
    521      */
    522     if (var_psc_helo_required && state->helo_name == 0)
    523 	return (PSC_SEND_REPLY(state,
    524 			       "503 5.5.1 Error: send HELO/EHLO first\r\n"));
    525     if (state->sender != 0)
    526 	return (PSC_SEND_REPLY(state,
    527 			       "503 5.5.1 Error: nested MAIL command\r\n"));
    528     if (args == 0 || (colon = strchr(args, ':')) == 0)
    529 	return (PSC_SEND_REPLY(state,
    530 			       "501 5.5.4 Syntax: MAIL FROM:<address>\r\n"));
    531     if ((addr = psc_extract_addr(psc_temp, colon + 1)) == 0)
    532 	return (PSC_SEND_REPLY(state,
    533 			       "501 5.1.7 Bad sender address syntax\r\n"));
    534     PSC_STRING_UPDATE(state->sender, addr);
    535     return (PSC_SEND_REPLY(state, "250 2.1.0 Ok\r\n"));
    536 }
    537 
    538 /* psc_soften_reply - copy and soft-bounce a reply */
    539 
    540 static char *psc_soften_reply(const char *reply)
    541 {
    542     static VSTRING *buf = 0;
    543 
    544     if (buf == 0)
    545 	buf = vstring_alloc(100);
    546     vstring_strcpy(buf, reply);
    547     if (reply[0] == '5')
    548 	STR(buf)[0] = '4';
    549     if (reply[4] == '5')
    550 	STR(buf)[4] = '4';
    551     return (STR(buf));
    552 }
    553 
    554 /* psc_rcpt_cmd record RCPT and respond */
    555 
    556 static int psc_rcpt_cmd(PSC_STATE *state, char *args)
    557 {
    558     char   *colon;
    559     char   *addr;
    560 
    561     /*
    562      * smtpd(8) incompatibility: we reject all recipients, and ignore
    563      * additional arguments.
    564      */
    565     if (state->sender == 0)
    566 	return (PSC_SEND_REPLY(state,
    567 			       "503 5.5.1 Error: need MAIL command\r\n"));
    568     if (args == 0 || (colon = strchr(args, ':')) == 0)
    569 	return (PSC_SEND_REPLY(state,
    570 			       "501 5.5.4 Syntax: RCPT TO:<address>\r\n"));
    571     if ((addr = psc_extract_addr(psc_temp, colon + 1)) == 0)
    572 	return (PSC_SEND_REPLY(state,
    573 			     "501 5.1.3 Bad recipient address syntax\r\n"));
    574     msg_info("NOQUEUE: reject: RCPT from [%s]:%s: %.*s; "
    575 	     "from=<%s>, to=<%s>, proto=%s, helo=<%s>",
    576 	     PSC_CLIENT_ADDR_PORT(state),
    577 	     (int) strlen(state->rcpt_reply) - 2,
    578 	     var_soft_bounce == 0 ? state->rcpt_reply :
    579 	     psc_soften_reply(state->rcpt_reply),
    580 	     info_log_addr_form_sender(state->sender),
    581 	     info_log_addr_form_recipient(addr), state->protocol,
    582 	     state->helo_name ? state->helo_name : "");
    583     return (PSC_SEND_REPLY(state, state->rcpt_reply));
    584 }
    585 
    586 /* psc_data_cmd - respond to DATA and disconnect */
    587 
    588 static int psc_data_cmd(PSC_STATE *state, char *args)
    589 {
    590     const char myname[] = "psc_data_cmd";
    591 
    592     /*
    593      * smtpd(8) incompatibility: postscreen(8) drops the connection, instead
    594      * of waiting for the next command. Justification: postscreen(8) should
    595      * never see DATA from a legitimate client, because 1) the server rejects
    596      * every recipient, and 2) the server does not announce PIPELINING.
    597      */
    598     msg_info("DATA without valid RCPT from [%s]:%s",
    599 	     PSC_CLIENT_ADDR_PORT(state));
    600     if (PSC_SMTPD_NEXT_TOKEN(args) != 0)
    601 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    602 					   psc_smtpd_time_event,
    603 					   "501 5.5.4 Syntax: DATA\r\n");
    604     else if (state->sender == 0)
    605 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    606 					   psc_smtpd_time_event,
    607 				  "503 5.5.1 Error: need RCPT command\r\n");
    608     else
    609 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    610 					   psc_smtpd_time_event,
    611 				"554 5.5.1 Error: no valid recipients\r\n");
    612     /* Caution: state is now a dangling pointer. */
    613     return (0);
    614 }
    615 
    616 /* psc_bdat_cmd - respond to BDAT and disconnect */
    617 
    618 static int psc_bdat_cmd(PSC_STATE *state, char *args)
    619 {
    620     const char *myname = "psc_bdat_cmd";
    621 
    622     /*
    623      * smtpd(8) incompatibility: postscreen(8) drops the connection, instead
    624      * of reading the entire BDAT chunk and staying in sync with the client.
    625      * Justification: postscreen(8) should never see BDAT from a legitimate
    626      * client, because 1) the server rejects every recipient, and 2) the
    627      * server does not announce PIPELINING.
    628      */
    629     msg_info("BDAT without valid RCPT from [%s]:%s",
    630 	     PSC_CLIENT_ADDR_PORT(state));
    631     if (state->ehlo_discard_mask & EHLO_MASK_CHUNKING)
    632 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    633 					   psc_smtpd_time_event,
    634 			    "502 5.5.1 Error: command not implemented\r\n");
    635     else if (PSC_SMTPD_NEXT_TOKEN(args) == 0)
    636 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    637 					   psc_smtpd_time_event,
    638 				 "501 5.5.4 Syntax: BDAT count [LAST]\r\n");
    639     else if (state->sender == 0)
    640 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    641 					   psc_smtpd_time_event,
    642 				  "554 5.5.1 Error: need MAIL command\r\n");
    643     else
    644 	PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    645 					   psc_smtpd_time_event,
    646 				"554 5.5.1 Error: no valid recipients\r\n");
    647     /* Caution: state is now a dangling pointer. */
    648     return (0);
    649 }
    650 
    651 /* psc_rset_cmd - reset, send 250 OK */
    652 
    653 static int psc_rset_cmd(PSC_STATE *state, char *unused_args)
    654 {
    655     PSC_STRING_RESET(state->sender);
    656     return (PSC_SEND_REPLY(state, "250 2.0.0 Ok\r\n"));
    657 }
    658 
    659 /* psc_noop_cmd - respond to something */
    660 
    661 static int psc_noop_cmd(PSC_STATE *state, char *unused_args)
    662 {
    663     return (PSC_SEND_REPLY(state, "250 2.0.0 Ok\r\n"));
    664 }
    665 
    666 /* psc_vrfy_cmd - respond to VRFY */
    667 
    668 static int psc_vrfy_cmd(PSC_STATE *state, char *args)
    669 {
    670 
    671     /*
    672      * smtpd(8) incompatibility: we reject all requests, and ignore
    673      * additional arguments.
    674      */
    675     if (PSC_SMTPD_NEXT_TOKEN(args) == 0)
    676 	return (PSC_SEND_REPLY(state,
    677 			       "501 5.5.4 Syntax: VRFY address\r\n"));
    678     if (var_psc_disable_vrfy)
    679 	return (PSC_SEND_REPLY(state,
    680 			       "502 5.5.1 VRFY command is disabled\r\n"));
    681     return (PSC_SEND_REPLY(state, state->rcpt_reply));
    682 }
    683 
    684 /* psc_etrn_cmd - reset, send 250 OK */
    685 
    686 static int psc_etrn_cmd(PSC_STATE *state, char *args)
    687 {
    688 
    689     /*
    690      * smtpd(8) incompatibility: we reject all requests, and ignore
    691      * additional arguments.
    692      */
    693     if (var_psc_helo_required && state->helo_name == 0)
    694 	return (PSC_SEND_REPLY(state,
    695 			       "503 5.5.1 Error: send HELO/EHLO first\r\n"));
    696     if (PSC_SMTPD_NEXT_TOKEN(args) == 0)
    697 	return (PSC_SEND_REPLY(state,
    698 			       "500 Syntax: ETRN domain\r\n"));
    699     return (PSC_SEND_REPLY(state, "458 Unable to queue messages\r\n"));
    700 }
    701 
    702 /* psc_quit_cmd - respond to QUIT and disconnect */
    703 
    704 static int psc_quit_cmd(PSC_STATE *state, char *unused_args)
    705 {
    706     const char *myname = "psc_quit_cmd";
    707 
    708     PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
    709 				       "221 2.0.0 Bye\r\n");
    710     /* Caution: state is now a dangling pointer. */
    711     return (0);
    712 }
    713 
    714 /* psc_smtpd_time_event - handle per-session time limit */
    715 
    716 static void psc_smtpd_time_event(int event, void *context)
    717 {
    718     const char *myname = "psc_smtpd_time_event";
    719     PSC_STATE *state = (PSC_STATE *) context;
    720 
    721     if (msg_verbose > 1)
    722 	msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
    723 		 myname, psc_post_queue_length, psc_check_queue_length,
    724 		 event, vstream_fileno(state->smtp_client_stream),
    725 		 state->smtp_client_addr, state->smtp_client_port,
    726 		 psc_print_state_flags(state->flags, myname));
    727 
    728     msg_info("COMMAND TIME LIMIT from [%s]:%s after %s",
    729 	     PSC_CLIENT_ADDR_PORT(state), state->where);
    730     PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
    731 				       psc_smtpd_timeout_reply);
    732 }
    733 
    734  /*
    735   * The table of all SMTP commands that we know.
    736   */
    737 typedef struct {
    738     const char *name;
    739     int     (*action) (PSC_STATE *, char *);
    740     int     flags;			/* see below */
    741 } PSC_SMTPD_COMMAND;
    742 
    743 #define PSC_SMTPD_CMD_FLAG_NONE		(0)	/* no flags (i.e. disabled) */
    744 #define PSC_SMTPD_CMD_FLAG_ENABLE	(1<<0)	/* command is enabled */
    745 #define PSC_SMTPD_CMD_FLAG_DESTROY	(1<<1)	/* dangling pointer alert */
    746 #define PSC_SMTPD_CMD_FLAG_PRE_TLS	(1<<2)	/* allowed with mandatory TLS */
    747 #define PSC_SMTPD_CMD_FLAG_SUSPEND	(1<<3)	/* suspend command engine */
    748 #define PSC_SMTPD_CMD_FLAG_HAS_PAYLOAD	(1<<4)	/* command has payload */
    749 
    750 static const PSC_SMTPD_COMMAND command_table[] = {
    751     "HELO", psc_helo_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS,
    752     "EHLO", psc_ehlo_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS,
    753     "STARTTLS", psc_starttls_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS | PSC_SMTPD_CMD_FLAG_SUSPEND,
    754     "XCLIENT", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_NONE,
    755     "XFORWARD", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_NONE,
    756     "AUTH", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_NONE,
    757     "MAIL", psc_mail_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
    758     "RCPT", psc_rcpt_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
    759     "DATA", psc_data_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_DESTROY,
    760     /* ".", psc_dot_cmd, PSC_SMTPD_CMD_FLAG_NONE, */
    761     "BDAT", psc_bdat_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_DESTROY | PSC_SMTPD_CMD_FLAG_HAS_PAYLOAD,
    762     "RSET", psc_rset_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
    763     "NOOP", psc_noop_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_PRE_TLS,
    764     "VRFY", psc_vrfy_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
    765     "ETRN", psc_etrn_cmd, PSC_SMTPD_CMD_FLAG_ENABLE,
    766     "QUIT", psc_quit_cmd, PSC_SMTPD_CMD_FLAG_ENABLE | PSC_SMTPD_CMD_FLAG_DESTROY | PSC_SMTPD_CMD_FLAG_PRE_TLS,
    767     0,
    768 };
    769 
    770 /* psc_smtpd_read_event - pseudo responder */
    771 
    772 static void psc_smtpd_read_event(int event, void *context)
    773 {
    774     const char *myname = "psc_smtpd_read_event";
    775     PSC_STATE *state = (PSC_STATE *) context;
    776     time_t *expire_time = state->client_info->expire_time;
    777     int     ch;
    778     struct cmd_trans {
    779 	int     state;
    780 	int     want;
    781 	int     next_state;
    782     };
    783     const char *saved_where;
    784 
    785 #define PSC_SMTPD_CMD_ST_ANY		0
    786 #define PSC_SMTPD_CMD_ST_CR		1
    787 #define PSC_SMTPD_CMD_ST_CR_LF		2
    788 
    789     static const struct cmd_trans cmd_trans[] = {
    790 	PSC_SMTPD_CMD_ST_ANY, '\r', PSC_SMTPD_CMD_ST_CR,
    791 	PSC_SMTPD_CMD_ST_CR, '\n', PSC_SMTPD_CMD_ST_CR_LF,
    792 	0, 0, 0,
    793     };
    794     const struct cmd_trans *transp;
    795     char   *cmd_buffer_ptr;
    796     char   *command;
    797     const PSC_SMTPD_COMMAND *cmdp;
    798     int     write_stat;
    799 
    800     if (msg_verbose > 1)
    801 	msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
    802 		 myname, psc_post_queue_length, psc_check_queue_length,
    803 		 event, vstream_fileno(state->smtp_client_stream),
    804 		 state->smtp_client_addr, state->smtp_client_port,
    805 		 psc_print_state_flags(state->flags, myname));
    806 
    807     /*
    808      * Basic liveness requirements.
    809      *
    810      * Drain all input in the VSTREAM buffer, otherwise this socket will not
    811      * receive further read event notification until the client disconnects!
    812      *
    813      * To suspend this loop temporarily before the buffer is drained, use the
    814      * PSC_SUSPEND_SMTP_CMD_EVENTS() and PSC_RESUME_SMTP_CMD_EVENTS() macros,
    815      * and set the PSC_SMTPD_CMD_FLAG_SUSPEND flag in the command table.
    816      *
    817      * Don't try to read input before it has arrived, otherwise we would starve
    818      * the pseudo threads of other sessions. Get out of here as soon as the
    819      * VSTREAM read buffer dries up. Do not look for more input in kernel
    820      * buffers. That input wasn't likely there when psc_smtpd_read_event()
    821      * was called. Also, yielding the pseudo thread will improve fairness for
    822      * other pseudo threads.
    823      */
    824 
    825     /*
    826      * Note: on entry into this function the VSTREAM buffer may or may not be
    827      * empty, so we test the "no more input" condition at the bottom of the
    828      * loops.
    829      */
    830     for (;;) {
    831 
    832 	/*
    833 	 * Read one command line, possibly one fragment at a time.
    834 	 */
    835 	for (;;) {
    836 
    837 	    if ((ch = PSC_SMTPD_NEXT_CHAR(state)) == VSTREAM_EOF) {
    838 		PSC_CLEAR_EVENT_HANGUP(state, psc_smtpd_time_event);
    839 		return;
    840 	    }
    841 
    842 	    /*
    843 	     * Sanity check. We don't want to store infinitely long commands.
    844 	     */
    845 	    if (state->read_state == PSC_SMTPD_CMD_ST_ANY
    846 		&& VSTRING_LEN(state->cmd_buffer) >= var_line_limit) {
    847 		msg_info("COMMAND LENGTH LIMIT from [%s]:%s after %s",
    848 			 PSC_CLIENT_ADDR_PORT(state), state->where);
    849 		PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
    850 						   psc_smtpd_421_reply);
    851 		return;
    852 	    }
    853 	    VSTRING_ADDCH(state->cmd_buffer, ch);
    854 
    855 	    /*
    856 	     * Try to match the current character desired by the state
    857 	     * machine. If that fails, try to restart the machine with a
    858 	     * match for its first state. Like smtpd(8), we understand lines
    859 	     * ending in <CR><LF> and bare <LF>. Unlike smtpd(8), we may
    860 	     * treat lines ending in bare <LF> as an offense.
    861 	     */
    862 	    for (transp = cmd_trans; transp->state != state->read_state; transp++)
    863 		if (transp->want == 0)
    864 		    msg_panic("%s: command_read: unknown state: %d",
    865 			      myname, state->read_state);
    866 	    if (ch == transp->want)
    867 		state->read_state = transp->next_state;
    868 	    else if (ch == cmd_trans[0].want)
    869 		state->read_state = cmd_trans[0].next_state;
    870 	    else
    871 		state->read_state = PSC_SMTPD_CMD_ST_ANY;
    872 	    if (state->read_state == PSC_SMTPD_CMD_ST_CR_LF) {
    873 		vstring_truncate(state->cmd_buffer,
    874 				 VSTRING_LEN(state->cmd_buffer) - 2);
    875 		break;
    876 	    }
    877 
    878 	    /*
    879 	     * Bare newline test. Note: at this point, state->cmd_buffer is
    880 	     * not null-terminated and may contain embedded null bytes.
    881 	     */
    882 	    if (ch == '\n') {
    883 		if ((state->flags & PSC_STATE_MASK_BARLF_TODO_SKIP)
    884 		    == PSC_STATE_FLAG_BARLF_TODO) {
    885 		    PSC_SMTPD_ESCAPE_TEXT(psc_temp, STR(state->cmd_buffer),
    886 				   VSTRING_LEN(state->cmd_buffer) - 1, 100);
    887 		    msg_info("BARE NEWLINE from [%s]:%s after %s",
    888 			     PSC_CLIENT_ADDR_PORT(state), STR(psc_temp));
    889 		    PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BARLF_FAIL);
    890 		    PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_BARLF_PASS);
    891 		    expire_time[PSC_TINDX_BARLF] = PSC_TIME_STAMP_DISABLED;	/* XXX */
    892 		    /* Skip this test for the remainder of this session. */
    893 		    PSC_SKIP_SESSION_STATE(state, "bare newline test",
    894 					   PSC_STATE_FLAG_BARLF_SKIP);
    895 		    switch (psc_barlf_action) {
    896 		    case PSC_ACT_DROP:
    897 			PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
    898 						       psc_smtpd_time_event,
    899 					    "521 5.5.1 Protocol error\r\n");
    900 			return;
    901 		    case PSC_ACT_ENFORCE:
    902 			PSC_ENFORCE_SESSION_STATE(state,
    903 					    "550 5.5.1 Protocol error\r\n");
    904 			break;
    905 		    case PSC_ACT_IGNORE:
    906 			PSC_UNFAIL_SESSION_STATE(state,
    907 						 PSC_STATE_FLAG_BARLF_FAIL);
    908 			/* Temporarily allowlist until something expires. */
    909 			PSC_PASS_SESSION_STATE(state, "bare newline test",
    910 					       PSC_STATE_FLAG_BARLF_PASS);
    911 			expire_time[PSC_TINDX_BARLF] = event_time() + psc_min_ttl;
    912 			break;
    913 		    default:
    914 			msg_panic("%s: unknown bare_newline action value %d",
    915 				  myname, psc_barlf_action);
    916 		    }
    917 		}
    918 		vstring_truncate(state->cmd_buffer,
    919 				 VSTRING_LEN(state->cmd_buffer) - 1);
    920 		break;
    921 	    }
    922 
    923 	    /*
    924 	     * Yield this pseudo thread when the VSTREAM buffer is empty in
    925 	     * the middle of a command.
    926 	     *
    927 	     * XXX Do not reset the read timeout. The entire command must be
    928 	     * received within the time limit.
    929 	     */
    930 	    if (PSC_SMTPD_BUFFER_EMPTY(state))
    931 		return;
    932 	}
    933 
    934 	/*
    935 	 * Avoid complaints from Postfix maps about malformed content. Note:
    936 	 * this will stop at the first null byte, just like the code that
    937 	 * parses the command name or command arguments.
    938 	 */
    939 #define PSC_BAD_UTF8(str) \
    940 	(var_smtputf8_enable && !valid_utf8_stringz(str))
    941 
    942 	/*
    943 	 * Terminate the command buffer, and apply the last-resort command
    944 	 * editing workaround.
    945 	 */
    946 	VSTRING_TERMINATE(state->cmd_buffer);
    947 	if (psc_cmd_filter != 0 && !PSC_BAD_UTF8(STR(state->cmd_buffer))) {
    948 	    const char *cp;
    949 
    950 	    for (cp = STR(state->cmd_buffer); *cp && IS_SPACE_TAB(*cp); cp++)
    951 		 /* void */ ;
    952 	    if ((cp = psc_dict_get(psc_cmd_filter, cp)) != 0) {
    953 		msg_info("[%s]:%s: replacing command \"%.100s\" with \"%.100s\"",
    954 			 state->smtp_client_addr, state->smtp_client_port,
    955 			 STR(state->cmd_buffer), cp);
    956 		vstring_strcpy(state->cmd_buffer, cp);
    957 	    } else if (psc_cmd_filter->error != 0) {
    958 		msg_fatal("%s:%s lookup error for \"%.100s\"",
    959 			  psc_cmd_filter->type, psc_cmd_filter->name,
    960 			  STR(state->cmd_buffer));
    961 	    }
    962 	}
    963 
    964 	/*
    965 	 * Reset the command buffer write pointer and state machine in
    966 	 * preparation for the next command. For this to work as expected,
    967 	 * VSTRING_RESET() must be non-destructive. We just can't ask for the
    968 	 * VSTRING_LEN() and vstring_end() results.
    969 	 */
    970 	state->read_state = PSC_SMTPD_CMD_ST_ANY;
    971 	VSTRING_RESET(state->cmd_buffer);
    972 
    973 	/*
    974 	 * Process the command line.
    975 	 *
    976 	 * Caution: some command handlers terminate the session and destroy the
    977 	 * session state structure. When this happens we must leave the SMTP
    978 	 * engine to avoid a dangling pointer problem.
    979 	 */
    980 	cmd_buffer_ptr = STR(state->cmd_buffer);
    981 	if (msg_verbose)
    982 	    msg_info("< [%s]:%s: %s", state->smtp_client_addr,
    983 		     state->smtp_client_port, cmd_buffer_ptr);
    984 
    985 	/* Parse the command name. */
    986 	if ((command = PSC_SMTPD_NEXT_TOKEN(cmd_buffer_ptr)) == 0)
    987 	    command = "";
    988 
    989 	/*
    990 	 * The non-SMTP, PIPELINING and command COUNT tests depend on the
    991 	 * client command handler.
    992 	 *
    993 	 * Caution: cmdp->name and cmdp->action may be null on loop exit.
    994 	 */
    995 	saved_where = state->where;
    996 	state->where = PSC_SMTPD_CMD_UNIMPL;
    997 	for (cmdp = command_table; cmdp->name != 0; cmdp++) {
    998 	    if (strcasecmp(command, cmdp->name) == 0) {
    999 		state->where = cmdp->name;
   1000 		break;
   1001 	    }
   1002 	}
   1003 
   1004 	if ((state->flags & PSC_STATE_FLAG_SMTPD_X21)
   1005 	    && cmdp->action != psc_quit_cmd) {
   1006 	    PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
   1007 					       state->final_reply);
   1008 	    return;
   1009 	}
   1010 	/* Non-SMTP command test. */
   1011 	if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_SKIP)
   1012 	    == PSC_STATE_FLAG_NSMTP_TODO && cmdp->name == 0
   1013 	    && (is_header(command)
   1014 		|| PSC_BAD_UTF8(command)
   1015 	/* Ignore forbid_cmds lookup errors. Non-critical feature. */
   1016 		|| (*var_psc_forbid_cmds
   1017 		    && string_list_match(psc_forbid_cmds, command)))) {
   1018 	    printable(command, '?');
   1019 	    PSC_SMTPD_ESCAPE_TEXT(psc_temp, cmd_buffer_ptr,
   1020 				  strlen(cmd_buffer_ptr), 100);
   1021 	    msg_info("NON-SMTP COMMAND from [%s]:%s after %s: %.100s %s",
   1022 		     PSC_CLIENT_ADDR_PORT(state), saved_where,
   1023 		     command, STR(psc_temp));
   1024 	    PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_FAIL);
   1025 	    PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_PASS);
   1026 	    expire_time[PSC_TINDX_NSMTP] = PSC_TIME_STAMP_DISABLED;	/* XXX */
   1027 	    /* Skip this test for the remainder of this SMTP session. */
   1028 	    PSC_SKIP_SESSION_STATE(state, "non-smtp test",
   1029 				   PSC_STATE_FLAG_NSMTP_SKIP);
   1030 	    switch (psc_nsmtp_action) {
   1031 	    case PSC_ACT_DROP:
   1032 		PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
   1033 						   psc_smtpd_time_event,
   1034 		   "521 5.7.0 Error: I can break rules, too. Goodbye.\r\n");
   1035 		return;
   1036 	    case PSC_ACT_ENFORCE:
   1037 		PSC_ENFORCE_SESSION_STATE(state,
   1038 					  "550 5.5.1 Protocol error\r\n");
   1039 		break;
   1040 	    case PSC_ACT_IGNORE:
   1041 		PSC_UNFAIL_SESSION_STATE(state,
   1042 					 PSC_STATE_FLAG_NSMTP_FAIL);
   1043 		/* Temporarily allowlist until something else expires. */
   1044 		PSC_PASS_SESSION_STATE(state, "non-smtp test",
   1045 				       PSC_STATE_FLAG_NSMTP_PASS);
   1046 		expire_time[PSC_TINDX_NSMTP] = event_time() + psc_min_ttl;
   1047 		break;
   1048 	    default:
   1049 		msg_panic("%s: unknown non_smtp_command action value %d",
   1050 			  myname, psc_nsmtp_action);
   1051 	    }
   1052 	}
   1053 	/* Command PIPELINING test. */
   1054 	if ((cmdp->flags & PSC_SMTPD_CMD_FLAG_HAS_PAYLOAD) == 0
   1055 	    && (state->flags & PSC_STATE_MASK_PIPEL_TODO_SKIP)
   1056 	    == PSC_STATE_FLAG_PIPEL_TODO && !PSC_SMTPD_BUFFER_EMPTY(state)) {
   1057 	    printable(command, '?');
   1058 	    PSC_SMTPD_ESCAPE_TEXT(psc_temp, PSC_SMTPD_PEEK_DATA(state),
   1059 				  PSC_SMTPD_PEEK_LEN(state), 100);
   1060 	    msg_info("COMMAND PIPELINING from [%s]:%s after %.100s: %s",
   1061 		     PSC_CLIENT_ADDR_PORT(state), command, STR(psc_temp));
   1062 	    PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PIPEL_FAIL);
   1063 	    PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_PIPEL_PASS);
   1064 	    expire_time[PSC_TINDX_PIPEL] = PSC_TIME_STAMP_DISABLED;	/* XXX */
   1065 	    /* Skip this test for the remainder of this SMTP session. */
   1066 	    PSC_SKIP_SESSION_STATE(state, "pipelining test",
   1067 				   PSC_STATE_FLAG_PIPEL_SKIP);
   1068 	    switch (psc_pipel_action) {
   1069 	    case PSC_ACT_DROP:
   1070 		PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
   1071 						   psc_smtpd_time_event,
   1072 					    "521 5.5.1 Protocol error\r\n");
   1073 		return;
   1074 	    case PSC_ACT_ENFORCE:
   1075 		PSC_ENFORCE_SESSION_STATE(state,
   1076 					  "550 5.5.1 Protocol error\r\n");
   1077 		break;
   1078 	    case PSC_ACT_IGNORE:
   1079 		PSC_UNFAIL_SESSION_STATE(state,
   1080 					 PSC_STATE_FLAG_PIPEL_FAIL);
   1081 		/* Temporarily allowlist until something else expires. */
   1082 		PSC_PASS_SESSION_STATE(state, "pipelining test",
   1083 				       PSC_STATE_FLAG_PIPEL_PASS);
   1084 		expire_time[PSC_TINDX_PIPEL] = event_time() + psc_min_ttl;
   1085 		break;
   1086 	    default:
   1087 		msg_panic("%s: unknown pipelining action value %d",
   1088 			  myname, psc_pipel_action);
   1089 	    }
   1090 	}
   1091 
   1092 	/*
   1093 	 * The following tests don't pass until the client gets all the way
   1094 	 * to the RCPT TO command. However, the client can still fail these
   1095 	 * tests with some later command.
   1096 	 */
   1097 	if (cmdp->action == psc_rcpt_cmd) {
   1098 	    if ((state->flags & PSC_STATE_MASK_BARLF_TODO_PASS_FAIL)
   1099 		== PSC_STATE_FLAG_BARLF_TODO) {
   1100 		PSC_PASS_SESSION_STATE(state, "bare newline test",
   1101 				       PSC_STATE_FLAG_BARLF_PASS);
   1102 		/* XXX Reset to PSC_TIME_STAMP_DISABLED on failure. */
   1103 		expire_time[PSC_TINDX_BARLF] = event_time() + var_psc_barlf_ttl;
   1104 	    }
   1105 	    if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_PASS_FAIL)
   1106 		== PSC_STATE_FLAG_NSMTP_TODO) {
   1107 		PSC_PASS_SESSION_STATE(state, "non-smtp test",
   1108 				       PSC_STATE_FLAG_NSMTP_PASS);
   1109 		/* XXX Reset to PSC_TIME_STAMP_DISABLED on failure. */
   1110 		expire_time[PSC_TINDX_NSMTP] = event_time() + var_psc_nsmtp_ttl;
   1111 	    }
   1112 	    if ((state->flags & PSC_STATE_MASK_PIPEL_TODO_PASS_FAIL)
   1113 		== PSC_STATE_FLAG_PIPEL_TODO) {
   1114 		PSC_PASS_SESSION_STATE(state, "pipelining test",
   1115 				       PSC_STATE_FLAG_PIPEL_PASS);
   1116 		/* XXX Reset to PSC_TIME_STAMP_DISABLED on failure. */
   1117 		expire_time[PSC_TINDX_PIPEL] = event_time() + var_psc_pipel_ttl;
   1118 	    }
   1119 	}
   1120 	/* Command COUNT limit test. */
   1121 	if (++state->command_count > var_psc_cmd_count
   1122 	    && cmdp->action != psc_quit_cmd) {
   1123 	    msg_info("COMMAND COUNT LIMIT from [%s]:%s after %s",
   1124 		     PSC_CLIENT_ADDR_PORT(state), saved_where);
   1125 	    PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
   1126 					       psc_smtpd_421_reply);
   1127 	    return;
   1128 	}
   1129 	/* Finally, execute the command. */
   1130 	if (cmdp->name == 0 || (cmdp->flags & PSC_SMTPD_CMD_FLAG_ENABLE) == 0) {
   1131 	    write_stat = PSC_SEND_REPLY(state,
   1132 			     "502 5.5.2 Error: command not recognized\r\n");
   1133 	} else if (var_psc_enforce_tls
   1134 		   && (state->flags & PSC_STATE_FLAG_USING_TLS) == 0
   1135 		   && (cmdp->flags & PSC_SMTPD_CMD_FLAG_PRE_TLS) == 0) {
   1136 	    write_stat = PSC_SEND_REPLY(state,
   1137 		       "530 5.7.0 Must issue a STARTTLS command first\r\n");
   1138 	} else {
   1139 	    write_stat = cmdp->action(state, cmd_buffer_ptr);
   1140 	    if (cmdp->flags & PSC_SMTPD_CMD_FLAG_DESTROY)
   1141 		return;
   1142 	}
   1143 
   1144 	/*
   1145 	 * Terminate the session after a write error.
   1146 	 */
   1147 	if (write_stat < 0) {
   1148 	    PSC_CLEAR_EVENT_HANGUP(state, psc_smtpd_time_event);
   1149 	    return;
   1150 	}
   1151 
   1152 	/*
   1153 	 * We're suspended, waiting for some external event to happen.
   1154 	 * Hopefully, someone will call us back to process the remainder of
   1155 	 * the pending input, otherwise we could hang.
   1156 	 */
   1157 	if (cmdp->flags & PSC_SMTPD_CMD_FLAG_SUSPEND)
   1158 	    return;
   1159 
   1160 	/*
   1161 	 * Reset the command read timeout before reading the next command.
   1162 	 */
   1163 	event_request_timer(psc_smtpd_time_event, (void *) state,
   1164 			    PSC_EFF_CMD_TIME_LIMIT);
   1165 
   1166 	/*
   1167 	 * Yield this pseudo thread when the VSTREAM buffer is empty.
   1168 	 */
   1169 	if (PSC_SMTPD_BUFFER_EMPTY(state))
   1170 	    return;
   1171     }
   1172 }
   1173 
   1174 /* psc_smtpd_tests - per-session deep protocol test initialization */
   1175 
   1176 void    psc_smtpd_tests(PSC_STATE *state)
   1177 {
   1178     static char *myname = "psc_smtpd_tests";
   1179 
   1180     /*
   1181      * Report errors and progress in the context of this test.
   1182      */
   1183     PSC_BEGIN_TESTS(state, "tests after SMTP handshake");
   1184 
   1185     /*
   1186      * Initialize per-session state that is used only by the dummy engine:
   1187      * the command read buffer and the command read state machine.
   1188      */
   1189     state->cmd_buffer = vstring_alloc(100);
   1190     state->read_state = PSC_SMTPD_CMD_ST_ANY;
   1191 
   1192     /*
   1193      * Disable all after-220 tests when we need to reply with 421 and hang up
   1194      * after reading the next SMTP client command.
   1195      *
   1196      * Opportunistically make postscreen more useful, by turning on all
   1197      * after-220 tests when a bad client failed a before-220 test.
   1198      *
   1199      * Otherwise, only apply the explicitly-configured after-220 tests.
   1200      */
   1201     if (state->flags & PSC_STATE_FLAG_SMTPD_X21) {
   1202 	state->flags &= ~PSC_STATE_MASK_SMTPD_TODO;
   1203     } else if (state->flags & PSC_STATE_MASK_ANY_FAIL) {
   1204 	state->flags |= PSC_STATE_MASK_SMTPD_TODO;
   1205     }
   1206 
   1207     /*
   1208      * Send no SMTP banner to pregreeting clients. This eliminates a lot of
   1209      * "NON-SMTP COMMAND" events, and improves sender/recipient logging.
   1210      */
   1211     if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL) == 0
   1212 	&& PSC_SEND_REPLY(state, psc_smtpd_greeting) != 0) {
   1213 	psc_hangup_event(state);
   1214 	return;
   1215     }
   1216 
   1217     /*
   1218      * Wait for the client to respond.
   1219      */
   1220     PSC_READ_EVENT_REQUEST2(vstream_fileno(state->smtp_client_stream),
   1221 			    psc_smtpd_read_event, psc_smtpd_time_event,
   1222 			    (void *) state, PSC_EFF_CMD_TIME_LIMIT);
   1223 }
   1224 
   1225 /* psc_smtpd_init - per-process deep protocol test initialization */
   1226 
   1227 void    psc_smtpd_init(void)
   1228 {
   1229 
   1230     /*
   1231      * Initialize the server banner.
   1232      */
   1233     vstring_sprintf(psc_temp, "220 %s\r\n", var_smtpd_banner);
   1234     psc_smtpd_greeting = mystrdup(STR(psc_temp));
   1235 
   1236     /*
   1237      * Initialize the HELO reply.
   1238      */
   1239     vstring_sprintf(psc_temp, "250 %s\r\n", var_myhostname);
   1240     psc_smtpd_helo_reply = mystrdup(STR(psc_temp));
   1241 
   1242     /*
   1243      * STARTTLS support. Note the complete absence of #ifdef USE_TLS
   1244      * throughout the postscreen(8) source code. If Postfix is built without
   1245      * TLS support, then the TLS proxy will simply report that TLS is not
   1246      * available, and conventional error handling will take care of the
   1247      * issue.
   1248      *
   1249      * Legacy code copied from smtpd(8). The pre-fabricated EHLO reply depends
   1250      * on this.
   1251      */
   1252     if (*var_psc_tls_level) {
   1253 	switch (tls_level_lookup(var_psc_tls_level)) {
   1254 	default:
   1255 	    msg_fatal("Invalid TLS level \"%s\"", var_psc_tls_level);
   1256 	    /* NOTREACHED */
   1257 	    break;
   1258 	case TLS_LEV_SECURE:
   1259 	case TLS_LEV_VERIFY:
   1260 	case TLS_LEV_FPRINT:
   1261 	    msg_warn("%s: unsupported TLS level \"%s\", using \"encrypt\"",
   1262 		     VAR_PSC_TLS_LEVEL, var_psc_tls_level);
   1263 	    /* FALLTHROUGH */
   1264 	case TLS_LEV_ENCRYPT:
   1265 	    var_psc_enforce_tls = var_psc_use_tls = 1;
   1266 	    break;
   1267 	case TLS_LEV_MAY:
   1268 	    var_psc_enforce_tls = 0;
   1269 	    var_psc_use_tls = 1;
   1270 	    break;
   1271 	case TLS_LEV_NONE:
   1272 	    var_psc_enforce_tls = var_psc_use_tls = 0;
   1273 	    break;
   1274 	}
   1275     }
   1276     var_psc_use_tls = var_psc_use_tls || var_psc_enforce_tls;
   1277 #ifdef TODO_SASL_AUTH
   1278     var_psc_tls_auth_only = var_psc_tls_auth_only || var_psc_enforce_tls;
   1279 #endif
   1280 
   1281     /*
   1282      * Initialize the EHLO reply. Once for plaintext sessions, and once for
   1283      * TLS sessions.
   1284      */
   1285     psc_smtpd_format_ehlo_reply(psc_temp, psc_ehlo_discard_mask);
   1286     psc_smtpd_ehlo_reply_plain = mystrdup(STR(psc_temp));
   1287 
   1288     psc_smtpd_format_ehlo_reply(psc_temp,
   1289 				psc_ehlo_discard_mask | EHLO_MASK_STARTTLS);
   1290     psc_smtpd_ehlo_reply_tls = mystrdup(STR(psc_temp));
   1291 
   1292     /*
   1293      * Initialize the 421 timeout reply.
   1294      */
   1295     vstring_sprintf(psc_temp, "421 4.4.2 %s Error: timeout exceeded\r\n",
   1296 		    var_myhostname);
   1297     psc_smtpd_timeout_reply = mystrdup(STR(psc_temp));
   1298 
   1299     /*
   1300      * Initialize the generic 421 reply.
   1301      */
   1302     vstring_sprintf(psc_temp, "421 %s Service unavailable - try again later\r\n",
   1303 		    var_myhostname);
   1304     psc_smtpd_421_reply = mystrdup(STR(psc_temp));
   1305 
   1306     /*
   1307      * Initialize the reply footer.
   1308      */
   1309     if (*var_psc_rej_footer || *var_psc_rej_ftr_maps)
   1310 	psc_expand_init();
   1311 }
   1312 
   1313 /* psc_smtpd_pre_jail_init - per-process deep protocol test initialization */
   1314 
   1315 void    psc_smtpd_pre_jail_init(void)
   1316 {
   1317 
   1318     /*
   1319      * Determine what server ESMTP features to suppress, typically to avoid
   1320      * inter-operability problems. We do the default filter here, and
   1321      * determine client-dependent filtering on the fly.
   1322      *
   1323      * XXX Bugger. This means we have to restart when the table changes!
   1324      */
   1325     if (*var_psc_ehlo_dis_maps)
   1326 	psc_ehlo_discard_maps = maps_create(VAR_PSC_EHLO_DIS_MAPS,
   1327 					    var_psc_ehlo_dis_maps,
   1328 					    DICT_FLAG_LOCK);
   1329     psc_ehlo_discard_mask = ehlo_mask(var_psc_ehlo_dis_words);
   1330 
   1331     /*
   1332      * Last-resort command editing support.
   1333      */
   1334     if (*var_psc_cmd_filter)
   1335 	psc_cmd_filter = dict_open(var_psc_cmd_filter, O_RDONLY,
   1336 				   DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
   1337 
   1338     /*
   1339      * SMTP server reply footer.
   1340      */
   1341     if (*var_psc_rej_ftr_maps)
   1342 	pcs_send_pre_jail_init();
   1343 }
   1344