upstream nginx-0.7.31
[nginx.git] / nginx / src / mail / ngx_mail_handler.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10 #include <ngx_mail.h>
11
12
13 static void ngx_mail_init_session(ngx_connection_t *c);
14
15 #if (NGX_MAIL_SSL)
16 static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
17 static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
18 #endif
19
20
21 void
22 ngx_mail_init_connection(ngx_connection_t *c)
23 {
24     in_addr_t             in_addr;
25     socklen_t             len;
26     ngx_uint_t            i;
27     struct sockaddr_in    sin;
28     ngx_mail_log_ctx_t   *ctx;
29     ngx_mail_in_port_t   *imip;
30     ngx_mail_in_addr_t   *imia;
31     ngx_mail_session_t   *s;
32
33     /* find the server configuration for the address:port */
34
35     /* AF_INET only */
36
37     imip = c->listening->servers;
38     imia = imip->addrs;
39
40     i = 0;
41
42     if (imip->naddrs > 1) {
43
44         /*
45          * There are several addresses on this port and one of them
46          * is the "*:port" wildcard so getsockname() is needed to determine
47          * the server address.
48          *
49          * AcceptEx() already gave this address.
50          */
51
52 #if (NGX_WIN32)
53         if (c->local_sockaddr) {
54             in_addr =
55                    ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr;
56
57         } else
58 #endif
59         {
60             len = sizeof(struct sockaddr_in);
61             if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
62                 ngx_connection_error(c, ngx_socket_errno,
63                                      "getsockname() failed");
64                 ngx_mail_close_connection(c);
65                 return;
66             }
67
68             in_addr = sin.sin_addr.s_addr;
69         }
70
71         /* the last address is "*" */
72
73         for ( /* void */ ; i < imip->naddrs - 1; i++) {
74             if (in_addr == imia[i].addr) {
75                 break;
76             }
77         }
78     }
79
80
81     s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
82     if (s == NULL) {
83         ngx_mail_close_connection(c);
84         return;
85     }
86
87     s->main_conf = imia[i].ctx->main_conf;
88     s->srv_conf = imia[i].ctx->srv_conf;
89
90     s->addr_text = &imia[i].addr_text;
91
92     c->data = s;
93     s->connection = c;
94
95     ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V",
96                   c->number, &c->addr_text, s->addr_text);
97
98     ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
99     if (ctx == NULL) {
100         ngx_mail_close_connection(c);
101         return;
102     }
103
104     ctx->client = &c->addr_text;
105     ctx->session = s;
106
107     c->log->connection = c->number;
108     c->log->handler = ngx_mail_log_error;
109     c->log->data = ctx;
110     c->log->action = "sending client greeting line";
111
112     c->log_error = NGX_ERROR_INFO;
113
114 #if (NGX_MAIL_SSL)
115     {
116     ngx_mail_ssl_conf_t  *sslcf;
117
118     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
119
120     if (sslcf->enable) {
121         c->log->action = "SSL handshaking";
122
123         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
124         return;
125     }
126
127     if (imia[i].ssl) {
128
129         c->log->action = "SSL handshaking";
130
131         if (sslcf->ssl.ctx == NULL) {
132             ngx_log_error(NGX_LOG_ERR, c->log, 0,
133                           "no \"ssl_certificate\" is defined "
134                           "in server listening on SSL port");
135             ngx_mail_close_connection(c);
136             return;
137         }
138
139         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
140         return;
141     }
142
143     }
144 #endif
145
146     ngx_mail_init_session(c);
147 }
148
149
150 #if (NGX_MAIL_SSL)
151
152 void
153 ngx_mail_starttls_handler(ngx_event_t *rev)
154 {
155     ngx_connection_t     *c;
156     ngx_mail_session_t   *s;
157     ngx_mail_ssl_conf_t  *sslcf;
158
159     c = rev->data;
160     s = c->data;
161     s->starttls = 1;
162
163     c->log->action = "in starttls state";
164
165     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
166
167     ngx_mail_ssl_init_connection(&sslcf->ssl, c);
168 }
169
170
171 static void
172 ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
173 {
174     ngx_mail_session_t        *s;
175     ngx_mail_core_srv_conf_t  *cscf;
176
177     if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
178         ngx_mail_close_connection(c);
179         return;
180     }
181
182     if (ngx_ssl_handshake(c) == NGX_AGAIN) {
183
184         s = c->data;
185
186         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
187
188         ngx_add_timer(c->read, cscf->timeout);
189
190         c->ssl->handler = ngx_mail_ssl_handshake_handler;
191
192         return;
193     }
194
195     ngx_mail_ssl_handshake_handler(c);
196 }
197
198
199 static void
200 ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
201 {
202     ngx_mail_session_t        *s;
203     ngx_mail_core_srv_conf_t  *cscf;
204
205     if (c->ssl->handshaked) {
206
207         s = c->data;
208
209         if (s->starttls) {
210             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
211
212             c->read->handler = cscf->protocol->init_protocol;
213             c->write->handler = ngx_mail_send;
214
215             cscf->protocol->init_protocol(c->read);
216
217             return;
218         }
219
220         c->read->ready = 0;
221
222         ngx_mail_init_session(c);
223         return;
224     }
225
226     ngx_mail_close_connection(c);
227 }
228
229 #endif
230
231
232 static void
233 ngx_mail_init_session(ngx_connection_t *c)
234 {
235     ngx_mail_session_t        *s;
236     ngx_mail_core_srv_conf_t  *cscf;
237
238     s = c->data;
239
240     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
241
242     s->protocol = cscf->protocol->type;
243
244     s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);
245     if (s->ctx == NULL) {
246         ngx_mail_session_internal_server_error(s);
247         return;
248     }
249
250     c->write->handler = ngx_mail_send;
251
252     cscf->protocol->init_session(s, c);
253 }
254
255
256 ngx_int_t
257 ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
258     ngx_mail_core_srv_conf_t *cscf)
259 {
260     s->salt.data = ngx_pnalloc(c->pool,
261                                sizeof(" <18446744073709551616.@>" CRLF) - 1
262                                + NGX_TIME_T_LEN
263                                + cscf->server_name.len);
264     if (s->salt.data == NULL) {
265         return NGX_ERROR;
266     }
267
268     s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,
269                               ngx_random(), ngx_time(), &cscf->server_name)
270                   - s->salt.data;
271
272     return NGX_OK;
273 }
274
275
276 #if (NGX_MAIL_SSL)
277
278 ngx_int_t
279 ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
280 {
281     ngx_mail_ssl_conf_t  *sslcf;
282
283     if (c->ssl) {
284         return 0;
285     }
286
287     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
288
289     if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
290         return 1;
291     }
292
293     return 0;
294 }
295
296 #endif
297
298
299 ngx_int_t
300 ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
301 {
302     u_char     *p, *last;
303     ngx_str_t  *arg, plain;
304
305     arg = s->args.elts;
306
307 #if (NGX_DEBUG_MAIL_PASSWD)
308     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
309                    "mail auth plain: \"%V\"", &arg[n]);
310 #endif
311
312     plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
313     if (plain.data == NULL){
314         return NGX_ERROR;
315     }
316
317     if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {
318         ngx_log_error(NGX_LOG_INFO, c->log, 0,
319             "client sent invalid base64 encoding in AUTH PLAIN command");
320         return NGX_MAIL_PARSE_INVALID_COMMAND;
321     }
322
323     p = plain.data;
324     last = p + plain.len;
325
326     while (p < last && *p++) { /* void */ }
327
328     if (p == last) {
329         ngx_log_error(NGX_LOG_INFO, c->log, 0,
330                       "client sent invalid login in AUTH PLAIN command");
331         return NGX_MAIL_PARSE_INVALID_COMMAND;
332     }
333
334     s->login.data = p;
335
336     while (p < last && *p) { p++; }
337
338     if (p == last) {
339         ngx_log_error(NGX_LOG_INFO, c->log, 0,
340                       "client sent invalid password in AUTH PLAIN command");
341         return NGX_MAIL_PARSE_INVALID_COMMAND;
342     }
343
344     s->login.len = p++ - s->login.data;
345
346     s->passwd.len = last - p;
347     s->passwd.data = p;
348
349 #if (NGX_DEBUG_MAIL_PASSWD)
350     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
351                    "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);
352 #endif
353
354     return NGX_DONE;
355 }
356
357
358 ngx_int_t
359 ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c)
360 {
361     ngx_str_t  *arg;
362
363     arg = s->args.elts;
364
365     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
366                    "mail auth login username: \"%V\"", &arg[0]);
367
368     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
369     if (s->login.data == NULL){
370         return NGX_ERROR;
371     }
372
373     if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
374         ngx_log_error(NGX_LOG_INFO, c->log, 0,
375             "client sent invalid base64 encoding in AUTH LOGIN command");
376         return NGX_MAIL_PARSE_INVALID_COMMAND;
377     }
378
379     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
380                    "mail auth login username: \"%V\"", &s->login);
381
382     return NGX_OK;
383 }
384
385
386 ngx_int_t
387 ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
388 {
389     ngx_str_t  *arg;
390
391     arg = s->args.elts;
392
393 #if (NGX_DEBUG_MAIL_PASSWD)
394     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
395                    "mail auth login password: \"%V\"", &arg[0]);
396 #endif
397
398     s->passwd.data = ngx_pnalloc(c->pool,
399                                  ngx_base64_decoded_length(arg[0].len));
400     if (s->passwd.data == NULL){
401         return NGX_ERROR;
402     }
403
404     if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
405         ngx_log_error(NGX_LOG_INFO, c->log, 0,
406             "client sent invalid base64 encoding in AUTH LOGIN command");
407         return NGX_MAIL_PARSE_INVALID_COMMAND;
408     }
409
410 #if (NGX_DEBUG_MAIL_PASSWD)
411     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
412                    "mail auth login password: \"%V\"", &s->passwd);
413 #endif
414
415     return NGX_DONE;
416 }
417
418
419 ngx_int_t
420 ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
421     char *prefix, size_t len)
422 {
423     u_char      *p;
424     ngx_str_t    salt;
425     ngx_uint_t   n;
426
427     p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
428     if (p == NULL) {
429         return NGX_ERROR;
430     }
431
432     salt.data = ngx_cpymem(p, prefix, len);
433     s->salt.len -= 2;
434
435     ngx_encode_base64(&salt, &s->salt);
436
437     s->salt.len += 2;
438     n = len + salt.len;
439     p[n++] = CR; p[n++] = LF;
440
441     s->out.len = n;
442     s->out.data = p;
443
444     return NGX_OK;
445 }
446
447
448 ngx_int_t
449 ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
450 {
451     u_char     *p, *last;
452     ngx_str_t  *arg;
453
454     arg = s->args.elts;
455
456     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
457                    "mail auth cram-md5: \"%V\"", &arg[0]);
458
459     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
460     if (s->login.data == NULL){
461         return NGX_ERROR;
462     }
463
464     if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
465         ngx_log_error(NGX_LOG_INFO, c->log, 0,
466             "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
467         return NGX_MAIL_PARSE_INVALID_COMMAND;
468     }
469
470     p = s->login.data;
471     last = p + s->login.len;
472
473     while (p < last) {
474         if (*p++ == ' ') {
475             s->login.len = p - s->login.data - 1;
476             s->passwd.len = last - p;
477             s->passwd.data = p;
478             break;
479         }
480     }
481
482     if (s->passwd.len != 32) {
483         ngx_log_error(NGX_LOG_INFO, c->log, 0,
484             "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
485         return NGX_MAIL_PARSE_INVALID_COMMAND;
486     }
487
488     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
489                    "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
490
491     s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
492
493     return NGX_DONE;
494 }
495
496
497 void
498 ngx_mail_send(ngx_event_t *wev)
499 {
500     ngx_int_t                  n;
501     ngx_connection_t          *c;
502     ngx_mail_session_t        *s;
503     ngx_mail_core_srv_conf_t  *cscf;
504
505     c = wev->data;
506     s = c->data;
507
508     if (wev->timedout) {
509         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
510         c->timedout = 1;
511         ngx_mail_close_connection(c);
512         return;
513     }
514
515     if (s->out.len == 0) {
516         if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
517             ngx_mail_close_connection(c);
518         }
519
520         return;
521     }
522
523     n = c->send(c, s->out.data, s->out.len);
524
525     if (n > 0) {
526         s->out.len -= n;
527
528         if (wev->timer_set) {
529             ngx_del_timer(wev);
530         }
531
532         if (s->quit) {
533             ngx_mail_close_connection(c);
534             return;
535         }
536
537         if (s->blocked) {
538             c->read->handler(c->read);
539         }
540
541         return;
542     }
543
544     if (n == NGX_ERROR) {
545         ngx_mail_close_connection(c);
546         return;
547     }
548
549     /* n == NGX_AGAIN */
550
551     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
552
553     ngx_add_timer(c->write, cscf->timeout);
554
555     if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
556         ngx_mail_close_connection(c);
557         return;
558     }
559 }
560
561
562 ngx_int_t
563 ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
564 {
565     ssize_t                    n;
566     ngx_int_t                  rc;
567     ngx_str_t                  l;
568     ngx_mail_core_srv_conf_t  *cscf;
569
570     n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
571
572     if (n == NGX_ERROR || n == 0) {
573         ngx_mail_close_connection(c);
574         return NGX_ERROR;
575     }
576
577     if (n > 0) {
578         s->buffer->last += n;
579     }
580
581     if (n == NGX_AGAIN) {
582         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
583             ngx_mail_session_internal_server_error(s);
584             return NGX_ERROR;
585         }
586
587         return NGX_AGAIN;
588     }
589
590     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
591
592     rc = cscf->protocol->parse_command(s);
593
594     if (rc == NGX_AGAIN) {
595
596         if (s->buffer->last < s->buffer->end) {
597             return rc;
598         }
599
600         l.len = s->buffer->last - s->buffer->start;
601         l.data = s->buffer->start;
602
603         ngx_log_error(NGX_LOG_INFO, c->log, 0,
604                       "client sent too long command \"%V\"", &l);
605
606         s->quit = 1;
607
608         return NGX_MAIL_PARSE_INVALID_COMMAND;
609     }
610
611     if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
612         return rc;
613     }
614
615     if (rc == NGX_ERROR) {
616         ngx_mail_close_connection(c);
617         return NGX_ERROR;
618     }
619
620     return NGX_OK;
621 }
622
623
624 void
625 ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
626 {
627     s->args.nelts = 0;
628     s->buffer->pos = s->buffer->start;
629     s->buffer->last = s->buffer->start;
630     s->state = 0;
631
632     if (c->read->timer_set) {
633         ngx_del_timer(c->read);
634     }
635
636     s->login_attempt++;
637
638     ngx_mail_auth_http_init(s);
639 }
640
641
642 void
643 ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
644 {
645     ngx_mail_core_srv_conf_t  *cscf;
646
647     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
648
649     s->out = cscf->protocol->internal_server_error;
650     s->quit = 1;
651
652     ngx_mail_send(s->connection->write);
653 }
654
655
656 void
657 ngx_mail_close_connection(ngx_connection_t *c)
658 {
659     ngx_pool_t  *pool;
660
661     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
662                    "close mail connection: %d", c->fd);
663
664 #if (NGX_MAIL_SSL)
665
666     if (c->ssl) {
667         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
668             c->ssl->handler = ngx_mail_close_connection;
669             return;
670         }
671     }
672
673 #endif
674
675 #if (NGX_STAT_STUB)
676     ngx_atomic_fetch_add(ngx_stat_active, -1);
677 #endif
678
679     c->destroyed = 1;
680
681     pool = c->pool;
682
683     ngx_close_connection(c);
684
685     ngx_destroy_pool(pool);
686 }
687
688
689 u_char *
690 ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
691 {
692     u_char              *p;
693     ngx_mail_session_t  *s;
694     ngx_mail_log_ctx_t  *ctx;
695
696     if (log->action) {
697         p = ngx_snprintf(buf, len, " while %s", log->action);
698         len -= p - buf;
699         buf = p;
700     }
701
702     ctx = log->data;
703
704     p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
705     len -= p - buf;
706     buf = p;
707
708     s = ctx->session;
709
710     if (s == NULL) {
711         return p;
712     }
713
714     p = ngx_snprintf(buf, len, "%s, server: %V",
715                      s->starttls ? " using starttls" : "",
716                      s->addr_text);
717     len -= p - buf;
718     buf = p;
719
720     if (s->login.len == 0) {
721         return p;
722     }
723
724     p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
725     len -= p - buf;
726     buf = p;
727
728     if (s->proxy == NULL) {
729         return p;
730     }
731
732     p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
733
734     return p;
735 }