Mercurial > hg > nginx-mail
annotate src/mail/ngx_mail_smtp_handler.c @ 468:2580fe1c5a9a PATCH_NGINX_MAIL_0_6
Mail: support for smtp auth login with username.
According to "[MS-XLOGIN]: SMTP Protocol AUTH LOGIN Extension Specification"
(http://msdn.microsoft.com/en-us/library/cc433484.aspx) there is special mode
called "AUTH LOGIN with User Name". And it seems to be used by some Microsoft
software seen in wild.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sun, 11 Jan 2009 23:19:26 +0300 |
parents | d67e93e97b4a |
children | a28a95b7a86d |
rev | line source |
---|---|
336 | 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 #include <ngx_mail_smtp_module.h> | |
12 | |
13 | |
366 | 14 static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx); |
15 static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx); | |
16 static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c); | |
336 | 17 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); |
18 static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, | |
19 ngx_connection_t *c); | |
20 | |
21 static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c); | |
22 static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c); | |
23 static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); | |
24 static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, | |
25 ngx_connection_t *c); | |
449 | 26 static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c); |
27 static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c); | |
336 | 28 |
29 static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, | |
30 ngx_connection_t *c, char *err); | |
31 static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, | |
32 ngx_connection_t *c, char *err); | |
33 | |
34 | |
35 static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; | |
36 static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; | |
358 | 37 static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF; |
336 | 38 static u_char smtp_next[] = "334 " CRLF; |
39 static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; | |
40 static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; | |
41 static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; | |
42 static u_char smtp_invalid_pipelining[] = | |
43 "503 5.5.0 Improper use of SMTP command pipelining" CRLF; | |
44 static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; | |
45 static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; | |
449 | 46 static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF; |
336 | 47 |
48 | |
366 | 49 static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]"); |
50 static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]"); | |
51 | |
52 | |
336 | 53 void |
54 ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) | |
55 { | |
366 | 56 struct sockaddr_in *sin; |
57 ngx_resolver_ctx_t *ctx; | |
58 ngx_mail_core_srv_conf_t *cscf; | |
59 | |
60 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
61 | |
62 if (cscf->resolver == NULL) { | |
63 s->host = smtp_unavailable; | |
64 ngx_mail_smtp_greeting(s, c); | |
65 return; | |
66 } | |
67 | |
68 c->log->action = "in resolving client address"; | |
69 | |
70 ctx = ngx_resolve_start(cscf->resolver, NULL); | |
71 if (ctx == NULL) { | |
72 ngx_mail_close_connection(c); | |
73 return; | |
74 } | |
75 | |
76 /* AF_INET only */ | |
77 | |
78 sin = (struct sockaddr_in *) c->sockaddr; | |
79 | |
80 ctx->addr = sin->sin_addr.s_addr; | |
81 ctx->handler = ngx_mail_smtp_resolve_addr_handler; | |
82 ctx->data = s; | |
83 ctx->timeout = cscf->resolver_timeout; | |
84 | |
85 if (ngx_resolve_addr(ctx) != NGX_OK) { | |
86 ngx_mail_close_connection(c); | |
87 } | |
88 } | |
89 | |
90 | |
91 static void | |
92 ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) | |
93 { | |
94 ngx_connection_t *c; | |
95 ngx_mail_session_t *s; | |
96 ngx_mail_core_srv_conf_t *cscf; | |
97 | |
98 s = ctx->data; | |
99 c = s->connection; | |
100 | |
101 if (ctx->state) { | |
102 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
103 "%V could not be resolved (%i: %s)", | |
104 &c->addr_text, ctx->state, | |
105 ngx_resolver_strerror(ctx->state)); | |
106 | |
107 if (ctx->state == NGX_RESOLVE_NXDOMAIN) { | |
108 s->host = smtp_unavailable; | |
109 | |
110 } else { | |
111 s->host = smtp_tempunavail; | |
112 } | |
113 | |
114 ngx_resolve_addr_done(ctx); | |
115 | |
116 ngx_mail_smtp_greeting(s, s->connection); | |
117 | |
118 return; | |
119 } | |
120 | |
121 c->log->action = "in resolving client hostname"; | |
122 | |
123 s->host.data = ngx_pstrdup(c->pool, &ctx->name); | |
124 if (s->host.data == NULL) { | |
125 ngx_resolve_addr_done(ctx); | |
126 ngx_mail_close_connection(c); | |
127 return; | |
128 } | |
129 | |
130 s->host.len = ctx->name.len; | |
131 | |
132 ngx_resolve_addr_done(ctx); | |
133 | |
134 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
135 "address resolved: %V", &s->host); | |
136 | |
137 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
138 | |
139 ctx = ngx_resolve_start(cscf->resolver, NULL); | |
140 if (ctx == NULL) { | |
141 ngx_mail_close_connection(c); | |
142 return; | |
143 } | |
144 | |
145 ctx->name = s->host; | |
146 ctx->type = NGX_RESOLVE_A; | |
147 ctx->handler = ngx_mail_smtp_resolve_name_handler; | |
148 ctx->data = s; | |
149 ctx->timeout = cscf->resolver_timeout; | |
150 | |
151 if (ngx_resolve_name(ctx) != NGX_OK) { | |
152 ngx_mail_close_connection(c); | |
153 } | |
154 } | |
155 | |
156 | |
157 static void | |
158 ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx) | |
159 { | |
160 in_addr_t addr; | |
161 ngx_uint_t i; | |
162 ngx_connection_t *c; | |
163 struct sockaddr_in *sin; | |
164 ngx_mail_session_t *s; | |
165 | |
166 s = ctx->data; | |
167 c = s->connection; | |
168 | |
169 if (ctx->state) { | |
170 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
171 "%V could not be resolved (%i: %s)", | |
172 &ctx->name, ctx->state, | |
173 ngx_resolver_strerror(ctx->state)); | |
174 | |
175 if (ctx->state == NGX_RESOLVE_NXDOMAIN) { | |
176 s->host = smtp_unavailable; | |
177 | |
178 } else { | |
179 s->host = smtp_tempunavail; | |
180 } | |
181 | |
182 } else { | |
183 | |
184 /* AF_INET only */ | |
185 | |
186 sin = (struct sockaddr_in *) c->sockaddr; | |
187 | |
188 for (i = 0; i < ctx->naddrs; i++) { | |
189 | |
190 addr = ctx->addrs[i]; | |
191 | |
192 ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
193 "name was resolved to %ud.%ud.%ud.%ud", | |
194 (ntohl(addr) >> 24) & 0xff, | |
195 (ntohl(addr) >> 16) & 0xff, | |
196 (ntohl(addr) >> 8) & 0xff, | |
197 ntohl(addr) & 0xff); | |
198 | |
199 if (addr == sin->sin_addr.s_addr) { | |
200 goto found; | |
201 } | |
202 } | |
203 | |
204 s->host = smtp_unavailable; | |
205 } | |
206 | |
207 found: | |
208 | |
209 ngx_resolve_name_done(ctx); | |
210 | |
211 ngx_mail_smtp_greeting(s, c); | |
212 } | |
213 | |
214 | |
215 static void | |
216 ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c) | |
217 { | |
336 | 218 ngx_msec_t timeout; |
219 ngx_mail_core_srv_conf_t *cscf; | |
220 ngx_mail_smtp_srv_conf_t *sscf; | |
221 | |
366 | 222 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, |
223 "smtp greeting for \"%V\"", &s->host); | |
224 | |
336 | 225 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); |
226 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
227 | |
228 timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; | |
229 ngx_add_timer(c->read, timeout); | |
230 | |
459 | 231 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
336 | 232 ngx_mail_close_connection(c); |
233 } | |
234 | |
235 if (sscf->greeting_delay) { | |
236 c->read->handler = ngx_mail_smtp_invalid_pipelining; | |
237 return; | |
238 } | |
239 | |
240 c->read->handler = ngx_mail_smtp_init_protocol; | |
241 | |
242 s->out = sscf->greeting; | |
243 | |
244 ngx_mail_send(c->write); | |
245 } | |
246 | |
247 | |
248 static void | |
249 ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev) | |
250 { | |
251 ngx_connection_t *c; | |
252 ngx_mail_session_t *s; | |
253 ngx_mail_core_srv_conf_t *cscf; | |
254 ngx_mail_smtp_srv_conf_t *sscf; | |
255 | |
256 c = rev->data; | |
257 s = c->data; | |
258 | |
259 c->log->action = "in delay pipelining state"; | |
260 | |
261 if (rev->timedout) { | |
262 | |
263 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting"); | |
264 | |
265 rev->timedout = 0; | |
266 | |
267 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
268 | |
269 c->read->handler = ngx_mail_smtp_init_protocol; | |
270 | |
271 ngx_add_timer(c->read, cscf->timeout); | |
272 | |
459 | 273 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
336 | 274 ngx_mail_close_connection(c); |
275 return; | |
276 } | |
277 | |
278 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
279 | |
280 s->out = sscf->greeting; | |
281 | |
282 } else { | |
283 | |
284 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining"); | |
285 | |
286 if (s->buffer == NULL) { | |
287 if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { | |
288 return; | |
289 } | |
290 } | |
291 | |
292 if (ngx_mail_smtp_discard_command(s, c, | |
293 "client was rejected before greeting: \"%V\"") | |
294 != NGX_OK) | |
295 { | |
296 return; | |
297 } | |
298 | |
299 s->out.len = sizeof(smtp_invalid_pipelining) - 1; | |
300 s->out.data = smtp_invalid_pipelining; | |
435
e2df123bbbe2
Mail: close session on smtp_greeting_delay violation.
Maxim Dounin <mdounin@mdounin.ru>
parents:
410
diff
changeset
|
301 s->quit = 1; |
336 | 302 } |
303 | |
304 ngx_mail_send(c->write); | |
305 } | |
306 | |
307 | |
308 void | |
309 ngx_mail_smtp_init_protocol(ngx_event_t *rev) | |
310 { | |
311 ngx_connection_t *c; | |
312 ngx_mail_session_t *s; | |
313 | |
314 c = rev->data; | |
315 | |
316 c->log->action = "in auth state"; | |
317 | |
318 if (rev->timedout) { | |
319 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
320 c->timedout = 1; | |
321 ngx_mail_close_connection(c); | |
322 return; | |
323 } | |
324 | |
325 s = c->data; | |
326 | |
327 if (s->buffer == NULL) { | |
328 if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { | |
329 return; | |
330 } | |
331 } | |
332 | |
333 s->mail_state = ngx_smtp_start; | |
334 c->read->handler = ngx_mail_smtp_auth_state; | |
335 | |
336 ngx_mail_smtp_auth_state(rev); | |
337 } | |
338 | |
339 | |
340 static ngx_int_t | |
341 ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c) | |
342 { | |
343 ngx_mail_smtp_srv_conf_t *sscf; | |
344 | |
345 if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { | |
346 ngx_mail_session_internal_server_error(s); | |
347 return NGX_ERROR; | |
348 } | |
349 | |
350 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
351 | |
352 s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size); | |
353 if (s->buffer == NULL) { | |
354 ngx_mail_session_internal_server_error(s); | |
355 return NGX_ERROR; | |
356 } | |
357 | |
358 return NGX_OK; | |
359 } | |
360 | |
361 | |
362 void | |
363 ngx_mail_smtp_auth_state(ngx_event_t *rev) | |
364 { | |
365 ngx_int_t rc; | |
366 ngx_connection_t *c; | |
367 ngx_mail_session_t *s; | |
368 | |
369 c = rev->data; | |
370 s = c->data; | |
371 | |
372 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); | |
373 | |
374 if (rev->timedout) { | |
375 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
376 c->timedout = 1; | |
377 ngx_mail_close_connection(c); | |
378 return; | |
379 } | |
380 | |
381 if (s->out.len) { | |
382 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); | |
383 s->blocked = 1; | |
384 return; | |
385 } | |
386 | |
387 s->blocked = 0; | |
388 | |
389 rc = ngx_mail_read_command(s, c); | |
390 | |
391 if (rc == NGX_AGAIN || rc == NGX_ERROR) { | |
392 return; | |
393 } | |
394 | |
395 s->out.len = sizeof(smtp_ok) - 1; | |
396 s->out.data = smtp_ok; | |
397 | |
398 if (rc == NGX_OK) { | |
399 switch (s->mail_state) { | |
400 | |
401 case ngx_smtp_start: | |
402 | |
403 switch (s->command) { | |
404 | |
405 case NGX_SMTP_HELO: | |
406 case NGX_SMTP_EHLO: | |
407 rc = ngx_mail_smtp_helo(s, c); | |
408 break; | |
409 | |
410 case NGX_SMTP_AUTH: | |
411 rc = ngx_mail_smtp_auth(s, c); | |
412 break; | |
413 | |
414 case NGX_SMTP_QUIT: | |
415 s->quit = 1; | |
416 s->out.len = sizeof(smtp_bye) - 1; | |
417 s->out.data = smtp_bye; | |
418 break; | |
419 | |
420 case NGX_SMTP_MAIL: | |
421 rc = ngx_mail_smtp_mail(s, c); | |
422 break; | |
423 | |
449 | 424 case NGX_SMTP_RCPT: |
425 rc = ngx_mail_smtp_rcpt(s, c); | |
426 break; | |
427 | |
428 case NGX_SMTP_RSET: | |
429 rc = ngx_mail_smtp_rset(s, c); | |
430 break; | |
431 | |
336 | 432 case NGX_SMTP_NOOP: |
433 break; | |
434 | |
435 case NGX_SMTP_STARTTLS: | |
436 rc = ngx_mail_smtp_starttls(s, c); | |
358 | 437 s->out.len = sizeof(smtp_starttls) - 1; |
438 s->out.data = smtp_starttls; | |
336 | 439 break; |
440 | |
441 default: | |
442 rc = NGX_MAIL_PARSE_INVALID_COMMAND; | |
443 break; | |
444 } | |
445 | |
446 break; | |
447 | |
448 case ngx_smtp_auth_login_username: | |
468
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
449 rc = ngx_mail_auth_login_username(s, c, 0); |
336 | 450 |
451 s->out.len = sizeof(smtp_password) - 1; | |
452 s->out.data = smtp_password; | |
453 s->mail_state = ngx_smtp_auth_login_password; | |
454 break; | |
455 | |
456 case ngx_smtp_auth_login_password: | |
457 rc = ngx_mail_auth_login_password(s, c); | |
458 break; | |
459 | |
460 case ngx_smtp_auth_plain: | |
461 rc = ngx_mail_auth_plain(s, c, 0); | |
462 break; | |
463 | |
464 case ngx_smtp_auth_cram_md5: | |
465 rc = ngx_mail_auth_cram_md5(s, c); | |
466 break; | |
467 } | |
468 } | |
469 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
470 if (s->buffer->pos < s->buffer->last) { |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
471 s->blocked = 1; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
472 } |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
473 |
336 | 474 switch (rc) { |
475 | |
476 case NGX_DONE: | |
477 ngx_mail_auth(s, c); | |
478 return; | |
479 | |
480 case NGX_ERROR: | |
481 ngx_mail_session_internal_server_error(s); | |
482 return; | |
483 | |
484 case NGX_MAIL_PARSE_INVALID_COMMAND: | |
485 s->mail_state = ngx_smtp_start; | |
486 s->state = 0; | |
487 | |
488 s->out.len = sizeof(smtp_invalid_command) - 1; | |
489 s->out.data = smtp_invalid_command; | |
490 | |
491 /* fall through */ | |
492 | |
493 case NGX_OK: | |
494 s->args.nelts = 0; | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
495 |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
496 if (s->buffer->pos == s->buffer->last) { |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
497 s->buffer->pos = s->buffer->start; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
498 s->buffer->last = s->buffer->start; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
499 } |
336 | 500 |
501 if (s->state) { | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
502 s->arg_start = s->buffer->pos; |
336 | 503 } |
504 | |
505 ngx_mail_send(c->write); | |
506 } | |
507 } | |
508 | |
509 | |
510 static ngx_int_t | |
511 ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c) | |
512 { | |
513 ngx_str_t *arg; | |
514 ngx_mail_smtp_srv_conf_t *sscf; | |
515 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
516 if (s->args.nelts != 2) { |
336 | 517 s->out.len = sizeof(smtp_invalid_argument) - 1; |
518 s->out.data = smtp_invalid_argument; | |
519 s->state = 0; | |
520 return NGX_OK; | |
521 } | |
522 | |
523 arg = s->args.elts; | |
524 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
525 s->smtp_helo.len = arg[1].len; |
336 | 526 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
527 s->smtp_helo.data = ngx_pnalloc(c->pool, arg[1].len); |
336 | 528 if (s->smtp_helo.data == NULL) { |
529 return NGX_ERROR; | |
530 } | |
531 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
532 ngx_memcpy(s->smtp_helo.data, arg[1].data, arg[1].len); |
336 | 533 |
449 | 534 s->smtp_from.len = 0; |
535 s->smtp_from.data = NULL; | |
536 s->smtp_to.len = 0; | |
537 s->smtp_to.data = NULL; | |
538 | |
336 | 539 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); |
540 | |
541 if (s->command == NGX_SMTP_HELO) { | |
542 s->out = sscf->server_name; | |
543 | |
544 } else { | |
545 s->esmtp = 1; | |
546 | |
547 #if (NGX_MAIL_SSL) | |
548 | |
549 if (c->ssl == NULL) { | |
356 | 550 ngx_mail_ssl_conf_t *sslcf; |
551 | |
336 | 552 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); |
553 | |
554 if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { | |
555 s->out = sscf->starttls_capability; | |
556 return NGX_OK; | |
557 } | |
558 | |
559 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { | |
560 s->out = sscf->starttls_only_capability; | |
561 return NGX_OK; | |
562 } | |
563 } | |
564 #endif | |
565 | |
566 s->out = sscf->capability; | |
567 } | |
568 | |
569 return NGX_OK; | |
570 } | |
571 | |
572 | |
573 static ngx_int_t | |
574 ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) | |
575 { | |
576 ngx_int_t rc; | |
577 ngx_mail_core_srv_conf_t *cscf; | |
578 ngx_mail_smtp_srv_conf_t *sscf; | |
579 | |
580 #if (NGX_MAIL_SSL) | |
581 if (ngx_mail_starttls_only(s, c)) { | |
582 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
583 } | |
584 #endif | |
585 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
586 if (s->args.nelts < 2) { |
336 | 587 s->out.len = sizeof(smtp_invalid_argument) - 1; |
588 s->out.data = smtp_invalid_argument; | |
589 s->state = 0; | |
590 return NGX_OK; | |
591 } | |
592 | |
593 rc = ngx_mail_auth_parse(s, c); | |
594 | |
595 switch (rc) { | |
596 | |
597 case NGX_MAIL_AUTH_LOGIN: | |
598 | |
599 s->out.len = sizeof(smtp_username) - 1; | |
600 s->out.data = smtp_username; | |
601 s->mail_state = ngx_smtp_auth_login_username; | |
602 | |
603 return NGX_OK; | |
604 | |
468
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
605 case NGX_MAIL_AUTH_LOGIN_USERNAME: |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
606 |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
607 s->out.len = sizeof(smtp_password) - 1; |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
608 s->out.data = smtp_password; |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
609 s->mail_state = ngx_smtp_auth_login_password; |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
610 |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
611 return ngx_mail_auth_login_username(s, c, 1); |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
467
diff
changeset
|
612 |
336 | 613 case NGX_MAIL_AUTH_PLAIN: |
614 | |
615 s->out.len = sizeof(smtp_next) - 1; | |
616 s->out.data = smtp_next; | |
617 s->mail_state = ngx_smtp_auth_plain; | |
618 | |
619 return NGX_OK; | |
620 | |
621 case NGX_MAIL_AUTH_CRAM_MD5: | |
622 | |
623 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
624 | |
625 if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { | |
626 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
627 } | |
628 | |
629 if (s->salt.data == NULL) { | |
630 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
631 | |
632 if (ngx_mail_salt(s, c, cscf) != NGX_OK) { | |
633 return NGX_ERROR; | |
634 } | |
635 } | |
636 | |
637 if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) { | |
638 s->mail_state = ngx_smtp_auth_cram_md5; | |
639 return NGX_OK; | |
640 } | |
641 | |
642 return NGX_ERROR; | |
643 } | |
644 | |
645 return rc; | |
646 } | |
647 | |
648 | |
649 static ngx_int_t | |
650 ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) | |
651 { | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
652 ngx_str_t *arg, *end, cmd; |
449 | 653 ngx_mail_smtp_srv_conf_t *sscf; |
654 | |
655 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
656 | |
657 if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) { | |
658 ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); | |
659 | |
660 s->out.len = sizeof(smtp_auth_required) - 1; | |
661 s->out.data = smtp_auth_required; | |
662 | |
663 return NGX_OK; | |
664 } | |
665 | |
666 /* auth none */ | |
667 | |
668 if (s->smtp_from.len) { | |
669 s->out.len = sizeof(smtp_bad_sequence) - 1; | |
670 s->out.data = smtp_bad_sequence; | |
671 return NGX_OK; | |
672 } | |
673 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
674 arg = s->args.elts; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
675 end = arg + s->args.nelts - 1; |
449 | 676 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
677 cmd.len = end->data + end->len - arg->data; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
678 cmd.data = arg->data; |
449 | 679 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
680 s->smtp_from.len = cmd.len; |
449 | 681 |
467 | 682 s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len); |
449 | 683 if (s->smtp_from.data == NULL) { |
684 return NGX_ERROR; | |
685 } | |
686 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
687 ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len); |
449 | 688 |
689 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
690 "smtp mail from:\"%V\"", &s->smtp_from); | |
691 | |
692 s->out.len = sizeof(smtp_ok) - 1; | |
693 s->out.data = smtp_ok; | |
694 | |
695 return NGX_OK; | |
696 } | |
336 | 697 |
449 | 698 |
699 static ngx_int_t | |
700 ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c) | |
701 { | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
702 ngx_str_t *arg, *end, cmd; |
449 | 703 |
704 if (s->smtp_from.len == 0) { | |
705 s->out.len = sizeof(smtp_bad_sequence) - 1; | |
706 s->out.data = smtp_bad_sequence; | |
707 return NGX_OK; | |
708 } | |
709 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
710 arg = s->args.elts; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
711 end = arg + s->args.nelts - 1; |
449 | 712 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
713 cmd.len = end->data + end->len - arg->data; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
714 cmd.data = arg->data; |
449 | 715 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
716 s->smtp_to.len = cmd.len; |
449 | 717 |
467 | 718 s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len); |
449 | 719 if (s->smtp_to.data == NULL) { |
720 return NGX_ERROR; | |
721 } | |
722 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
723 ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len); |
449 | 724 |
725 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
726 "smtp rcpt to:\"%V\"", &s->smtp_to); | |
727 | |
728 s->auth_method = NGX_MAIL_AUTH_NONE; | |
729 | |
730 return NGX_DONE; | |
731 } | |
732 | |
733 | |
734 static ngx_int_t | |
735 ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c) | |
736 { | |
737 s->smtp_from.len = 0; | |
738 s->smtp_from.data = NULL; | |
739 s->smtp_to.len = 0; | |
740 s->smtp_to.data = NULL; | |
741 | |
742 s->out.len = sizeof(smtp_ok) - 1; | |
743 s->out.data = smtp_ok; | |
336 | 744 |
745 return NGX_OK; | |
746 } | |
747 | |
748 | |
749 static ngx_int_t | |
750 ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c) | |
751 { | |
752 #if (NGX_MAIL_SSL) | |
753 ngx_mail_ssl_conf_t *sslcf; | |
754 | |
755 if (c->ssl == NULL) { | |
756 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); | |
757 if (sslcf->starttls) { | |
758 | |
759 /* | |
760 * RFC3207 requires us to discard any knowledge | |
761 * obtained from client before STARTTLS. | |
762 */ | |
763 | |
764 s->smtp_helo.len = 0; | |
765 s->smtp_helo.data = NULL; | |
449 | 766 s->smtp_from.len = 0; |
767 s->smtp_from.data = NULL; | |
768 s->smtp_to.len = 0; | |
769 s->smtp_to.data = NULL; | |
336 | 770 |
771 c->read->handler = ngx_mail_starttls_handler; | |
772 return NGX_OK; | |
773 } | |
774 } | |
775 | |
776 #endif | |
777 | |
778 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
779 } | |
780 | |
781 | |
782 static ngx_int_t | |
783 ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, | |
784 char *err) | |
785 { | |
786 ssize_t n; | |
787 | |
788 n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); | |
789 | |
790 if (n == NGX_ERROR || n == 0) { | |
791 ngx_mail_close_connection(c); | |
792 return NGX_ERROR; | |
793 } | |
794 | |
795 if (n > 0) { | |
796 s->buffer->last += n; | |
797 } | |
798 | |
799 if (n == NGX_AGAIN) { | |
459 | 800 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
336 | 801 ngx_mail_session_internal_server_error(s); |
802 return NGX_ERROR; | |
803 } | |
804 | |
805 return NGX_AGAIN; | |
806 } | |
807 | |
808 ngx_mail_smtp_log_rejected_command(s, c, err); | |
809 | |
810 s->buffer->pos = s->buffer->start; | |
811 s->buffer->last = s->buffer->start; | |
812 | |
813 return NGX_OK; | |
814 } | |
815 | |
816 | |
817 static void | |
818 ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c, | |
819 char *err) | |
820 { | |
821 u_char ch; | |
822 ngx_str_t cmd; | |
823 ngx_uint_t i; | |
824 | |
825 if (c->log->log_level < NGX_LOG_INFO) { | |
826 return; | |
827 } | |
828 | |
829 cmd.len = s->buffer->last - s->buffer->start; | |
830 cmd.data = s->buffer->start; | |
831 | |
832 for (i = 0; i < cmd.len; i++) { | |
833 ch = cmd.data[i]; | |
834 | |
835 if (ch != CR && ch != LF) { | |
836 continue; | |
837 } | |
838 | |
839 cmd.data[i] = '_'; | |
840 } | |
841 | |
842 cmd.len = i; | |
843 | |
844 ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd); | |
845 } |