xref: /netbsd-src/external/ibm-public/postfix/dist/src/postscreen/postscreen_tests.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: postscreen_tests.c,v 1.1.1.1 2011/03/02 19:32:27 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 these tests were disabled
181      * when 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 a cache entry as expired just because some test was never
221      * passed.
222      *
223      * Don't flag disabled tests as "todo", because there would be no way to
224      * make those bits go away.
225      */
226     if (PSC_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp) {
227 	state->flags |= PSC_STATE_FLAG_PREGR_TODO;
228 	if (state->pregr_stamp > PSC_TIME_STAMP_DISABLED)
229 	    state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED;
230     }
231     if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp) {
232 	state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
233 	if (state->dnsbl_stamp > PSC_TIME_STAMP_DISABLED)
234 	    state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED;
235     }
236     if (var_psc_pipel_enable && time_value > state->pipel_stamp) {
237 	state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
238 	if (state->pipel_stamp > PSC_TIME_STAMP_DISABLED)
239 	    state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED;
240     }
241     if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp) {
242 	state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
243 	if (state->nsmtp_stamp > PSC_TIME_STAMP_DISABLED)
244 	    state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED;
245     }
246     if (var_psc_barlf_enable && time_value > state->barlf_stamp) {
247 	state->flags |= PSC_STATE_FLAG_BARLF_TODO;
248 	if (state->barlf_stamp > PSC_TIME_STAMP_DISABLED)
249 	    state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED;
250     }
251 
252     /*
253      * Gratuitously make postscreen logging more useful by turning on all
254      * enabled pre-handshake tests when any pre-handshake test is turned on.
255      *
256      * XXX Don't enable PREGREET gratuitously before the test expires. With a
257      * short TTL for DNSBL whitelisting, turning on PREGREET would force a
258      * full postscreen_greet_wait too frequently.
259      */
260 #if 0
261     if (state->flags & PSC_STATE_MASK_EARLY_TODO) {
262 	if (PSC_PREGR_TEST_ENABLE())
263 	    state->flags |= PSC_STATE_FLAG_PREGR_TODO;
264 	if (PSC_DNSBL_TEST_ENABLE())
265 	    state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
266     }
267 #endif
268 
269     /*
270      * Apply unexpired penalty for past behavior.
271      *
272      * XXX Before we can drop connections, change this function to return
273      * success/fail, to inform the caller that the state object no longer
274      * exists.
275      */
276 #ifdef NONPROD
277     if ((penalty_left = state->penal_stamp - event_time()) > 0) {
278 	msg_info("PENALTY %ld for %s",
279 		 (long) penalty_left, state->smtp_client_addr);
280 	PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL);
281 #if 0
282 	switch (psc_penal_action) {
283 	case PSC_ACT_DROP:
284 	    PSC_DROP_SESSION_STATE(state,
285 			     "421 4.3.2 Service currently unavailable\r\n");
286 	    break;
287 	case PSC_ACT_ENFORCE:
288 #endif
289 	    PSC_ENFORCE_SESSION_STATE(state,
290 			     "450 4.3.2 Service currently unavailable\r\n");
291 #if 0
292 	    break;
293 	case PSC_ACT_IGNORE:
294 	    PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL);
295 	    break;
296 	default:
297 	    msg_panic("%s: unknown penalty action value %d",
298 		      myname, psc_penal_action);
299 	}
300 #endif
301     }
302 #endif						/* NONPROD */
303 }
304 
305 /* psc_print_tests - print postscreen cache record */
306 
307 char   *psc_print_tests(VSTRING *buf, PSC_STATE *state)
308 {
309     const char *myname = "psc_print_tests";
310 
311     /*
312      * Sanity check.
313      */
314     if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0)
315 	msg_panic("%s: attempt to save a no-update record", myname);
316 
317     /*
318      * Don't record a client as "passed" while subject to penalty. Be sure to
319      * produce correct PASS OLD/NEW logging.
320      *
321      * XXX This needs to be refined - we should not reset the result of tests
322      * that were passed in previous sessions, otherwise a client may never
323      * pass a multi-stage test such as greylisting. One solution is to keep
324      * the original and updated time stamps around, and to save an updated
325      * time stamp only when the corresponding "pass" flag is raised.
326      */
327 #ifdef NONPROD
328     if (state->flags & PSC_STATE_FLAG_PENAL_FAIL) {
329 	state->pregr_stamp = state->dnsbl_stamp = state->pipel_stamp =
330 	    state->nsmtp_stamp = state->barlf_stamp =
331 	    ((state->flags & PSC_STATE_FLAG_NEW) ?
332 	     PSC_TIME_STAMP_NEW : PSC_TIME_STAMP_DISABLED);
333     }
334 #endif
335 
336     /*
337      * Give disabled tests a dummy time stamp so that we don't log a client
338      * with "pass new" when some disabled test becomes enabled at some later
339      * time.
340      */
341     if (PSC_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PSC_TIME_STAMP_NEW)
342 	state->pregr_stamp = PSC_TIME_STAMP_DISABLED;
343     if (PSC_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PSC_TIME_STAMP_NEW)
344 	state->dnsbl_stamp = PSC_TIME_STAMP_DISABLED;
345     if (var_psc_pipel_enable == 0 && state->pipel_stamp == PSC_TIME_STAMP_NEW)
346 	state->pipel_stamp = PSC_TIME_STAMP_DISABLED;
347     if (var_psc_nsmtp_enable == 0 && state->nsmtp_stamp == PSC_TIME_STAMP_NEW)
348 	state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED;
349     if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW)
350 	state->barlf_stamp = PSC_TIME_STAMP_DISABLED;
351 
352     vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu;%lu",
353 		    (unsigned long) state->pregr_stamp,
354 		    (unsigned long) state->dnsbl_stamp,
355 		    (unsigned long) state->pipel_stamp,
356 		    (unsigned long) state->nsmtp_stamp,
357 		    (unsigned long) state->barlf_stamp,
358 		    (unsigned long) state->penal_stamp);
359     return (STR(buf));
360 }
361 
362 /* psc_print_grey_key - print postscreen cache record */
363 
364 char   *psc_print_grey_key(VSTRING *buf, const char *client,
365 			           const char *helo, const char *sender,
366 			           const char *rcpt)
367 {
368     return (STR(vstring_sprintf(buf, "%s/%s/%s/%s",
369 				client, helo, sender, rcpt)));
370 }
371