1 /* $NetBSD: postscreen_tests.c,v 1.1.1.1 2011/03/02 19:32:27 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 these tests were disabled 181 * when 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 a cache entry as expired just because some test was never 221 * passed. 222 * 223 * Don't flag disabled tests as "todo", because there would be no way to 224 * make those bits go away. 225 */ 226 if (PSC_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp) { 227 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 228 if (state->pregr_stamp > PSC_TIME_STAMP_DISABLED) 229 state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED; 230 } 231 if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp) { 232 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 233 if (state->dnsbl_stamp > PSC_TIME_STAMP_DISABLED) 234 state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED; 235 } 236 if (var_psc_pipel_enable && time_value > state->pipel_stamp) { 237 state->flags |= PSC_STATE_FLAG_PIPEL_TODO; 238 if (state->pipel_stamp > PSC_TIME_STAMP_DISABLED) 239 state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED; 240 } 241 if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp) { 242 state->flags |= PSC_STATE_FLAG_NSMTP_TODO; 243 if (state->nsmtp_stamp > PSC_TIME_STAMP_DISABLED) 244 state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED; 245 } 246 if (var_psc_barlf_enable && time_value > state->barlf_stamp) { 247 state->flags |= PSC_STATE_FLAG_BARLF_TODO; 248 if (state->barlf_stamp > PSC_TIME_STAMP_DISABLED) 249 state->flags |= PSC_STATE_FLAG_CACHE_EXPIRED; 250 } 251 252 /* 253 * Gratuitously make postscreen logging more useful by turning on all 254 * enabled pre-handshake tests when any pre-handshake test is turned on. 255 * 256 * XXX Don't enable PREGREET gratuitously before the test expires. With a 257 * short TTL for DNSBL whitelisting, turning on PREGREET would force a 258 * full postscreen_greet_wait too frequently. 259 */ 260 #if 0 261 if (state->flags & PSC_STATE_MASK_EARLY_TODO) { 262 if (PSC_PREGR_TEST_ENABLE()) 263 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 264 if (PSC_DNSBL_TEST_ENABLE()) 265 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 266 } 267 #endif 268 269 /* 270 * Apply unexpired penalty for past behavior. 271 * 272 * XXX Before we can drop connections, change this function to return 273 * success/fail, to inform the caller that the state object no longer 274 * exists. 275 */ 276 #ifdef NONPROD 277 if ((penalty_left = state->penal_stamp - event_time()) > 0) { 278 msg_info("PENALTY %ld for %s", 279 (long) penalty_left, state->smtp_client_addr); 280 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); 281 #if 0 282 switch (psc_penal_action) { 283 case PSC_ACT_DROP: 284 PSC_DROP_SESSION_STATE(state, 285 "421 4.3.2 Service currently unavailable\r\n"); 286 break; 287 case PSC_ACT_ENFORCE: 288 #endif 289 PSC_ENFORCE_SESSION_STATE(state, 290 "450 4.3.2 Service currently unavailable\r\n"); 291 #if 0 292 break; 293 case PSC_ACT_IGNORE: 294 PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); 295 break; 296 default: 297 msg_panic("%s: unknown penalty action value %d", 298 myname, psc_penal_action); 299 } 300 #endif 301 } 302 #endif /* NONPROD */ 303 } 304 305 /* psc_print_tests - print postscreen cache record */ 306 307 char *psc_print_tests(VSTRING *buf, PSC_STATE *state) 308 { 309 const char *myname = "psc_print_tests"; 310 311 /* 312 * Sanity check. 313 */ 314 if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0) 315 msg_panic("%s: attempt to save a no-update record", myname); 316 317 /* 318 * Don't record a client as "passed" while subject to penalty. Be sure to 319 * produce correct PASS OLD/NEW logging. 320 * 321 * XXX This needs to be refined - we should not reset the result of tests 322 * that were passed in previous sessions, otherwise a client may never 323 * pass a multi-stage test such as greylisting. One solution is to keep 324 * the original and updated time stamps around, and to save an updated 325 * time stamp only when the corresponding "pass" flag is raised. 326 */ 327 #ifdef NONPROD 328 if (state->flags & PSC_STATE_FLAG_PENAL_FAIL) { 329 state->pregr_stamp = state->dnsbl_stamp = state->pipel_stamp = 330 state->nsmtp_stamp = state->barlf_stamp = 331 ((state->flags & PSC_STATE_FLAG_NEW) ? 332 PSC_TIME_STAMP_NEW : PSC_TIME_STAMP_DISABLED); 333 } 334 #endif 335 336 /* 337 * Give disabled tests a dummy time stamp so that we don't log a client 338 * with "pass new" when some disabled test becomes enabled at some later 339 * time. 340 */ 341 if (PSC_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PSC_TIME_STAMP_NEW) 342 state->pregr_stamp = PSC_TIME_STAMP_DISABLED; 343 if (PSC_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PSC_TIME_STAMP_NEW) 344 state->dnsbl_stamp = PSC_TIME_STAMP_DISABLED; 345 if (var_psc_pipel_enable == 0 && state->pipel_stamp == PSC_TIME_STAMP_NEW) 346 state->pipel_stamp = PSC_TIME_STAMP_DISABLED; 347 if (var_psc_nsmtp_enable == 0 && state->nsmtp_stamp == PSC_TIME_STAMP_NEW) 348 state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED; 349 if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW) 350 state->barlf_stamp = PSC_TIME_STAMP_DISABLED; 351 352 vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu;%lu", 353 (unsigned long) state->pregr_stamp, 354 (unsigned long) state->dnsbl_stamp, 355 (unsigned long) state->pipel_stamp, 356 (unsigned long) state->nsmtp_stamp, 357 (unsigned long) state->barlf_stamp, 358 (unsigned long) state->penal_stamp); 359 return (STR(buf)); 360 } 361 362 /* psc_print_grey_key - print postscreen cache record */ 363 364 char *psc_print_grey_key(VSTRING *buf, const char *client, 365 const char *helo, const char *sender, 366 const char *rcpt) 367 { 368 return (STR(vstring_sprintf(buf, "%s/%s/%s/%s", 369 client, helo, sender, rcpt))); 370 } 371