1*3c275423Schristos /* $NetBSD: postscreen_send.c,v 1.3 2020/03/18 19:05:19 christos Exp $ */
2e8314800Stron
3e8314800Stron /*++
4e8314800Stron /* NAME
5e8314800Stron /* postscreen_send 3
6e8314800Stron /* SUMMARY
7e8314800Stron /* postscreen low-level output
8e8314800Stron /* SYNOPSIS
9e8314800Stron /* #include <postscreen.h>
10e8314800Stron /*
11*3c275423Schristos /* void pcs_send_pre_jail_init(void)
12*3c275423Schristos /*
13e8314800Stron /* int psc_send_reply(state, text)
14e8314800Stron /* PSC_STATE *state;
15e8314800Stron /* const char *text;
16e8314800Stron /*
17e8314800Stron /* int PSC_SEND_REPLY(state, text)
18e8314800Stron /* PSC_STATE *state;
19e8314800Stron /* const char *text;
20e8314800Stron /*
21e8314800Stron /* void psc_send_socket(state)
22e8314800Stron /* PSC_STATE *state;
23e8314800Stron /* DESCRIPTION
24*3c275423Schristos /* pcs_send_pre_jail_init() performs one-time initialization.
25*3c275423Schristos /*
26e8314800Stron /* psc_send_reply() sends the specified text to the specified
27e8314800Stron /* remote SMTP client. In case of an immediate error, it logs
28e8314800Stron /* a warning (except EPIPE) with the client address and port,
29e8314800Stron /* and returns a non-zero result (all errors including EPIPE).
30e8314800Stron /*
31e8314800Stron /* psc_send_reply() does a best effort to send the reply, but
32e8314800Stron /* it won't block when the output is throttled by a hostile
33e8314800Stron /* peer.
34e8314800Stron /*
35e8314800Stron /* PSC_SEND_REPLY() is a legacy wrapper for psc_send_reply().
36e8314800Stron /* It will eventually be replaced by its expansion.
37e8314800Stron /*
38e8314800Stron /* psc_send_socket() sends the specified socket to the real
39e8314800Stron /* Postfix SMTP server. The socket is delivered in the background.
40e8314800Stron /* This function must be called after all other session-related
41e8314800Stron /* work is finished including postscreen cache updates.
42e8314800Stron /*
43e8314800Stron /* In case of an immediate error, psc_send_socket() sends a 421
44e8314800Stron /* reply to the remote SMTP client and closes the connection.
45e694ac3bStron /* If the 220- greeting was sent, sending 421 would be invalid;
46e694ac3bStron /* instead, the client is redirected to the dummy SMTP engine
47e694ac3bStron /* which sends the 421 reply at the first legitimate opportunity.
48e8314800Stron /* LICENSE
49e8314800Stron /* .ad
50e8314800Stron /* .fi
51e8314800Stron /* The Secure Mailer license must be distributed with this software.
52e8314800Stron /* AUTHOR(S)
53e8314800Stron /* Wietse Venema
54e8314800Stron /* IBM T.J. Watson Research
55e8314800Stron /* P.O. Box 704
56e8314800Stron /* Yorktown Heights, NY 10598, USA
57*3c275423Schristos /*
58*3c275423Schristos /* Wietse Venema
59*3c275423Schristos /* Google, Inc.
60*3c275423Schristos /* 111 8th Avenue
61*3c275423Schristos /* New York, NY 10011, USA
62e8314800Stron /*--*/
63e8314800Stron
64e8314800Stron /* System library. */
65e8314800Stron
66e8314800Stron #include <sys_defs.h>
67e8314800Stron #include <string.h>
68e8314800Stron #include <errno.h>
69e8314800Stron
70e8314800Stron /* Utility library. */
71e8314800Stron
72e8314800Stron #include <msg.h>
73e8314800Stron #include <iostuff.h>
74e8314800Stron #include <connect.h>
75b6432918Stron #include <attr.h>
76b6432918Stron #include <vstream.h>
77e8314800Stron
78e8314800Stron /* Global library. */
79e8314800Stron
80e8314800Stron #include <mail_params.h>
81e8314800Stron #include <smtp_reply_footer.h>
82b6432918Stron #include <mail_proto.h>
83*3c275423Schristos #include <maps.h>
84e8314800Stron
85e8314800Stron /* Application-specific. */
86e8314800Stron
87e8314800Stron #include <postscreen.h>
88e8314800Stron
89*3c275423Schristos static MAPS *psc_rej_ftr_maps;
90*3c275423Schristos
91e8314800Stron /*
92e8314800Stron * This program screens all inbound SMTP connections, so it better not waste
93e8314800Stron * time.
94e8314800Stron */
95e8314800Stron #define PSC_SEND_SOCK_CONNECT_TIMEOUT 1
96e8314800Stron #define PSC_SEND_SOCK_NOTIFY_TIMEOUT 100
97e8314800Stron
98*3c275423Schristos /* pcs_send_pre_jail_init - initialize */
99*3c275423Schristos
pcs_send_pre_jail_init(void)100*3c275423Schristos void pcs_send_pre_jail_init(void)
101*3c275423Schristos {
102*3c275423Schristos static int init_count = 0;
103*3c275423Schristos
104*3c275423Schristos if (init_count++ != 0)
105*3c275423Schristos msg_panic("pcs_send_pre_jail_init: multiple calls");
106*3c275423Schristos
107*3c275423Schristos /*
108*3c275423Schristos * SMTP server reject footer.
109*3c275423Schristos */
110*3c275423Schristos if (*var_psc_rej_ftr_maps)
111*3c275423Schristos psc_rej_ftr_maps = maps_create(VAR_SMTPD_REJ_FTR_MAPS,
112*3c275423Schristos var_psc_rej_ftr_maps,
113*3c275423Schristos DICT_FLAG_LOCK);
114*3c275423Schristos }
115*3c275423Schristos
116*3c275423Schristos /* psc_get_footer - find that footer */
117*3c275423Schristos
psc_get_footer(const char * text,ssize_t text_len)118*3c275423Schristos static const char *psc_get_footer(const char *text, ssize_t text_len)
119*3c275423Schristos {
120*3c275423Schristos static VSTRING *footer_buf = 0;
121*3c275423Schristos
122*3c275423Schristos if (footer_buf == 0)
123*3c275423Schristos footer_buf = vstring_alloc(100);
124*3c275423Schristos /* Strip the \r\n for consistency with smtpd. */
125*3c275423Schristos vstring_strncpy(footer_buf, text, text_len);
126*3c275423Schristos return (psc_maps_find(psc_rej_ftr_maps, STR(footer_buf), 0));
127*3c275423Schristos }
128*3c275423Schristos
129e8314800Stron /* psc_send_reply - send reply to remote SMTP client */
130e8314800Stron
psc_send_reply(PSC_STATE * state,const char * text)131e8314800Stron int psc_send_reply(PSC_STATE *state, const char *text)
132e8314800Stron {
133e8314800Stron ssize_t start;
134e8314800Stron int ret;
135*3c275423Schristos const char *footer;
136*3c275423Schristos ssize_t text_len = strlen(text) - 2;
137e8314800Stron
138e8314800Stron if (msg_verbose)
139e8314800Stron msg_info("> [%s]:%s: %.*s", state->smtp_client_addr,
140*3c275423Schristos state->smtp_client_port, (int) text_len, text);
141e8314800Stron
142e8314800Stron /*
143e8314800Stron * Append the new text to earlier text that could not be sent because the
144e8314800Stron * output was throttled.
145e8314800Stron */
146e8314800Stron start = VSTRING_LEN(state->send_buf);
147e8314800Stron vstring_strcat(state->send_buf, text);
148e8314800Stron
149e8314800Stron /*
150e8314800Stron * For soft_bounce support, we also fix the REJECT logging before the
151e8314800Stron * dummy SMTP engine calls the psc_send_reply() output routine. We do
152e8314800Stron * some double work, but it is for debugging only.
153e8314800Stron */
154e8314800Stron if (var_soft_bounce) {
155e8314800Stron if (text[0] == '5')
156e8314800Stron STR(state->send_buf)[start + 0] = '4';
157e8314800Stron if (text[4] == '5')
158e8314800Stron STR(state->send_buf)[start + 4] = '4';
159e8314800Stron }
160e8314800Stron
161e8314800Stron /*
162e8314800Stron * Append the optional reply footer.
163e8314800Stron */
164*3c275423Schristos if ((*text == '4' || *text == '5')
165*3c275423Schristos && ((psc_rej_ftr_maps != 0
166*3c275423Schristos && (footer = psc_get_footer(text, text_len)) != 0)
167*3c275423Schristos || *(footer = var_psc_rej_footer) != 0))
168*3c275423Schristos smtp_reply_footer(state->send_buf, start, footer,
169e8314800Stron STR(psc_expand_filter), psc_expand_lookup,
170837e7c1aSchristos (void *) state);
171e8314800Stron
172e8314800Stron /*
173e8314800Stron * Do a best effort sending text, but don't block when the output is
174e8314800Stron * throttled by a hostile peer.
175e8314800Stron */
176e8314800Stron ret = write(vstream_fileno(state->smtp_client_stream),
177e8314800Stron STR(state->send_buf), LEN(state->send_buf));
178e8314800Stron if (ret > 0)
179e8314800Stron vstring_truncate(state->send_buf, ret - LEN(state->send_buf));
180e8314800Stron if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET)
181e8314800Stron msg_warn("write [%s]:%s: %m", state->smtp_client_addr,
182e8314800Stron state->smtp_client_port);
183e8314800Stron return (ret < 0 && errno != EAGAIN);
184e8314800Stron }
185e8314800Stron
186e8314800Stron /* psc_send_socket_close_event - file descriptor has arrived or timeout */
187e8314800Stron
psc_send_socket_close_event(int event,void * context)188837e7c1aSchristos static void psc_send_socket_close_event(int event, void *context)
189e8314800Stron {
190e8314800Stron const char *myname = "psc_send_socket_close_event";
191e8314800Stron PSC_STATE *state = (PSC_STATE *) context;
192e8314800Stron
193e8314800Stron if (msg_verbose > 1)
194e8314800Stron msg_info("%s: sq=%d cq=%d event %d on send socket %d from [%s]:%s",
195e8314800Stron myname, psc_post_queue_length, psc_check_queue_length,
196e8314800Stron event, state->smtp_server_fd, state->smtp_client_addr,
197e8314800Stron state->smtp_client_port);
198e8314800Stron
199e8314800Stron /*
200e8314800Stron * The real SMTP server has closed the local IPC channel, or we have
201e8314800Stron * reached the limit of our patience. In the latter case it is still
202e8314800Stron * possible that the real SMTP server will receive the socket so we
203e8314800Stron * should not interfere.
204e8314800Stron */
205e8314800Stron PSC_CLEAR_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event,
206e8314800Stron context);
207e8314800Stron if (event == EVENT_TIME)
208e8314800Stron msg_warn("timeout sending connection to service %s",
209e8314800Stron psc_smtpd_service_name);
210e8314800Stron psc_free_session_state(state);
211e8314800Stron }
212e8314800Stron
213e8314800Stron /* psc_send_socket - send socket to real SMTP server process */
214e8314800Stron
psc_send_socket(PSC_STATE * state)215e8314800Stron void psc_send_socket(PSC_STATE *state)
216e8314800Stron {
217e8314800Stron const char *myname = "psc_send_socket";
218e8314800Stron int server_fd;
219b6432918Stron int pass_err;
220b6432918Stron VSTREAM *fp;
221e8314800Stron
222e8314800Stron if (msg_verbose > 1)
223e8314800Stron msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s",
224e8314800Stron myname, psc_post_queue_length, psc_check_queue_length,
225e8314800Stron vstream_fileno(state->smtp_client_stream),
226e8314800Stron state->smtp_client_addr, state->smtp_client_port);
227e8314800Stron
228e8314800Stron /*
229e8314800Stron * Connect to the real SMTP service over a local IPC channel, send the
230e8314800Stron * file descriptor, and close the file descriptor to save resources.
231e8314800Stron * Experience has shown that some systems will discard information when
232e8314800Stron * we close a channel immediately after writing. Thus, we waste resources
233e8314800Stron * waiting for the remote side to close the local IPC channel first. The
234e8314800Stron * good side of waiting is that we learn when the real SMTP server is
235e8314800Stron * falling behind.
236e8314800Stron *
237e8314800Stron * This is where we would forward the connection to an SMTP server that
238e8314800Stron * provides an appropriate level of service for this client class. For
239e8314800Stron * example, a server that is more forgiving, or one that is more
240e8314800Stron * suspicious. Alternatively, we could send attributes along with the
241e8314800Stron * socket with client reputation information, making everything even more
242e8314800Stron * Postfix-specific.
243e8314800Stron */
244e8314800Stron if ((server_fd =
245b6432918Stron LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING,
246e8314800Stron PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) {
247e8314800Stron msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name);
248e694ac3bStron if (state->flags & PSC_STATE_FLAG_PREGR_TODO) {
249e694ac3bStron PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n");
250e694ac3bStron } else {
251e8314800Stron PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n");
252e8314800Stron psc_free_session_state(state);
253e694ac3bStron }
254e8314800Stron return;
255e8314800Stron }
256b6432918Stron /* XXX Note: no dummy read between LOCAL_SEND_FD() and attr_print(). */
257b6432918Stron fp = vstream_fdopen(server_fd, O_RDWR);
258b6432918Stron pass_err =
259b6432918Stron (LOCAL_SEND_FD(server_fd,
260b6432918Stron vstream_fileno(state->smtp_client_stream)) < 0
261b6432918Stron || (attr_print(fp, ATTR_FLAG_NONE,
262837e7c1aSchristos SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, state->smtp_client_addr),
263837e7c1aSchristos SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_PORT, state->smtp_client_port),
264837e7c1aSchristos SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_ADDR, state->smtp_server_addr),
265837e7c1aSchristos SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_PORT, state->smtp_server_port),
266b6432918Stron ATTR_TYPE_END) || vstream_fflush(fp)));
267837e7c1aSchristos /* XXX Note: no read between attr_print() and vstream_fdclose(). */
268b6432918Stron (void) vstream_fdclose(fp);
269b6432918Stron if (pass_err != 0) {
270e8314800Stron msg_warn("cannot pass connection to service %s: %m",
271e8314800Stron psc_smtpd_service_name);
272e694ac3bStron (void) close(server_fd);
273e694ac3bStron if (state->flags & PSC_STATE_FLAG_PREGR_TODO) {
274e694ac3bStron PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n");
275e694ac3bStron } else {
276e8314800Stron PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n");
277e8314800Stron psc_free_session_state(state);
278e694ac3bStron }
279e8314800Stron return;
280e8314800Stron } else {
281e8314800Stron
282e8314800Stron /*
283e8314800Stron * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug
284e8314800Stron * where smtp-source sometimes sees the connection being closed after
285e8314800Stron * it has already received the real SMTP server's 220 greeting!
286e8314800Stron */
287e8314800Stron #if 0
288e8314800Stron PSC_DEL_CLIENT_STATE(state);
289e8314800Stron #endif
290e694ac3bStron PSC_ADD_SERVER_STATE(state, server_fd);
291e8314800Stron PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event,
292837e7c1aSchristos (void *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT);
293e8314800Stron return;
294e8314800Stron }
295e8314800Stron }
296