1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
11#include <ngx_mail.h>
12#include <ngx_mail_pop3_module.h>
13
14
15static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);
16static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);
17static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,
18    ngx_int_t stls);
19static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);
20static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);
21static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);
22
23
24static u_char  pop3_greeting[] = "+OK POP3 ready" CRLF;
25static u_char  pop3_ok[] = "+OK" CRLF;
26static u_char  pop3_next[] = "+ " CRLF;
27static u_char  pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
28static u_char  pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
29static u_char  pop3_invalid_command[] = "-ERR invalid command" CRLF;
30
31
32void
33ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
34{
35    u_char                    *p;
36    ngx_mail_core_srv_conf_t  *cscf;
37    ngx_mail_pop3_srv_conf_t  *pscf;
38
39    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
40    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
41
42    if (pscf->auth_methods
43        & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
44    {
45        if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
46            ngx_mail_session_internal_server_error(s);
47            return;
48        }
49
50        s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
51        if (s->out.data == NULL) {
52            ngx_mail_session_internal_server_error(s);
53            return;
54        }
55
56        p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);
57        *p++ = ' ';
58        p = ngx_cpymem(p, s->salt.data, s->salt.len);
59
60        s->out.len = p - s->out.data;
61
62    } else {
63        ngx_str_set(&s->out, pop3_greeting);
64    }
65
66    c->read->handler = ngx_mail_pop3_init_protocol;
67
68    ngx_add_timer(c->read, cscf->timeout);
69
70    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
71        ngx_mail_close_connection(c);
72    }
73
74    ngx_mail_send(c->write);
75}
76
77
78void
79ngx_mail_pop3_init_protocol(ngx_event_t *rev)
80{
81    ngx_connection_t    *c;
82    ngx_mail_session_t  *s;
83
84    c = rev->data;
85
86    c->log->action = "in auth state";
87
88    if (rev->timedout) {
89        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
90        c->timedout = 1;
91        ngx_mail_close_connection(c);
92        return;
93    }
94
95    s = c->data;
96
97    if (s->buffer == NULL) {
98        if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
99            == NGX_ERROR)
100        {
101            ngx_mail_session_internal_server_error(s);
102            return;
103        }
104
105        s->buffer = ngx_create_temp_buf(c->pool, 128);
106        if (s->buffer == NULL) {
107            ngx_mail_session_internal_server_error(s);
108            return;
109        }
110    }
111
112    s->mail_state = ngx_pop3_start;
113    c->read->handler = ngx_mail_pop3_auth_state;
114
115    ngx_mail_pop3_auth_state(rev);
116}
117
118
119void
120ngx_mail_pop3_auth_state(ngx_event_t *rev)
121{
122    ngx_int_t            rc;
123    ngx_connection_t    *c;
124    ngx_mail_session_t  *s;
125
126    c = rev->data;
127    s = c->data;
128
129    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state");
130
131    if (rev->timedout) {
132        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
133        c->timedout = 1;
134        ngx_mail_close_connection(c);
135        return;
136    }
137
138    if (s->out.len) {
139        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");
140        s->blocked = 1;
141        return;
142    }
143
144    s->blocked = 0;
145
146    rc = ngx_mail_read_command(s, c);
147
148    if (rc == NGX_AGAIN || rc == NGX_ERROR) {
149        return;
150    }
151
152    ngx_str_set(&s->out, pop3_ok);
153
154    if (rc == NGX_OK) {
155        switch (s->mail_state) {
156
157        case ngx_pop3_start:
158
159            switch (s->command) {
160
161            case NGX_POP3_USER:
162                rc = ngx_mail_pop3_user(s, c);
163                break;
164
165            case NGX_POP3_CAPA:
166                rc = ngx_mail_pop3_capa(s, c, 1);
167                break;
168
169            case NGX_POP3_APOP:
170                rc = ngx_mail_pop3_apop(s, c);
171                break;
172
173            case NGX_POP3_AUTH:
174                rc = ngx_mail_pop3_auth(s, c);
175                break;
176
177            case NGX_POP3_QUIT:
178                s->quit = 1;
179                break;
180
181            case NGX_POP3_NOOP:
182                break;
183
184            case NGX_POP3_STLS:
185                rc = ngx_mail_pop3_stls(s, c);
186                break;
187
188            default:
189                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
190                break;
191            }
192
193            break;
194
195        case ngx_pop3_user:
196
197            switch (s->command) {
198
199            case NGX_POP3_PASS:
200                rc = ngx_mail_pop3_pass(s, c);
201                break;
202
203            case NGX_POP3_CAPA:
204                rc = ngx_mail_pop3_capa(s, c, 0);
205                break;
206
207            case NGX_POP3_QUIT:
208                s->quit = 1;
209                break;
210
211            case NGX_POP3_NOOP:
212                break;
213
214            default:
215                rc = NGX_MAIL_PARSE_INVALID_COMMAND;
216                break;
217            }
218
219            break;
220
221        /* suppress warnings */
222        case ngx_pop3_passwd:
223            break;
224
225        case ngx_pop3_auth_login_username:
226            rc = ngx_mail_auth_login_username(s, c, 0);
227
228            ngx_str_set(&s->out, pop3_password);
229            s->mail_state = ngx_pop3_auth_login_password;
230            break;
231
232        case ngx_pop3_auth_login_password:
233            rc = ngx_mail_auth_login_password(s, c);
234            break;
235
236        case ngx_pop3_auth_plain:
237            rc = ngx_mail_auth_plain(s, c, 0);
238            break;
239
240        case ngx_pop3_auth_cram_md5:
241            rc = ngx_mail_auth_cram_md5(s, c);
242            break;
243
244        case ngx_pop3_auth_external:
245            rc = ngx_mail_auth_external(s, c, 0);
246            break;
247        }
248    }
249
250    switch (rc) {
251
252    case NGX_DONE:
253        ngx_mail_auth(s, c);
254        return;
255
256    case NGX_ERROR:
257        ngx_mail_session_internal_server_error(s);
258        return;
259
260    case NGX_MAIL_PARSE_INVALID_COMMAND:
261        s->mail_state = ngx_pop3_start;
262        s->state = 0;
263
264        ngx_str_set(&s->out, pop3_invalid_command);
265
266        /* fall through */
267
268    case NGX_OK:
269
270        s->args.nelts = 0;
271        s->buffer->pos = s->buffer->start;
272        s->buffer->last = s->buffer->start;
273
274        if (s->state) {
275            s->arg_start = s->buffer->start;
276        }
277
278        ngx_mail_send(c->write);
279    }
280}
281
282static ngx_int_t
283ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
284{
285    ngx_str_t  *arg;
286
287#if (NGX_MAIL_SSL)
288    if (ngx_mail_starttls_only(s, c)) {
289        return NGX_MAIL_PARSE_INVALID_COMMAND;
290    }
291#endif
292
293    if (s->args.nelts != 1) {
294        return NGX_MAIL_PARSE_INVALID_COMMAND;
295    }
296
297    arg = s->args.elts;
298    s->login.len = arg[0].len;
299    s->login.data = ngx_pnalloc(c->pool, s->login.len);
300    if (s->login.data == NULL) {
301        return NGX_ERROR;
302    }
303
304    ngx_memcpy(s->login.data, arg[0].data, s->login.len);
305
306    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
307                   "pop3 login: \"%V\"", &s->login);
308
309    s->mail_state = ngx_pop3_user;
310
311    return NGX_OK;
312}
313
314
315static ngx_int_t
316ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)
317{
318    ngx_str_t  *arg;
319
320    if (s->args.nelts != 1) {
321        return NGX_MAIL_PARSE_INVALID_COMMAND;
322    }
323
324    arg = s->args.elts;
325    s->passwd.len = arg[0].len;
326    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
327    if (s->passwd.data == NULL) {
328        return NGX_ERROR;
329    }
330
331    ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
332
333#if (NGX_DEBUG_MAIL_PASSWD)
334    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
335                   "pop3 passwd: \"%V\"", &s->passwd);
336#endif
337
338    return NGX_DONE;
339}
340
341
342static ngx_int_t
343ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
344{
345    ngx_mail_pop3_srv_conf_t  *pscf;
346
347    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
348
349#if (NGX_MAIL_SSL)
350
351    if (stls && c->ssl == NULL) {
352        ngx_mail_ssl_conf_t  *sslcf;
353
354        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
355
356        if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
357            s->out = pscf->starttls_capability;
358            return NGX_OK;
359        }
360
361        if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
362            s->out = pscf->starttls_only_capability;
363            return NGX_OK;
364        }
365    }
366
367#endif
368
369    s->out = pscf->capability;
370    return NGX_OK;
371}
372
373
374static ngx_int_t
375ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
376{
377#if (NGX_MAIL_SSL)
378    ngx_mail_ssl_conf_t  *sslcf;
379
380    if (c->ssl == NULL) {
381        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
382        if (sslcf->starttls) {
383            c->read->handler = ngx_mail_starttls_handler;
384            return NGX_OK;
385        }
386    }
387
388#endif
389
390    return NGX_MAIL_PARSE_INVALID_COMMAND;
391}
392
393
394static ngx_int_t
395ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
396{
397    ngx_str_t                 *arg;
398    ngx_mail_pop3_srv_conf_t  *pscf;
399
400#if (NGX_MAIL_SSL)
401    if (ngx_mail_starttls_only(s, c)) {
402        return NGX_MAIL_PARSE_INVALID_COMMAND;
403    }
404#endif
405
406    if (s->args.nelts != 2) {
407        return NGX_MAIL_PARSE_INVALID_COMMAND;
408    }
409
410    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
411
412    if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
413        return NGX_MAIL_PARSE_INVALID_COMMAND;
414    }
415
416    arg = s->args.elts;
417
418    s->login.len = arg[0].len;
419    s->login.data = ngx_pnalloc(c->pool, s->login.len);
420    if (s->login.data == NULL) {
421        return NGX_ERROR;
422    }
423
424    ngx_memcpy(s->login.data, arg[0].data, s->login.len);
425
426    s->passwd.len = arg[1].len;
427    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
428    if (s->passwd.data == NULL) {
429        return NGX_ERROR;
430    }
431
432    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
433
434    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
435                   "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);
436
437    s->auth_method = NGX_MAIL_AUTH_APOP;
438
439    return NGX_DONE;
440}
441
442
443static ngx_int_t
444ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
445{
446    ngx_int_t                  rc;
447    ngx_mail_pop3_srv_conf_t  *pscf;
448
449#if (NGX_MAIL_SSL)
450    if (ngx_mail_starttls_only(s, c)) {
451        return NGX_MAIL_PARSE_INVALID_COMMAND;
452    }
453#endif
454
455    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
456
457    if (s->args.nelts == 0) {
458        s->out = pscf->auth_capability;
459        s->state = 0;
460
461        return NGX_OK;
462    }
463
464    rc = ngx_mail_auth_parse(s, c);
465
466    switch (rc) {
467
468    case NGX_MAIL_AUTH_LOGIN:
469
470        ngx_str_set(&s->out, pop3_username);
471        s->mail_state = ngx_pop3_auth_login_username;
472
473        return NGX_OK;
474
475    case NGX_MAIL_AUTH_LOGIN_USERNAME:
476
477        ngx_str_set(&s->out, pop3_password);
478        s->mail_state = ngx_pop3_auth_login_password;
479
480        return ngx_mail_auth_login_username(s, c, 1);
481
482    case NGX_MAIL_AUTH_PLAIN:
483
484        ngx_str_set(&s->out, pop3_next);
485        s->mail_state = ngx_pop3_auth_plain;
486
487        return NGX_OK;
488
489    case NGX_MAIL_AUTH_CRAM_MD5:
490
491        if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
492            return NGX_MAIL_PARSE_INVALID_COMMAND;
493        }
494
495        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
496            s->mail_state = ngx_pop3_auth_cram_md5;
497            return NGX_OK;
498        }
499
500        return NGX_ERROR;
501
502    case NGX_MAIL_AUTH_EXTERNAL:
503
504        if (!(pscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
505            return NGX_MAIL_PARSE_INVALID_COMMAND;
506        }
507
508        ngx_str_set(&s->out, pop3_username);
509        s->mail_state = ngx_pop3_auth_external;
510
511        return NGX_OK;
512    }
513
514    return rc;
515}
516