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