1 /*	$NetBSD: postscreen_tests.c,v 1.4 2022/10/08 16:12:48 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 /*	Wietse Venema
86 /*	Google, Inc.
87 /*	111 8th Avenue
88 /*	New York, NY 10011, USA
89 /*--*/
90 
91 /* System library. */
92 
93 #include <sys_defs.h>
94 #include <stdio.h>			/* sscanf */
95 
96 /* Utility library. */
97 
98 #include <msg.h>
99 #include <name_code.h>
100 #include <sane_strtol.h>
101 
102 /* Global library. */
103 
104 #include <mail_params.h>
105 
106 /* Application-specific. */
107 
108 #include <postscreen.h>
109 
110  /*
111   * Kludge to detect if some test is enabled.
112   */
113 #define PSC_PREGR_TEST_ENABLE()	(*var_psc_pregr_banner != 0)
114 #define PSC_DNSBL_TEST_ENABLE()	(*var_psc_dnsbl_sites != 0)
115 
116  /*
117   * Format of a persistent cache entry (which is almost but not quite the
118   * same as the in-memory representation).
119   *
120   * Each cache entry has one time stamp for each test.
121   *
122   * - A time stamp of PSC_TIME_STAMP_INVALID must never appear in the cache. It
123   * is reserved for in-memory objects that are still being initialized.
124   *
125   * - A time stamp of PSC_TIME_STAMP_NEW indicates that the test never passed.
126   * Postscreen will log the client with "pass new" when it passes the final
127   * test.
128   *
129   * - A time stamp of PSC_TIME_STAMP_DISABLED indicates that the test never
130   * passed, and that the test was disabled when the cache entry was written.
131   *
132   * - Otherwise, the test was passed, and the time stamp indicates when that
133   * test result expires.
134   *
135   * A cache entry is expired when the time stamps of all passed tests are
136   * expired.
137   */
138 
139 /* psc_new_tests - initialize new test results from scratch */
140 
psc_new_tests(PSC_STATE * state)141 void    psc_new_tests(PSC_STATE *state)
142 {
143     time_t *expire_time = state->client_info->expire_time;
144 
145     /*
146      * Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later
147      * recognize cache entries that haven't passed all enabled tests. When we
148      * write a cache entry to the database, any new-but-disabled tests will
149      * get a PSC_TIME_STAMP_DISABLED time stamp.
150      */
151     expire_time[PSC_TINDX_PREGR] = PSC_TIME_STAMP_NEW;
152     expire_time[PSC_TINDX_DNSBL] = PSC_TIME_STAMP_NEW;
153     expire_time[PSC_TINDX_PIPEL] = PSC_TIME_STAMP_NEW;
154     expire_time[PSC_TINDX_NSMTP] = PSC_TIME_STAMP_NEW;
155     expire_time[PSC_TINDX_BARLF] = PSC_TIME_STAMP_NEW;
156 
157     /*
158      * Determine what tests need to be completed.
159      */
160     psc_todo_tests(state, PSC_TIME_STAMP_NEW + 1);
161 }
162 
163 /* psc_parse_tests - parse test results from cache */
164 
psc_parse_tests(PSC_STATE * state,const char * stamp_str,time_t time_value)165 void    psc_parse_tests(PSC_STATE *state,
166 			        const char *stamp_str,
167 			        time_t time_value)
168 {
169     const char *start = stamp_str;
170     char   *cp;
171     time_t *time_stamps = state->client_info->expire_time;
172     time_t *sp;
173 
174     /*
175      * Parse the cache entry, and allow for older postscreen versions that
176      * implemented fewer tests. We pretend that the newer tests were disabled
177      * at the time that the cache entry was written.
178      */
179     for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
180 	*sp = sane_strtoul(start, &cp, 10);
181 	if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE)
182 	    *sp = PSC_TIME_STAMP_DISABLED;
183 	if (msg_verbose)
184 	    msg_info("%s -> %lu", start, (unsigned long) *sp);
185 	if (*cp == ';')
186 	    start = cp + 1;
187 	else
188 	    start = cp;
189     }
190 
191     /*
192      * Determine what tests need to be completed.
193      */
194     psc_todo_tests(state, time_value);
195 }
196 
197 /* psc_todo_tests - determine what tests to perform */
198 
psc_todo_tests(PSC_STATE * state,time_t time_value)199 void    psc_todo_tests(PSC_STATE *state, time_t time_value)
200 {
201     time_t *expire_time = state->client_info->expire_time;
202     time_t *sp;
203 
204     /*
205      * Reset all per-session flags.
206      */
207     state->flags = 0;
208 
209     /*
210      * Flag the tests as "new" when the cache entry has fields for all
211      * enabled tests, but the remote SMTP client has not yet passed all those
212      * tests.
213      */
214     for (sp = expire_time; sp < expire_time + PSC_TINDX_COUNT; sp++) {
215 	if (*sp == PSC_TIME_STAMP_NEW)
216 	    state->flags |= PSC_STATE_FLAG_NEW;
217     }
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 > expire_time[PSC_TINDX_PREGR])
224 	state->flags |= PSC_STATE_FLAG_PREGR_TODO;
225     if (PSC_DNSBL_TEST_ENABLE() && time_value > expire_time[PSC_TINDX_DNSBL])
226 	state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
227     if (var_psc_pipel_enable && time_value > expire_time[PSC_TINDX_PIPEL])
228 	state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
229     if (var_psc_nsmtp_enable && time_value > expire_time[PSC_TINDX_NSMTP])
230 	state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
231     if (var_psc_barlf_enable && time_value > expire_time[PSC_TINDX_BARLF])
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 > expire_time[PSC_TINDX_PREGR])
247 	    state->flags |= PSC_STATE_FLAG_PREGR_TODO;
248 	if (PSC_DNSBL_TEST_ENABLE() && refresh_time > expire_time[PSC_TINDX_DNSBL])
249 	    state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
250 	if (var_psc_pipel_enable && refresh_time > expire_time[PSC_TINDX_PIPEL])
251 	    state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
252 	if (var_psc_nsmtp_enable && refresh_time > expire_time[PSC_TINDX_NSMTP])
253 	    state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
254 	if (var_psc_barlf_enable && refresh_time > expire_time[PSC_TINDX_BARLF])
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 allowlisting, 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 /* psc_print_tests - print postscreen cache record */
278 
psc_print_tests(VSTRING * buf,PSC_STATE * state)279 char   *psc_print_tests(VSTRING *buf, PSC_STATE *state)
280 {
281     const char *myname = "psc_print_tests";
282     time_t *expire_time = state->client_info->expire_time;
283 
284     /*
285      * Sanity check.
286      */
287     if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0)
288 	msg_panic("%s: attempt to save a no-update record", myname);
289 
290     /*
291      * Give disabled tests a dummy time stamp so that we don't log a client
292      * with "pass new" when some disabled test becomes enabled at some later
293      * time.
294      */
295     if (PSC_PREGR_TEST_ENABLE() == 0 && expire_time[PSC_TINDX_PREGR] == PSC_TIME_STAMP_NEW)
296 	expire_time[PSC_TINDX_PREGR] = PSC_TIME_STAMP_DISABLED;
297     if (PSC_DNSBL_TEST_ENABLE() == 0 && expire_time[PSC_TINDX_DNSBL] == PSC_TIME_STAMP_NEW)
298 	expire_time[PSC_TINDX_DNSBL] = PSC_TIME_STAMP_DISABLED;
299     if (var_psc_pipel_enable == 0 && expire_time[PSC_TINDX_PIPEL] == PSC_TIME_STAMP_NEW)
300 	expire_time[PSC_TINDX_PIPEL] = PSC_TIME_STAMP_DISABLED;
301     if (var_psc_nsmtp_enable == 0 && expire_time[PSC_TINDX_NSMTP] == PSC_TIME_STAMP_NEW)
302 	expire_time[PSC_TINDX_NSMTP] = PSC_TIME_STAMP_DISABLED;
303     if (var_psc_barlf_enable == 0 && expire_time[PSC_TINDX_BARLF] == PSC_TIME_STAMP_NEW)
304 	expire_time[PSC_TINDX_BARLF] = PSC_TIME_STAMP_DISABLED;
305 
306     vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu",
307 		    (unsigned long) expire_time[PSC_TINDX_PREGR],
308 		    (unsigned long) expire_time[PSC_TINDX_DNSBL],
309 		    (unsigned long) expire_time[PSC_TINDX_PIPEL],
310 		    (unsigned long) expire_time[PSC_TINDX_NSMTP],
311 		    (unsigned long) expire_time[PSC_TINDX_BARLF]);
312     return (STR(buf));
313 }
314 
315 /* psc_print_grey_key - print postscreen cache record */
316 
psc_print_grey_key(VSTRING * buf,const char * client,const char * helo,const char * sender,const char * rcpt)317 char   *psc_print_grey_key(VSTRING *buf, const char *client,
318 			           const char *helo, const char *sender,
319 			           const char *rcpt)
320 {
321     return (STR(vstring_sprintf(buf, "%s/%s/%s/%s",
322 				client, helo, sender, rcpt)));
323 }
324 
325 /* psc_test_name - map test index to symbolic name */
326 
psc_test_name(int tindx)327 const char *psc_test_name(int tindx)
328 {
329     const char *myname = "psc_test_name";
330     const NAME_CODE test_name_map[] = {
331 	PSC_TNAME_PREGR, PSC_TINDX_PREGR,
332 	PSC_TNAME_DNSBL, PSC_TINDX_DNSBL,
333 	PSC_TNAME_PIPEL, PSC_TINDX_PIPEL,
334 	PSC_TNAME_NSMTP, PSC_TINDX_NSMTP,
335 	PSC_TNAME_BARLF, PSC_TINDX_BARLF,
336 	0, -1,
337     };
338     const char *result;
339 
340     if ((result = str_name_code(test_name_map, tindx)) == 0)
341 	msg_panic("%s: bad index %d", myname, tindx);
342     return (result);
343 }
344