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