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