Mercurial > hg > nginx-quic
annotate src/stream/ngx_stream_ssl_preread_module.c @ 6982:ac9b1df5b246
SSL: disabled renegotiation detection in client mode.
CVE-2009-3555 is no longer relevant and mitigated by the renegotiation
info extension (secure renegotiation). On the other hand, unexpected
renegotiation still introduces potential security risks, and hence we do
not allow renegotiation on the server side, as we never request renegotiation.
On the client side the situation is different though. There are backends
which explicitly request renegotiation, and disabled renegotiation
introduces interoperability problems. This change allows renegotiation
on the client side, and fixes interoperability problems as observed with
such backends (ticket #872).
Additionally, with TLSv1.3 the SSL_CB_HANDSHAKE_START flag is currently set
by OpenSSL when receiving a NewSessionTicket message, and was detected by
nginx as a renegotiation attempt. This looks like a bug in OpenSSL, though
this change also allows better interoperability till the problem is fixed.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Tue, 18 Apr 2017 16:08:44 +0300 |
parents | 01adb18a5d23 |
children | 2a288909abc6 |
rev | line source |
---|---|
6695 | 1 |
2 /* | |
3 * Copyright (C) Nginx, Inc. | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_stream.h> | |
10 | |
11 | |
12 typedef struct { | |
13 ngx_flag_t enabled; | |
14 } ngx_stream_ssl_preread_srv_conf_t; | |
15 | |
16 | |
17 typedef struct { | |
18 size_t left; | |
19 size_t size; | |
20 u_char *pos; | |
21 u_char *dst; | |
22 u_char buf[4]; | |
23 ngx_str_t host; | |
24 ngx_log_t *log; | |
25 ngx_pool_t *pool; | |
26 ngx_uint_t state; | |
27 } ngx_stream_ssl_preread_ctx_t; | |
28 | |
29 | |
30 static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s); | |
31 static ngx_int_t ngx_stream_ssl_preread_parse_record( | |
32 ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last); | |
33 static ngx_int_t ngx_stream_ssl_preread_server_name_variable( | |
34 ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); | |
35 static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf); | |
36 static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf); | |
37 static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, | |
38 void *child); | |
39 static ngx_int_t ngx_stream_ssl_preread_init(ngx_conf_t *cf); | |
40 | |
41 | |
42 static ngx_command_t ngx_stream_ssl_preread_commands[] = { | |
43 | |
44 { ngx_string("ssl_preread"), | |
45 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, | |
46 ngx_conf_set_flag_slot, | |
47 NGX_STREAM_SRV_CONF_OFFSET, | |
48 offsetof(ngx_stream_ssl_preread_srv_conf_t, enabled), | |
49 NULL }, | |
50 | |
51 ngx_null_command | |
52 }; | |
53 | |
54 | |
55 static ngx_stream_module_t ngx_stream_ssl_preread_module_ctx = { | |
56 ngx_stream_ssl_preread_add_variables, /* preconfiguration */ | |
57 ngx_stream_ssl_preread_init, /* postconfiguration */ | |
58 | |
59 NULL, /* create main configuration */ | |
60 NULL, /* init main configuration */ | |
61 | |
62 ngx_stream_ssl_preread_create_srv_conf, /* create server configuration */ | |
63 ngx_stream_ssl_preread_merge_srv_conf /* merge server configuration */ | |
64 }; | |
65 | |
66 | |
67 ngx_module_t ngx_stream_ssl_preread_module = { | |
68 NGX_MODULE_V1, | |
69 &ngx_stream_ssl_preread_module_ctx, /* module context */ | |
70 ngx_stream_ssl_preread_commands, /* module directives */ | |
71 NGX_STREAM_MODULE, /* module type */ | |
72 NULL, /* init master */ | |
73 NULL, /* init module */ | |
74 NULL, /* init process */ | |
75 NULL, /* init thread */ | |
76 NULL, /* exit thread */ | |
77 NULL, /* exit process */ | |
78 NULL, /* exit master */ | |
79 NGX_MODULE_V1_PADDING | |
80 }; | |
81 | |
82 | |
83 static ngx_stream_variable_t ngx_stream_ssl_preread_vars[] = { | |
84 | |
85 { ngx_string("ssl_preread_server_name"), NULL, | |
86 ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 }, | |
87 | |
88 { ngx_null_string, NULL, NULL, 0, 0, 0 } | |
89 }; | |
90 | |
91 | |
92 static ngx_int_t | |
93 ngx_stream_ssl_preread_handler(ngx_stream_session_t *s) | |
94 { | |
95 u_char *last, *p; | |
96 size_t len; | |
97 ngx_int_t rc; | |
98 ngx_connection_t *c; | |
99 ngx_stream_ssl_preread_ctx_t *ctx; | |
100 ngx_stream_ssl_preread_srv_conf_t *sscf; | |
101 | |
102 c = s->connection; | |
103 | |
104 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "ssl preread handler"); | |
105 | |
106 sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_preread_module); | |
107 | |
108 if (!sscf->enabled) { | |
109 return NGX_DECLINED; | |
110 } | |
111 | |
112 if (c->type != SOCK_STREAM) { | |
113 return NGX_DECLINED; | |
114 } | |
115 | |
116 if (c->buffer == NULL) { | |
117 return NGX_AGAIN; | |
118 } | |
119 | |
120 ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); | |
121 if (ctx == NULL) { | |
122 ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ssl_preread_ctx_t)); | |
123 if (ctx == NULL) { | |
124 return NGX_ERROR; | |
125 } | |
126 | |
127 ngx_stream_set_ctx(s, ctx, ngx_stream_ssl_preread_module); | |
128 | |
129 ctx->pool = c->pool; | |
130 ctx->log = c->log; | |
131 ctx->pos = c->buffer->pos; | |
132 } | |
133 | |
134 p = ctx->pos; | |
135 last = c->buffer->last; | |
136 | |
137 while (last - p >= 5) { | |
138 | |
139 if (p[0] != 0x16) { | |
6696
e83540f825cd
Stream ssl_preread: removed internal macro.
Vladimir Homutov <vl@nginx.com>
parents:
6695
diff
changeset
|
140 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, |
e83540f825cd
Stream ssl_preread: removed internal macro.
Vladimir Homutov <vl@nginx.com>
parents:
6695
diff
changeset
|
141 "ssl preread: not a handshake"); |
6695 | 142 return NGX_DECLINED; |
143 } | |
144 | |
6849
01adb18a5d23
Stream ssl_preread: relaxed SSL version check.
Roman Arutyunyan <arut@nginx.com>
parents:
6728
diff
changeset
|
145 if (p[1] != 3) { |
6696
e83540f825cd
Stream ssl_preread: removed internal macro.
Vladimir Homutov <vl@nginx.com>
parents:
6695
diff
changeset
|
146 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, |
e83540f825cd
Stream ssl_preread: removed internal macro.
Vladimir Homutov <vl@nginx.com>
parents:
6695
diff
changeset
|
147 "ssl preread: unsupported SSL version"); |
6695 | 148 return NGX_DECLINED; |
149 } | |
150 | |
151 len = (p[3] << 8) + p[4]; | |
152 | |
153 /* read the whole record before parsing */ | |
154 if ((size_t) (last - p) < len + 5) { | |
155 break; | |
156 } | |
157 | |
158 p += 5; | |
159 | |
160 rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len); | |
161 if (rc != NGX_AGAIN) { | |
162 return rc; | |
163 } | |
164 | |
165 p += len; | |
166 } | |
167 | |
168 ctx->pos = p; | |
169 | |
170 return NGX_AGAIN; | |
171 } | |
172 | |
173 | |
174 static ngx_int_t | |
175 ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx, | |
176 u_char *pos, u_char *last) | |
177 { | |
178 size_t left, n, size; | |
179 u_char *dst, *p; | |
180 | |
181 enum { | |
182 sw_start = 0, | |
183 sw_header, /* handshake msg_type, length */ | |
184 sw_head_tail, /* version, random */ | |
185 sw_sid_len, /* session_id length */ | |
186 sw_sid, /* session_id */ | |
187 sw_cs_len, /* cipher_suites length */ | |
188 sw_cs, /* cipher_suites */ | |
189 sw_cm_len, /* compression_methods length */ | |
190 sw_cm, /* compression_methods */ | |
191 sw_ext, /* extension */ | |
192 sw_ext_header, /* extension_type, extension_data length */ | |
193 sw_sni_len, /* SNI length */ | |
194 sw_sni_host_head, /* SNI name_type, host_name length */ | |
195 sw_sni_host /* SNI host_name */ | |
196 } state; | |
197 | |
198 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, | |
199 "ssl preread: state %ui left %z", ctx->state, ctx->left); | |
200 | |
201 state = ctx->state; | |
202 size = ctx->size; | |
203 left = ctx->left; | |
204 dst = ctx->dst; | |
205 p = ctx->buf; | |
206 | |
207 for ( ;; ) { | |
208 n = ngx_min((size_t) (last - pos), size); | |
209 | |
210 if (dst) { | |
211 dst = ngx_cpymem(dst, pos, n); | |
212 } | |
213 | |
214 pos += n; | |
215 size -= n; | |
216 left -= n; | |
217 | |
218 if (size != 0) { | |
219 break; | |
220 } | |
221 | |
222 switch (state) { | |
223 | |
224 case sw_start: | |
225 state = sw_header; | |
226 dst = p; | |
227 size = 4; | |
228 left = size; | |
229 break; | |
230 | |
231 case sw_header: | |
232 if (p[0] != 1) { | |
6696
e83540f825cd
Stream ssl_preread: removed internal macro.
Vladimir Homutov <vl@nginx.com>
parents:
6695
diff
changeset
|
233 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, |
e83540f825cd
Stream ssl_preread: removed internal macro.
Vladimir Homutov <vl@nginx.com>
parents:
6695
diff
changeset
|
234 "ssl preread: not a client hello"); |
6695 | 235 return NGX_DECLINED; |
236 } | |
237 | |
238 state = sw_head_tail; | |
239 dst = NULL; | |
240 size = 34; | |
241 left = (p[1] << 16) + (p[2] << 8) + p[3]; | |
242 break; | |
243 | |
244 case sw_head_tail: | |
245 state = sw_sid_len; | |
246 dst = p; | |
247 size = 1; | |
248 break; | |
249 | |
250 case sw_sid_len: | |
251 state = sw_sid; | |
252 dst = NULL; | |
253 size = p[0]; | |
254 break; | |
255 | |
256 case sw_sid: | |
257 state = sw_cs_len; | |
258 dst = p; | |
259 size = 2; | |
260 break; | |
261 | |
262 case sw_cs_len: | |
263 state = sw_cs; | |
264 dst = NULL; | |
265 size = (p[0] << 8) + p[1]; | |
266 break; | |
267 | |
268 case sw_cs: | |
269 state = sw_cm_len; | |
270 dst = p; | |
271 size = 1; | |
272 break; | |
273 | |
274 case sw_cm_len: | |
275 state = sw_cm; | |
276 dst = NULL; | |
277 size = p[0]; | |
278 break; | |
279 | |
280 case sw_cm: | |
281 if (left == 0) { | |
282 /* no extensions */ | |
283 return NGX_OK; | |
284 } | |
285 | |
286 state = sw_ext; | |
287 dst = p; | |
288 size = 2; | |
289 break; | |
290 | |
291 case sw_ext: | |
292 if (left == 0) { | |
293 return NGX_OK; | |
294 } | |
295 | |
296 state = sw_ext_header; | |
297 dst = p; | |
298 size = 4; | |
299 break; | |
300 | |
301 case sw_ext_header: | |
302 if (p[0] == 0 && p[1] == 0) { | |
303 /* SNI extension */ | |
304 state = sw_sni_len; | |
305 dst = NULL; | |
306 size = 2; | |
307 break; | |
308 } | |
309 | |
310 state = sw_ext; | |
311 dst = NULL; | |
312 size = (p[2] << 8) + p[3]; | |
313 break; | |
314 | |
315 case sw_sni_len: | |
316 state = sw_sni_host_head; | |
317 dst = p; | |
318 size = 3; | |
319 break; | |
320 | |
321 case sw_sni_host_head: | |
322 if (p[0] != 0) { | |
323 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, | |
324 "ssl preread: SNI hostname type is not DNS"); | |
325 return NGX_DECLINED; | |
326 } | |
327 | |
328 state = sw_sni_host; | |
329 size = (p[1] << 8) + p[2]; | |
330 | |
331 ctx->host.data = ngx_pnalloc(ctx->pool, size); | |
332 if (ctx->host.data == NULL) { | |
333 return NGX_ERROR; | |
334 } | |
335 | |
336 dst = ctx->host.data; | |
337 break; | |
338 | |
339 case sw_sni_host: | |
6728
8f75d9883730
Stream ssl_preread: fixed $ssl_preread_server_name variable.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6696
diff
changeset
|
340 ctx->host.len = (p[1] << 8) + p[2]; |
8f75d9883730
Stream ssl_preread: fixed $ssl_preread_server_name variable.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6696
diff
changeset
|
341 |
6695 | 342 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, |
343 "ssl preread: SNI hostname \"%V\"", &ctx->host); | |
344 return NGX_OK; | |
345 } | |
346 | |
347 if (left < size) { | |
348 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, | |
349 "ssl preread: failed to parse handshake"); | |
350 return NGX_DECLINED; | |
351 } | |
352 } | |
353 | |
354 ctx->state = state; | |
355 ctx->size = size; | |
356 ctx->left = left; | |
357 ctx->dst = dst; | |
358 | |
359 return NGX_AGAIN; | |
360 } | |
361 | |
362 | |
363 static ngx_int_t | |
364 ngx_stream_ssl_preread_server_name_variable(ngx_stream_session_t *s, | |
365 ngx_variable_value_t *v, uintptr_t data) | |
366 { | |
367 ngx_stream_ssl_preread_ctx_t *ctx; | |
368 | |
369 ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); | |
370 | |
371 if (ctx == NULL) { | |
372 v->not_found = 1; | |
373 return NGX_OK; | |
374 } | |
375 | |
376 v->valid = 1; | |
377 v->no_cacheable = 0; | |
378 v->not_found = 0; | |
379 v->len = ctx->host.len; | |
380 v->data = ctx->host.data; | |
381 | |
382 return NGX_OK; | |
383 } | |
384 | |
385 | |
386 static ngx_int_t | |
387 ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf) | |
388 { | |
389 ngx_stream_variable_t *var, *v; | |
390 | |
391 for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) { | |
392 var = ngx_stream_add_variable(cf, &v->name, v->flags); | |
393 if (var == NULL) { | |
394 return NGX_ERROR; | |
395 } | |
396 | |
397 var->get_handler = v->get_handler; | |
398 var->data = v->data; | |
399 } | |
400 | |
401 return NGX_OK; | |
402 } | |
403 | |
404 | |
405 static void * | |
406 ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf) | |
407 { | |
408 ngx_stream_ssl_preread_srv_conf_t *conf; | |
409 | |
410 conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_preread_srv_conf_t)); | |
411 if (conf == NULL) { | |
412 return NULL; | |
413 } | |
414 | |
415 conf->enabled = NGX_CONF_UNSET; | |
416 | |
417 return conf; | |
418 } | |
419 | |
420 | |
421 static char * | |
422 ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) | |
423 { | |
424 ngx_stream_ssl_preread_srv_conf_t *prev = parent; | |
425 ngx_stream_ssl_preread_srv_conf_t *conf = child; | |
426 | |
427 ngx_conf_merge_value(conf->enabled, prev->enabled, 0); | |
428 | |
429 return NGX_CONF_OK; | |
430 } | |
431 | |
432 | |
433 static ngx_int_t | |
434 ngx_stream_ssl_preread_init(ngx_conf_t *cf) | |
435 { | |
436 ngx_stream_handler_pt *h; | |
437 ngx_stream_core_main_conf_t *cmcf; | |
438 | |
439 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); | |
440 | |
441 h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers); | |
442 if (h == NULL) { | |
443 return NGX_ERROR; | |
444 } | |
445 | |
446 *h = ngx_stream_ssl_preread_handler; | |
447 | |
448 return NGX_OK; | |
449 } |