~ubuntu-branches/ubuntu/natty/postfix/natty-security

« back to all changes in this revision

Viewing changes to src/postscreen/postscreen_early.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2011-02-22 05:26:41 UTC
  • mfrom: (1.1.27 upstream)
  • Revision ID: james.westby@ubuntu.com-20110222052641-l05d2bsz2kka0yca
Tags: 2.8.0-1~build1
natty version

Show diffs side-by-side

added added

removed removed

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