merge r2995, r2996, r2997, r2998, r3003, r3141, r3210, r3211, r3232:
various SSL fixes and features:
*) $ssl_client_verify
*) "ssl_verify_client ask" was changed to "ssl_verify_client optional"
*) ssl_crl
*) delete OpenSSL pre-0.9.7 compatibility: the sources were not actually
compatible with OpenSSL 0.9.6 since ssl_session_cache introduction
*) fix memory corruption in $ssl_client_cert
*) issue SNI warning instead of failure: this is too common case
*) use ngx_log_error(), since OpenSSL does not set an error on the failure
*) add SNI support in -V output
Igor Sysoev
12 years ago
238 | 238 | #ifdef NGX_COMPILER |
239 | 239 | ngx_log_stderr(0, "built by " NGX_COMPILER); |
240 | 240 | #endif |
241 | #if (NGX_SSL) | |
242 | #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME | |
243 | ngx_log_stderr(0, "TLS SNI support enabled"); | |
244 | #else | |
245 | ngx_log_stderr(0, "TLS SNI support disabled"); | |
246 | #endif | |
247 | #endif | |
241 | 248 | ngx_log_stderr(0, "configure arguments:" NGX_CONFIGURE); |
242 | 249 | } |
243 | 250 |
96 | 96 | ngx_int_t |
97 | 97 | ngx_ssl_init(ngx_log_t *log) |
98 | 98 | { |
99 | #if OPENSSL_VERSION_NUMBER >= 0x00907000 | |
100 | 99 | OPENSSL_config(NULL); |
101 | #endif | |
102 | 100 | |
103 | 101 | SSL_library_init(); |
104 | 102 | SSL_load_error_strings(); |
105 | 103 | |
106 | #if (NGX_SSL_ENGINE) | |
107 | 104 | ENGINE_load_builtin_engines(); |
108 | #endif | |
109 | 105 | |
110 | 106 | ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); |
111 | 107 | |
168 | 164 | SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG); |
169 | 165 | SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG); |
170 | 166 | |
171 | #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS | |
172 | 167 | SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); |
173 | #endif | |
174 | 168 | |
175 | 169 | SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE); |
176 | 170 | |
261 | 255 | ERR_clear_error(); |
262 | 256 | |
263 | 257 | SSL_CTX_set_client_CA_list(ssl->ctx, list); |
258 | ||
259 | return NGX_OK; | |
260 | } | |
261 | ||
262 | ||
263 | ngx_int_t | |
264 | ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl) | |
265 | { | |
266 | X509_STORE *store; | |
267 | X509_LOOKUP *lookup; | |
268 | ||
269 | if (crl->len == 0) { | |
270 | return NGX_OK; | |
271 | } | |
272 | ||
273 | if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) { | |
274 | return NGX_ERROR; | |
275 | } | |
276 | ||
277 | store = SSL_CTX_get_cert_store(ssl->ctx); | |
278 | ||
279 | if (store == NULL) { | |
280 | ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
281 | "SSL_CTX_get_cert_store() failed"); | |
282 | return NGX_ERROR; | |
283 | } | |
284 | ||
285 | lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); | |
286 | ||
287 | if (lookup == NULL) { | |
288 | ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
289 | "X509_STORE_add_lookup() failed"); | |
290 | return NGX_ERROR; | |
291 | } | |
292 | ||
293 | if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM) | |
294 | == 0) | |
295 | { | |
296 | ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
297 | "X509_LOOKUP_load_file(\"%s\") failed", crl->data); | |
298 | return NGX_ERROR; | |
299 | } | |
300 | ||
301 | X509_STORE_set_flags(store, | |
302 | X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); | |
264 | 303 | |
265 | 304 | return NGX_OK; |
266 | 305 | } |
1200 | 1239 | if (err == NGX_ECONNRESET |
1201 | 1240 | || err == NGX_EPIPE |
1202 | 1241 | || err == NGX_ENOTCONN |
1203 | #if !(NGX_CRIT_ETIMEDOUT) | |
1204 | 1242 | || err == NGX_ETIMEDOUT |
1205 | #endif | |
1206 | 1243 | || err == NGX_ECONNREFUSED |
1207 | 1244 | || err == NGX_ENETDOWN |
1208 | 1245 | || err == NGX_ENETUNREACH |
1973 | 2010 | |
1974 | 2011 | p = s->data; |
1975 | 2012 | |
1976 | for (i = 0; i < len; i++) { | |
2013 | for (i = 0; i < cert.len - 1; i++) { | |
1977 | 2014 | *p++ = cert.data[i]; |
1978 | 2015 | if (cert.data[i] == LF) { |
1979 | 2016 | *p++ = '\t'; |
2107 | 2144 | } |
2108 | 2145 | |
2109 | 2146 | |
2147 | ngx_int_t | |
2148 | ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) | |
2149 | { | |
2150 | X509 *cert; | |
2151 | ||
2152 | if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { | |
2153 | s->len = sizeof("FAILED") - 1; | |
2154 | s->data = (u_char *) "FAILED"; | |
2155 | ||
2156 | return NGX_OK; | |
2157 | } | |
2158 | ||
2159 | cert = SSL_get_peer_certificate(c->ssl->connection); | |
2160 | ||
2161 | if (cert) { | |
2162 | s->len = sizeof("SUCCESS") - 1; | |
2163 | s->data = (u_char *) "SUCCESS"; | |
2164 | ||
2165 | } else { | |
2166 | s->len = sizeof("NONE") - 1; | |
2167 | s->data = (u_char *) "NONE"; | |
2168 | } | |
2169 | ||
2170 | X509_free(cert); | |
2171 | ||
2172 | return NGX_OK; | |
2173 | } | |
2174 | ||
2175 | ||
2110 | 2176 | static void * |
2111 | 2177 | ngx_openssl_create_conf(ngx_cycle_t *cycle) |
2112 | 2178 | { |
2130 | 2196 | static char * |
2131 | 2197 | ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) |
2132 | 2198 | { |
2133 | #if (NGX_SSL_ENGINE) | |
2134 | 2199 | ngx_openssl_conf_t *oscf = conf; |
2135 | 2200 | |
2136 | 2201 | ENGINE *engine; |
2165 | 2230 | ENGINE_free(engine); |
2166 | 2231 | |
2167 | 2232 | return NGX_CONF_OK; |
2168 | ||
2169 | #else | |
2170 | ||
2171 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
2172 | "\"ssl_engine\" directive is available only in " | |
2173 | "OpenSSL 0.9.7 and higher,"); | |
2174 | ||
2175 | return NGX_CONF_ERROR; | |
2176 | ||
2177 | #endif | |
2178 | 2233 | } |
2179 | 2234 | |
2180 | 2235 | |
2181 | 2236 | static void |
2182 | 2237 | ngx_openssl_exit(ngx_cycle_t *cycle) |
2183 | 2238 | { |
2184 | #if (NGX_SSL_ENGINE) | |
2185 | 2239 | ENGINE_cleanup(); |
2186 | #endif | |
2187 | } | |
2240 | } |
12 | 12 | |
13 | 13 | #include <openssl/ssl.h> |
14 | 14 | #include <openssl/err.h> |
15 | ||
16 | #if OPENSSL_VERSION_NUMBER >= 0x00907000 | |
17 | 15 | #include <openssl/conf.h> |
18 | 16 | #include <openssl/engine.h> |
19 | #define NGX_SSL_ENGINE 1 | |
20 | #endif | |
21 | 17 | |
22 | 18 | #define NGX_SSL_NAME "OpenSSL" |
23 | 19 | |
99 | 95 | ngx_str_t *cert, ngx_str_t *key); |
100 | 96 | ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, |
101 | 97 | ngx_str_t *cert, ngx_int_t depth); |
98 | ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl); | |
102 | 99 | ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl); |
103 | 100 | ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); |
104 | 101 | ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, |
130 | 127 | ngx_str_t *s); |
131 | 128 | ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, |
132 | 129 | ngx_str_t *s); |
130 | ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, | |
131 | ngx_str_t *s); | |
133 | 132 | |
134 | 133 | |
135 | 134 | ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); |
29 | 29 | void *conf); |
30 | 30 | static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, |
31 | 31 | void *conf); |
32 | ||
33 | #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) | |
34 | ||
35 | static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, | |
36 | void *conf); | |
37 | ||
38 | static char ngx_http_ssl_openssl097[] = "OpenSSL 0.9.7 and higher"; | |
39 | ||
40 | #endif | |
41 | 32 | |
42 | 33 | |
43 | 34 | static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { |
51 | 42 | static ngx_conf_enum_t ngx_http_ssl_verify[] = { |
52 | 43 | { ngx_string("off"), 0 }, |
53 | 44 | { ngx_string("on"), 1 }, |
54 | { ngx_string("ask"), 2 }, | |
45 | { ngx_string("optional"), 2 }, | |
55 | 46 | { ngx_null_string, 0 } |
56 | 47 | }; |
57 | 48 | |
123 | 114 | |
124 | 115 | { ngx_string("ssl_prefer_server_ciphers"), |
125 | 116 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, |
126 | #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE | |
127 | 117 | ngx_conf_set_flag_slot, |
128 | 118 | NGX_HTTP_SRV_CONF_OFFSET, |
129 | 119 | offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers), |
130 | 120 | NULL }, |
131 | #else | |
132 | ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 }, | |
133 | #endif | |
134 | 121 | |
135 | 122 | { ngx_string("ssl_session_cache"), |
136 | 123 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12, |
144 | 131 | ngx_conf_set_sec_slot, |
145 | 132 | NGX_HTTP_SRV_CONF_OFFSET, |
146 | 133 | offsetof(ngx_http_ssl_srv_conf_t, session_timeout), |
134 | NULL }, | |
135 | ||
136 | { ngx_string("ssl_crl"), | |
137 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, | |
138 | ngx_conf_set_str_slot, | |
139 | NGX_HTTP_SRV_CONF_OFFSET, | |
140 | offsetof(ngx_http_ssl_srv_conf_t, crl), | |
147 | 141 | NULL }, |
148 | 142 | |
149 | 143 | ngx_null_command |
205 | 199 | { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable, |
206 | 200 | (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 }, |
207 | 201 | |
202 | { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable, | |
203 | (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 }, | |
204 | ||
208 | 205 | { ngx_null_string, NULL, NULL, 0, 0, 0 } |
209 | 206 | }; |
210 | 207 | |
312 | 309 | * sscf->certificate_key = { 0, NULL }; |
313 | 310 | * sscf->dhparam = { 0, NULL }; |
314 | 311 | * sscf->client_certificate = { 0, NULL }; |
312 | * sscf->crl = { 0, NULL }; | |
315 | 313 | * sscf->ciphers.len = 0; |
316 | 314 | * sscf->ciphers.data = NULL; |
317 | 315 | * sscf->shm_zone = NULL; |
358 | 356 | |
359 | 357 | ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, |
360 | 358 | ""); |
359 | ngx_conf_merge_str_value(conf->crl, prev->crl, ""); | |
361 | 360 | |
362 | 361 | ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); |
363 | 362 | |
406 | 405 | ngx_http_ssl_servername) |
407 | 406 | == 0) |
408 | 407 | { |
409 | ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, | |
410 | "SSL_CTX_set_tlsext_servername_callback() failed"); | |
411 | return NGX_CONF_ERROR; | |
408 | ngx_log_error(NGX_LOG_WARN, cf->log, 0, | |
409 | "nginx was built with SNI support, however, now it is linked " | |
410 | "dynamically to an OpenSSL library which has no tlsext support, " | |
411 | "therefore SNI is not available"); | |
412 | 412 | } |
413 | 413 | |
414 | 414 | #endif |
452 | 452 | { |
453 | 453 | return NGX_CONF_ERROR; |
454 | 454 | } |
455 | } | |
456 | ||
457 | #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE | |
455 | ||
456 | if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) { | |
457 | return NGX_CONF_ERROR; | |
458 | } | |
459 | } | |
458 | 460 | |
459 | 461 | if (conf->prefer_server_ciphers) { |
460 | 462 | SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); |
461 | 463 | } |
462 | ||
463 | #endif | |
464 | 464 | |
465 | 465 | /* a temporary 512-bit RSA key is required for export versions of MSIE */ |
466 | 466 | if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) { |
619 | 619 | |
620 | 620 | return NGX_CONF_ERROR; |
621 | 621 | } |
622 | ||
623 | ||
624 | #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) | |
625 | ||
626 | static char * | |
627 | ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
628 | { | |
629 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
630 | "\"%V\" directive is available only in %s,", | |
631 | &cmd->name, cmd->post); | |
632 | ||
633 | return NGX_CONF_ERROR; | |
634 | } | |
635 | ||
636 | #endif |
32 | 32 | ngx_str_t certificate_key; |
33 | 33 | ngx_str_t dhparam; |
34 | 34 | ngx_str_t client_certificate; |
35 | ngx_str_t crl; | |
35 | 36 | |
36 | 37 | ngx_str_t ciphers; |
37 | 38 |
1519 | 1519 | |
1520 | 1520 | sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); |
1521 | 1521 | |
1522 | if (sscf->verify == 1) { | |
1522 | if (sscf->verify) { | |
1523 | 1523 | rc = SSL_get_verify_result(c->ssl->connection); |
1524 | 1524 | |
1525 | 1525 | if (rc != X509_V_OK) { |
1534 | 1534 | return; |
1535 | 1535 | } |
1536 | 1536 | |
1537 | cert = SSL_get_peer_certificate(c->ssl->connection); | |
1538 | ||
1539 | if (cert == NULL) { | |
1540 | ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
1541 | "client sent no required SSL certificate"); | |
1542 | ||
1543 | ngx_ssl_remove_cached_session(sscf->ssl.ctx, | |
1537 | if (sscf->verify == 1) { | |
1538 | cert = SSL_get_peer_certificate(c->ssl->connection); | |
1539 | ||
1540 | if (cert == NULL) { | |
1541 | ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
1542 | "client sent no required SSL certificate"); | |
1543 | ||
1544 | ngx_ssl_remove_cached_session(sscf->ssl.ctx, | |
1544 | 1545 | (SSL_get0_session(c->ssl->connection))); |
1545 | 1546 | |
1546 | ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT); | |
1547 | return; | |
1548 | } | |
1549 | ||
1550 | X509_free(cert); | |
1547 | ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT); | |
1548 | return; | |
1549 | } | |
1550 | ||
1551 | X509_free(cert); | |
1552 | } | |
1551 | 1553 | } |
1552 | 1554 | } |
1553 | 1555 |
21 | 21 | static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, |
22 | 22 | void *conf); |
23 | 23 | |
24 | #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) | |
25 | ||
26 | static char *ngx_mail_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, | |
27 | void *conf); | |
28 | ||
29 | static char ngx_mail_ssl_openssl097[] = "OpenSSL 0.9.7 and higher"; | |
30 | ||
31 | #endif | |
32 | ||
33 | 24 | |
34 | 25 | static ngx_conf_enum_t ngx_http_starttls_state[] = { |
35 | 26 | { ngx_string("off"), NGX_MAIL_STARTTLS_OFF }, |
101 | 92 | |
102 | 93 | { ngx_string("ssl_prefer_server_ciphers"), |
103 | 94 | NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, |
104 | #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE | |
105 | 95 | ngx_conf_set_flag_slot, |
106 | 96 | NGX_MAIL_SRV_CONF_OFFSET, |
107 | 97 | offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers), |
108 | 98 | NULL }, |
109 | #else | |
110 | ngx_mail_ssl_nosupported, 0, 0, ngx_mail_ssl_openssl097 }, | |
111 | #endif | |
112 | 99 | |
113 | 100 | { ngx_string("ssl_session_cache"), |
114 | 101 | NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12, |
296 | 283 | } |
297 | 284 | } |
298 | 285 | |
299 | #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE | |
300 | ||
301 | 286 | if (conf->prefer_server_ciphers) { |
302 | 287 | SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); |
303 | 288 | } |
304 | ||
305 | #endif | |
306 | 289 | |
307 | 290 | if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) { |
308 | 291 | return NGX_CONF_ERROR; |
491 | 474 | |
492 | 475 | return NGX_CONF_ERROR; |
493 | 476 | } |
494 | ||
495 | ||
496 | #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) | |
497 | ||
498 | static char * | |
499 | ngx_mail_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
500 | { | |
501 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
502 | "\"%V\" directive is available only in %s,", | |
503 | &cmd->name, cmd->post); | |
504 | ||
505 | return NGX_CONF_ERROR; | |
506 | } | |
507 | ||
508 | #endif |