1 /* $NetBSD: postscreen_tests.c,v 1.1.1.2 2013/01/02 18:59:04 tron 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 /* char *psc_print_tests(buffer, state) 23 /* VSTRING *buffer; 24 /* PSC_STATE *state; 25 /* 26 /* char *psc_print_grey_key(buffer, client, helo, sender, rcpt) 27 /* VSTRING *buffer; 28 /* const char *client; 29 /* const char *helo; 30 /* const char *sender; 31 /* const char *rcpt; 32 /* DESCRIPTION 33 /* The functions in this module overwrite the per-test expiration 34 /* time stamps and all flags bits. Some functions are implemented 35 /* as unsafe macros, meaning they evaluate one or more arguments 36 /* multiple times. 37 /* 38 /* PSC_INIT_TESTS() is an unsafe macro that sets the per-test 39 /* expiration time stamps to PSC_TIME_STAMP_INVALID, and that 40 /* zeroes all the flags bits. These values are not meant to 41 /* be stored into the postscreen(8) cache. 42 /* 43 /* psc_new_tests() sets all test expiration time stamps to 44 /* PSC_TIME_STAMP_NEW, and overwrites all flags bits. Only 45 /* enabled tests are flagged with PSC_STATE_FLAG_TODO; the 46 /* object is flagged with PSC_STATE_FLAG_NEW. 47 /* 48 /* psc_parse_tests() parses a cache file record and overwrites 49 /* all flags bits. Tests are considered "expired" when they 50 /* would be expired at the specified time value. Only enabled 51 /* tests are flagged as "expired"; the object is flagged as 52 /* "new" if some enabled tests have "new" time stamps. 53 /* 54 /* psc_print_tests() creates a cache file record for the 55 /* specified flags and per-test expiration time stamps. 56 /* This may modify the time stamps for disabled tests. 57 /* 58 /* psc_print_grey_key() prints a greylist lookup key. 59 /* LICENSE 60 /* .ad 61 /* .fi 62 /* The Secure Mailer license must be distributed with this software. 63 /* AUTHOR(S) 64 /* Wietse Venema 65 /* IBM T.J. Watson Research 66 /* P.O. Box 704 67 /* Yorktown Heights, NY 10598, USA 68 /*--*/ 69 70 /* System library. */ 71 72 #include <sys_defs.h> 73 #include <stdio.h> /* sscanf */ 74 75 /* Utility library. */ 76 77 #include <msg.h> 78 79 /* Global library. */ 80 81 #include <mail_params.h> 82 83 /* Application-specific. */ 84 85 #include <postscreen.h> 86 87 /* 88 * Kludge to detect if some test is enabled. 89 */ 90 #define PSC_PREGR_TEST_ENABLE() (*var_psc_pregr_banner != 0) 91 #define PSC_DNSBL_TEST_ENABLE() (*var_psc_dnsbl_sites != 0) 92 93 /* 94 * Format of a persistent cache entry (which is almost but not quite the 95 * same as the in-memory representation). 96 * 97 * Each cache entry has one time stamp for each test. 98 * 99 * - A time stamp of PSC_TIME_STAMP_INVALID must never appear in the cache. It 100 * is reserved for in-memory objects that are still being initialized. 101 * 102 * - A time stamp of PSC_TIME_STAMP_NEW indicates that the test never passed. 103 * Postscreen will log the client with "pass new" when it passes the final 104 * test. 105 * 106 * - A time stamp of PSC_TIME_STAMP_DISABLED indicates that the test never 107 * passed, and that the test was disabled when the cache entry was written. 108 * 109 * - Otherwise, the test was passed, and the time stamp indicates when that 110 * test result expires. 111 * 112 * A cache entry is expired when the time stamps of all passed tests are 113 * expired. 114 */ 115 116 /* psc_new_tests - initialize new test results from scratch */ 117 118 void psc_new_tests(PSC_STATE *state) 119 { 120 121 /* 122 * We know this client is brand new. 123 */ 124 state->flags = PSC_STATE_FLAG_NEW; 125 126 /* 127 * Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later 128 * recognize cache entries that haven't passed all enabled tests. When we 129 * write a cache entry to the database, any new-but-disabled tests will 130 * get a PSC_TIME_STAMP_DISABLED time stamp. 131 */ 132 state->pregr_stamp = PSC_TIME_STAMP_NEW; 133 state->dnsbl_stamp = PSC_TIME_STAMP_NEW; 134 state->pipel_stamp = PSC_TIME_STAMP_NEW; 135 state->nsmtp_stamp = PSC_TIME_STAMP_NEW; 136 state->barlf_stamp = PSC_TIME_STAMP_NEW; 137 state->penal_stamp = PSC_TIME_STAMP_NEW; 138 139 /* 140 * Don't flag disabled tests as "todo", because there would be no way to 141 * make those bits go away. 142 */ 143 if (PSC_PREGR_TEST_ENABLE()) 144 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 145 if (PSC_DNSBL_TEST_ENABLE()) 146 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 147 if (var_psc_pipel_enable) 148 state->flags |= PSC_STATE_FLAG_PIPEL_TODO; 149 if (var_psc_nsmtp_enable) 150 state->flags |= PSC_STATE_FLAG_NSMTP_TODO; 151 if (var_psc_barlf_enable) 152 state->flags |= PSC_STATE_FLAG_BARLF_TODO; 153 } 154 155 /* psc_parse_tests - parse test results from cache */ 156 157 void psc_parse_tests(PSC_STATE *state, 158 const char *stamp_str, 159 time_t time_value) 160 { 161 unsigned long pregr_stamp; 162 unsigned long dnsbl_stamp; 163 unsigned long pipel_stamp; 164 unsigned long nsmtp_stamp; 165 unsigned long barlf_stamp; 166 unsigned long penal_stamp; 167 168 #ifdef NONPROD 169 time_t penalty_left; 170 171 #endif 172 173 /* 174 * We don't know what tests have expired or have never passed. 175 */ 176 state->flags = 0; 177 178 /* 179 * Parse the cache entry, and allow for older postscreen versions that 180 * implemented fewer tests. We pretend that the newer tests were disabled 181 * at the time that the cache entry was written. 182 * 183 * Flag the cache entry as "new" when the cache entry has fields for all 184 * enabled tests, but the remote SMTP client has not yet passed all those 185 * tests. 186 */ 187 switch (sscanf(stamp_str, "%lu;%lu;%lu;%lu;%lu;%lu", 188 &pregr_stamp, &dnsbl_stamp, &pipel_stamp, &nsmtp_stamp, 189 &barlf_stamp, &penal_stamp)) { 190 case 0: 191 pregr_stamp = PSC_TIME_STAMP_DISABLED; 192 case 1: 193 dnsbl_stamp = PSC_TIME_STAMP_DISABLED; 194 case 2: 195 pipel_stamp = PSC_TIME_STAMP_DISABLED; 196 case 3: 197 nsmtp_stamp = PSC_TIME_STAMP_DISABLED; 198 case 4: 199 barlf_stamp = PSC_TIME_STAMP_DISABLED; 200 case 5: 201 penal_stamp = PSC_TIME_STAMP_DISABLED; 202 default: 203 break; 204 } 205 state->pregr_stamp = pregr_stamp; 206 state->dnsbl_stamp = dnsbl_stamp; 207 state->pipel_stamp = pipel_stamp; 208 state->nsmtp_stamp = nsmtp_stamp; 209 state->barlf_stamp = barlf_stamp; 210 state->penal_stamp = penal_stamp; 211 212 if (pregr_stamp == PSC_TIME_STAMP_NEW 213 || dnsbl_stamp == PSC_TIME_STAMP_NEW 214 || pipel_stamp == PSC_TIME_STAMP_NEW 215 || nsmtp_stamp == PSC_TIME_STAMP_NEW 216 || barlf_stamp == PSC_TIME_STAMP_NEW) 217 state->flags |= PSC_STATE_FLAG_NEW; 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 > state->pregr_stamp) 224 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 225 if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp) 226 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 227 if (var_psc_pipel_enable && time_value > state->pipel_stamp) 228 state->flags |= PSC_STATE_FLAG_PIPEL_TODO; 229 if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp) 230 state->flags |= PSC_STATE_FLAG_NSMTP_TODO; 231 if (var_psc_barlf_enable && time_value > state->barlf_stamp) 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 > state->pregr_stamp) 247 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 248 if (PSC_DNSBL_TEST_ENABLE() && refresh_time > state->dnsbl_stamp) 249 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 250 if (var_psc_pipel_enable && refresh_time > state->pipel_stamp) 251 state->flags |= PSC_STATE_FLAG_PIPEL_TODO; 252 if (var_psc_nsmtp_enable && refresh_time > state->nsmtp_stamp) 253 state->flags |= PSC_STATE_FLAG_NSMTP_TODO; 254 if (var_psc_barlf_enable && refresh_time > state->barlf_stamp) 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 whitelisting, 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 * Apply unexpired penalty for past behavior. 278 * 279 * XXX Before we can drop connections, change this function to return 280 * success/fail, to inform the caller that the state object no longer 281 * exists. 282 */ 283 #ifdef NONPROD 284 if ((penalty_left = state->penal_stamp - event_time()) > 0) { 285 msg_info("PENALTY %ld for %s", 286 (long) penalty_left, state->smtp_client_addr); 287 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); 288 #if 0 289 switch (psc_penal_action) { 290 case PSC_ACT_DROP: 291 PSC_DROP_SESSION_STATE(state, 292 "421 4.3.2 Service currently unavailable\r\n"); 293 break; 294 case PSC_ACT_ENFORCE: 295 #endif 296 PSC_ENFORCE_SESSION_STATE(state, 297 "450 4.3.2 Service currently unavailable\r\n"); 298 #if 0 299 break; 300 case PSC_ACT_IGNORE: 301 PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); 302 break; 303 default: 304 msg_panic("%s: unknown penalty action value %d", 305 myname, psc_penal_action); 306 } 307 #endif 308 } 309 #endif /* NONPROD */ 310 } 311 312 /* psc_print_tests - print postscreen cache record */ 313 314 char *psc_print_tests(VSTRING *buf, PSC_STATE *state) 315 { 316 const char *myname = "psc_print_tests"; 317 318 /* 319 * Sanity check. 320 */ 321 if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0) 322 msg_panic("%s: attempt to save a no-update record", myname); 323 324 /* 325 * Don't record a client as "passed" while subject to penalty. Be sure to 326 * produce correct PASS OLD/NEW logging. 327 * 328 * XXX This needs to be refined - we should not reset the result of tests 329 * that were passed in previous sessions, otherwise a client may never 330 * pass a multi-stage test such as greylisting. One solution is to keep 331 * the original and updated time stamps around, and to save an updated 332 * time stamp only when the corresponding "pass" flag is raised. 333 */ 334 #ifdef NONPROD 335 if (state->flags & PSC_STATE_FLAG_PENAL_FAIL) { 336 state->pregr_stamp = state->dnsbl_stamp = state->pipel_stamp = 337 state->nsmtp_stamp = state->barlf_stamp = 338 ((state->flags & PSC_STATE_FLAG_NEW) ? 339 PSC_TIME_STAMP_NEW : PSC_TIME_STAMP_DISABLED); 340 } 341 #endif 342 343 /* 344 * Give disabled tests a dummy time stamp so that we don't log a client 345 * with "pass new" when some disabled test becomes enabled at some later 346 * time. 347 */ 348 if (PSC_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PSC_TIME_STAMP_NEW) 349 state->pregr_stamp = PSC_TIME_STAMP_DISABLED; 350 if (PSC_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PSC_TIME_STAMP_NEW) 351 state->dnsbl_stamp = PSC_TIME_STAMP_DISABLED; 352 if (var_psc_pipel_enable == 0 && state->pipel_stamp == PSC_TIME_STAMP_NEW) 353 state->pipel_stamp = PSC_TIME_STAMP_DISABLED; 354 if (var_psc_nsmtp_enable == 0 && state->nsmtp_stamp == PSC_TIME_STAMP_NEW) 355 state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED; 356 if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW) 357 state->barlf_stamp = PSC_TIME_STAMP_DISABLED; 358 359 vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu;%lu", 360 (unsigned long) state->pregr_stamp, 361 (unsigned long) state->dnsbl_stamp, 362 (unsigned long) state->pipel_stamp, 363 (unsigned long) state->nsmtp_stamp, 364 (unsigned long) state->barlf_stamp, 365 (unsigned long) state->penal_stamp); 366 return (STR(buf)); 367 } 368 369 /* psc_print_grey_key - print postscreen cache record */ 370 371 char *psc_print_grey_key(VSTRING *buf, const char *client, 372 const char *helo, const char *sender, 373 const char *rcpt) 374 { 375 return (STR(vstring_sprintf(buf, "%s/%s/%s/%s", 376 client, helo, sender, rcpt))); 377 } 378