upstream nginx-0.7.36
[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     ngx_uint_t n)
361 {
362     ngx_str_t  *arg;
363
364     arg = s->args.elts;
365
366     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
367                    "mail auth login username: \"%V\"", &arg[n]);
368
369     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
370     if (s->login.data == NULL){
371         return NGX_ERROR;
372     }
373
374     if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {
375         ngx_log_error(NGX_LOG_INFO, c->log, 0,
376             "client sent invalid base64 encoding in AUTH LOGIN command");
377         return NGX_MAIL_PARSE_INVALID_COMMAND;
378     }
379
380     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
381                    "mail auth login username: \"%V\"", &s->login);
382
383     return NGX_OK;
384 }
385
386
387 ngx_int_t
388 ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
389 {
390     ngx_str_t  *arg;
391
392     arg = s->args.elts;
393
394 #if (NGX_DEBUG_MAIL_PASSWD)
395     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
396                    "mail auth login password: \"%V\"", &arg[0]);
397 #endif
398
399     s->passwd.data = ngx_pnalloc(c->pool,
400                                  ngx_base64_decoded_length(arg[0].len));
401     if (s->passwd.data == NULL){
402         return NGX_ERROR;
403     }
404
405     if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
406         ngx_log_error(NGX_LOG_INFO, c->log, 0,
407             "client sent invalid base64 encoding in AUTH LOGIN command");
408         return NGX_MAIL_PARSE_INVALID_COMMAND;
409     }
410
411 #if (NGX_DEBUG_MAIL_PASSWD)
412     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
413                    "mail auth login password: \"%V\"", &s->passwd);
414 #endif
415
416     return NGX_DONE;
417 }
418
419
420 ngx_int_t
421 ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
422     char *prefix, size_t len)
423 {
424     u_char      *p;
425     ngx_str_t    salt;
426     ngx_uint_t   n;
427
428     p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
429     if (p == NULL) {
430         return NGX_ERROR;
431     }
432
433     salt.data = ngx_cpymem(p, prefix, len);
434     s->salt.len -= 2;
435
436     ngx_encode_base64(&salt, &s->salt);
437
438     s->salt.len += 2;
439     n = len + salt.len;
440     p[n++] = CR; p[n++] = LF;
441
442     s->out.len = n;
443     s->out.data = p;
444
445     return NGX_OK;
446 }
447
448
449 ngx_int_t
450 ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
451 {
452     u_char     *p, *last;
453     ngx_str_t  *arg;
454
455     arg = s->args.elts;
456
457     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
458                    "mail auth cram-md5: \"%V\"", &arg[0]);
459
460     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
461     if (s->login.data == NULL){
462         return NGX_ERROR;
463     }
464
465     if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
466         ngx_log_error(NGX_LOG_INFO, c->log, 0,
467             "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
468         return NGX_MAIL_PARSE_INVALID_COMMAND;
469     }
470
471     p = s->login.data;
472     last = p + s->login.len;
473
474     while (p < last) {
475         if (*p++ == ' ') {
476             s->login.len = p - s->login.data - 1;
477             s->passwd.len = last - p;
478             s->passwd.data = p;
479             break;
480         }
481     }
482
483     if (s->passwd.len != 32) {
484         ngx_log_error(NGX_LOG_INFO, c->log, 0,
485             "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
486         return NGX_MAIL_PARSE_INVALID_COMMAND;
487     }
488
489     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
490                    "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
491
492     s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
493
494     return NGX_DONE;
495 }
496
497
498 void
499 ngx_mail_send(ngx_event_t *wev)
500 {
501     ngx_int_t                  n;
502     ngx_connection_t          *c;
503     ngx_mail_session_t        *s;
504     ngx_mail_core_srv_conf_t  *cscf;
505
506     c = wev->data;
507     s = c->data;
508
509     if (wev->timedout) {
510         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
511         c->timedout = 1;
512         ngx_mail_close_connection(c);
513         return;
514     }
515
516     if (s->out.len == 0) {
517         if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
518             ngx_mail_close_connection(c);
519         }
520
521         return;
522     }
523
524     n = c->send(c, s->out.data, s->out.len);
525
526     if (n > 0) {
527         s->out.len -= n;
528
529         if (wev->timer_set) {
530             ngx_del_timer(wev);
531         }
532
533         if (s->quit) {
534             ngx_mail_close_connection(c);
535             return;
536         }
537
538         if (s->blocked) {
539             c->read->handler(c->read);
540         }
541
542         return;
543     }
544
545     if (n == NGX_ERROR) {
546         ngx_mail_close_connection(c);
547         return;
548     }
549
550     /* n == NGX_AGAIN */
551
552     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
553
554     ngx_add_timer(c->write, cscf->timeout);
555
556     if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
557         ngx_mail_close_connection(c);
558         return;
559     }
560 }
561
562
563 ngx_int_t
564 ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
565 {
566     ssize_t                    n;
567     ngx_int_t                  rc;
568     ngx_str_t                  l;
569     ngx_mail_core_srv_conf_t  *cscf;
570
571     n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
572
573     if (n == NGX_ERROR || n == 0) {
574         ngx_mail_close_connection(c);
575         return NGX_ERROR;
576     }
577
578     if (n > 0) {
579         s->buffer->last += n;
580     }
581
582     if (n == NGX_AGAIN) {
583         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
584             ngx_mail_session_internal_server_error(s);
585             return NGX_ERROR;
586         }
587
588         return NGX_AGAIN;
589     }
590
591     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
592
593     rc = cscf->protocol->parse_command(s);
594
595     if (rc == NGX_AGAIN) {
596
597         if (s->buffer->last < s->buffer->end) {
598             return rc;
599         }
600
601         l.len = s->buffer->last - s->buffer->start;
602         l.data = s->buffer->start;
603
604         ngx_log_error(NGX_LOG_INFO, c->log, 0,
605                       "client sent too long command \"%V\"", &l);
606
607         s->quit = 1;
608
609         return NGX_MAIL_PARSE_INVALID_COMMAND;
610     }
611
612     if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
613         return rc;
614     }
615
616     if (rc == NGX_ERROR) {
617         ngx_mail_close_connection(c);
618         return NGX_ERROR;
619     }
620
621     return NGX_OK;
622 }
623
624
625 void
626 ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
627 {
628     s->args.nelts = 0;
629     s->buffer->pos = s->buffer->start;
630     s->buffer->last = s->buffer->start;
631     s->state = 0;
632
633     if (c->read->timer_set) {
634         ngx_del_timer(c->read);
635     }
636
637     s->login_attempt++;
638
639     ngx_mail_auth_http_init(s);
640 }
641
642
643 void
644 ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
645 {
646     ngx_mail_core_srv_conf_t  *cscf;
647
648     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
649
650     s->out = cscf->protocol->internal_server_error;
651     s->quit = 1;
652
653     ngx_mail_send(s->connection->write);
654 }
655
656
657 void
658 ngx_mail_close_connection(ngx_connection_t *c)
659 {
660     ngx_pool_t  *pool;
661
662     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
663                    "close mail connection: %d", c->fd);
664
665 #if (NGX_MAIL_SSL)
666
667     if (c->ssl) {
668         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
669             c->ssl->handler = ngx_mail_close_connection;
670             return;
671         }
672     }
673
674 #endif
675
676 #if (NGX_STAT_STUB)
677     ngx_atomic_fetch_add(ngx_stat_active, -1);
678 #endif
679
680     c->destroyed = 1;
681
682     pool = c->pool;
683
684     ngx_close_connection(c);
685
686     ngx_destroy_pool(pool);
687 }
688
689
690 u_char *
691 ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
692 {
693     u_char              *p;
694     ngx_mail_session_t  *s;
695     ngx_mail_log_ctx_t  *ctx;
696
697     if (log->action) {
698         p = ngx_snprintf(buf, len, " while %s", log->action);
699         len -= p - buf;
700         buf = p;
701     }
702
703     ctx = log->data;
704
705     p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
706     len -= p - buf;
707     buf = p;
708
709     s = ctx->session;
710
711     if (s == NULL) {
712         return p;
713     }
714
715     p = ngx_snprintf(buf, len, "%s, server: %V",
716                      s->starttls ? " using starttls" : "",
717                      s->addr_text);
718     len -= p - buf;
719     buf = p;
720
721     if (s->login.len == 0) {
722         return p;
723     }
724
725     p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
726     len -= p - buf;
727     buf = p;
728
729     if (s->proxy == NULL) {
730         return p;
731     }
732
733     p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
734
735     return p;
736 }