xref: /netbsd-src/external/ibm-public/postfix/dist/src/postscreen/postscreen_early.c (revision 7788a0781fe6ff2cce37368b4578a7ade0850cb1)
1 /*	$NetBSD: postscreen_early.c,v 1.1.1.1 2011/03/02 19:32:26 tron Exp $	*/
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 
52 static char *psc_teaser_greeting;
53 static VSTRING *psc_escape_buf;
54 
55 /* psc_early_event - handle pre-greet, EOF, and DNSBL results. */
56 
57 static 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 
227 static 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 
248 void    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 
290 void    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