1 /* $NetBSD: postscreen_smtpd.c,v 1.4 2022/10/08 16:12: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
psc_helo_cmd(PSC_STATE * state,char * args)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
psc_smtpd_format_ehlo_reply(VSTRING * buf,int discard_mask)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
psc_ehlo_cmd(PSC_STATE * state,char * args)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
psc_starttls_resume(int unused_event,void * context)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
psc_starttls_cmd(PSC_STATE * state,char * args)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 Syntax: EHLO hostname\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
psc_extract_addr(VSTRING * result,const char * string)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
psc_mail_cmd(PSC_STATE * state,char * args)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
psc_soften_reply(const char * reply)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
psc_rcpt_cmd(PSC_STATE * state,char * args)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
psc_data_cmd(PSC_STATE * state,char * args)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
psc_bdat_cmd(PSC_STATE * state,char * args)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
psc_rset_cmd(PSC_STATE * state,char * unused_args)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
psc_noop_cmd(PSC_STATE * state,char * unused_args)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
psc_vrfy_cmd(PSC_STATE * state,char * args)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
psc_etrn_cmd(PSC_STATE * state,char * args)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
psc_quit_cmd(PSC_STATE * state,char * unused_args)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
psc_smtpd_time_event(int event,void * context)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
psc_smtpd_read_event(int event,void * context)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.
880 */
881 if (ch == '\n') {
882 if ((state->flags & PSC_STATE_MASK_BARLF_TODO_SKIP)
883 == PSC_STATE_FLAG_BARLF_TODO) {
884 PSC_SMTPD_ESCAPE_TEXT(psc_temp, STR(state->cmd_buffer),
885 VSTRING_LEN(state->cmd_buffer) - 1, 100);
886 msg_info("BARE NEWLINE from [%s]:%s after %s",
887 PSC_CLIENT_ADDR_PORT(state), STR(psc_temp));
888 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BARLF_FAIL);
889 PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_BARLF_PASS);
890 expire_time[PSC_TINDX_BARLF] = PSC_TIME_STAMP_DISABLED; /* XXX */
891 /* Skip this test for the remainder of this session. */
892 PSC_SKIP_SESSION_STATE(state, "bare newline test",
893 PSC_STATE_FLAG_BARLF_SKIP);
894 switch (psc_barlf_action) {
895 case PSC_ACT_DROP:
896 PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
897 psc_smtpd_time_event,
898 "521 5.5.1 Protocol error\r\n");
899 return;
900 case PSC_ACT_ENFORCE:
901 PSC_ENFORCE_SESSION_STATE(state,
902 "550 5.5.1 Protocol error\r\n");
903 break;
904 case PSC_ACT_IGNORE:
905 PSC_UNFAIL_SESSION_STATE(state,
906 PSC_STATE_FLAG_BARLF_FAIL);
907 /* Temporarily allowlist until something expires. */
908 PSC_PASS_SESSION_STATE(state, "bare newline test",
909 PSC_STATE_FLAG_BARLF_PASS);
910 expire_time[PSC_TINDX_BARLF] = event_time() + psc_min_ttl;
911 break;
912 default:
913 msg_panic("%s: unknown bare_newline action value %d",
914 myname, psc_barlf_action);
915 }
916 }
917 vstring_truncate(state->cmd_buffer,
918 VSTRING_LEN(state->cmd_buffer) - 1);
919 break;
920 }
921
922 /*
923 * Yield this pseudo thread when the VSTREAM buffer is empty in
924 * the middle of a command.
925 *
926 * XXX Do not reset the read timeout. The entire command must be
927 * received within the time limit.
928 */
929 if (PSC_SMTPD_BUFFER_EMPTY(state))
930 return;
931 }
932
933 /*
934 * Avoid complaints from Postfix maps about malformed content.
935 */
936 #define PSC_BAD_UTF8(str, len) \
937 (var_smtputf8_enable && !valid_utf8_string((str), (len)))
938
939 /*
940 * Terminate the command buffer, and apply the last-resort command
941 * editing workaround.
942 */
943 VSTRING_TERMINATE(state->cmd_buffer);
944 if (psc_cmd_filter != 0 && !PSC_BAD_UTF8(STR(state->cmd_buffer),
945 LEN(state->cmd_buffer))) {
946 const char *cp;
947
948 for (cp = STR(state->cmd_buffer); *cp && IS_SPACE_TAB(*cp); cp++)
949 /* void */ ;
950 if ((cp = psc_dict_get(psc_cmd_filter, cp)) != 0) {
951 msg_info("[%s]:%s: replacing command \"%.100s\" with \"%.100s\"",
952 state->smtp_client_addr, state->smtp_client_port,
953 STR(state->cmd_buffer), cp);
954 vstring_strcpy(state->cmd_buffer, cp);
955 } else if (psc_cmd_filter->error != 0) {
956 msg_fatal("%s:%s lookup error for \"%.100s\"",
957 psc_cmd_filter->type, psc_cmd_filter->name,
958 STR(state->cmd_buffer));
959 }
960 }
961
962 /*
963 * Reset the command buffer write pointer and state machine in
964 * preparation for the next command. For this to work as expected,
965 * VSTRING_RESET() must be non-destructive. We just can't ask for the
966 * VSTRING_LEN() and vstring_end() results.
967 */
968 state->read_state = PSC_SMTPD_CMD_ST_ANY;
969 VSTRING_RESET(state->cmd_buffer);
970
971 /*
972 * Process the command line.
973 *
974 * Caution: some command handlers terminate the session and destroy the
975 * session state structure. When this happens we must leave the SMTP
976 * engine to avoid a dangling pointer problem.
977 */
978 cmd_buffer_ptr = STR(state->cmd_buffer);
979 if (msg_verbose)
980 msg_info("< [%s]:%s: %s", state->smtp_client_addr,
981 state->smtp_client_port, cmd_buffer_ptr);
982
983 /* Parse the command name. */
984 if ((command = PSC_SMTPD_NEXT_TOKEN(cmd_buffer_ptr)) == 0)
985 command = "";
986
987 /*
988 * The non-SMTP, PIPELINING and command COUNT tests depend on the
989 * client command handler.
990 *
991 * Caution: cmdp->name and cmdp->action may be null on loop exit.
992 */
993 saved_where = state->where;
994 state->where = PSC_SMTPD_CMD_UNIMPL;
995 for (cmdp = command_table; cmdp->name != 0; cmdp++) {
996 if (strcasecmp(command, cmdp->name) == 0) {
997 state->where = cmdp->name;
998 break;
999 }
1000 }
1001
1002 if ((state->flags & PSC_STATE_FLAG_SMTPD_X21)
1003 && cmdp->action != psc_quit_cmd) {
1004 PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
1005 state->final_reply);
1006 return;
1007 }
1008 /* Non-SMTP command test. */
1009 if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_SKIP)
1010 == PSC_STATE_FLAG_NSMTP_TODO && cmdp->name == 0
1011 && (is_header(command)
1012 || PSC_BAD_UTF8(command, strlen(command))
1013 /* Ignore forbid_cmds lookup errors. Non-critical feature. */
1014 || (*var_psc_forbid_cmds
1015 && string_list_match(psc_forbid_cmds, command)))) {
1016 printable(command, '?');
1017 PSC_SMTPD_ESCAPE_TEXT(psc_temp, cmd_buffer_ptr,
1018 strlen(cmd_buffer_ptr), 100);
1019 msg_info("NON-SMTP COMMAND from [%s]:%s after %s: %.100s %s",
1020 PSC_CLIENT_ADDR_PORT(state), saved_where,
1021 command, STR(psc_temp));
1022 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_FAIL);
1023 PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_NSMTP_PASS);
1024 expire_time[PSC_TINDX_NSMTP] = PSC_TIME_STAMP_DISABLED; /* XXX */
1025 /* Skip this test for the remainder of this SMTP session. */
1026 PSC_SKIP_SESSION_STATE(state, "non-smtp test",
1027 PSC_STATE_FLAG_NSMTP_SKIP);
1028 switch (psc_nsmtp_action) {
1029 case PSC_ACT_DROP:
1030 PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
1031 psc_smtpd_time_event,
1032 "521 5.7.0 Error: I can break rules, too. Goodbye.\r\n");
1033 return;
1034 case PSC_ACT_ENFORCE:
1035 PSC_ENFORCE_SESSION_STATE(state,
1036 "550 5.5.1 Protocol error\r\n");
1037 break;
1038 case PSC_ACT_IGNORE:
1039 PSC_UNFAIL_SESSION_STATE(state,
1040 PSC_STATE_FLAG_NSMTP_FAIL);
1041 /* Temporarily allowlist until something else expires. */
1042 PSC_PASS_SESSION_STATE(state, "non-smtp test",
1043 PSC_STATE_FLAG_NSMTP_PASS);
1044 expire_time[PSC_TINDX_NSMTP] = event_time() + psc_min_ttl;
1045 break;
1046 default:
1047 msg_panic("%s: unknown non_smtp_command action value %d",
1048 myname, psc_nsmtp_action);
1049 }
1050 }
1051 /* Command PIPELINING test. */
1052 if ((cmdp->flags & PSC_SMTPD_CMD_FLAG_HAS_PAYLOAD) == 0
1053 && (state->flags & PSC_STATE_MASK_PIPEL_TODO_SKIP)
1054 == PSC_STATE_FLAG_PIPEL_TODO && !PSC_SMTPD_BUFFER_EMPTY(state)) {
1055 printable(command, '?');
1056 PSC_SMTPD_ESCAPE_TEXT(psc_temp, PSC_SMTPD_PEEK_DATA(state),
1057 PSC_SMTPD_PEEK_LEN(state), 100);
1058 msg_info("COMMAND PIPELINING from [%s]:%s after %.100s: %s",
1059 PSC_CLIENT_ADDR_PORT(state), command, STR(psc_temp));
1060 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PIPEL_FAIL);
1061 PSC_UNPASS_SESSION_STATE(state, PSC_STATE_FLAG_PIPEL_PASS);
1062 expire_time[PSC_TINDX_PIPEL] = PSC_TIME_STAMP_DISABLED; /* XXX */
1063 /* Skip this test for the remainder of this SMTP session. */
1064 PSC_SKIP_SESSION_STATE(state, "pipelining test",
1065 PSC_STATE_FLAG_PIPEL_SKIP);
1066 switch (psc_pipel_action) {
1067 case PSC_ACT_DROP:
1068 PSC_CLEAR_EVENT_DROP_SESSION_STATE(state,
1069 psc_smtpd_time_event,
1070 "521 5.5.1 Protocol error\r\n");
1071 return;
1072 case PSC_ACT_ENFORCE:
1073 PSC_ENFORCE_SESSION_STATE(state,
1074 "550 5.5.1 Protocol error\r\n");
1075 break;
1076 case PSC_ACT_IGNORE:
1077 PSC_UNFAIL_SESSION_STATE(state,
1078 PSC_STATE_FLAG_PIPEL_FAIL);
1079 /* Temporarily allowlist until something else expires. */
1080 PSC_PASS_SESSION_STATE(state, "pipelining test",
1081 PSC_STATE_FLAG_PIPEL_PASS);
1082 expire_time[PSC_TINDX_PIPEL] = event_time() + psc_min_ttl;
1083 break;
1084 default:
1085 msg_panic("%s: unknown pipelining action value %d",
1086 myname, psc_pipel_action);
1087 }
1088 }
1089
1090 /*
1091 * The following tests don't pass until the client gets all the way
1092 * to the RCPT TO command. However, the client can still fail these
1093 * tests with some later command.
1094 */
1095 if (cmdp->action == psc_rcpt_cmd) {
1096 if ((state->flags & PSC_STATE_MASK_BARLF_TODO_PASS_FAIL)
1097 == PSC_STATE_FLAG_BARLF_TODO) {
1098 PSC_PASS_SESSION_STATE(state, "bare newline test",
1099 PSC_STATE_FLAG_BARLF_PASS);
1100 /* XXX Reset to PSC_TIME_STAMP_DISABLED on failure. */
1101 expire_time[PSC_TINDX_BARLF] = event_time() + var_psc_barlf_ttl;
1102 }
1103 if ((state->flags & PSC_STATE_MASK_NSMTP_TODO_PASS_FAIL)
1104 == PSC_STATE_FLAG_NSMTP_TODO) {
1105 PSC_PASS_SESSION_STATE(state, "non-smtp test",
1106 PSC_STATE_FLAG_NSMTP_PASS);
1107 /* XXX Reset to PSC_TIME_STAMP_DISABLED on failure. */
1108 expire_time[PSC_TINDX_NSMTP] = event_time() + var_psc_nsmtp_ttl;
1109 }
1110 if ((state->flags & PSC_STATE_MASK_PIPEL_TODO_PASS_FAIL)
1111 == PSC_STATE_FLAG_PIPEL_TODO) {
1112 PSC_PASS_SESSION_STATE(state, "pipelining test",
1113 PSC_STATE_FLAG_PIPEL_PASS);
1114 /* XXX Reset to PSC_TIME_STAMP_DISABLED on failure. */
1115 expire_time[PSC_TINDX_PIPEL] = event_time() + var_psc_pipel_ttl;
1116 }
1117 }
1118 /* Command COUNT limit test. */
1119 if (++state->command_count > var_psc_cmd_count
1120 && cmdp->action != psc_quit_cmd) {
1121 msg_info("COMMAND COUNT LIMIT from [%s]:%s after %s",
1122 PSC_CLIENT_ADDR_PORT(state), saved_where);
1123 PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event,
1124 psc_smtpd_421_reply);
1125 return;
1126 }
1127 /* Finally, execute the command. */
1128 if (cmdp->name == 0 || (cmdp->flags & PSC_SMTPD_CMD_FLAG_ENABLE) == 0) {
1129 write_stat = PSC_SEND_REPLY(state,
1130 "502 5.5.2 Error: command not recognized\r\n");
1131 } else if (var_psc_enforce_tls
1132 && (state->flags & PSC_STATE_FLAG_USING_TLS) == 0
1133 && (cmdp->flags & PSC_SMTPD_CMD_FLAG_PRE_TLS) == 0) {
1134 write_stat = PSC_SEND_REPLY(state,
1135 "530 5.7.0 Must issue a STARTTLS command first\r\n");
1136 } else {
1137 write_stat = cmdp->action(state, cmd_buffer_ptr);
1138 if (cmdp->flags & PSC_SMTPD_CMD_FLAG_DESTROY)
1139 return;
1140 }
1141
1142 /*
1143 * Terminate the session after a write error.
1144 */
1145 if (write_stat < 0) {
1146 PSC_CLEAR_EVENT_HANGUP(state, psc_smtpd_time_event);
1147 return;
1148 }
1149
1150 /*
1151 * We're suspended, waiting for some external event to happen.
1152 * Hopefully, someone will call us back to process the remainder of
1153 * the pending input, otherwise we could hang.
1154 */
1155 if (cmdp->flags & PSC_SMTPD_CMD_FLAG_SUSPEND)
1156 return;
1157
1158 /*
1159 * Reset the command read timeout before reading the next command.
1160 */
1161 event_request_timer(psc_smtpd_time_event, (void *) state,
1162 PSC_EFF_CMD_TIME_LIMIT);
1163
1164 /*
1165 * Yield this pseudo thread when the VSTREAM buffer is empty.
1166 */
1167 if (PSC_SMTPD_BUFFER_EMPTY(state))
1168 return;
1169 }
1170 }
1171
1172 /* psc_smtpd_tests - per-session deep protocol test initialization */
1173
psc_smtpd_tests(PSC_STATE * state)1174 void psc_smtpd_tests(PSC_STATE *state)
1175 {
1176 static char *myname = "psc_smtpd_tests";
1177
1178 /*
1179 * Report errors and progress in the context of this test.
1180 */
1181 PSC_BEGIN_TESTS(state, "tests after SMTP handshake");
1182
1183 /*
1184 * Initialize per-session state that is used only by the dummy engine:
1185 * the command read buffer and the command read state machine.
1186 */
1187 state->cmd_buffer = vstring_alloc(100);
1188 state->read_state = PSC_SMTPD_CMD_ST_ANY;
1189
1190 /*
1191 * Disable all after-220 tests when we need to reply with 421 and hang up
1192 * after reading the next SMTP client command.
1193 *
1194 * Opportunistically make postscreen more useful, by turning on all
1195 * after-220 tests when a bad client failed a before-220 test.
1196 *
1197 * Otherwise, only apply the explicitly-configured after-220 tests.
1198 */
1199 if (state->flags & PSC_STATE_FLAG_SMTPD_X21) {
1200 state->flags &= ~PSC_STATE_MASK_SMTPD_TODO;
1201 } else if (state->flags & PSC_STATE_MASK_ANY_FAIL) {
1202 state->flags |= PSC_STATE_MASK_SMTPD_TODO;
1203 }
1204
1205 /*
1206 * Send no SMTP banner to pregreeting clients. This eliminates a lot of
1207 * "NON-SMTP COMMAND" events, and improves sender/recipient logging.
1208 */
1209 if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL) == 0
1210 && PSC_SEND_REPLY(state, psc_smtpd_greeting) != 0) {
1211 psc_hangup_event(state);
1212 return;
1213 }
1214
1215 /*
1216 * Wait for the client to respond.
1217 */
1218 PSC_READ_EVENT_REQUEST2(vstream_fileno(state->smtp_client_stream),
1219 psc_smtpd_read_event, psc_smtpd_time_event,
1220 (void *) state, PSC_EFF_CMD_TIME_LIMIT);
1221 }
1222
1223 /* psc_smtpd_init - per-process deep protocol test initialization */
1224
psc_smtpd_init(void)1225 void psc_smtpd_init(void)
1226 {
1227
1228 /*
1229 * Initialize the server banner.
1230 */
1231 vstring_sprintf(psc_temp, "220 %s\r\n", var_smtpd_banner);
1232 psc_smtpd_greeting = mystrdup(STR(psc_temp));
1233
1234 /*
1235 * Initialize the HELO reply.
1236 */
1237 vstring_sprintf(psc_temp, "250 %s\r\n", var_myhostname);
1238 psc_smtpd_helo_reply = mystrdup(STR(psc_temp));
1239
1240 /*
1241 * STARTTLS support. Note the complete absence of #ifdef USE_TLS
1242 * throughout the postscreen(8) source code. If Postfix is built without
1243 * TLS support, then the TLS proxy will simply report that TLS is not
1244 * available, and conventional error handling will take care of the
1245 * issue.
1246 *
1247 * Legacy code copied from smtpd(8). The pre-fabricated EHLO reply depends
1248 * on this.
1249 */
1250 if (*var_psc_tls_level) {
1251 switch (tls_level_lookup(var_psc_tls_level)) {
1252 default:
1253 msg_fatal("Invalid TLS level \"%s\"", var_psc_tls_level);
1254 /* NOTREACHED */
1255 break;
1256 case TLS_LEV_SECURE:
1257 case TLS_LEV_VERIFY:
1258 case TLS_LEV_FPRINT:
1259 msg_warn("%s: unsupported TLS level \"%s\", using \"encrypt\"",
1260 VAR_PSC_TLS_LEVEL, var_psc_tls_level);
1261 /* FALLTHROUGH */
1262 case TLS_LEV_ENCRYPT:
1263 var_psc_enforce_tls = var_psc_use_tls = 1;
1264 break;
1265 case TLS_LEV_MAY:
1266 var_psc_enforce_tls = 0;
1267 var_psc_use_tls = 1;
1268 break;
1269 case TLS_LEV_NONE:
1270 var_psc_enforce_tls = var_psc_use_tls = 0;
1271 break;
1272 }
1273 }
1274 var_psc_use_tls = var_psc_use_tls || var_psc_enforce_tls;
1275 #ifdef TODO_SASL_AUTH
1276 var_psc_tls_auth_only = var_psc_tls_auth_only || var_psc_enforce_tls;
1277 #endif
1278
1279 /*
1280 * Initialize the EHLO reply. Once for plaintext sessions, and once for
1281 * TLS sessions.
1282 */
1283 psc_smtpd_format_ehlo_reply(psc_temp, psc_ehlo_discard_mask);
1284 psc_smtpd_ehlo_reply_plain = mystrdup(STR(psc_temp));
1285
1286 psc_smtpd_format_ehlo_reply(psc_temp,
1287 psc_ehlo_discard_mask | EHLO_MASK_STARTTLS);
1288 psc_smtpd_ehlo_reply_tls = mystrdup(STR(psc_temp));
1289
1290 /*
1291 * Initialize the 421 timeout reply.
1292 */
1293 vstring_sprintf(psc_temp, "421 4.4.2 %s Error: timeout exceeded\r\n",
1294 var_myhostname);
1295 psc_smtpd_timeout_reply = mystrdup(STR(psc_temp));
1296
1297 /*
1298 * Initialize the generic 421 reply.
1299 */
1300 vstring_sprintf(psc_temp, "421 %s Service unavailable - try again later\r\n",
1301 var_myhostname);
1302 psc_smtpd_421_reply = mystrdup(STR(psc_temp));
1303
1304 /*
1305 * Initialize the reply footer.
1306 */
1307 if (*var_psc_rej_footer || *var_psc_rej_ftr_maps)
1308 psc_expand_init();
1309 }
1310
1311 /* psc_smtpd_pre_jail_init - per-process deep protocol test initialization */
1312
psc_smtpd_pre_jail_init(void)1313 void psc_smtpd_pre_jail_init(void)
1314 {
1315
1316 /*
1317 * Determine what server ESMTP features to suppress, typically to avoid
1318 * inter-operability problems. We do the default filter here, and
1319 * determine client-dependent filtering on the fly.
1320 *
1321 * XXX Bugger. This means we have to restart when the table changes!
1322 */
1323 if (*var_psc_ehlo_dis_maps)
1324 psc_ehlo_discard_maps = maps_create(VAR_PSC_EHLO_DIS_MAPS,
1325 var_psc_ehlo_dis_maps,
1326 DICT_FLAG_LOCK);
1327 psc_ehlo_discard_mask = ehlo_mask(var_psc_ehlo_dis_words);
1328
1329 /*
1330 * Last-resort command editing support.
1331 */
1332 if (*var_psc_cmd_filter)
1333 psc_cmd_filter = dict_open(var_psc_cmd_filter, O_RDONLY,
1334 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
1335
1336 /*
1337 * SMTP server reply footer.
1338 */
1339 if (*var_psc_rej_ftr_maps)
1340 pcs_send_pre_jail_init();
1341 }
1342