Mercurial > hg > nginx
annotate src/stream/ngx_stream_ssl_module.c @ 6749:f88a145b093e stable-1.10
HTTP/2: the "421 Misdirected Request" response (closes #848).
Since 4fbef397c753 nginx rejects with the 400 error any attempts of
requesting different host over the same connection, if the relevant
virtual server requires verification of a client certificate.
While requesting hosts other than negotiated isn't something legal
in HTTP/1.x, the HTTP/2 specification explicitly permits such requests
for connection reuse and has introduced a special response code 421.
According to RFC 7540 Section 9.1.2 this code can be sent by a server
that is not configured to produce responses for the combination of
scheme and authority that are included in the request URI. And the
client may retry the request over a different connection.
Now this code is used for requests that aren't authorized in current
connection. After receiving the 421 response a client will be able
to open a new connection, provide the required certificate and retry
the request.
Unfortunately, not all clients currently are able to handle it well.
Notably Chrome just shows an error, while at least the latest version
of Firefox retries the request over a new connection.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Fri, 20 May 2016 18:41:17 +0300 |
parents | c256dfdd469d |
children | 51e1f047d15d |
rev | line source |
---|---|
6115 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_stream.h> | |
11 | |
12 | |
13 #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" | |
14 #define NGX_DEFAULT_ECDH_CURVE "prime256v1" | |
15 | |
16 | |
17 static void *ngx_stream_ssl_create_conf(ngx_conf_t *cf); | |
18 static char *ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, | |
19 void *child); | |
20 | |
21 static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, | |
22 void *conf); | |
23 static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, | |
24 void *conf); | |
25 | |
26 | |
27 static ngx_conf_bitmask_t ngx_stream_ssl_protocols[] = { | |
28 { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, | |
29 { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, | |
30 { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, | |
31 { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, | |
32 { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, | |
33 { ngx_null_string, 0 } | |
34 }; | |
35 | |
36 | |
37 static ngx_command_t ngx_stream_ssl_commands[] = { | |
38 | |
39 { ngx_string("ssl_handshake_timeout"), | |
40 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
41 ngx_conf_set_msec_slot, | |
42 NGX_STREAM_SRV_CONF_OFFSET, | |
43 offsetof(ngx_stream_ssl_conf_t, handshake_timeout), | |
44 NULL }, | |
45 | |
46 { ngx_string("ssl_certificate"), | |
47 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
48 ngx_conf_set_str_slot, | |
49 NGX_STREAM_SRV_CONF_OFFSET, | |
50 offsetof(ngx_stream_ssl_conf_t, certificate), | |
51 NULL }, | |
52 | |
53 { ngx_string("ssl_certificate_key"), | |
54 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
55 ngx_conf_set_str_slot, | |
56 NGX_STREAM_SRV_CONF_OFFSET, | |
57 offsetof(ngx_stream_ssl_conf_t, certificate_key), | |
58 NULL }, | |
59 | |
60 { ngx_string("ssl_password_file"), | |
61 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
62 ngx_stream_ssl_password_file, | |
63 NGX_STREAM_SRV_CONF_OFFSET, | |
64 0, | |
65 NULL }, | |
66 | |
67 { ngx_string("ssl_dhparam"), | |
68 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
69 ngx_conf_set_str_slot, | |
70 NGX_STREAM_SRV_CONF_OFFSET, | |
71 offsetof(ngx_stream_ssl_conf_t, dhparam), | |
72 NULL }, | |
73 | |
74 { ngx_string("ssl_ecdh_curve"), | |
75 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
76 ngx_conf_set_str_slot, | |
77 NGX_STREAM_SRV_CONF_OFFSET, | |
78 offsetof(ngx_stream_ssl_conf_t, ecdh_curve), | |
79 NULL }, | |
80 | |
81 { ngx_string("ssl_protocols"), | |
82 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, | |
83 ngx_conf_set_bitmask_slot, | |
84 NGX_STREAM_SRV_CONF_OFFSET, | |
85 offsetof(ngx_stream_ssl_conf_t, protocols), | |
86 &ngx_stream_ssl_protocols }, | |
87 | |
88 { ngx_string("ssl_ciphers"), | |
89 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
90 ngx_conf_set_str_slot, | |
91 NGX_STREAM_SRV_CONF_OFFSET, | |
92 offsetof(ngx_stream_ssl_conf_t, ciphers), | |
93 NULL }, | |
94 | |
95 { ngx_string("ssl_prefer_server_ciphers"), | |
96 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, | |
97 ngx_conf_set_flag_slot, | |
98 NGX_STREAM_SRV_CONF_OFFSET, | |
99 offsetof(ngx_stream_ssl_conf_t, prefer_server_ciphers), | |
100 NULL }, | |
101 | |
102 { ngx_string("ssl_session_cache"), | |
103 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12, | |
104 ngx_stream_ssl_session_cache, | |
105 NGX_STREAM_SRV_CONF_OFFSET, | |
106 0, | |
107 NULL }, | |
108 | |
109 { ngx_string("ssl_session_tickets"), | |
110 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, | |
111 ngx_conf_set_flag_slot, | |
112 NGX_STREAM_SRV_CONF_OFFSET, | |
113 offsetof(ngx_stream_ssl_conf_t, session_tickets), | |
114 NULL }, | |
115 | |
116 { ngx_string("ssl_session_ticket_key"), | |
117 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
118 ngx_conf_set_str_array_slot, | |
119 NGX_STREAM_SRV_CONF_OFFSET, | |
120 offsetof(ngx_stream_ssl_conf_t, session_ticket_keys), | |
121 NULL }, | |
122 | |
123 { ngx_string("ssl_session_timeout"), | |
124 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, | |
125 ngx_conf_set_sec_slot, | |
126 NGX_STREAM_SRV_CONF_OFFSET, | |
127 offsetof(ngx_stream_ssl_conf_t, session_timeout), | |
128 NULL }, | |
129 | |
130 ngx_null_command | |
131 }; | |
132 | |
133 | |
134 static ngx_stream_module_t ngx_stream_ssl_module_ctx = { | |
6174
68c106e6fa0a
Stream: added postconfiguration method to stream modules.
Vladimir Homutov <vl@nginx.com>
parents:
6157
diff
changeset
|
135 NULL, /* postconfiguration */ |
68c106e6fa0a
Stream: added postconfiguration method to stream modules.
Vladimir Homutov <vl@nginx.com>
parents:
6157
diff
changeset
|
136 |
6115 | 137 NULL, /* create main configuration */ |
138 NULL, /* init main configuration */ | |
139 | |
140 ngx_stream_ssl_create_conf, /* create server configuration */ | |
141 ngx_stream_ssl_merge_conf /* merge server configuration */ | |
142 }; | |
143 | |
144 | |
145 ngx_module_t ngx_stream_ssl_module = { | |
146 NGX_MODULE_V1, | |
147 &ngx_stream_ssl_module_ctx, /* module context */ | |
148 ngx_stream_ssl_commands, /* module directives */ | |
149 NGX_STREAM_MODULE, /* module type */ | |
150 NULL, /* init master */ | |
151 NULL, /* init module */ | |
152 NULL, /* init process */ | |
153 NULL, /* init thread */ | |
154 NULL, /* exit thread */ | |
155 NULL, /* exit process */ | |
156 NULL, /* exit master */ | |
157 NGX_MODULE_V1_PADDING | |
158 }; | |
159 | |
160 | |
161 static ngx_str_t ngx_stream_ssl_sess_id_ctx = ngx_string("STREAM"); | |
162 | |
163 | |
164 static void * | |
165 ngx_stream_ssl_create_conf(ngx_conf_t *cf) | |
166 { | |
167 ngx_stream_ssl_conf_t *scf; | |
168 | |
169 scf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_conf_t)); | |
170 if (scf == NULL) { | |
171 return NULL; | |
172 } | |
173 | |
174 /* | |
175 * set by ngx_pcalloc(): | |
176 * | |
177 * scf->protocols = 0; | |
178 * scf->certificate = { 0, NULL }; | |
179 * scf->certificate_key = { 0, NULL }; | |
180 * scf->dhparam = { 0, NULL }; | |
181 * scf->ecdh_curve = { 0, NULL }; | |
182 * scf->ciphers = { 0, NULL }; | |
183 * scf->shm_zone = NULL; | |
184 */ | |
185 | |
186 scf->handshake_timeout = NGX_CONF_UNSET_MSEC; | |
187 scf->passwords = NGX_CONF_UNSET_PTR; | |
188 scf->prefer_server_ciphers = NGX_CONF_UNSET; | |
189 scf->builtin_session_cache = NGX_CONF_UNSET; | |
190 scf->session_timeout = NGX_CONF_UNSET; | |
191 scf->session_tickets = NGX_CONF_UNSET; | |
192 scf->session_ticket_keys = NGX_CONF_UNSET_PTR; | |
193 | |
194 return scf; | |
195 } | |
196 | |
197 | |
198 static char * | |
199 ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
200 { | |
201 ngx_stream_ssl_conf_t *prev = parent; | |
202 ngx_stream_ssl_conf_t *conf = child; | |
203 | |
204 ngx_pool_cleanup_t *cln; | |
205 | |
206 ngx_conf_merge_msec_value(conf->handshake_timeout, | |
207 prev->handshake_timeout, 60000); | |
208 | |
209 ngx_conf_merge_value(conf->session_timeout, | |
210 prev->session_timeout, 300); | |
211 | |
212 ngx_conf_merge_value(conf->prefer_server_ciphers, | |
213 prev->prefer_server_ciphers, 0); | |
214 | |
215 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, | |
6157
b2899e7d0ef8
Disabled SSLv3 by default (ticket #653).
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
216 (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 |
6115 | 217 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); |
218 | |
219 ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); | |
220 ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); | |
221 | |
222 ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); | |
223 | |
224 ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); | |
225 | |
226 ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve, | |
227 NGX_DEFAULT_ECDH_CURVE); | |
228 | |
229 ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); | |
230 | |
231 | |
232 conf->ssl.log = cf->log; | |
233 | |
234 if (conf->certificate.len == 0) { | |
235 return NGX_CONF_OK; | |
236 } | |
237 | |
238 if (conf->certificate_key.len == 0) { | |
239 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
240 "no \"ssl_certificate_key\" is defined " | |
241 "for certificate \"%V\"", | |
242 &conf->certificate); | |
243 return NGX_CONF_ERROR; | |
244 } | |
245 | |
246 if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) { | |
247 return NGX_CONF_ERROR; | |
248 } | |
249 | |
250 cln = ngx_pool_cleanup_add(cf->pool, 0); | |
251 if (cln == NULL) { | |
252 return NGX_CONF_ERROR; | |
253 } | |
254 | |
255 cln->handler = ngx_ssl_cleanup_ctx; | |
256 cln->data = &conf->ssl; | |
257 | |
258 if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, | |
259 &conf->certificate_key, conf->passwords) | |
260 != NGX_OK) | |
261 { | |
262 return NGX_CONF_ERROR; | |
263 } | |
264 | |
265 if (SSL_CTX_set_cipher_list(conf->ssl.ctx, | |
266 (const char *) conf->ciphers.data) | |
267 == 0) | |
268 { | |
269 ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, | |
270 "SSL_CTX_set_cipher_list(\"%V\") failed", | |
271 &conf->ciphers); | |
272 return NGX_CONF_ERROR; | |
273 } | |
274 | |
275 if (conf->prefer_server_ciphers) { | |
276 SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); | |
277 } | |
278 | |
6489
c256dfdd469d
SSL: RSA_generate_key() is deprecated in OpenSSL 1.1.0.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6199
diff
changeset
|
279 #if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) |
6115 | 280 SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback); |
6199
4b703a5a4631
Stream: avoid SSL_CTX_set_tmp_rsa_callback() call with LibreSSL.
Piotr Sikora <piotr@cloudflare.com>
parents:
6174
diff
changeset
|
281 #endif |
6115 | 282 |
283 if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { | |
284 return NGX_CONF_ERROR; | |
285 } | |
286 | |
287 if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) { | |
288 return NGX_CONF_ERROR; | |
289 } | |
290 | |
291 ngx_conf_merge_value(conf->builtin_session_cache, | |
292 prev->builtin_session_cache, NGX_SSL_NONE_SCACHE); | |
293 | |
294 if (conf->shm_zone == NULL) { | |
295 conf->shm_zone = prev->shm_zone; | |
296 } | |
297 | |
298 if (ngx_ssl_session_cache(&conf->ssl, &ngx_stream_ssl_sess_id_ctx, | |
299 conf->builtin_session_cache, | |
300 conf->shm_zone, conf->session_timeout) | |
301 != NGX_OK) | |
302 { | |
303 return NGX_CONF_ERROR; | |
304 } | |
305 | |
306 ngx_conf_merge_value(conf->session_tickets, | |
307 prev->session_tickets, 1); | |
308 | |
309 #ifdef SSL_OP_NO_TICKET | |
310 if (!conf->session_tickets) { | |
311 SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET); | |
312 } | |
313 #endif | |
314 | |
315 ngx_conf_merge_ptr_value(conf->session_ticket_keys, | |
316 prev->session_ticket_keys, NULL); | |
317 | |
318 if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys) | |
319 != NGX_OK) | |
320 { | |
321 return NGX_CONF_ERROR; | |
322 } | |
323 | |
324 return NGX_CONF_OK; | |
325 } | |
326 | |
327 | |
328 static char * | |
329 ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
330 { | |
331 ngx_stream_ssl_conf_t *scf = conf; | |
332 | |
333 ngx_str_t *value; | |
334 | |
335 if (scf->passwords != NGX_CONF_UNSET_PTR) { | |
336 return "is duplicate"; | |
337 } | |
338 | |
339 value = cf->args->elts; | |
340 | |
341 scf->passwords = ngx_ssl_read_password_file(cf, &value[1]); | |
342 | |
343 if (scf->passwords == NULL) { | |
344 return NGX_CONF_ERROR; | |
345 } | |
346 | |
347 return NGX_CONF_OK; | |
348 } | |
349 | |
350 | |
351 static char * | |
352 ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
353 { | |
354 ngx_stream_ssl_conf_t *scf = conf; | |
355 | |
356 size_t len; | |
357 ngx_str_t *value, name, size; | |
358 ngx_int_t n; | |
359 ngx_uint_t i, j; | |
360 | |
361 value = cf->args->elts; | |
362 | |
363 for (i = 1; i < cf->args->nelts; i++) { | |
364 | |
365 if (ngx_strcmp(value[i].data, "off") == 0) { | |
366 scf->builtin_session_cache = NGX_SSL_NO_SCACHE; | |
367 continue; | |
368 } | |
369 | |
370 if (ngx_strcmp(value[i].data, "none") == 0) { | |
371 scf->builtin_session_cache = NGX_SSL_NONE_SCACHE; | |
372 continue; | |
373 } | |
374 | |
375 if (ngx_strcmp(value[i].data, "builtin") == 0) { | |
376 scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; | |
377 continue; | |
378 } | |
379 | |
380 if (value[i].len > sizeof("builtin:") - 1 | |
381 && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1) | |
382 == 0) | |
383 { | |
384 n = ngx_atoi(value[i].data + sizeof("builtin:") - 1, | |
385 value[i].len - (sizeof("builtin:") - 1)); | |
386 | |
387 if (n == NGX_ERROR) { | |
388 goto invalid; | |
389 } | |
390 | |
391 scf->builtin_session_cache = n; | |
392 | |
393 continue; | |
394 } | |
395 | |
396 if (value[i].len > sizeof("shared:") - 1 | |
397 && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1) | |
398 == 0) | |
399 { | |
400 len = 0; | |
401 | |
402 for (j = sizeof("shared:") - 1; j < value[i].len; j++) { | |
403 if (value[i].data[j] == ':') { | |
404 break; | |
405 } | |
406 | |
407 len++; | |
408 } | |
409 | |
410 if (len == 0) { | |
411 goto invalid; | |
412 } | |
413 | |
414 name.len = len; | |
415 name.data = value[i].data + sizeof("shared:") - 1; | |
416 | |
417 size.len = value[i].len - j - 1; | |
418 size.data = name.data + len + 1; | |
419 | |
420 n = ngx_parse_size(&size); | |
421 | |
422 if (n == NGX_ERROR) { | |
423 goto invalid; | |
424 } | |
425 | |
426 if (n < (ngx_int_t) (8 * ngx_pagesize)) { | |
427 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
428 "session cache \"%V\" is too small", | |
429 &value[i]); | |
430 | |
431 return NGX_CONF_ERROR; | |
432 } | |
433 | |
434 scf->shm_zone = ngx_shared_memory_add(cf, &name, n, | |
435 &ngx_stream_ssl_module); | |
436 if (scf->shm_zone == NULL) { | |
437 return NGX_CONF_ERROR; | |
438 } | |
439 | |
440 scf->shm_zone->init = ngx_ssl_session_cache_init; | |
441 | |
442 continue; | |
443 } | |
444 | |
445 goto invalid; | |
446 } | |
447 | |
448 if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) { | |
449 scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE; | |
450 } | |
451 | |
452 return NGX_CONF_OK; | |
453 | |
454 invalid: | |
455 | |
456 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
457 "invalid session cache \"%V\"", &value[i]); | |
458 | |
459 return NGX_CONF_ERROR; | |
460 } |