Mercurial > hg > nginx
annotate src/mail/ngx_mail_ssl_module.c @ 3959:b1f48fa31e6c
MSIE export versions are rare now, so RSA 512 key is generated on demand
and is shared among all hosts instead of pregenerating for every HTTPS host
on configuraiton phase. This decreases start time for configuration with
large number of HTTPS hosts.
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Wed, 20 Jul 2011 12:59:24 +0000 |
parents | 1e90599af73b |
children | 0832a6997227 |
rev | line source |
---|---|
539 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
1136 | 9 #include <ngx_mail.h> |
539 | 10 |
11 | |
3938
1e90599af73b
use !aNULL to disable all anonymous cipher suites
Igor Sysoev <igor@sysoev.ru>
parents:
3516
diff
changeset
|
12 #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" |
539 | 13 |
14 | |
1136 | 15 static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf); |
16 static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child); | |
2224 | 17 |
18 static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, | |
19 void *conf); | |
20 static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, | |
21 void *conf); | |
1136 | 22 static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, |
976 | 23 void *conf); |
539 | 24 |
25 | |
583 | 26 static ngx_conf_enum_t ngx_http_starttls_state[] = { |
1136 | 27 { ngx_string("off"), NGX_MAIL_STARTTLS_OFF }, |
28 { ngx_string("on"), NGX_MAIL_STARTTLS_ON }, | |
29 { ngx_string("only"), NGX_MAIL_STARTTLS_ONLY }, | |
583 | 30 { ngx_null_string, 0 } |
31 }; | |
32 | |
33 | |
34 | |
1136 | 35 static ngx_conf_bitmask_t ngx_mail_ssl_protocols[] = { |
547 | 36 { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, |
37 { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, | |
38 { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, | |
39 { ngx_null_string, 0 } | |
40 }; | |
41 | |
42 | |
1136 | 43 static ngx_command_t ngx_mail_ssl_commands[] = { |
539 | 44 |
45 { ngx_string("ssl"), | |
1136 | 46 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, |
2224 | 47 ngx_mail_ssl_enable, |
1136 | 48 NGX_MAIL_SRV_CONF_OFFSET, |
49 offsetof(ngx_mail_ssl_conf_t, enable), | |
539 | 50 NULL }, |
51 | |
583 | 52 { ngx_string("starttls"), |
1136 | 53 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, |
2224 | 54 ngx_mail_ssl_starttls, |
1136 | 55 NGX_MAIL_SRV_CONF_OFFSET, |
56 offsetof(ngx_mail_ssl_conf_t, starttls), | |
583 | 57 ngx_http_starttls_state }, |
58 | |
539 | 59 { ngx_string("ssl_certificate"), |
1136 | 60 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, |
539 | 61 ngx_conf_set_str_slot, |
1136 | 62 NGX_MAIL_SRV_CONF_OFFSET, |
63 offsetof(ngx_mail_ssl_conf_t, certificate), | |
539 | 64 NULL }, |
65 | |
66 { ngx_string("ssl_certificate_key"), | |
1136 | 67 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, |
539 | 68 ngx_conf_set_str_slot, |
1136 | 69 NGX_MAIL_SRV_CONF_OFFSET, |
70 offsetof(ngx_mail_ssl_conf_t, certificate_key), | |
539 | 71 NULL }, |
72 | |
2044 | 73 { ngx_string("ssl_dhparam"), |
74 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, | |
75 ngx_conf_set_str_slot, | |
76 NGX_MAIL_SRV_CONF_OFFSET, | |
77 offsetof(ngx_mail_ssl_conf_t, dhparam), | |
78 NULL }, | |
79 | |
547 | 80 { ngx_string("ssl_protocols"), |
1136 | 81 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, |
547 | 82 ngx_conf_set_bitmask_slot, |
1136 | 83 NGX_MAIL_SRV_CONF_OFFSET, |
84 offsetof(ngx_mail_ssl_conf_t, protocols), | |
85 &ngx_mail_ssl_protocols }, | |
547 | 86 |
539 | 87 { ngx_string("ssl_ciphers"), |
1136 | 88 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, |
539 | 89 ngx_conf_set_str_slot, |
1136 | 90 NGX_MAIL_SRV_CONF_OFFSET, |
91 offsetof(ngx_mail_ssl_conf_t, ciphers), | |
539 | 92 NULL }, |
93 | |
547 | 94 { ngx_string("ssl_prefer_server_ciphers"), |
1136 | 95 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, |
547 | 96 ngx_conf_set_flag_slot, |
1136 | 97 NGX_MAIL_SRV_CONF_OFFSET, |
98 offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers), | |
547 | 99 NULL }, |
563 | 100 |
976 | 101 { ngx_string("ssl_session_cache"), |
1136 | 102 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12, |
103 ngx_mail_ssl_session_cache, | |
104 NGX_MAIL_SRV_CONF_OFFSET, | |
976 | 105 0, |
106 NULL }, | |
107 | |
573 | 108 { ngx_string("ssl_session_timeout"), |
1136 | 109 NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, |
573 | 110 ngx_conf_set_sec_slot, |
1136 | 111 NGX_MAIL_SRV_CONF_OFFSET, |
112 offsetof(ngx_mail_ssl_conf_t, session_timeout), | |
573 | 113 NULL }, |
547 | 114 |
539 | 115 ngx_null_command |
116 }; | |
117 | |
118 | |
1136 | 119 static ngx_mail_module_t ngx_mail_ssl_module_ctx = { |
1487
f69493e8faab
ngx_mail_pop3_module, ngx_mail_imap_module, and ngx_mail_smtp_module
Igor Sysoev <igor@sysoev.ru>
parents:
1136
diff
changeset
|
120 NULL, /* protocol */ |
f69493e8faab
ngx_mail_pop3_module, ngx_mail_imap_module, and ngx_mail_smtp_module
Igor Sysoev <igor@sysoev.ru>
parents:
1136
diff
changeset
|
121 |
539 | 122 NULL, /* create main configuration */ |
123 NULL, /* init main configuration */ | |
124 | |
1136 | 125 ngx_mail_ssl_create_conf, /* create server configuration */ |
126 ngx_mail_ssl_merge_conf /* merge server configuration */ | |
539 | 127 }; |
128 | |
129 | |
1136 | 130 ngx_module_t ngx_mail_ssl_module = { |
539 | 131 NGX_MODULE_V1, |
1136 | 132 &ngx_mail_ssl_module_ctx, /* module context */ |
133 ngx_mail_ssl_commands, /* module directives */ | |
134 NGX_MAIL_MODULE, /* module type */ | |
541 | 135 NULL, /* init master */ |
539 | 136 NULL, /* init module */ |
541 | 137 NULL, /* init process */ |
138 NULL, /* init thread */ | |
139 NULL, /* exit thread */ | |
140 NULL, /* exit process */ | |
141 NULL, /* exit master */ | |
142 NGX_MODULE_V1_PADDING | |
539 | 143 }; |
144 | |
145 | |
1136 | 146 static ngx_str_t ngx_mail_ssl_sess_id_ctx = ngx_string("MAIL"); |
543 | 147 |
148 | |
539 | 149 static void * |
1136 | 150 ngx_mail_ssl_create_conf(ngx_conf_t *cf) |
577 | 151 { |
1136 | 152 ngx_mail_ssl_conf_t *scf; |
577 | 153 |
1136 | 154 scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t)); |
539 | 155 if (scf == NULL) { |
2912
c7d57b539248
return NULL instead of NGX_CONF_ERROR on a create conf failure
Igor Sysoev <igor@sysoev.ru>
parents:
2759
diff
changeset
|
156 return NULL; |
539 | 157 } |
158 | |
159 /* | |
577 | 160 * set by ngx_pcalloc(): |
539 | 161 * |
547 | 162 * scf->protocols = 0; |
2044 | 163 * scf->certificate = { 0, NULL }; |
164 * scf->certificate_key = { 0, NULL }; | |
165 * scf->dhparam = { 0, NULL }; | |
3516
dd1570b6f237
ngx_str_set() and ngx_str_null()
Igor Sysoev <igor@sysoev.ru>
parents:
3196
diff
changeset
|
166 * scf->ciphers = { 0, NULL }; |
976 | 167 * scf->shm_zone = NULL; |
539 | 168 */ |
169 | |
170 scf->enable = NGX_CONF_UNSET; | |
2759 | 171 scf->starttls = NGX_CONF_UNSET_UINT; |
976 | 172 scf->prefer_server_ciphers = NGX_CONF_UNSET; |
173 scf->builtin_session_cache = NGX_CONF_UNSET; | |
573 | 174 scf->session_timeout = NGX_CONF_UNSET; |
539 | 175 |
176 return scf; | |
177 } | |
178 | |
179 | |
180 static char * | |
1136 | 181 ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) |
539 | 182 { |
1136 | 183 ngx_mail_ssl_conf_t *prev = parent; |
184 ngx_mail_ssl_conf_t *conf = child; | |
539 | 185 |
2224 | 186 char *mode; |
563 | 187 ngx_pool_cleanup_t *cln; |
188 | |
539 | 189 ngx_conf_merge_value(conf->enable, prev->enable, 0); |
2224 | 190 ngx_conf_merge_uint_value(conf->starttls, prev->starttls, |
191 NGX_MAIL_STARTTLS_OFF); | |
539 | 192 |
573 | 193 ngx_conf_merge_value(conf->session_timeout, |
194 prev->session_timeout, 300); | |
195 | |
547 | 196 ngx_conf_merge_value(conf->prefer_server_ciphers, |
197 prev->prefer_server_ciphers, 0); | |
198 | |
199 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, | |
3190
dd2ae3872634
disable SSLv2 and low ciphers by default
Igor Sysoev <igor@sysoev.ru>
parents:
2996
diff
changeset
|
200 (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); |
547 | 201 |
2224 | 202 ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); |
203 ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); | |
539 | 204 |
2044 | 205 ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); |
206 | |
2124 | 207 ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); |
539 | 208 |
209 | |
547 | 210 conf->ssl.log = cf->log; |
539 | 211 |
2224 | 212 if (conf->enable) { |
213 mode = "ssl"; | |
214 | |
215 } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) { | |
216 mode = "starttls"; | |
217 | |
218 } else { | |
219 mode = ""; | |
220 } | |
221 | |
222 if (*mode) { | |
223 | |
224 if (conf->certificate.len == 0) { | |
225 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
226 "no \"ssl_certificate\" is defined for " | |
227 "the \"%s\" directive in %s:%ui", | |
228 mode, conf->file, conf->line); | |
229 return NGX_CONF_ERROR; | |
230 } | |
231 | |
232 if (conf->certificate_key.len == 0) { | |
233 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
234 "no \"ssl_certificate_key\" is defined for " | |
235 "the \"%s\" directive in %s:%ui", | |
236 mode, conf->file, conf->line); | |
237 return NGX_CONF_ERROR; | |
238 } | |
239 | |
240 } else { | |
241 | |
242 if (conf->certificate.len == 0) { | |
243 return NGX_CONF_OK; | |
244 } | |
245 | |
246 if (conf->certificate_key.len == 0) { | |
247 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
248 "no \"ssl_certificate_key\" is defined " | |
249 "for certificate \"%V\"", | |
250 &conf->certificate); | |
251 return NGX_CONF_ERROR; | |
252 } | |
253 } | |
254 | |
969 | 255 if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) { |
539 | 256 return NGX_CONF_ERROR; |
257 } | |
258 | |
563 | 259 cln = ngx_pool_cleanup_add(cf->pool, 0); |
260 if (cln == NULL) { | |
539 | 261 return NGX_CONF_ERROR; |
262 } | |
263 | |
563 | 264 cln->handler = ngx_ssl_cleanup_ctx; |
265 cln->data = &conf->ssl; | |
266 | |
267 if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, | |
268 &conf->certificate_key) | |
269 != NGX_OK) | |
547 | 270 { |
271 return NGX_CONF_ERROR; | |
272 } | |
539 | 273 |
274 if (conf->ciphers.len) { | |
547 | 275 if (SSL_CTX_set_cipher_list(conf->ssl.ctx, |
563 | 276 (const char *) conf->ciphers.data) |
277 == 0) | |
539 | 278 { |
279 ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, | |
280 "SSL_CTX_set_cipher_list(\"%V\") failed", | |
281 &conf->ciphers); | |
282 } | |
283 } | |
284 | |
563 | 285 if (conf->prefer_server_ciphers) { |
286 SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); | |
287 } | |
288 | |
3959
b1f48fa31e6c
MSIE export versions are rare now, so RSA 512 key is generated on demand
Igor Sysoev <igor@sysoev.ru>
parents:
3938
diff
changeset
|
289 SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback); |
539 | 290 |
2044 | 291 if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { |
292 return NGX_CONF_ERROR; | |
293 } | |
294 | |
976 | 295 ngx_conf_merge_value(conf->builtin_session_cache, |
2032 | 296 prev->builtin_session_cache, NGX_SSL_NONE_SCACHE); |
976 | 297 |
298 if (conf->shm_zone == NULL) { | |
299 conf->shm_zone = prev->shm_zone; | |
300 } | |
539 | 301 |
1136 | 302 if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx, |
976 | 303 conf->builtin_session_cache, |
304 conf->shm_zone, conf->session_timeout) | |
305 != NGX_OK) | |
306 { | |
307 return NGX_CONF_ERROR; | |
308 } | |
573 | 309 |
539 | 310 return NGX_CONF_OK; |
311 } | |
563 | 312 |
577 | 313 |
976 | 314 static char * |
2224 | 315 ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
316 { | |
317 ngx_mail_ssl_conf_t *scf = conf; | |
318 | |
319 char *rv; | |
320 | |
321 rv = ngx_conf_set_flag_slot(cf, cmd, conf); | |
322 | |
323 if (rv != NGX_CONF_OK) { | |
324 return rv; | |
325 } | |
326 | |
327 if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) { | |
328 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
329 "\"starttls\" directive conflicts with \"ssl on\""); | |
330 return NGX_CONF_ERROR; | |
331 } | |
332 | |
333 scf->file = cf->conf_file->file.name.data; | |
334 scf->line = cf->conf_file->line; | |
335 | |
336 return NGX_CONF_OK; | |
337 } | |
338 | |
339 | |
340 static char * | |
341 ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
342 { | |
343 ngx_mail_ssl_conf_t *scf = conf; | |
344 | |
345 char *rv; | |
346 | |
347 rv = ngx_conf_set_enum_slot(cf, cmd, conf); | |
348 | |
349 if (rv != NGX_CONF_OK) { | |
350 return rv; | |
351 } | |
352 | |
353 if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) { | |
354 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
355 "\"ssl\" directive conflicts with \"starttls\""); | |
356 return NGX_CONF_ERROR; | |
357 } | |
358 | |
359 scf->file = cf->conf_file->file.name.data; | |
360 scf->line = cf->conf_file->line; | |
361 | |
362 return NGX_CONF_OK; | |
363 } | |
364 | |
365 | |
366 static char * | |
1136 | 367 ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
976 | 368 { |
1136 | 369 ngx_mail_ssl_conf_t *scf = conf; |
976 | 370 |
371 size_t len; | |
372 ngx_str_t *value, name, size; | |
373 ngx_int_t n; | |
374 ngx_uint_t i, j; | |
375 | |
376 value = cf->args->elts; | |
377 | |
378 for (i = 1; i < cf->args->nelts; i++) { | |
379 | |
1778 | 380 if (ngx_strcmp(value[i].data, "off") == 0) { |
381 scf->builtin_session_cache = NGX_SSL_NO_SCACHE; | |
382 continue; | |
383 } | |
384 | |
2032 | 385 if (ngx_strcmp(value[i].data, "none") == 0) { |
386 scf->builtin_session_cache = NGX_SSL_NONE_SCACHE; | |
387 continue; | |
388 } | |
389 | |
976 | 390 if (ngx_strcmp(value[i].data, "builtin") == 0) { |
391 scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; | |
392 continue; | |
393 } | |
394 | |
395 if (value[i].len > sizeof("builtin:") - 1 | |
396 && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1) | |
397 == 0) | |
398 { | |
399 n = ngx_atoi(value[i].data + sizeof("builtin:") - 1, | |
400 value[i].len - (sizeof("builtin:") - 1)); | |
401 | |
402 if (n == NGX_ERROR) { | |
403 goto invalid; | |
404 } | |
405 | |
406 scf->builtin_session_cache = n; | |
407 | |
408 continue; | |
409 } | |
410 | |
411 if (value[i].len > sizeof("shared:") - 1 | |
412 && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1) | |
413 == 0) | |
414 { | |
415 len = 0; | |
416 | |
417 for (j = sizeof("shared:") - 1; j < value[i].len; j++) { | |
418 if (value[i].data[j] == ':') { | |
419 break; | |
420 } | |
421 | |
422 len++; | |
423 } | |
424 | |
425 if (len == 0) { | |
426 goto invalid; | |
427 } | |
428 | |
429 name.len = len; | |
430 name.data = value[i].data + sizeof("shared:") - 1; | |
431 | |
432 size.len = value[i].len - j - 1; | |
433 size.data = name.data + len + 1; | |
434 | |
435 n = ngx_parse_size(&size); | |
436 | |
437 if (n == NGX_ERROR) { | |
438 goto invalid; | |
439 } | |
440 | |
441 if (n < (ngx_int_t) (8 * ngx_pagesize)) { | |
442 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
443 "session cache \"%V\" is too small", | |
444 &value[i]); | |
445 | |
446 return NGX_CONF_ERROR; | |
447 } | |
448 | |
449 scf->shm_zone = ngx_shared_memory_add(cf, &name, n, | |
1136 | 450 &ngx_mail_ssl_module); |
976 | 451 if (scf->shm_zone == NULL) { |
452 return NGX_CONF_ERROR; | |
453 } | |
454 | |
455 continue; | |
456 } | |
457 | |
458 goto invalid; | |
459 } | |
460 | |
461 if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) { | |
462 scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE; | |
463 } | |
464 | |
465 return NGX_CONF_OK; | |
466 | |
467 invalid: | |
468 | |
469 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
470 "invalid session cache \"%V\"", &value[i]); | |
471 | |
472 return NGX_CONF_ERROR; | |
473 } |