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 141void 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 165void 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 199void 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 279char *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 317char *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 327const 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