1 /* $NetBSD: postscreen_tests.c,v 1.2 2017/02/14 01:16:47 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 86 /* System library. */ 87 88 #include <sys_defs.h> 89 #include <stdio.h> /* sscanf */ 90 #include <stdlib.h> /* strtoul */ 91 92 /* Utility library. */ 93 94 #include <msg.h> 95 #include <name_code.h> 96 97 /* Global library. */ 98 99 #include <mail_params.h> 100 101 /* Application-specific. */ 102 103 #include <postscreen.h> 104 105 /* 106 * Kludge to detect if some test is enabled. 107 */ 108 #define PSC_PREGR_TEST_ENABLE() (*var_psc_pregr_banner != 0) 109 #define PSC_DNSBL_TEST_ENABLE() (*var_psc_dnsbl_sites != 0) 110 111 /* 112 * Format of a persistent cache entry (which is almost but not quite the 113 * same as the in-memory representation). 114 * 115 * Each cache entry has one time stamp for each test. 116 * 117 * - A time stamp of PSC_TIME_STAMP_INVALID must never appear in the cache. It 118 * is reserved for in-memory objects that are still being initialized. 119 * 120 * - A time stamp of PSC_TIME_STAMP_NEW indicates that the test never passed. 121 * Postscreen will log the client with "pass new" when it passes the final 122 * test. 123 * 124 * - A time stamp of PSC_TIME_STAMP_DISABLED indicates that the test never 125 * passed, and that the test was disabled when the cache entry was written. 126 * 127 * - Otherwise, the test was passed, and the time stamp indicates when that 128 * test result expires. 129 * 130 * A cache entry is expired when the time stamps of all passed tests are 131 * expired. 132 */ 133 134 /* psc_new_tests - initialize new test results from scratch */ 135 136 void psc_new_tests(PSC_STATE *state) 137 { 138 139 /* 140 * Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later 141 * recognize cache entries that haven't passed all enabled tests. When we 142 * write a cache entry to the database, any new-but-disabled tests will 143 * get a PSC_TIME_STAMP_DISABLED time stamp. 144 */ 145 state->pregr_stamp = PSC_TIME_STAMP_NEW; 146 state->dnsbl_stamp = PSC_TIME_STAMP_NEW; 147 state->pipel_stamp = PSC_TIME_STAMP_NEW; 148 state->nsmtp_stamp = PSC_TIME_STAMP_NEW; 149 state->barlf_stamp = PSC_TIME_STAMP_NEW; 150 151 /* 152 * Determine what tests need to be completed. 153 */ 154 psc_todo_tests(state, PSC_TIME_STAMP_NEW + 1); 155 } 156 157 /* psc_parse_tests - parse test results from cache */ 158 159 void psc_parse_tests(PSC_STATE *state, 160 const char *stamp_str, 161 time_t time_value) 162 { 163 const char *start = stamp_str; 164 char *cp; 165 time_t *time_stamps = state->client_info->expire_time; 166 time_t *sp; 167 168 /* 169 * Parse the cache entry, and allow for older postscreen versions that 170 * implemented fewer tests. We pretend that the newer tests were disabled 171 * at the time that the cache entry was written. 172 */ 173 for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) { 174 *sp = strtoul(start, &cp, 10); 175 if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE) 176 *sp = PSC_TIME_STAMP_DISABLED; 177 if (msg_verbose) 178 msg_info("%s -> %lu", start, (unsigned long) *sp); 179 if (*cp == ';') 180 start = cp + 1; 181 else 182 start = cp; 183 } 184 185 /* 186 * Determine what tests need to be completed. 187 */ 188 psc_todo_tests(state, time_value); 189 } 190 191 /* psc_todo_tests - determine what tests to perform */ 192 193 void psc_todo_tests(PSC_STATE *state, time_t time_value) 194 { 195 time_t *time_stamps = state->client_info->expire_time; 196 time_t *sp; 197 198 /* 199 * Reset all per-session flags. 200 */ 201 state->flags = 0; 202 203 /* 204 * Flag the tests as "new" when the cache entry has fields for all 205 * enabled tests, but the remote SMTP client has not yet passed all those 206 * tests. 207 */ 208 for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) { 209 if (*sp == PSC_TIME_STAMP_NEW) 210 state->flags |= PSC_STATE_FLAG_NEW; 211 } 212 213 /* 214 * Don't flag disabled tests as "todo", because there would be no way to 215 * make those bits go away. 216 */ 217 if (PSC_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp) 218 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 219 if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp) 220 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 221 if (var_psc_pipel_enable && time_value > state->pipel_stamp) 222 state->flags |= PSC_STATE_FLAG_PIPEL_TODO; 223 if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp) 224 state->flags |= PSC_STATE_FLAG_NSMTP_TODO; 225 if (var_psc_barlf_enable && time_value > state->barlf_stamp) 226 state->flags |= PSC_STATE_FLAG_BARLF_TODO; 227 228 /* 229 * If any test has expired, proactively refresh tests that will expire 230 * soon. This can increase the occurrence of client-visible delays, but 231 * avoids questions about why a client can pass some test and then fail 232 * within seconds. The proactive refresh time is really a surrogate for 233 * the user's curiosity level, and therefore hard to choose optimally. 234 */ 235 #ifdef VAR_PSC_REFRESH_TIME 236 if ((state->flags & PSC_STATE_MASK_ANY_TODO) != 0 237 && var_psc_refresh_time > 0) { 238 time_t refresh_time = time_value + var_psc_refresh_time; 239 240 if (PSC_PREGR_TEST_ENABLE() && refresh_time > state->pregr_stamp) 241 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 242 if (PSC_DNSBL_TEST_ENABLE() && refresh_time > state->dnsbl_stamp) 243 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 244 if (var_psc_pipel_enable && refresh_time > state->pipel_stamp) 245 state->flags |= PSC_STATE_FLAG_PIPEL_TODO; 246 if (var_psc_nsmtp_enable && refresh_time > state->nsmtp_stamp) 247 state->flags |= PSC_STATE_FLAG_NSMTP_TODO; 248 if (var_psc_barlf_enable && refresh_time > state->barlf_stamp) 249 state->flags |= PSC_STATE_FLAG_BARLF_TODO; 250 } 251 #endif 252 253 /* 254 * Gratuitously make postscreen logging more useful by turning on all 255 * enabled pre-handshake tests when any pre-handshake test is turned on. 256 * 257 * XXX Don't enable PREGREET gratuitously before the test expires. With a 258 * short TTL for DNSBL whitelisting, turning on PREGREET would force a 259 * full postscreen_greet_wait too frequently. 260 */ 261 #if 0 262 if (state->flags & PSC_STATE_MASK_EARLY_TODO) { 263 if (PSC_PREGR_TEST_ENABLE()) 264 state->flags |= PSC_STATE_FLAG_PREGR_TODO; 265 if (PSC_DNSBL_TEST_ENABLE()) 266 state->flags |= PSC_STATE_FLAG_DNSBL_TODO; 267 } 268 #endif 269 } 270 271 /* psc_print_tests - print postscreen cache record */ 272 273 char *psc_print_tests(VSTRING *buf, PSC_STATE *state) 274 { 275 const char *myname = "psc_print_tests"; 276 277 /* 278 * Sanity check. 279 */ 280 if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0) 281 msg_panic("%s: attempt to save a no-update record", myname); 282 283 /* 284 * Give disabled tests a dummy time stamp so that we don't log a client 285 * with "pass new" when some disabled test becomes enabled at some later 286 * time. 287 */ 288 if (PSC_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PSC_TIME_STAMP_NEW) 289 state->pregr_stamp = PSC_TIME_STAMP_DISABLED; 290 if (PSC_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PSC_TIME_STAMP_NEW) 291 state->dnsbl_stamp = PSC_TIME_STAMP_DISABLED; 292 if (var_psc_pipel_enable == 0 && state->pipel_stamp == PSC_TIME_STAMP_NEW) 293 state->pipel_stamp = PSC_TIME_STAMP_DISABLED; 294 if (var_psc_nsmtp_enable == 0 && state->nsmtp_stamp == PSC_TIME_STAMP_NEW) 295 state->nsmtp_stamp = PSC_TIME_STAMP_DISABLED; 296 if (var_psc_barlf_enable == 0 && state->barlf_stamp == PSC_TIME_STAMP_NEW) 297 state->barlf_stamp = PSC_TIME_STAMP_DISABLED; 298 299 vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu", 300 (unsigned long) state->pregr_stamp, 301 (unsigned long) state->dnsbl_stamp, 302 (unsigned long) state->pipel_stamp, 303 (unsigned long) state->nsmtp_stamp, 304 (unsigned long) state->barlf_stamp); 305 return (STR(buf)); 306 } 307 308 /* psc_print_grey_key - print postscreen cache record */ 309 310 char *psc_print_grey_key(VSTRING *buf, const char *client, 311 const char *helo, const char *sender, 312 const char *rcpt) 313 { 314 return (STR(vstring_sprintf(buf, "%s/%s/%s/%s", 315 client, helo, sender, rcpt))); 316 } 317 318 /* psc_test_name - map test index to symbolic name */ 319 320 const char *psc_test_name(int tindx) 321 { 322 const char *myname = "psc_test_name"; 323 const NAME_CODE test_name_map[] = { 324 PSC_TNAME_PREGR, PSC_TINDX_PREGR, 325 PSC_TNAME_DNSBL, PSC_TINDX_DNSBL, 326 PSC_TNAME_PIPEL, PSC_TINDX_PIPEL, 327 PSC_TNAME_NSMTP, PSC_TINDX_NSMTP, 328 PSC_TNAME_BARLF, PSC_TINDX_BARLF, 329 0, -1, 330 }; 331 const char *result; 332 333 if ((result = str_name_code(test_name_map, tindx)) == 0) 334 msg_panic("%s: bad index %d", myname, tindx); 335 return (result); 336 } 337