1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	postscreen_early 3
6/* SUMMARY
7/*	postscreen pre-handshake tests
8/* SYNOPSIS
9/*	#include <postscreen.h>
10/*
11/*	void	psc_early_init(void)
12/*
13/*	void	psc_early_tests(state)
14/*	PSC_STATE *state;
15/* DESCRIPTION
16/*	psc_early_tests() performs protocol tests before the SMTP
17/*	handshake: the pregreet test and the DNSBL test. Control
18/*	is passed to the psc_smtpd_tests() routine as appropriate.
19/*
20/*	psc_early_init() performs one-time initialization.
21/* LICENSE
22/* .ad
23/* .fi
24/*	The Secure Mailer license must be distributed with this software.
25/* AUTHOR(S)
26/*	Wietse Venema
27/*	IBM T.J. Watson Research
28/*	P.O. Box 704
29/*	Yorktown Heights, NY 10598, USA
30/*--*/
31
32/* System library. */
33
34#include <sys_defs.h>
35#include <sys/socket.h>
36
37/* Utility library. */
38
39#include <msg.h>
40#include <stringops.h>
41#include <mymalloc.h>
42#include <vstring.h>
43
44/* Global library. */
45
46#include <mail_params.h>
47
48/* Application-specific. */
49
50#include <postscreen.h>
51
52static char *psc_teaser_greeting;
53static VSTRING *psc_escape_buf;
54
55/* psc_early_event - handle pre-greet, EOF, and DNSBL results. */
56
57static void psc_early_event(int event, char *context)
58{
59    const char *myname = "psc_early_event";
60    PSC_STATE *state = (PSC_STATE *) context;
61    char    read_buf[PSC_READ_BUF_SIZE];
62    int     read_count;
63    int     dnsbl_score;
64    DELTA_TIME elapsed;
65    const char *dnsbl_name;
66
67    if (msg_verbose > 1)
68	msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
69		 myname, psc_post_queue_length, psc_check_queue_length,
70		 event, vstream_fileno(state->smtp_client_stream),
71		 state->smtp_client_addr, state->smtp_client_port,
72		 psc_print_state_flags(state->flags, myname));
73
74    PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
75			    psc_early_event, context);
76
77    /*
78     * XXX Be sure to empty the DNSBL lookup buffer otherwise we have a
79     * memory leak.
80     *
81     * XXX We can avoid "forgetting" to do this by keeping a pointer to the
82     * DNSBL lookup buffer in the PSC_STATE structure. This also allows us to
83     * shave off a hash table lookup when retrieving the DNSBL result.
84     */
85    switch (event) {
86
87	/*
88	 * We reached the end of the early tests time limit.
89	 */
90    case EVENT_TIME:
91
92	/*
93	 * Check if the SMTP client spoke before its turn.
94	 */
95	if ((state->flags & PSC_STATE_MASK_PREGR_TODO_FAIL)
96	    == PSC_STATE_FLAG_PREGR_TODO) {
97	    state->pregr_stamp = event_time() + var_psc_pregr_ttl;
98	    PSC_PASS_SESSION_STATE(state, "pregreet test",
99				   PSC_STATE_FLAG_PREGR_PASS);
100	}
101	if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL)
102	    && psc_pregr_action == PSC_ACT_IGNORE) {
103	    PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL);
104	    /* Not: PSC_PASS_SESSION_STATE. Repeat this test the next time. */
105	}
106
107	/*
108	 * If the client is DNS blocklisted, drop the connection, send the
109	 * client to a dummy protocol engine, or continue to the next test.
110	 */
111#define PSC_DNSBL_FORMAT \
112	"%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n"
113
114	if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) {
115	    dnsbl_score =
116		psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name,
117				   state->dnsbl_index);
118	    if (dnsbl_score < var_psc_dnsbl_thresh) {
119		state->dnsbl_stamp = event_time() + var_psc_dnsbl_ttl;
120		PSC_PASS_SESSION_STATE(state, "dnsbl test",
121				       PSC_STATE_FLAG_DNSBL_PASS);
122	    } else {
123		msg_info("DNSBL rank %d for [%s]:%s",
124			 dnsbl_score, PSC_CLIENT_ADDR_PORT(state));
125		PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL);
126		switch (psc_dnsbl_action) {
127		case PSC_ACT_DROP:
128		    state->dnsbl_reply = vstring_sprintf(vstring_alloc(100),
129						    PSC_DNSBL_FORMAT, "521",
130				       state->smtp_client_addr, dnsbl_name);
131		    PSC_DROP_SESSION_STATE(state, STR(state->dnsbl_reply));
132		    return;
133		case PSC_ACT_ENFORCE:
134		    state->dnsbl_reply = vstring_sprintf(vstring_alloc(100),
135						    PSC_DNSBL_FORMAT, "550",
136				       state->smtp_client_addr, dnsbl_name);
137		    PSC_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply));
138		    break;
139		case PSC_ACT_IGNORE:
140		    PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL);
141		    /* Not: PSC_PASS_SESSION_STATE. Repeat this test. */
142		    break;
143		default:
144		    msg_panic("%s: unknown dnsbl action value %d",
145			      myname, psc_dnsbl_action);
146
147		}
148	    }
149	}
150
151	/*
152	 * Pass the connection to a real SMTP server, or enter the dummy
153	 * engine for deep tests.
154	 */
155	if (state->flags & (PSC_STATE_FLAG_NOFORWARD | PSC_STATE_MASK_SMTPD_TODO))
156	    psc_smtpd_tests(state);
157	else
158	    psc_conclude(state);
159	return;
160
161	/*
162	 * EOF, or the client spoke before its turn. We simply drop the
163	 * connection, or we continue waiting and allow DNS replies to
164	 * trickle in.
165	 */
166    default:
167	if ((read_count = recv(vstream_fileno(state->smtp_client_stream),
168			  read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
169	    /* Avoid memory leak. */
170	    if (state->flags & PSC_STATE_FLAG_DNSBL_TODO)
171		(void) psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name,
172					  state->dnsbl_index);
173	    /* XXX Wait for DNS replies to come in. */
174	    psc_hangup_event(state);
175	    return;
176	}
177	read_buf[read_count] = 0;
178	escape(psc_escape_buf, read_buf, read_count);
179	msg_info("PREGREET %d after %s from [%s]:%s: %.100s", read_count,
180	       psc_format_delta_time(psc_temp, state->start_time, &elapsed),
181		 PSC_CLIENT_ADDR_PORT(state), STR(psc_escape_buf));
182	PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL);
183	switch (psc_pregr_action) {
184	case PSC_ACT_DROP:
185	    /* Avoid memory leak. */
186	    if (state->flags & PSC_STATE_FLAG_DNSBL_TODO)
187		(void) psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name,
188					  state->dnsbl_index);
189	    PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n");
190	    return;
191	case PSC_ACT_ENFORCE:
192	    /* We call psc_dnsbl_retrieve() when the timer expires. */
193	    PSC_ENFORCE_SESSION_STATE(state, "550 5.5.1 Protocol error\r\n");
194	    break;
195	case PSC_ACT_IGNORE:
196	    /* We call psc_dnsbl_retrieve() when the timer expires. */
197	    /* We must handle this case after the timer expires. */
198	    break;
199	default:
200	    msg_panic("%s: unknown pregreet action value %d",
201		      myname, psc_pregr_action);
202	}
203
204	/*
205	 * Terminate the greet delay if we're just waiting for the pregreet
206	 * test to complete. It is safe to call psc_early_event directly,
207	 * since we are already in that function.
208	 *
209	 * XXX After this code passes all tests, swap around the two blocks in
210	 * this switch statement and fall through from EVENT_READ into
211	 * EVENT_TIME, instead of calling psc_early_event recursively.
212	 */
213	state->flags |= PSC_STATE_FLAG_PREGR_DONE;
214	if (elapsed.dt_sec >= PSC_EFF_GREET_WAIT
215	    || ((state->flags & PSC_STATE_MASK_EARLY_DONE)
216		== PSC_STATE_FLAGS_TODO_TO_DONE(state->flags & PSC_STATE_MASK_EARLY_TODO)))
217	    psc_early_event(EVENT_TIME, context);
218	else
219	    event_request_timer(psc_early_event, context,
220				PSC_EFF_GREET_WAIT - elapsed.dt_sec);
221	return;
222    }
223}
224
225/* psc_early_dnsbl_event - cancel pregreet timer if waiting for DNS only */
226
227static void psc_early_dnsbl_event(int unused_event, char *context)
228{
229    const char *myname = "psc_early_dnsbl_event";
230    PSC_STATE *state = (PSC_STATE *) context;
231
232    if (msg_verbose)
233	msg_info("%s: notify [%s]:%s", myname, PSC_CLIENT_ADDR_PORT(state));
234
235    /*
236     * Terminate the greet delay if we're just waiting for DNSBL lookup to
237     * complete. Don't call psc_early_event directly, that would result in a
238     * dangling pointer.
239     */
240    state->flags |= PSC_STATE_FLAG_DNSBL_DONE;
241    if ((state->flags & PSC_STATE_MASK_EARLY_DONE)
242	== PSC_STATE_FLAGS_TODO_TO_DONE(state->flags & PSC_STATE_MASK_EARLY_TODO))
243	event_request_timer(psc_early_event, context, EVENT_NULL_DELAY);
244}
245
246/* psc_early_tests - start the early (before protocol) tests */
247
248void    psc_early_tests(PSC_STATE *state)
249{
250    const char *myname = "psc_early_tests";
251
252    /*
253     * Report errors and progress in the context of this test.
254     */
255    PSC_BEGIN_TESTS(state, "tests before SMTP handshake");
256
257    /*
258     * Run a PREGREET test. Send half the greeting banner, by way of teaser,
259     * then wait briefly to see if the client speaks before its turn.
260     */
261    if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0
262	&& psc_teaser_greeting != 0
263	&& PSC_SEND_REPLY(state, psc_teaser_greeting) != 0) {
264	psc_hangup_event(state);
265	return;
266    }
267
268    /*
269     * Run a DNS blocklist query.
270     */
271    if ((state->flags & PSC_STATE_FLAG_DNSBL_TODO) != 0)
272	state->dnsbl_index =
273	    psc_dnsbl_request(state->smtp_client_addr, psc_early_dnsbl_event,
274			      (char *) state);
275    else
276	state->dnsbl_index = -1;
277
278    /*
279     * Wait for the client to respond or for DNS lookup to complete.
280     */
281    if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0)
282	PSC_READ_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
283		       psc_early_event, (char *) state, PSC_EFF_GREET_WAIT);
284    else
285	event_request_timer(psc_early_event, (char *) state, PSC_EFF_GREET_WAIT);
286}
287
288/* psc_early_init - initialize early tests */
289
290void    psc_early_init(void)
291{
292    if (*var_psc_pregr_banner) {
293	vstring_sprintf(psc_temp, "220-%s\r\n", var_psc_pregr_banner);
294	psc_teaser_greeting = mystrdup(STR(psc_temp));
295	psc_escape_buf = vstring_alloc(100);
296    }
297}
298