xref: /netbsd-src/external/ibm-public/postfix/dist/src/postscreen/postscreen_tests.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: postscreen_tests.c,v 1.1.1.2 2013/01/02 18:59:04 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	postscreen_tests 3
6 /* SUMMARY
7 /*	postscreen tests timestamp/flag bulk support
8 /* SYNOPSIS
9 /*	#include <postscreen.h>
10 /*
11 /*	void	PSC_INIT_TESTS(state)
12 /*	PSC_STATE *state;
13 /*
14 /*	void	psc_new_tests(state)
15 /*	PSC_STATE *state;
16 /*
17 /*	void	psc_parse_tests(state, stamp_text, time_value)
18 /*	PSC_STATE *state;
19 /*	const char *stamp_text;
20 /*	time_t time_value;
21 /*
22 /*	char	*psc_print_tests(buffer, state)
23 /*	VSTRING	*buffer;
24 /*	PSC_STATE *state;
25 /*
26 /*	char	*psc_print_grey_key(buffer, client, helo, sender, rcpt)
27 /*	VSTRING	*buffer;
28 /*	const char *client;
29 /*	const char *helo;
30 /*	const char *sender;
31 /*	const char *rcpt;
32 /* DESCRIPTION
33 /*	The functions in this module overwrite the per-test expiration
34 /*	time stamps and all flags bits.  Some functions are implemented
35 /*	as unsafe macros, meaning they evaluate one or more arguments
36 /*	multiple times.
37 /*
38 /*	PSC_INIT_TESTS() is an unsafe macro that sets the per-test
39 /*	expiration time stamps to PSC_TIME_STAMP_INVALID, and that
40 /*	zeroes all the flags bits. These values are not meant to
41 /*	be stored into the postscreen(8) cache.
42 /*
43 /*	psc_new_tests() sets all test expiration time stamps to
44 /*	PSC_TIME_STAMP_NEW, and overwrites all flags bits. Only
45 /*	enabled tests are flagged with PSC_STATE_FLAG_TODO; the
46 /*	object is flagged with PSC_STATE_FLAG_NEW.
47 /*
48 /*	psc_parse_tests() parses a cache file record and overwrites
49 /*	all flags bits. Tests are considered "expired" when they
50 /*	would be expired at the specified time value. Only enabled
51 /*	tests are flagged as "expired"; the object is flagged as
52 /*	"new" if some enabled tests have "new" time stamps.
53 /*
54 /*	psc_print_tests() creates a cache file record for the
55 /*	specified flags and per-test expiration time stamps.
56 /*	This may modify the time stamps for disabled tests.
57 /*
58 /*	psc_print_grey_key() prints a greylist lookup key.
59 /* LICENSE
60 /* .ad
61 /* .fi
62 /*	The Secure Mailer license must be distributed with this software.
63 /* AUTHOR(S)
64 /*	Wietse Venema
65 /*	IBM T.J. Watson Research
66 /*	P.O. Box 704
67 /*	Yorktown Heights, NY 10598, USA
68 /*--*/
69 
70 /* System library. */
71 
72 #include <sys_defs.h>
73 #include <stdio.h>			/* sscanf */
74 
75 /* Utility library. */
76 
77 #include <msg.h>
78 
79 /* Global library. */
80 
81 #include <mail_params.h>
82 
83 /* Application-specific. */
84 
85 #include <postscreen.h>
86 
87  /*
88   * Kludge to detect if some test is enabled.
89   */
90 #define PSC_PREGR_TEST_ENABLE()	(*var_psc_pregr_banner != 0)
91 #define PSC_DNSBL_TEST_ENABLE()	(*var_psc_dnsbl_sites != 0)
92 
93  /*
94   * Format of a persistent cache entry (which is almost but not quite the
95   * same as the in-memory representation).
96   *
97   * Each cache entry has one time stamp for each test.
98   *
99   * - A time stamp of PSC_TIME_STAMP_INVALID must never appear in the cache. It
100   * is reserved for in-memory objects that are still being initialized.
101   *
102   * - A time stamp of PSC_TIME_STAMP_NEW indicates that the test never passed.
103   * Postscreen will log the client with "pass new" when it passes the final
104   * test.
105   *
106   * - A time stamp of PSC_TIME_STAMP_DISABLED indicates that the test never
107   * passed, and that the test was disabled when the cache entry was written.
108   *
109   * - Otherwise, the test was passed, and the time stamp indicates when that
110   * test result expires.
111   *
112   * A cache entry is expired when the time stamps of all passed tests are
113   * expired.
114   */
115 
116 /* psc_new_tests - initialize new test results from scratch */
117 
118 void    psc_new_tests(PSC_STATE *state)
119 {
120 
121     /*
122      * We know this client is brand new.
123      */
124     state->flags = PSC_STATE_FLAG_NEW;
125 
126     /*
127      * Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later
128      * recognize cache entries that haven't passed all enabled tests. When we
129      * write a cache entry to the database, any new-but-disabled tests will
130      * get a PSC_TIME_STAMP_DISABLED time stamp.
131      */
132     state->pregr_stamp = PSC_TIME_STAMP_NEW;
133     state->dnsbl_stamp = PSC_TIME_STAMP_NEW;
134     state->pipel_stamp = PSC_TIME_STAMP_NEW;
135     state->nsmtp_stamp = PSC_TIME_STAMP_NEW;
136     state->barlf_stamp = PSC_TIME_STAMP_NEW;
137     state->penal_stamp = PSC_TIME_STAMP_NEW;
138 
139     /*
140      * Don't flag disabled tests as "todo", because there would be no way to
141      * make those bits go away.
142      */
143     if (PSC_PREGR_TEST_ENABLE())
144 	state->flags |= PSC_STATE_FLAG_PREGR_TODO;
145     if (PSC_DNSBL_TEST_ENABLE())
146 	state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
147     if (var_psc_pipel_enable)
148 	state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
149     if (var_psc_nsmtp_enable)
150 	state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
151     if (var_psc_barlf_enable)
152 	state->flags |= PSC_STATE_FLAG_BARLF_TODO;
153 }
154 
155 /* psc_parse_tests - parse test results from cache */
156 
157 void    psc_parse_tests(PSC_STATE *state,
158 			        const char *stamp_str,
159 			        time_t time_value)
160 {
161     unsigned long pregr_stamp;
162     unsigned long dnsbl_stamp;
163     unsigned long pipel_stamp;
164     unsigned long nsmtp_stamp;
165     unsigned long barlf_stamp;
166     unsigned long penal_stamp;
167 
168 #ifdef NONPROD
169     time_t  penalty_left;
170 
171 #endif
172 
173     /*
174      * We don't know what tests have expired or have never passed.
175      */
176     state->flags = 0;
177 
178     /*
179      * Parse the cache entry, and allow for older postscreen versions that
180      * implemented fewer tests. We pretend that the newer tests were disabled
181      * at the time that the cache entry was written.
182      *
183      * Flag the cache entry as "new" when the cache entry has fields for all
184      * enabled tests, but the remote SMTP client has not yet passed all those
185      * tests.
186      */
187     switch (sscanf(stamp_str, "%lu;%lu;%lu;%lu;%lu;%lu",
188 		   &pregr_stamp, &dnsbl_stamp, &pipel_stamp, &nsmtp_stamp,
189 		   &barlf_stamp, &penal_stamp)) {
190     case 0:
191 	pregr_stamp = PSC_TIME_STAMP_DISABLED;
192     case 1:
193 	dnsbl_stamp = PSC_TIME_STAMP_DISABLED;
194     case 2:
195 	pipel_stamp = PSC_TIME_STAMP_DISABLED;
196     case 3:
197 	nsmtp_stamp = PSC_TIME_STAMP_DISABLED;
198     case 4:
199 	barlf_stamp = PSC_TIME_STAMP_DISABLED;
200     case 5:
201 	penal_stamp = PSC_TIME_STAMP_DISABLED;
202     default:
203 	break;
204     }
205     state->pregr_stamp = pregr_stamp;
206     state->dnsbl_stamp = dnsbl_stamp;
207     state->pipel_stamp = pipel_stamp;
208     state->nsmtp_stamp = nsmtp_stamp;
209     state->barlf_stamp = barlf_stamp;
210     state->penal_stamp = penal_stamp;
211 
212     if (pregr_stamp == PSC_TIME_STAMP_NEW
213 	|| dnsbl_stamp == PSC_TIME_STAMP_NEW
214 	|| pipel_stamp == PSC_TIME_STAMP_NEW
215 	|| nsmtp_stamp == PSC_TIME_STAMP_NEW
216 	|| barlf_stamp == PSC_TIME_STAMP_NEW)
217 	state->flags |= PSC_STATE_FLAG_NEW;
218 
219     /*
220      * Don't flag disabled tests as "todo", because there would be no way to
221      * make those bits go away.
222      */
223     if (PSC_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp)
224 	state->flags |= PSC_STATE_FLAG_PREGR_TODO;
225     if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp)
226 	state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
227     if (var_psc_pipel_enable && time_value > state->pipel_stamp)
228 	state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
229     if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp)
230 	state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
231     if (var_psc_barlf_enable && time_value > state->barlf_stamp)
232 	state->flags |= PSC_STATE_FLAG_BARLF_TODO;
233 
234     /*
235      * If any test has expired, proactively refresh tests that will expire
236      * soon. This can increase the occurrence of client-visible delays, but
237      * avoids questions about why a client can pass some test and then fail
238      * within seconds. The proactive refresh time is really a surrogate for
239      * the user's curiosity level, and therefore hard to choose optimally.
240      */
241 #ifdef VAR_PSC_REFRESH_TIME
242     if ((state->flags & PSC_STATE_MASK_ANY_TODO) != 0
243 	&& var_psc_refresh_time > 0) {
244 	time_t  refresh_time = time_value + var_psc_refresh_time;
245 
246 	if (PSC_PREGR_TEST_ENABLE() && refresh_time > state->pregr_stamp)
247 	    state->flags |= PSC_STATE_FLAG_PREGR_TODO;
248 	if (PSC_DNSBL_TEST_ENABLE() && refresh_time > state->dnsbl_stamp)
249 	    state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
250 	if (var_psc_pipel_enable && refresh_time > state->pipel_stamp)
251 	    state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
252 	if (var_psc_nsmtp_enable && refresh_time > state->nsmtp_stamp)
253 	    state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
254 	if (var_psc_barlf_enable && refresh_time > state->barlf_stamp)
255 	    state->flags |= PSC_STATE_FLAG_BARLF_TODO;
256     }
257 #endif
258 
259     /*
260      * Gratuitously make postscreen logging more useful by turning on all
261      * enabled pre-handshake tests when any pre-handshake test is turned on.
262      *
263      * XXX Don't enable PREGREET gratuitously before the test expires. With a
264      * short TTL for DNSBL whitelisting, turning on PREGREET would force a
265      * full postscreen_greet_wait too frequently.
266      */
267 #if 0
268     if (state->flags & PSC_STATE_MASK_EARLY_TODO) {
269 	if (PSC_PREGR_TEST_ENABLE())
270 	    state->flags |= PSC_STATE_FLAG_PREGR_TODO;
271 	if (PSC_DNSBL_TEST_ENABLE())
272 	    state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
273     }
274 #endif
275 
276     /*
277      * Apply unexpired penalty for past behavior.
278      *
279      * XXX Before we can drop connections, change this function to return
280      * success/fail, to inform the caller that the state object no longer
281      * exists.
282      */
283 #ifdef NONPROD
284     if ((penalty_left = state->penal_stamp - event_time()) > 0) {
285 	msg_info("PENALTY %ld for %s",
286 		 (long) penalty_left, state->smtp_client_addr);
287 	PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL);
288 #if 0
289 	switch (psc_penal_action) {
290 	case PSC_ACT_DROP:
291 	    PSC_DROP_SESSION_STATE(state,
292 			     "421 4.3.2 Service currently unavailable\r\n");
293 	    break;
294 	case PSC_ACT_ENFORCE:
295 #endif
296 	    PSC_ENFORCE_SESSION_STATE(state,
297 			     "450 4.3.2 Service currently unavailable\r\n");
298 #if 0
299 	    break;
300 	case PSC_ACT_IGNORE:
301 	    PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL);
302 	    break;
303 	default:
304 	    msg_panic("%s: unknown penalty action value %d",
305 		      myname, psc_penal_action);
306 	}
307 #endif
308     }
309 #endif						/* NONPROD */
310 }
311 
312 /* psc_print_tests - print postscreen cache record */
313 
314 char   *psc_print_tests(VSTRING *buf, PSC_STATE *state)
315 {
316     const char *myname = "psc_print_tests";
317 
318     /*
319      * Sanity check.
320      */
321     if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0)
322 	msg_panic("%s: attempt to save a no-update record", myname);
323 
324     /*
325      * Don't record a client as "passed" while subject to penalty. Be sure to
326      * produce correct PASS OLD/NEW logging.
327      *
328      * XXX This needs to be refined - we should not reset the result of tests
329      * that were passed in previous sessions, otherwise a client may never
330      * pass a multi-stage test such as greylisting. One solution is to keep
331      * the original and updated time stamps around, and to save an updated
332      * time stamp only when the corresponding "pass" flag is raised.
333      */
334 #ifdef NONPROD
335     if (state->flags & PSC_STATE_FLAG_PENAL_FAIL) {
336 	state->pregr_stamp = state->dnsbl_stamp = state->pipel_stamp =
337 	    state->nsmtp_stamp = state->barlf_stamp =
338 	    ((state->flags & PSC_STATE_FLAG_NEW) ?
339 	     PSC_TIME_STAMP_NEW : PSC_TIME_STAMP_DISABLED);
340     }
341 #endif
342 
343     /*
344      * Give disabled tests a dummy time stamp so that we don't log a client
345      * with "pass new" when some disabled test becomes enabled at some later
346      * time.
347      */
348     if (PSC_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PSC_TIME_STAMP_NEW)
349 	state->pregr_stamp = PSC_TIME_STAMP_DISABLED;
350     if (PSC_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PSC_TIME_STAMP_NEW)
351 	state->dnsbl_stamp = PSC_TIME_STAMP_DISABLED;
352     if (var_psc_pipel_enable == 0 && state->pipel_stamp == PSC_TIME_STAMP_NEW)
353 	state->pipel_stamp = PSC_TIME_STAMP_DISABLED;
354     if (var_psc_nsmtp_enable == 0 && state->nsmtp_stamp == PSC_TIME_STAMP_NEW)
355 	state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED;
356     if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW)
357 	state->barlf_stamp = PSC_TIME_STAMP_DISABLED;
358 
359     vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu;%lu",
360 		    (unsigned long) state->pregr_stamp,
361 		    (unsigned long) state->dnsbl_stamp,
362 		    (unsigned long) state->pipel_stamp,
363 		    (unsigned long) state->nsmtp_stamp,
364 		    (unsigned long) state->barlf_stamp,
365 		    (unsigned long) state->penal_stamp);
366     return (STR(buf));
367 }
368 
369 /* psc_print_grey_key - print postscreen cache record */
370 
371 char   *psc_print_grey_key(VSTRING *buf, const char *client,
372 			           const char *helo, const char *sender,
373 			           const char *rcpt)
374 {
375     return (STR(vstring_sprintf(buf, "%s/%s/%s/%s",
376 				client, helo, sender, rcpt)));
377 }
378