Home | History | Annotate | Line # | Download | only in postscreen
      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 
    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 
    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 
    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 
    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 
    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 
    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