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 
15 static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);
16 static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);
17 static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,
18     ngx_int_t stls);
19 static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);
20 static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);
21 static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);
22 
23 
24 static u_char  pop3_greeting[] = "+OK POP3 ready" CRLF;
25 static u_char  pop3_ok[] = "+OK" CRLF;
26 static u_char  pop3_next[] = "+ " CRLF;
27 static u_char  pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
28 static u_char  pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
29 static u_char  pop3_invalid_command[] = "-ERR invalid command" CRLF;
30 
31 
32 void
ngx_mail_pop3_init_session(ngx_mail_session_t * s,ngx_connection_t * c)33 ngx_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 
78 void
ngx_mail_pop3_init_protocol(ngx_event_t * rev)79 ngx_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 
119 void
ngx_mail_pop3_auth_state(ngx_event_t * rev)120 ngx_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 
142         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
143             ngx_mail_close_connection(c);
144             return;
145         }
146 
147         return;
148     }
149 
150     s->blocked = 0;
151 
152     rc = ngx_mail_read_command(s, c);
153 
154     if (rc == NGX_AGAIN) {
155         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
156             ngx_mail_session_internal_server_error(s);
157             return;
158         }
159 
160         return;
161     }
162 
163     if (rc == NGX_ERROR) {
164         return;
165     }
166 
167     ngx_str_set(&s->out, pop3_ok);
168 
169     if (rc == NGX_OK) {
170         switch (s->mail_state) {
171 
172         case ngx_pop3_start:
173 
174             switch (s->command) {
175 
176             case NGX_POP3_USER:
177                 rc = ngx_mail_pop3_user(s, c);
178                 break;
179 
180             case NGX_POP3_CAPA:
181                 rc = ngx_mail_pop3_capa(s, c, 1);
182                 break;
183 
184             case NGX_POP3_APOP:
185                 rc = ngx_mail_pop3_apop(s, c);
186                 break;
187 
188             case NGX_POP3_AUTH:
189                 rc = ngx_mail_pop3_auth(s, c);
190                 break;
191 
192             case NGX_POP3_QUIT:
193                 s->quit = 1;
194                 break;
195 
196             case NGX_POP3_NOOP:
197                 break;
198 
199             case NGX_POP3_STLS:
200                 rc = ngx_mail_pop3_stls(s, c);
201                 break;
202 
203             default:
204                 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
205                 break;
206             }
207 
208             break;
209 
210         case ngx_pop3_user:
211 
212             switch (s->command) {
213 
214             case NGX_POP3_PASS:
215                 rc = ngx_mail_pop3_pass(s, c);
216                 break;
217 
218             case NGX_POP3_CAPA:
219                 rc = ngx_mail_pop3_capa(s, c, 0);
220                 break;
221 
222             case NGX_POP3_QUIT:
223                 s->quit = 1;
224                 break;
225 
226             case NGX_POP3_NOOP:
227                 break;
228 
229             default:
230                 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
231                 break;
232             }
233 
234             break;
235 
236         /* suppress warnings */
237         case ngx_pop3_passwd:
238             break;
239 
240         case ngx_pop3_auth_login_username:
241             rc = ngx_mail_auth_login_username(s, c, 0);
242 
243             ngx_str_set(&s->out, pop3_password);
244             s->mail_state = ngx_pop3_auth_login_password;
245             break;
246 
247         case ngx_pop3_auth_login_password:
248             rc = ngx_mail_auth_login_password(s, c);
249             break;
250 
251         case ngx_pop3_auth_plain:
252             rc = ngx_mail_auth_plain(s, c, 0);
253             break;
254 
255         case ngx_pop3_auth_cram_md5:
256             rc = ngx_mail_auth_cram_md5(s, c);
257             break;
258 
259         case ngx_pop3_auth_external:
260             rc = ngx_mail_auth_external(s, c, 0);
261             break;
262         }
263     }
264 
265     switch (rc) {
266 
267     case NGX_DONE:
268         ngx_mail_auth(s, c);
269         return;
270 
271     case NGX_ERROR:
272         ngx_mail_session_internal_server_error(s);
273         return;
274 
275     case NGX_MAIL_PARSE_INVALID_COMMAND:
276         s->mail_state = ngx_pop3_start;
277         s->state = 0;
278 
279         ngx_str_set(&s->out, pop3_invalid_command);
280 
281         /* fall through */
282 
283     case NGX_OK:
284 
285         s->args.nelts = 0;
286         s->buffer->pos = s->buffer->start;
287         s->buffer->last = s->buffer->start;
288 
289         if (s->state) {
290             s->arg_start = s->buffer->start;
291         }
292 
293         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
294             ngx_mail_session_internal_server_error(s);
295             return;
296         }
297 
298         ngx_mail_send(c->write);
299     }
300 }
301 
302 static ngx_int_t
ngx_mail_pop3_user(ngx_mail_session_t * s,ngx_connection_t * c)303 ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
304 {
305     ngx_str_t  *arg;
306 
307 #if (NGX_MAIL_SSL)
308     if (ngx_mail_starttls_only(s, c)) {
309         return NGX_MAIL_PARSE_INVALID_COMMAND;
310     }
311 #endif
312 
313     if (s->args.nelts != 1) {
314         return NGX_MAIL_PARSE_INVALID_COMMAND;
315     }
316 
317     arg = s->args.elts;
318     s->login.len = arg[0].len;
319     s->login.data = ngx_pnalloc(c->pool, s->login.len);
320     if (s->login.data == NULL) {
321         return NGX_ERROR;
322     }
323 
324     ngx_memcpy(s->login.data, arg[0].data, s->login.len);
325 
326     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
327                    "pop3 login: \"%V\"", &s->login);
328 
329     s->mail_state = ngx_pop3_user;
330 
331     return NGX_OK;
332 }
333 
334 
335 static ngx_int_t
ngx_mail_pop3_pass(ngx_mail_session_t * s,ngx_connection_t * c)336 ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)
337 {
338     ngx_str_t  *arg;
339 
340     if (s->args.nelts != 1) {
341         return NGX_MAIL_PARSE_INVALID_COMMAND;
342     }
343 
344     arg = s->args.elts;
345     s->passwd.len = arg[0].len;
346     s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
347     if (s->passwd.data == NULL) {
348         return NGX_ERROR;
349     }
350 
351     ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
352 
353 #if (NGX_DEBUG_MAIL_PASSWD)
354     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
355                    "pop3 passwd: \"%V\"", &s->passwd);
356 #endif
357 
358     return NGX_DONE;
359 }
360 
361 
362 static ngx_int_t
ngx_mail_pop3_capa(ngx_mail_session_t * s,ngx_connection_t * c,ngx_int_t stls)363 ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
364 {
365     ngx_mail_pop3_srv_conf_t  *pscf;
366 
367     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
368 
369 #if (NGX_MAIL_SSL)
370 
371     if (stls && c->ssl == NULL) {
372         ngx_mail_ssl_conf_t  *sslcf;
373 
374         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
375 
376         if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
377             s->out = pscf->starttls_capability;
378             return NGX_OK;
379         }
380 
381         if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
382             s->out = pscf->starttls_only_capability;
383             return NGX_OK;
384         }
385     }
386 
387 #endif
388 
389     s->out = pscf->capability;
390     return NGX_OK;
391 }
392 
393 
394 static ngx_int_t
ngx_mail_pop3_stls(ngx_mail_session_t * s,ngx_connection_t * c)395 ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
396 {
397 #if (NGX_MAIL_SSL)
398     ngx_mail_ssl_conf_t  *sslcf;
399 
400     if (c->ssl == NULL) {
401         sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
402         if (sslcf->starttls) {
403             c->read->handler = ngx_mail_starttls_handler;
404             return NGX_OK;
405         }
406     }
407 
408 #endif
409 
410     return NGX_MAIL_PARSE_INVALID_COMMAND;
411 }
412 
413 
414 static ngx_int_t
ngx_mail_pop3_apop(ngx_mail_session_t * s,ngx_connection_t * c)415 ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
416 {
417     ngx_str_t                 *arg;
418     ngx_mail_pop3_srv_conf_t  *pscf;
419 
420 #if (NGX_MAIL_SSL)
421     if (ngx_mail_starttls_only(s, c)) {
422         return NGX_MAIL_PARSE_INVALID_COMMAND;
423     }
424 #endif
425 
426     if (s->args.nelts != 2) {
427         return NGX_MAIL_PARSE_INVALID_COMMAND;
428     }
429 
430     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
431 
432     if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
433         return NGX_MAIL_PARSE_INVALID_COMMAND;
434     }
435 
436     arg = s->args.elts;
437 
438     s->login.len = arg[0].len;
439     s->login.data = ngx_pnalloc(c->pool, s->login.len);
440     if (s->login.data == NULL) {
441         return NGX_ERROR;
442     }
443 
444     ngx_memcpy(s->login.data, arg[0].data, s->login.len);
445 
446     s->passwd.len = arg[1].len;
447     s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
448     if (s->passwd.data == NULL) {
449         return NGX_ERROR;
450     }
451 
452     ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
453 
454     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
455                    "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);
456 
457     s->auth_method = NGX_MAIL_AUTH_APOP;
458 
459     return NGX_DONE;
460 }
461 
462 
463 static ngx_int_t
ngx_mail_pop3_auth(ngx_mail_session_t * s,ngx_connection_t * c)464 ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
465 {
466     ngx_int_t                  rc;
467     ngx_mail_pop3_srv_conf_t  *pscf;
468 
469 #if (NGX_MAIL_SSL)
470     if (ngx_mail_starttls_only(s, c)) {
471         return NGX_MAIL_PARSE_INVALID_COMMAND;
472     }
473 #endif
474 
475     pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
476 
477     if (s->args.nelts == 0) {
478         s->out = pscf->auth_capability;
479         s->state = 0;
480 
481         return NGX_OK;
482     }
483 
484     rc = ngx_mail_auth_parse(s, c);
485 
486     switch (rc) {
487 
488     case NGX_MAIL_AUTH_LOGIN:
489 
490         ngx_str_set(&s->out, pop3_username);
491         s->mail_state = ngx_pop3_auth_login_username;
492 
493         return NGX_OK;
494 
495     case NGX_MAIL_AUTH_LOGIN_USERNAME:
496 
497         ngx_str_set(&s->out, pop3_password);
498         s->mail_state = ngx_pop3_auth_login_password;
499 
500         return ngx_mail_auth_login_username(s, c, 1);
501 
502     case NGX_MAIL_AUTH_PLAIN:
503 
504         ngx_str_set(&s->out, pop3_next);
505         s->mail_state = ngx_pop3_auth_plain;
506 
507         return NGX_OK;
508 
509     case NGX_MAIL_AUTH_CRAM_MD5:
510 
511         if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
512             return NGX_MAIL_PARSE_INVALID_COMMAND;
513         }
514 
515         if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
516             s->mail_state = ngx_pop3_auth_cram_md5;
517             return NGX_OK;
518         }
519 
520         return NGX_ERROR;
521 
522     case NGX_MAIL_AUTH_EXTERNAL:
523 
524         if (!(pscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {
525             return NGX_MAIL_PARSE_INVALID_COMMAND;
526         }
527 
528         ngx_str_set(&s->out, pop3_username);
529         s->mail_state = ngx_pop3_auth_external;
530 
531         return NGX_OK;
532     }
533 
534     return rc;
535 }
536