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