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