1/* $NetBSD$ */ 2 3/*++ 4/* NAME 5/* postscreen_early 3 6/* SUMMARY 7/* postscreen pre-handshake tests 8/* SYNOPSIS 9/* #include <postscreen.h> 10/* 11/* void psc_early_init(void) 12/* 13/* void psc_early_tests(state) 14/* PSC_STATE *state; 15/* DESCRIPTION 16/* psc_early_tests() performs protocol tests before the SMTP 17/* handshake: the pregreet test and the DNSBL test. Control 18/* is passed to the psc_smtpd_tests() routine as appropriate. 19/* 20/* psc_early_init() performs one-time initialization. 21/* LICENSE 22/* .ad 23/* .fi 24/* The Secure Mailer license must be distributed with this software. 25/* AUTHOR(S) 26/* Wietse Venema 27/* IBM T.J. Watson Research 28/* P.O. Box 704 29/* Yorktown Heights, NY 10598, USA 30/*--*/ 31 32/* System library. */ 33 34#include <sys_defs.h> 35#include <sys/socket.h> 36 37/* Utility library. */ 38 39#include <msg.h> 40#include <stringops.h> 41#include <mymalloc.h> 42#include <vstring.h> 43 44/* Global library. */ 45 46#include <mail_params.h> 47 48/* Application-specific. */ 49 50#include <postscreen.h> 51 52static char *psc_teaser_greeting; 53static VSTRING *psc_escape_buf; 54 55/* psc_early_event - handle pre-greet, EOF, and DNSBL results. */ 56 57static void psc_early_event(int event, char *context) 58{ 59 const char *myname = "psc_early_event"; 60 PSC_STATE *state = (PSC_STATE *) context; 61 char read_buf[PSC_READ_BUF_SIZE]; 62 int read_count; 63 int dnsbl_score; 64 DELTA_TIME elapsed; 65 const char *dnsbl_name; 66 67 if (msg_verbose > 1) 68 msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s", 69 myname, psc_post_queue_length, psc_check_queue_length, 70 event, vstream_fileno(state->smtp_client_stream), 71 state->smtp_client_addr, state->smtp_client_port, 72 psc_print_state_flags(state->flags, myname)); 73 74 PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream), 75 psc_early_event, context); 76 77 /* 78 * XXX Be sure to empty the DNSBL lookup buffer otherwise we have a 79 * memory leak. 80 * 81 * XXX We can avoid "forgetting" to do this by keeping a pointer to the 82 * DNSBL lookup buffer in the PSC_STATE structure. This also allows us to 83 * shave off a hash table lookup when retrieving the DNSBL result. 84 */ 85 switch (event) { 86 87 /* 88 * We reached the end of the early tests time limit. 89 */ 90 case EVENT_TIME: 91 92 /* 93 * Check if the SMTP client spoke before its turn. 94 */ 95 if ((state->flags & PSC_STATE_MASK_PREGR_TODO_FAIL) 96 == PSC_STATE_FLAG_PREGR_TODO) { 97 state->pregr_stamp = event_time() + var_psc_pregr_ttl; 98 PSC_PASS_SESSION_STATE(state, "pregreet test", 99 PSC_STATE_FLAG_PREGR_PASS); 100 } 101 if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL) 102 && psc_pregr_action == PSC_ACT_IGNORE) { 103 PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL); 104 /* Not: PSC_PASS_SESSION_STATE. Repeat this test the next time. */ 105 } 106 107 /* 108 * If the client is DNS blocklisted, drop the connection, send the 109 * client to a dummy protocol engine, or continue to the next test. 110 */ 111#define PSC_DNSBL_FORMAT \ 112 "%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n" 113 114 if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) { 115 dnsbl_score = 116 psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name, 117 state->dnsbl_index); 118 if (dnsbl_score < var_psc_dnsbl_thresh) { 119 state->dnsbl_stamp = event_time() + var_psc_dnsbl_ttl; 120 PSC_PASS_SESSION_STATE(state, "dnsbl test", 121 PSC_STATE_FLAG_DNSBL_PASS); 122 } else { 123 msg_info("DNSBL rank %d for [%s]:%s", 124 dnsbl_score, PSC_CLIENT_ADDR_PORT(state)); 125 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL); 126 switch (psc_dnsbl_action) { 127 case PSC_ACT_DROP: 128 state->dnsbl_reply = vstring_sprintf(vstring_alloc(100), 129 PSC_DNSBL_FORMAT, "521", 130 state->smtp_client_addr, dnsbl_name); 131 PSC_DROP_SESSION_STATE(state, STR(state->dnsbl_reply)); 132 return; 133 case PSC_ACT_ENFORCE: 134 state->dnsbl_reply = vstring_sprintf(vstring_alloc(100), 135 PSC_DNSBL_FORMAT, "550", 136 state->smtp_client_addr, dnsbl_name); 137 PSC_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply)); 138 break; 139 case PSC_ACT_IGNORE: 140 PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL); 141 /* Not: PSC_PASS_SESSION_STATE. Repeat this test. */ 142 break; 143 default: 144 msg_panic("%s: unknown dnsbl action value %d", 145 myname, psc_dnsbl_action); 146 147 } 148 } 149 } 150 151 /* 152 * Pass the connection to a real SMTP server, or enter the dummy 153 * engine for deep tests. 154 */ 155 if (state->flags & (PSC_STATE_FLAG_NOFORWARD | PSC_STATE_MASK_SMTPD_TODO)) 156 psc_smtpd_tests(state); 157 else 158 psc_conclude(state); 159 return; 160 161 /* 162 * EOF, or the client spoke before its turn. We simply drop the 163 * connection, or we continue waiting and allow DNS replies to 164 * trickle in. 165 */ 166 default: 167 if ((read_count = recv(vstream_fileno(state->smtp_client_stream), 168 read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) { 169 /* Avoid memory leak. */ 170 if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) 171 (void) psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name, 172 state->dnsbl_index); 173 /* XXX Wait for DNS replies to come in. */ 174 psc_hangup_event(state); 175 return; 176 } 177 read_buf[read_count] = 0; 178 escape(psc_escape_buf, read_buf, read_count); 179 msg_info("PREGREET %d after %s from [%s]:%s: %.100s", read_count, 180 psc_format_delta_time(psc_temp, state->start_time, &elapsed), 181 PSC_CLIENT_ADDR_PORT(state), STR(psc_escape_buf)); 182 PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL); 183 switch (psc_pregr_action) { 184 case PSC_ACT_DROP: 185 /* Avoid memory leak. */ 186 if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) 187 (void) psc_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name, 188 state->dnsbl_index); 189 PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n"); 190 return; 191 case PSC_ACT_ENFORCE: 192 /* We call psc_dnsbl_retrieve() when the timer expires. */ 193 PSC_ENFORCE_SESSION_STATE(state, "550 5.5.1 Protocol error\r\n"); 194 break; 195 case PSC_ACT_IGNORE: 196 /* We call psc_dnsbl_retrieve() when the timer expires. */ 197 /* We must handle this case after the timer expires. */ 198 break; 199 default: 200 msg_panic("%s: unknown pregreet action value %d", 201 myname, psc_pregr_action); 202 } 203 204 /* 205 * Terminate the greet delay if we're just waiting for the pregreet 206 * test to complete. It is safe to call psc_early_event directly, 207 * since we are already in that function. 208 * 209 * XXX After this code passes all tests, swap around the two blocks in 210 * this switch statement and fall through from EVENT_READ into 211 * EVENT_TIME, instead of calling psc_early_event recursively. 212 */ 213 state->flags |= PSC_STATE_FLAG_PREGR_DONE; 214 if (elapsed.dt_sec >= PSC_EFF_GREET_WAIT 215 || ((state->flags & PSC_STATE_MASK_EARLY_DONE) 216 == PSC_STATE_FLAGS_TODO_TO_DONE(state->flags & PSC_STATE_MASK_EARLY_TODO))) 217 psc_early_event(EVENT_TIME, context); 218 else 219 event_request_timer(psc_early_event, context, 220 PSC_EFF_GREET_WAIT - elapsed.dt_sec); 221 return; 222 } 223} 224 225/* psc_early_dnsbl_event - cancel pregreet timer if waiting for DNS only */ 226 227static void psc_early_dnsbl_event(int unused_event, char *context) 228{ 229 const char *myname = "psc_early_dnsbl_event"; 230 PSC_STATE *state = (PSC_STATE *) context; 231 232 if (msg_verbose) 233 msg_info("%s: notify [%s]:%s", myname, PSC_CLIENT_ADDR_PORT(state)); 234 235 /* 236 * Terminate the greet delay if we're just waiting for DNSBL lookup to 237 * complete. Don't call psc_early_event directly, that would result in a 238 * dangling pointer. 239 */ 240 state->flags |= PSC_STATE_FLAG_DNSBL_DONE; 241 if ((state->flags & PSC_STATE_MASK_EARLY_DONE) 242 == PSC_STATE_FLAGS_TODO_TO_DONE(state->flags & PSC_STATE_MASK_EARLY_TODO)) 243 event_request_timer(psc_early_event, context, EVENT_NULL_DELAY); 244} 245 246/* psc_early_tests - start the early (before protocol) tests */ 247 248void psc_early_tests(PSC_STATE *state) 249{ 250 const char *myname = "psc_early_tests"; 251 252 /* 253 * Report errors and progress in the context of this test. 254 */ 255 PSC_BEGIN_TESTS(state, "tests before SMTP handshake"); 256 257 /* 258 * Run a PREGREET test. Send half the greeting banner, by way of teaser, 259 * then wait briefly to see if the client speaks before its turn. 260 */ 261 if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0 262 && psc_teaser_greeting != 0 263 && PSC_SEND_REPLY(state, psc_teaser_greeting) != 0) { 264 psc_hangup_event(state); 265 return; 266 } 267 268 /* 269 * Run a DNS blocklist query. 270 */ 271 if ((state->flags & PSC_STATE_FLAG_DNSBL_TODO) != 0) 272 state->dnsbl_index = 273 psc_dnsbl_request(state->smtp_client_addr, psc_early_dnsbl_event, 274 (char *) state); 275 else 276 state->dnsbl_index = -1; 277 278 /* 279 * Wait for the client to respond or for DNS lookup to complete. 280 */ 281 if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0) 282 PSC_READ_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream), 283 psc_early_event, (char *) state, PSC_EFF_GREET_WAIT); 284 else 285 event_request_timer(psc_early_event, (char *) state, PSC_EFF_GREET_WAIT); 286} 287 288/* psc_early_init - initialize early tests */ 289 290void psc_early_init(void) 291{ 292 if (*var_psc_pregr_banner) { 293 vstring_sprintf(psc_temp, "220-%s\r\n", var_psc_pregr_banner); 294 psc_teaser_greeting = mystrdup(STR(psc_temp)); 295 psc_escape_buf = vstring_alloc(100); 296 } 297} 298