xref: /netbsd-src/external/ibm-public/postfix/dist/src/postscreen/postscreen_tests.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: postscreen_tests.c,v 1.2 2017/02/14 01:16:47 christos 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 /*	void	psc_todo_tests(state, time_value)
23 /*	PSC_STATE *state;
24 /*	const char *stamp_text;
25 /*	time_t time_value;
26 /*
27 /*	char	*psc_print_tests(buffer, state)
28 /*	VSTRING	*buffer;
29 /*	PSC_STATE *state;
30 /*
31 /*	char	*psc_print_grey_key(buffer, client, helo, sender, rcpt)
32 /*	VSTRING	*buffer;
33 /*	const char *client;
34 /*	const char *helo;
35 /*	const char *sender;
36 /*	const char *rcpt;
37 /*
38 /*	const char *psc_test_name(tindx)
39 /*	int	tindx;
40 /* DESCRIPTION
41 /*	The functions in this module overwrite the per-test expiration
42 /*	time stamps and all flags bits.  Some functions are implemented
43 /*	as unsafe macros, meaning they evaluate one or more arguments
44 /*	multiple times.
45 /*
46 /*	PSC_INIT_TESTS() is an unsafe macro that sets the per-test
47 /*	expiration time stamps to PSC_TIME_STAMP_INVALID, and that
48 /*	zeroes all the flags bits. These values are not meant to
49 /*	be stored into the postscreen(8) cache.
50 /*
51 /*	PSC_INIT_TEST_FLAGS_ONLY() zeroes all the flag bits.  It
52 /*	should be used when the time stamps are already initialized.
53 /*
54 /*	psc_new_tests() sets all test expiration time stamps to
55 /*	PSC_TIME_STAMP_NEW, and invokes psc_todo_tests().
56 /*
57 /*	psc_parse_tests() parses a cache file record and invokes
58 /*	psc_todo_tests().
59 /*
60 /*	psc_todo_tests() overwrites all per-session flag bits, and
61 /*	populates the flags based on test expiration time stamp
62 /*	information.  Tests are considered "expired" when they
63 /*	would be expired at the specified time value. Only enabled
64 /*	tests are flagged as "expired"; the object is flagged as
65 /*	"new" if some enabled tests have "new" time stamps.
66 /*
67 /*	psc_print_tests() creates a cache file record for the
68 /*	specified flags and per-test expiration time stamps.
69 /*	This may modify the time stamps for disabled tests.
70 /*
71 /*	psc_print_grey_key() prints a greylist lookup key.
72 /*
73 /*	psc_test_name() returns the name for the specified text
74 /*	index.
75 /* LICENSE
76 /* .ad
77 /* .fi
78 /*	The Secure Mailer license must be distributed with this software.
79 /* AUTHOR(S)
80 /*	Wietse Venema
81 /*	IBM T.J. Watson Research
82 /*	P.O. Box 704
83 /*	Yorktown Heights, NY 10598, USA
84 /*--*/
85 
86 /* System library. */
87 
88 #include <sys_defs.h>
89 #include <stdio.h>			/* sscanf */
90 #include <stdlib.h>			/* strtoul */
91 
92 /* Utility library. */
93 
94 #include <msg.h>
95 #include <name_code.h>
96 
97 /* Global library. */
98 
99 #include <mail_params.h>
100 
101 /* Application-specific. */
102 
103 #include <postscreen.h>
104 
105  /*
106   * Kludge to detect if some test is enabled.
107   */
108 #define PSC_PREGR_TEST_ENABLE()	(*var_psc_pregr_banner != 0)
109 #define PSC_DNSBL_TEST_ENABLE()	(*var_psc_dnsbl_sites != 0)
110 
111  /*
112   * Format of a persistent cache entry (which is almost but not quite the
113   * same as the in-memory representation).
114   *
115   * Each cache entry has one time stamp for each test.
116   *
117   * - A time stamp of PSC_TIME_STAMP_INVALID must never appear in the cache. It
118   * is reserved for in-memory objects that are still being initialized.
119   *
120   * - A time stamp of PSC_TIME_STAMP_NEW indicates that the test never passed.
121   * Postscreen will log the client with "pass new" when it passes the final
122   * test.
123   *
124   * - A time stamp of PSC_TIME_STAMP_DISABLED indicates that the test never
125   * passed, and that the test was disabled when the cache entry was written.
126   *
127   * - Otherwise, the test was passed, and the time stamp indicates when that
128   * test result expires.
129   *
130   * A cache entry is expired when the time stamps of all passed tests are
131   * expired.
132   */
133 
134 /* psc_new_tests - initialize new test results from scratch */
135 
136 void    psc_new_tests(PSC_STATE *state)
137 {
138 
139     /*
140      * Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later
141      * recognize cache entries that haven't passed all enabled tests. When we
142      * write a cache entry to the database, any new-but-disabled tests will
143      * get a PSC_TIME_STAMP_DISABLED time stamp.
144      */
145     state->pregr_stamp = PSC_TIME_STAMP_NEW;
146     state->dnsbl_stamp = PSC_TIME_STAMP_NEW;
147     state->pipel_stamp = PSC_TIME_STAMP_NEW;
148     state->nsmtp_stamp = PSC_TIME_STAMP_NEW;
149     state->barlf_stamp = PSC_TIME_STAMP_NEW;
150 
151     /*
152      * Determine what tests need to be completed.
153      */
154     psc_todo_tests(state, PSC_TIME_STAMP_NEW + 1);
155 }
156 
157 /* psc_parse_tests - parse test results from cache */
158 
159 void    psc_parse_tests(PSC_STATE *state,
160 			        const char *stamp_str,
161 			        time_t time_value)
162 {
163     const char *start = stamp_str;
164     char   *cp;
165     time_t *time_stamps = state->client_info->expire_time;
166     time_t *sp;
167 
168     /*
169      * Parse the cache entry, and allow for older postscreen versions that
170      * implemented fewer tests. We pretend that the newer tests were disabled
171      * at the time that the cache entry was written.
172      */
173     for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
174 	*sp = strtoul(start, &cp, 10);
175 	if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE)
176 	    *sp = PSC_TIME_STAMP_DISABLED;
177 	if (msg_verbose)
178 	    msg_info("%s -> %lu", start, (unsigned long) *sp);
179 	if (*cp == ';')
180 	    start = cp + 1;
181 	else
182 	    start = cp;
183     }
184 
185     /*
186      * Determine what tests need to be completed.
187      */
188     psc_todo_tests(state, time_value);
189 }
190 
191 /* psc_todo_tests - determine what tests to perform */
192 
193 void    psc_todo_tests(PSC_STATE *state, time_t time_value)
194 {
195     time_t *time_stamps = state->client_info->expire_time;
196     time_t *sp;
197 
198     /*
199      * Reset all per-session flags.
200      */
201     state->flags = 0;
202 
203     /*
204      * Flag the tests as "new" when the cache entry has fields for all
205      * enabled tests, but the remote SMTP client has not yet passed all those
206      * tests.
207      */
208     for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
209 	if (*sp == PSC_TIME_STAMP_NEW)
210 	    state->flags |= PSC_STATE_FLAG_NEW;
211     }
212 
213     /*
214      * Don't flag disabled tests as "todo", because there would be no way to
215      * make those bits go away.
216      */
217     if (PSC_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp)
218 	state->flags |= PSC_STATE_FLAG_PREGR_TODO;
219     if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp)
220 	state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
221     if (var_psc_pipel_enable && time_value > state->pipel_stamp)
222 	state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
223     if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp)
224 	state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
225     if (var_psc_barlf_enable && time_value > state->barlf_stamp)
226 	state->flags |= PSC_STATE_FLAG_BARLF_TODO;
227 
228     /*
229      * If any test has expired, proactively refresh tests that will expire
230      * soon. This can increase the occurrence of client-visible delays, but
231      * avoids questions about why a client can pass some test and then fail
232      * within seconds. The proactive refresh time is really a surrogate for
233      * the user's curiosity level, and therefore hard to choose optimally.
234      */
235 #ifdef VAR_PSC_REFRESH_TIME
236     if ((state->flags & PSC_STATE_MASK_ANY_TODO) != 0
237 	&& var_psc_refresh_time > 0) {
238 	time_t  refresh_time = time_value + var_psc_refresh_time;
239 
240 	if (PSC_PREGR_TEST_ENABLE() && refresh_time > state->pregr_stamp)
241 	    state->flags |= PSC_STATE_FLAG_PREGR_TODO;
242 	if (PSC_DNSBL_TEST_ENABLE() && refresh_time > state->dnsbl_stamp)
243 	    state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
244 	if (var_psc_pipel_enable && refresh_time > state->pipel_stamp)
245 	    state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
246 	if (var_psc_nsmtp_enable && refresh_time > state->nsmtp_stamp)
247 	    state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
248 	if (var_psc_barlf_enable && refresh_time > state->barlf_stamp)
249 	    state->flags |= PSC_STATE_FLAG_BARLF_TODO;
250     }
251 #endif
252 
253     /*
254      * Gratuitously make postscreen logging more useful by turning on all
255      * enabled pre-handshake tests when any pre-handshake test is turned on.
256      *
257      * XXX Don't enable PREGREET gratuitously before the test expires. With a
258      * short TTL for DNSBL whitelisting, turning on PREGREET would force a
259      * full postscreen_greet_wait too frequently.
260      */
261 #if 0
262     if (state->flags & PSC_STATE_MASK_EARLY_TODO) {
263 	if (PSC_PREGR_TEST_ENABLE())
264 	    state->flags |= PSC_STATE_FLAG_PREGR_TODO;
265 	if (PSC_DNSBL_TEST_ENABLE())
266 	    state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
267     }
268 #endif
269 }
270 
271 /* psc_print_tests - print postscreen cache record */
272 
273 char   *psc_print_tests(VSTRING *buf, PSC_STATE *state)
274 {
275     const char *myname = "psc_print_tests";
276 
277     /*
278      * Sanity check.
279      */
280     if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0)
281 	msg_panic("%s: attempt to save a no-update record", myname);
282 
283     /*
284      * Give disabled tests a dummy time stamp so that we don't log a client
285      * with "pass new" when some disabled test becomes enabled at some later
286      * time.
287      */
288     if (PSC_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PSC_TIME_STAMP_NEW)
289 	state->pregr_stamp = PSC_TIME_STAMP_DISABLED;
290     if (PSC_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PSC_TIME_STAMP_NEW)
291 	state->dnsbl_stamp = PSC_TIME_STAMP_DISABLED;
292     if (var_psc_pipel_enable == 0 && state->pipel_stamp == PSC_TIME_STAMP_NEW)
293 	state->pipel_stamp = PSC_TIME_STAMP_DISABLED;
294     if (var_psc_nsmtp_enable == 0 && state->nsmtp_stamp == PSC_TIME_STAMP_NEW)
295 	state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED;
296     if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW)
297 	state->barlf_stamp = PSC_TIME_STAMP_DISABLED;
298 
299     vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu",
300 		    (unsigned long) state->pregr_stamp,
301 		    (unsigned long) state->dnsbl_stamp,
302 		    (unsigned long) state->pipel_stamp,
303 		    (unsigned long) state->nsmtp_stamp,
304 		    (unsigned long) state->barlf_stamp);
305     return (STR(buf));
306 }
307 
308 /* psc_print_grey_key - print postscreen cache record */
309 
310 char   *psc_print_grey_key(VSTRING *buf, const char *client,
311 			           const char *helo, const char *sender,
312 			           const char *rcpt)
313 {
314     return (STR(vstring_sprintf(buf, "%s/%s/%s/%s",
315 				client, helo, sender, rcpt)));
316 }
317 
318 /* psc_test_name - map test index to symbolic name */
319 
320 const char *psc_test_name(int tindx)
321 {
322     const char *myname = "psc_test_name";
323     const NAME_CODE test_name_map[] = {
324 	PSC_TNAME_PREGR, PSC_TINDX_PREGR,
325 	PSC_TNAME_DNSBL, PSC_TINDX_DNSBL,
326 	PSC_TNAME_PIPEL, PSC_TINDX_PIPEL,
327 	PSC_TNAME_NSMTP, PSC_TINDX_NSMTP,
328 	PSC_TNAME_BARLF, PSC_TINDX_BARLF,
329 	0, -1,
330     };
331     const char *result;
332 
333     if ((result = str_name_code(test_name_map, tindx)) == 0)
334 	msg_panic("%s: bad index %d", myname, tindx);
335     return (result);
336 }
337