Mercurial > hg > nginx-mail
annotate src/mail/ngx_mail_imap_handler.c @ 571:5938746e70c2 PATCH_NGINX_MAIL_0_8
Mail: get rid of ugly protocol check in ngx_mail_auth_parse().
Instead, use index of argument which holds authentication mechanism name. For
IMAP and POP3 it's 0, for SMTP - 1 as SMTP preserves command in first argument
to allow pipelining support.
While here, add check that we actually have argument holding authentication
mechanism name. Currently IMAP has no appropriate checks before calling
ngx_mail_auth_parse() which results in possible access of uninitialized
memory.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sun, 27 Sep 2009 00:52:15 +0400 |
parents | 2580fe1c5a9a |
children | 0b460e61bdcd |
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_imap_module.h> | |
12 | |
13 | |
14 static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, | |
15 ngx_connection_t *c); | |
16 static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s, | |
17 ngx_connection_t *c); | |
18 static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s, | |
19 ngx_connection_t *c); | |
20 static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s, | |
21 ngx_connection_t *c); | |
22 | |
23 | |
24 static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF; | |
25 static u_char imap_star[] = "* "; | |
26 static u_char imap_ok[] = "OK completed" CRLF; | |
27 static u_char imap_next[] = "+ OK" CRLF; | |
28 static u_char imap_plain_next[] = "+ " CRLF; | |
29 static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF; | |
30 static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF; | |
31 static u_char imap_bye[] = "* BYE" CRLF; | |
32 static u_char imap_invalid_command[] = "BAD invalid command" CRLF; | |
33 | |
34 | |
35 void | |
36 ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c) | |
37 { | |
38 ngx_mail_core_srv_conf_t *cscf; | |
39 | |
40 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
41 | |
42 s->out.len = sizeof(imap_greeting) - 1; | |
43 s->out.data = imap_greeting; | |
44 | |
45 c->read->handler = ngx_mail_imap_init_protocol; | |
46 | |
47 ngx_add_timer(c->read, cscf->timeout); | |
48 | |
459 | 49 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
336 | 50 ngx_mail_close_connection(c); |
51 } | |
52 | |
53 ngx_mail_send(c->write); | |
54 } | |
55 | |
56 | |
57 void | |
58 ngx_mail_imap_init_protocol(ngx_event_t *rev) | |
59 { | |
60 ngx_connection_t *c; | |
61 ngx_mail_session_t *s; | |
62 ngx_mail_imap_srv_conf_t *iscf; | |
63 | |
64 c = rev->data; | |
65 | |
66 c->log->action = "in auth state"; | |
67 | |
68 if (rev->timedout) { | |
69 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
70 c->timedout = 1; | |
71 ngx_mail_close_connection(c); | |
72 return; | |
73 } | |
74 | |
75 s = c->data; | |
76 | |
77 if (s->buffer == NULL) { | |
78 if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) | |
79 == NGX_ERROR) | |
80 { | |
81 ngx_mail_session_internal_server_error(s); | |
82 return; | |
83 } | |
84 | |
85 iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); | |
86 | |
87 s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size); | |
88 if (s->buffer == NULL) { | |
89 ngx_mail_session_internal_server_error(s); | |
90 return; | |
91 } | |
92 } | |
93 | |
94 s->mail_state = ngx_imap_start; | |
95 c->read->handler = ngx_mail_imap_auth_state; | |
96 | |
97 ngx_mail_imap_auth_state(rev); | |
98 } | |
99 | |
100 | |
101 void | |
102 ngx_mail_imap_auth_state(ngx_event_t *rev) | |
103 { | |
104 u_char *p, *dst, *src, *end; | |
105 ngx_str_t *arg; | |
106 ngx_int_t rc; | |
107 ngx_uint_t tag, i; | |
108 ngx_connection_t *c; | |
109 ngx_mail_session_t *s; | |
110 | |
111 c = rev->data; | |
112 s = c->data; | |
113 | |
114 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); | |
115 | |
116 if (rev->timedout) { | |
117 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
118 c->timedout = 1; | |
119 ngx_mail_close_connection(c); | |
120 return; | |
121 } | |
122 | |
123 if (s->out.len) { | |
124 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); | |
125 s->blocked = 1; | |
126 return; | |
127 } | |
128 | |
129 s->blocked = 0; | |
130 | |
131 rc = ngx_mail_read_command(s, c); | |
132 | |
133 if (rc == NGX_AGAIN || rc == NGX_ERROR) { | |
134 return; | |
135 } | |
136 | |
137 tag = 1; | |
138 s->text.len = 0; | |
139 s->out.len = sizeof(imap_ok) - 1; | |
140 s->out.data = imap_ok; | |
141 | |
142 if (rc == NGX_OK) { | |
143 | |
144 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", | |
145 s->command); | |
146 | |
147 if (s->backslash) { | |
148 | |
149 arg = s->args.elts; | |
150 | |
151 for (i = 0; i < s->args.nelts; i++) { | |
152 dst = arg[i].data; | |
153 end = dst + arg[i].len; | |
154 | |
155 for (src = dst; src < end; dst++) { | |
156 *dst = *src; | |
157 if (*src++ == '\\') { | |
158 *dst = *src++; | |
159 } | |
160 } | |
161 | |
162 arg[i].len = dst - arg[i].data; | |
163 } | |
164 | |
165 s->backslash = 0; | |
166 } | |
167 | |
168 switch (s->mail_state) { | |
169 | |
170 case ngx_imap_start: | |
171 | |
172 switch (s->command) { | |
173 | |
174 case NGX_IMAP_LOGIN: | |
175 rc = ngx_mail_imap_login(s, c); | |
176 break; | |
177 | |
178 case NGX_IMAP_AUTHENTICATE: | |
179 rc = ngx_mail_imap_authenticate(s, c); | |
180 tag = (rc != NGX_OK); | |
181 break; | |
182 | |
183 case NGX_IMAP_CAPABILITY: | |
184 rc = ngx_mail_imap_capability(s, c); | |
185 break; | |
186 | |
187 case NGX_IMAP_LOGOUT: | |
188 s->quit = 1; | |
189 s->text.len = sizeof(imap_bye) - 1; | |
190 s->text.data = imap_bye; | |
191 break; | |
192 | |
193 case NGX_IMAP_NOOP: | |
194 break; | |
195 | |
196 case NGX_IMAP_STARTTLS: | |
197 rc = ngx_mail_imap_starttls(s, c); | |
198 break; | |
199 | |
200 default: | |
201 rc = NGX_MAIL_PARSE_INVALID_COMMAND; | |
202 break; | |
203 } | |
204 | |
205 break; | |
206 | |
207 case ngx_imap_auth_login_username: | |
468
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
208 rc = ngx_mail_auth_login_username(s, c, 0); |
336 | 209 |
210 tag = 0; | |
211 s->out.len = sizeof(imap_password) - 1; | |
212 s->out.data = imap_password; | |
213 s->mail_state = ngx_imap_auth_login_password; | |
214 | |
215 break; | |
216 | |
217 case ngx_imap_auth_login_password: | |
218 rc = ngx_mail_auth_login_password(s, c); | |
219 break; | |
220 | |
221 case ngx_imap_auth_plain: | |
222 rc = ngx_mail_auth_plain(s, c, 0); | |
223 break; | |
224 | |
225 case ngx_imap_auth_cram_md5: | |
226 rc = ngx_mail_auth_cram_md5(s, c); | |
227 break; | |
228 } | |
229 | |
230 } else if (rc == NGX_IMAP_NEXT) { | |
231 tag = 0; | |
232 s->out.len = sizeof(imap_next) - 1; | |
233 s->out.data = imap_next; | |
234 } | |
235 | |
236 switch (rc) { | |
237 | |
238 case NGX_DONE: | |
239 ngx_mail_auth(s, c); | |
240 return; | |
241 | |
242 case NGX_ERROR: | |
243 ngx_mail_session_internal_server_error(s); | |
244 return; | |
245 | |
246 case NGX_MAIL_PARSE_INVALID_COMMAND: | |
247 s->state = 0; | |
248 s->out.len = sizeof(imap_invalid_command) - 1; | |
249 s->out.data = imap_invalid_command; | |
250 s->mail_state = ngx_imap_start; | |
251 break; | |
252 } | |
253 | |
254 if (tag) { | |
255 if (s->tag.len == 0) { | |
256 s->tag.len = sizeof(imap_star) - 1; | |
257 s->tag.data = (u_char *) imap_star; | |
258 } | |
259 | |
260 if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { | |
261 s->tagged_line.len = s->tag.len + s->text.len + s->out.len; | |
382 | 262 s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len); |
336 | 263 if (s->tagged_line.data == NULL) { |
264 ngx_mail_close_connection(c); | |
265 return; | |
266 } | |
267 } | |
268 | |
269 p = s->tagged_line.data; | |
270 | |
271 if (s->text.len) { | |
272 p = ngx_cpymem(p, s->text.data, s->text.len); | |
273 } | |
274 | |
275 p = ngx_cpymem(p, s->tag.data, s->tag.len); | |
276 ngx_memcpy(p, s->out.data, s->out.len); | |
277 | |
278 s->out.len = s->text.len + s->tag.len + s->out.len; | |
279 s->out.data = s->tagged_line.data; | |
280 } | |
281 | |
282 if (rc != NGX_IMAP_NEXT) { | |
283 s->args.nelts = 0; | |
284 | |
285 if (s->state) { | |
286 /* preserve tag */ | |
287 s->arg_start = s->buffer->start + s->tag.len; | |
288 s->buffer->pos = s->arg_start; | |
289 s->buffer->last = s->arg_start; | |
290 | |
291 } else { | |
292 s->buffer->pos = s->buffer->start; | |
293 s->buffer->last = s->buffer->start; | |
294 s->tag.len = 0; | |
295 } | |
296 } | |
297 | |
298 ngx_mail_send(c->write); | |
299 } | |
300 | |
301 | |
302 static ngx_int_t | |
303 ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c) | |
304 { | |
356 | 305 ngx_str_t *arg; |
336 | 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 arg = s->args.elts; | |
314 | |
315 if (s->args.nelts != 2 || arg[0].len == 0) { | |
316 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
317 } | |
318 | |
319 s->login.len = arg[0].len; | |
382 | 320 s->login.data = ngx_pnalloc(c->pool, s->login.len); |
336 | 321 if (s->login.data == NULL) { |
322 return NGX_ERROR; | |
323 } | |
324 | |
325 ngx_memcpy(s->login.data, arg[0].data, s->login.len); | |
326 | |
327 s->passwd.len = arg[1].len; | |
382 | 328 s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); |
336 | 329 if (s->passwd.data == NULL) { |
330 return NGX_ERROR; | |
331 } | |
332 | |
333 ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); | |
334 | |
335 #if (NGX_DEBUG_MAIL_PASSWD) | |
336 ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
337 "imap login:\"%V\" passwd:\"%V\"", | |
338 &s->login, &s->passwd); | |
339 #else | |
340 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
341 "imap login:\"%V\"", &s->login); | |
342 #endif | |
343 | |
344 return NGX_DONE; | |
345 } | |
346 | |
347 | |
348 static ngx_int_t | |
349 ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) | |
350 { | |
351 ngx_int_t rc; | |
352 ngx_mail_core_srv_conf_t *cscf; | |
353 ngx_mail_imap_srv_conf_t *iscf; | |
354 | |
355 #if (NGX_MAIL_SSL) | |
356 if (ngx_mail_starttls_only(s, c)) { | |
357 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
358 } | |
359 #endif | |
360 | |
571
5938746e70c2
Mail: get rid of ugly protocol check in ngx_mail_auth_parse().
Maxim Dounin <mdounin@mdounin.ru>
parents:
468
diff
changeset
|
361 rc = ngx_mail_auth_parse(s, c, 0); |
336 | 362 |
363 switch (rc) { | |
364 | |
365 case NGX_MAIL_AUTH_LOGIN: | |
366 | |
367 s->out.len = sizeof(imap_username) - 1; | |
368 s->out.data = imap_username; | |
369 s->mail_state = ngx_imap_auth_login_username; | |
370 | |
371 return NGX_OK; | |
372 | |
468
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
373 case NGX_MAIL_AUTH_LOGIN_USERNAME: |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
374 |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
375 s->out.len = sizeof(imap_password) - 1; |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
376 s->out.data = imap_password; |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
377 s->mail_state = ngx_imap_auth_login_password; |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
378 |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
379 return ngx_mail_auth_login_username(s, c, 1); |
2580fe1c5a9a
Mail: support for smtp auth login with username.
Maxim Dounin <mdounin@mdounin.ru>
parents:
459
diff
changeset
|
380 |
336 | 381 case NGX_MAIL_AUTH_PLAIN: |
382 | |
383 s->out.len = sizeof(imap_plain_next) - 1; | |
384 s->out.data = imap_plain_next; | |
385 s->mail_state = ngx_imap_auth_plain; | |
386 | |
387 return NGX_OK; | |
388 | |
389 case NGX_MAIL_AUTH_CRAM_MD5: | |
390 | |
391 iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); | |
392 | |
393 if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { | |
394 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
395 } | |
396 | |
397 if (s->salt.data == NULL) { | |
398 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
399 | |
400 if (ngx_mail_salt(s, c, cscf) != NGX_OK) { | |
401 return NGX_ERROR; | |
402 } | |
403 } | |
404 | |
405 if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { | |
406 s->mail_state = ngx_imap_auth_cram_md5; | |
407 return NGX_OK; | |
408 } | |
409 | |
410 return NGX_ERROR; | |
411 } | |
412 | |
413 return rc; | |
414 } | |
415 | |
416 | |
417 static ngx_int_t | |
418 ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c) | |
419 { | |
420 ngx_mail_imap_srv_conf_t *iscf; | |
421 | |
422 iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); | |
423 | |
424 #if (NGX_MAIL_SSL) | |
425 | |
426 if (c->ssl == NULL) { | |
356 | 427 ngx_mail_ssl_conf_t *sslcf; |
428 | |
336 | 429 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); |
430 | |
431 if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { | |
432 s->text = iscf->starttls_capability; | |
433 return NGX_OK; | |
434 } | |
435 | |
436 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { | |
437 s->text = iscf->starttls_only_capability; | |
438 return NGX_OK; | |
439 } | |
440 } | |
441 #endif | |
442 | |
443 s->text = iscf->capability; | |
444 | |
445 return NGX_OK; | |
446 } | |
447 | |
448 | |
449 static ngx_int_t | |
450 ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c) | |
451 { | |
452 #if (NGX_MAIL_SSL) | |
453 ngx_mail_ssl_conf_t *sslcf; | |
454 | |
455 if (c->ssl == NULL) { | |
456 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); | |
457 if (sslcf->starttls) { | |
458 c->read->handler = ngx_mail_starttls_handler; | |
459 return NGX_OK; | |
460 } | |
461 } | |
462 | |
463 #endif | |
464 | |
465 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
466 } |