Postfix3.3.1
postscreen_early.c
[詳解]
1 /*++
2 /* NAME
3 /* postscreen_early 3
4 /* SUMMARY
5 /* postscreen pre-handshake tests
6 /* SYNOPSIS
7 /* #include <postscreen.h>
8 /*
9 /* void psc_early_init(void)
10 /*
11 /* void psc_early_tests(state)
12 /* PSC_STATE *state;
13 /* DESCRIPTION
14 /* psc_early_tests() performs protocol tests before the SMTP
15 /* handshake: the pregreet test and the DNSBL test. Control
16 /* is passed to the psc_smtpd_tests() routine as appropriate.
17 /*
18 /* psc_early_init() performs one-time initialization.
19 /* LICENSE
20 /* .ad
21 /* .fi
22 /* The Secure Mailer license must be distributed with this software.
23 /* AUTHOR(S)
24 /* Wietse Venema
25 /* IBM T.J. Watson Research
26 /* P.O. Box 704
27 /* Yorktown Heights, NY 10598, USA
28 /*
29 /* Wietse Venema
30 /* Google, Inc.
31 /* 111 8th Avenue
32 /* New York, NY 10011, USA
33 /*--*/
34 
35 /* System library. */
36 
37 #include <sys_defs.h>
38 #include <sys/socket.h>
39 #include <limits.h>
40 
41 /* Utility library. */
42 
43 #include <msg.h>
44 #include <stringops.h>
45 #include <mymalloc.h>
46 #include <vstring.h>
47 
48 /* Global library. */
49 
50 #include <mail_params.h>
51 
52 /* Application-specific. */
53 
54 #include <postscreen.h>
55 
56 static char *psc_teaser_greeting;
57 static VSTRING *psc_escape_buf;
58 
59 /* psc_whitelist_non_dnsbl - whitelist pending non-dnsbl tests */
60 
61 static void psc_whitelist_non_dnsbl(PSC_STATE *state)
62 {
63  time_t now;
64  int tindx;
65 
66  /*
67  * If no tests failed (we can't undo those), and if the whitelist
68  * threshold is met, flag non-dnsbl tests that are pending or disabled as
69  * successfully completed, and set their expiration times equal to the
70  * DNSBL expiration time, except for tests that would expire later.
71  *
72  * Why flag disabled tests as passed? When a disabled test is turned on,
73  * postscreen should not apply that test to clients that are already
74  * whitelisted based on their combined DNSBL score.
75  */
76  if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0
79  && state->dnsbl_score <= var_psc_dnsbl_wthresh) {
80  now = event_time();
81  for (tindx = 0; tindx < PSC_TINDX_COUNT; tindx++) {
82  if (tindx == PSC_TINDX_DNSBL)
83  continue;
84  if ((state->flags & PSC_STATE_FLAG_BYTINDX_TODO(tindx))
85  && !(state->flags & PSC_STATE_FLAG_BYTINDX_PASS(tindx))) {
86  if (msg_verbose)
87  msg_info("skip %s test for [%s]:%s",
88  psc_test_name(tindx), PSC_CLIENT_ADDR_PORT(state));
89  /* Wrong for deep protocol tests, but we disable those. */
90  state->flags |= PSC_STATE_FLAG_BYTINDX_DONE(tindx);
91  /* This also disables pending deep protocol tests. */
92  state->flags |= PSC_STATE_FLAG_BYTINDX_PASS(tindx);
93  }
94  /* Update expiration even if the test was completed or disabled. */
95  if (state->client_info->expire_time[tindx] < now + state->dnsbl_ttl)
96  state->client_info->expire_time[tindx] = now + state->dnsbl_ttl;
97  }
98  }
99 }
100 
101 /* psc_early_event - handle pre-greet, EOF, and DNSBL results. */
102 
103 static void psc_early_event(int event, void *context)
104 {
105  const char *myname = "psc_early_event";
106  PSC_STATE *state = (PSC_STATE *) context;
107  time_t *expire_time = state->client_info->expire_time;
108  char read_buf[PSC_READ_BUF_SIZE];
109  int read_count;
110  DELTA_TIME elapsed;
111 
112  if (msg_verbose > 1)
113  msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
115  event, vstream_fileno(state->smtp_client_stream),
116  state->smtp_client_addr, state->smtp_client_port,
117  psc_print_state_flags(state->flags, myname));
118 
120  psc_early_event, context);
121 
122  /*
123  * XXX Be sure to empty the DNSBL lookup buffer otherwise we have a
124  * memory leak.
125  *
126  * XXX We can avoid "forgetting" to do this by keeping a pointer to the
127  * DNSBL lookup buffer in the PSC_STATE structure. This also allows us to
128  * shave off a hash table lookup when retrieving the DNSBL result.
129  *
130  * A direct pointer increases the odds of dangling pointers. Hash-table
131  * lookup is safer, and that is why it's done that way.
132  */
133  switch (event) {
134 
135  /*
136  * We either reached the end of the early tests time limit, or all
137  * early tests completed before the pregreet timer would go off.
138  */
139  case EVENT_TIME:
140 
141  /*
142  * Check if the SMTP client spoke before its turn.
143  */
144  if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0
145  && (state->flags & PSC_STATE_MASK_PREGR_FAIL_DONE) == 0) {
146  expire_time[PSC_TINDX_PREGR] = event_time() + var_psc_pregr_ttl;
147  PSC_PASS_SESSION_STATE(state, "pregreet test",
149  }
150  if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL)
153  /* Not: PSC_PASS_SESSION_STATE. Repeat this test the next time. */
154  }
155 
156  /*
157  * Collect the DNSBL score, and whitelist other tests if applicable.
158  * Note: this score will be partial when some DNS lookup did not
159  * complete before the pregreet timer expired.
160  *
161  * If the client is DNS blocklisted, drop the connection, send the
162  * client to a dummy protocol engine, or continue to the next test.
163  */
164 #define PSC_DNSBL_FORMAT \
165  "%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n"
166 #define NO_DNSBL_SCORE INT_MAX
167 
168  if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) {
169  if (state->dnsbl_score == NO_DNSBL_SCORE) {
170  state->dnsbl_score =
172  &state->dnsbl_name,
173  state->dnsbl_index,
174  &state->dnsbl_ttl);
175  if (var_psc_dnsbl_wthresh < 0)
176  psc_whitelist_non_dnsbl(state);
177  }
178  if (state->dnsbl_score < var_psc_dnsbl_thresh) {
179  expire_time[PSC_TINDX_DNSBL] = event_time() + state->dnsbl_ttl;
180  PSC_PASS_SESSION_STATE(state, "dnsbl test",
182  } else {
183  msg_info("DNSBL rank %d for [%s]:%s",
184  state->dnsbl_score, PSC_CLIENT_ADDR_PORT(state));
186  switch (psc_dnsbl_action) {
187  case PSC_ACT_DROP:
189  PSC_DNSBL_FORMAT, "521",
190  state->smtp_client_addr,
191  state->dnsbl_name);
192  PSC_DROP_SESSION_STATE(state, STR(state->dnsbl_reply));
193  return;
194  case PSC_ACT_ENFORCE:
196  PSC_DNSBL_FORMAT, "550",
197  state->smtp_client_addr,
198  state->dnsbl_name);
199  PSC_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply));
200  break;
201  case PSC_ACT_IGNORE:
203  /* Not: PSC_PASS_SESSION_STATE. Repeat this test. */
204  break;
205  default:
206  msg_panic("%s: unknown dnsbl action value %d",
207  myname, psc_dnsbl_action);
208 
209  }
210  }
211  }
212 
213  /*
214  * Pass the connection to a real SMTP server, or enter the dummy
215  * engine for deep tests.
216  */
217  if ((state->flags & PSC_STATE_FLAG_NOFORWARD) != 0
218  || ((state->flags & PSC_STATE_MASK_SMTPD_PASS)
220  psc_smtpd_tests(state);
221  else
222  psc_conclude(state);
223  return;
224 
225  /*
226  * EOF, or the client spoke before its turn. We simply drop the
227  * connection, or we continue waiting and allow DNS replies to
228  * trickle in.
229  */
230  default:
231  if ((read_count = recv(vstream_fileno(state->smtp_client_stream),
232  read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
233  /* Avoid memory leak. */
234  if (state->dnsbl_score == NO_DNSBL_SCORE
235  && (state->flags & PSC_STATE_FLAG_DNSBL_TODO))
236  (void) psc_dnsbl_retrieve(state->smtp_client_addr,
237  &state->dnsbl_name,
238  state->dnsbl_index,
239  &state->dnsbl_ttl);
240  /* XXX Wait for DNS replies to come in. */
241  psc_hangup_event(state);
242  return;
243  }
244  read_buf[read_count] = 0;
245  escape(psc_escape_buf, read_buf, read_count);
246  msg_info("PREGREET %d after %s from [%s]:%s: %.100s", read_count,
247  psc_format_delta_time(psc_temp, state->start_time, &elapsed),
248  PSC_CLIENT_ADDR_PORT(state), STR(psc_escape_buf));
250  switch (psc_pregr_action) {
251  case PSC_ACT_DROP:
252  /* Avoid memory leak. */
253  if (state->dnsbl_score == NO_DNSBL_SCORE
254  && (state->flags & PSC_STATE_FLAG_DNSBL_TODO))
255  (void) psc_dnsbl_retrieve(state->smtp_client_addr,
256  &state->dnsbl_name,
257  state->dnsbl_index,
258  &state->dnsbl_ttl);
259  PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n");
260  return;
261  case PSC_ACT_ENFORCE:
262  /* We call psc_dnsbl_retrieve() when the timer expires. */
263  PSC_ENFORCE_SESSION_STATE(state, "550 5.5.1 Protocol error\r\n");
264  break;
265  case PSC_ACT_IGNORE:
266  /* We call psc_dnsbl_retrieve() when the timer expires. */
267  /* We must handle this case after the timer expires. */
268  break;
269  default:
270  msg_panic("%s: unknown pregreet action value %d",
271  myname, psc_pregr_action);
272  }
273 
274  /*
275  * Terminate the greet delay if we're just waiting for the pregreet
276  * test to complete. It is safe to call psc_early_event directly,
277  * since we are already in that function.
278  *
279  * XXX After this code passes all tests, swap around the two blocks in
280  * this switch statement and fall through from EVENT_READ into
281  * EVENT_TIME, instead of calling psc_early_event recursively.
282  */
284  if (elapsed.dt_sec >= PSC_EFF_GREET_WAIT
285  || ((state->flags & PSC_STATE_MASK_EARLY_DONE)
287  psc_early_event(EVENT_TIME, context);
288  else
289  event_request_timer(psc_early_event, context,
290  PSC_EFF_GREET_WAIT - elapsed.dt_sec);
291  return;
292  }
293 }
294 
295 /* psc_early_dnsbl_event - cancel pregreet timer if waiting for DNS only */
296 
297 static void psc_early_dnsbl_event(int unused_event, void *context)
298 {
299  const char *myname = "psc_early_dnsbl_event";
300  PSC_STATE *state = (PSC_STATE *) context;
301 
302  if (msg_verbose)
303  msg_info("%s: notify [%s]:%s", myname, PSC_CLIENT_ADDR_PORT(state));
304 
305  /*
306  * Collect the DNSBL score, and whitelist other tests if applicable.
307  */
308  state->dnsbl_score =
310  state->dnsbl_index, &state->dnsbl_ttl);
311  if (var_psc_dnsbl_wthresh < 0)
312  psc_whitelist_non_dnsbl(state);
313 
314  /*
315  * Terminate the greet delay if we're just waiting for DNSBL lookup to
316  * complete. Don't call psc_early_event directly, that would result in a
317  * dangling pointer.
318  */
320  if ((state->flags & PSC_STATE_MASK_EARLY_DONE)
322  event_request_timer(psc_early_event, context, EVENT_NULL_DELAY);
323 }
324 
325 /* psc_early_tests - start the early (before protocol) tests */
326 
328 {
329  const char *myname = "psc_early_tests";
330 
331  /*
332  * Report errors and progress in the context of this test.
333  */
334  PSC_BEGIN_TESTS(state, "tests before SMTP handshake");
335 
336  /*
337  * Run a PREGREET test. Send half the greeting banner, by way of teaser,
338  * then wait briefly to see if the client speaks before its turn.
339  */
340  if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0
341  && psc_teaser_greeting != 0
342  && PSC_SEND_REPLY(state, psc_teaser_greeting) != 0) {
343  psc_hangup_event(state);
344  return;
345  }
346 
347  /*
348  * Run a DNS blocklist query.
349  */
350  if ((state->flags & PSC_STATE_FLAG_DNSBL_TODO) != 0)
351  state->dnsbl_index =
352  psc_dnsbl_request(state->smtp_client_addr, psc_early_dnsbl_event,
353  (void *) state);
354  else
355  state->dnsbl_index = -1;
356  state->dnsbl_score = NO_DNSBL_SCORE;
357 
358  /*
359  * Wait for the client to respond or for DNS lookup to complete.
360  */
361  if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0)
363  psc_early_event, (void *) state, PSC_EFF_GREET_WAIT);
364  else
365  event_request_timer(psc_early_event, (void *) state, PSC_EFF_GREET_WAIT);
366 }
367 
368 /* psc_early_init - initialize early tests */
369 
370 void psc_early_init(void)
371 {
372  if (*var_psc_pregr_banner) {
374  psc_teaser_greeting = mystrdup(STR(psc_temp));
375  psc_escape_buf = vstring_alloc(100);
376  }
377 }
int msg_verbose
Definition: msg.c:177
PSC_CLIENT_INFO * client_info
Definition: postscreen.h:83
#define PSC_TINDX_COUNT
Definition: postscreen.h:47
#define PSC_STATE_FLAG_DNSBL_TODO
Definition: postscreen.h:193
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define PSC_STATE_FLAG_BYTINDX_PASS(tindx)
Definition: postscreen.h:169
time_t expire_time[PSC_TINDX_COUNT]
Definition: postscreen.h:63
#define PSC_STATE_FLAG_BYTINDX_TODO(tindx)
Definition: postscreen.h:171
#define PSC_STATE_MASK_ANY_FAIL
Definition: postscreen.h:269
#define PSC_FAIL_SESSION_STATE(state, bits)
Definition: postscreen.h:422
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
char * var_psc_pregr_banner
Definition: postscreen.c:480
#define PSC_CLEAR_EVENT_REQUEST(fd, time_act, context)
Definition: postscreen.h:341
int dnsbl_score
Definition: postscreen.h:85
#define PSC_STATE_MASK_EARLY_DONE
Definition: postscreen.h:247
int var_psc_dnsbl_thresh
Definition: postscreen.c:486
char * smtp_client_port
Definition: postscreen.h:75
VSTRING * escape(VSTRING *, const char *, ssize_t)
Definition: unescape.c:133
#define PSC_STATE_FLAG_DNSBL_DONE
Definition: postscreen.h:194
int var_psc_pregr_ttl
Definition: postscreen.c:482
int var_psc_dnsbl_wthresh
Definition: postscreen.c:487
#define PSC_DNSBL_FORMAT
#define PSC_READ_BUF_SIZE
Definition: postscreen.h:36
int psc_check_queue_length
Definition: postscreen.c:527
#define PSC_UNFAIL_SESSION_STATE(state, bits)
Definition: postscreen.h:450
#define PSC_STATE_FLAG_BYTINDX_DONE(tindx)
Definition: postscreen.h:173
int flags
Definition: postscreen.h:70
void psc_early_init(void)
int psc_dnsbl_request(const char *, void(*)(int, void *), void *)
#define PSC_BEGIN_TESTS(state, name)
Definition: postscreen.h:498
#define PSC_READ_EVENT_REQUEST(fd, action, context, timeout)
Definition: postscreen.h:325
#define PSC_CLIENT_ADDR_PORT(state)
Definition: postscreen.h:414
int dnsbl_ttl
Definition: postscreen.h:86
#define PSC_TINDX_PREGR
Definition: postscreen.h:42
#define PSC_STATE_MASK_SMTPD_TODO
Definition: postscreen.h:256
char * smtp_client_addr
Definition: postscreen.h:74
long dt_sec
Definition: log_adhoc.c:76
#define PSC_ENFORCE_SESSION_STATE(state, reply)
Definition: postscreen.h:439
void psc_conclude(PSC_STATE *)
#define NO_DNSBL_SCORE
int psc_dnsbl_retrieve(const char *, const char **, int, int *)
#define PSC_STATE_FLAG_DNSBL_FAIL
Definition: postscreen.h:191
VSTRING * dnsbl_reply
Definition: postscreen.h:84
#define STR(x)
Definition: anvil.c:518
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
void psc_hangup_event(PSC_STATE *)
#define PSC_STATE_MASK_PREGR_FAIL_DONE
Definition: postscreen.h:234
const char * dnsbl_name
Definition: postscreen.h:87
#define PSC_STATE_FLAG_PREGR_PASS
Definition: postscreen.h:187
#define PSC_ACT_ENFORCE
Definition: postscreen.h:357
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
#define PSC_ACT_IGNORE
Definition: postscreen.h:358
#define PSC_STATE_FLAG_PREGR_DONE
Definition: postscreen.h:189
#define PSC_DROP_SESSION_STATE(state, reply)
Definition: postscreen.h:432
#define PSC_STATE_FLAG_NOFORWARD
Definition: postscreen.h:112
void psc_early_tests(PSC_STATE *state)
VSTRING * psc_temp
Definition: postscreen.c:530
char * psc_format_delta_time(VSTRING *, struct timeval, DELTA_TIME *)
time_t event_time(void)
Definition: events.c:647
#define PSC_STATE_FLAGS_TODO_TO_PASS(todo_flags)
Definition: postscreen.h:146
void psc_smtpd_tests(PSC_STATE *)
#define EVENT_TIME
Definition: events.h:43
#define PSC_STATE_MASK_EARLY_TODO
Definition: postscreen.h:249
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
Definition: events.c:894
#define PSC_STATE_FLAG_PREGR_FAIL
Definition: postscreen.h:186
#define PSC_PASS_SESSION_STATE(state, what, bits)
Definition: postscreen.h:417
#define vstream_fileno(vp)
Definition: vstream.h:115
int dnsbl_index
Definition: postscreen.h:88
#define PSC_STATE_FLAG_DNSBL_PASS
Definition: postscreen.h:192
const char * psc_test_name(int)
#define PSC_STATE_FLAGS_TODO_TO_DONE(todo_flags)
Definition: postscreen.h:147
int psc_pregr_action
Definition: postscreen.c:532
#define PSC_EFF_GREET_WAIT
Definition: postscreen.h:385
int psc_dnsbl_action
Definition: postscreen.c:533
#define PSC_SEND_REPLY
Definition: postscreen.h:542
int psc_post_queue_length
Definition: postscreen.c:528
#define PSC_TINDX_DNSBL
Definition: postscreen.h:43
#define PSC_STATE_MASK_SMTPD_PASS
Definition: postscreen.h:259
struct timeval start_time
Definition: postscreen.h:81
const char * psc_print_state_flags(int, const char *)
VSTREAM * smtp_client_stream
Definition: postscreen.h:72
#define PSC_STATE_FLAG_PREGR_TODO
Definition: postscreen.h:188
#define PSC_ACT_DROP
Definition: postscreen.h:356
#define EVENT_NULL_DELAY
Definition: events.h:52
void msg_info(const char *fmt,...)
Definition: msg.c:199