Mercurial > hg > nginx-vendor-current
comparison src/event/ngx_event_openssl_stapling.c @ 688:f31b19fe7f48 NGINX_1_3_7
nginx 1.3.7
*) Feature: OCSP stapling support.
Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work.
*) Feature: the "ssl_trusted_certificate" directive.
*) Feature: resolver now randomly rotates addresses returned from cache.
Thanks to Anton Jouline.
*) Bugfix: OpenSSL 0.9.7 compatibility.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Tue, 02 Oct 2012 00:00:00 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
687:a7305f494f1c | 688:f31b19fe7f48 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Maxim Dounin | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_event.h> | |
11 #include <ngx_event_connect.h> | |
12 | |
13 | |
14 #ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB | |
15 | |
16 | |
17 typedef struct { | |
18 ngx_str_t staple; | |
19 ngx_msec_t timeout; | |
20 | |
21 ngx_resolver_t *resolver; | |
22 ngx_msec_t resolver_timeout; | |
23 | |
24 ngx_addr_t *addrs; | |
25 ngx_str_t host; | |
26 ngx_str_t uri; | |
27 in_port_t port; | |
28 | |
29 SSL_CTX *ssl_ctx; | |
30 | |
31 X509 *cert; | |
32 X509 *issuer; | |
33 | |
34 time_t valid; | |
35 | |
36 unsigned verify:1; | |
37 unsigned loading:1; | |
38 } ngx_ssl_stapling_t; | |
39 | |
40 | |
41 typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t; | |
42 | |
43 struct ngx_ssl_ocsp_ctx_s { | |
44 X509 *cert; | |
45 X509 *issuer; | |
46 | |
47 ngx_uint_t naddrs; | |
48 | |
49 ngx_addr_t *addrs; | |
50 ngx_str_t host; | |
51 ngx_str_t uri; | |
52 in_port_t port; | |
53 | |
54 ngx_resolver_t *resolver; | |
55 ngx_msec_t resolver_timeout; | |
56 | |
57 ngx_msec_t timeout; | |
58 | |
59 void (*handler)(ngx_ssl_ocsp_ctx_t *r); | |
60 void *data; | |
61 | |
62 ngx_buf_t *request; | |
63 ngx_buf_t *response; | |
64 ngx_peer_connection_t peer; | |
65 | |
66 ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *r); | |
67 | |
68 ngx_uint_t state; | |
69 | |
70 ngx_uint_t code; | |
71 ngx_uint_t count; | |
72 | |
73 ngx_uint_t done; | |
74 | |
75 u_char *header_name_start; | |
76 u_char *header_name_end; | |
77 u_char *header_start; | |
78 u_char *header_end; | |
79 | |
80 ngx_pool_t *pool; | |
81 ngx_log_t *log; | |
82 }; | |
83 | |
84 | |
85 static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
86 ngx_str_t *file); | |
87 static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl); | |
88 static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
89 ngx_str_t *responder); | |
90 | |
91 static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, | |
92 void *data); | |
93 static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple); | |
94 static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); | |
95 | |
96 static void ngx_ssl_stapling_cleanup(void *data); | |
97 | |
98 static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void); | |
99 static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx); | |
100 static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx); | |
101 static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve); | |
102 static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx); | |
103 static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev); | |
104 static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev); | |
105 static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev); | |
106 | |
107 static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx); | |
108 static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx); | |
109 static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx); | |
110 static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx); | |
111 static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx); | |
112 static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx); | |
113 | |
114 static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len); | |
115 | |
116 | |
117 ngx_int_t | |
118 ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, | |
119 ngx_str_t *responder, ngx_uint_t verify) | |
120 { | |
121 ngx_int_t rc; | |
122 ngx_pool_cleanup_t *cln; | |
123 ngx_ssl_stapling_t *staple; | |
124 | |
125 staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t)); | |
126 if (staple == NULL) { | |
127 return NGX_ERROR; | |
128 } | |
129 | |
130 cln = ngx_pool_cleanup_add(cf->pool, 0); | |
131 if (cln == NULL) { | |
132 return NGX_ERROR; | |
133 } | |
134 | |
135 cln->handler = ngx_ssl_stapling_cleanup; | |
136 cln->data = staple; | |
137 | |
138 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple) | |
139 == 0) | |
140 { | |
141 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
142 "SSL_CTX_set_ex_data() failed"); | |
143 return NGX_ERROR; | |
144 } | |
145 | |
146 staple->ssl_ctx = ssl->ctx; | |
147 staple->timeout = 60000; | |
148 staple->verify = verify; | |
149 | |
150 if (file->len) { | |
151 /* use OCSP response from the file */ | |
152 | |
153 if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) { | |
154 return NGX_ERROR; | |
155 } | |
156 | |
157 goto done; | |
158 } | |
159 | |
160 rc = ngx_ssl_stapling_issuer(cf, ssl); | |
161 | |
162 if (rc == NGX_DECLINED) { | |
163 return NGX_OK; | |
164 } | |
165 | |
166 if (rc != NGX_OK) { | |
167 return NGX_ERROR; | |
168 } | |
169 | |
170 rc = ngx_ssl_stapling_responder(cf, ssl, responder); | |
171 | |
172 if (rc == NGX_DECLINED) { | |
173 return NGX_OK; | |
174 } | |
175 | |
176 if (rc != NGX_OK) { | |
177 return NGX_ERROR; | |
178 } | |
179 | |
180 done: | |
181 | |
182 SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback); | |
183 SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple); | |
184 | |
185 return NGX_OK; | |
186 } | |
187 | |
188 | |
189 static ngx_int_t | |
190 ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) | |
191 { | |
192 BIO *bio; | |
193 int len; | |
194 u_char *p, *buf; | |
195 OCSP_RESPONSE *response; | |
196 ngx_ssl_stapling_t *staple; | |
197 | |
198 staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); | |
199 | |
200 if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) { | |
201 return NGX_ERROR; | |
202 } | |
203 | |
204 bio = BIO_new_file((char *) file->data, "r"); | |
205 if (bio == NULL) { | |
206 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
207 "BIO_new_file(\"%s\") failed", file->data); | |
208 return NGX_ERROR; | |
209 } | |
210 | |
211 response = d2i_OCSP_RESPONSE_bio(bio, NULL); | |
212 if (response == NULL) { | |
213 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
214 "d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data); | |
215 BIO_free(bio); | |
216 return NGX_ERROR; | |
217 } | |
218 | |
219 len = i2d_OCSP_RESPONSE(response, NULL); | |
220 if (len <= 0) { | |
221 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
222 "i2d_OCSP_RESPONSE(\"%s\") failed", file->data); | |
223 goto failed; | |
224 } | |
225 | |
226 buf = ngx_alloc(len, ssl->log); | |
227 if (buf == NULL) { | |
228 goto failed; | |
229 } | |
230 | |
231 p = buf; | |
232 len = i2d_OCSP_RESPONSE(response, &p); | |
233 if (len <= 0) { | |
234 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
235 "i2d_OCSP_RESPONSE(\"%s\") failed", file->data); | |
236 ngx_free(buf); | |
237 goto failed; | |
238 } | |
239 | |
240 OCSP_RESPONSE_free(response); | |
241 BIO_free(bio); | |
242 | |
243 staple->staple.data = buf; | |
244 staple->staple.len = len; | |
245 | |
246 return NGX_OK; | |
247 | |
248 failed: | |
249 | |
250 OCSP_RESPONSE_free(response); | |
251 BIO_free(bio); | |
252 | |
253 return NGX_ERROR; | |
254 } | |
255 | |
256 | |
257 static ngx_int_t | |
258 ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl) | |
259 { | |
260 int i, n, rc; | |
261 X509 *cert, *issuer; | |
262 X509_STORE *store; | |
263 X509_STORE_CTX *store_ctx; | |
264 STACK_OF(X509) *chain; | |
265 ngx_ssl_stapling_t *staple; | |
266 | |
267 staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); | |
268 cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); | |
269 | |
270 #if OPENSSL_VERSION_NUMBER >= 0x10001000L | |
271 SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain); | |
272 #else | |
273 chain = ssl->ctx->extra_certs; | |
274 #endif | |
275 | |
276 n = sk_X509_num(chain); | |
277 | |
278 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, | |
279 "SSL get issuer: %d extra certs", n); | |
280 | |
281 for (i = 0; i < n; i++) { | |
282 issuer = sk_X509_value(chain, i); | |
283 if (X509_check_issued(issuer, cert) == X509_V_OK) { | |
284 CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509); | |
285 | |
286 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, | |
287 "SSL get issuer: found %p in extra certs", issuer); | |
288 | |
289 staple->cert = cert; | |
290 staple->issuer = issuer; | |
291 | |
292 return NGX_OK; | |
293 } | |
294 } | |
295 | |
296 store = SSL_CTX_get_cert_store(ssl->ctx); | |
297 if (store == NULL) { | |
298 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
299 "SSL_CTX_get_cert_store() failed"); | |
300 return NGX_ERROR; | |
301 } | |
302 | |
303 store_ctx = X509_STORE_CTX_new(); | |
304 if (store_ctx == NULL) { | |
305 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
306 "X509_STORE_CTX_new() failed"); | |
307 return NGX_ERROR; | |
308 } | |
309 | |
310 if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) { | |
311 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
312 "X509_STORE_CTX_init() failed"); | |
313 return NGX_ERROR; | |
314 } | |
315 | |
316 rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert); | |
317 | |
318 if (rc == -1) { | |
319 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
320 "X509_STORE_CTX_get1_issuer() failed"); | |
321 X509_STORE_CTX_free(store_ctx); | |
322 return NGX_ERROR; | |
323 } | |
324 | |
325 if (rc == 0) { | |
326 ngx_log_error(NGX_LOG_WARN, ssl->log, 0, | |
327 "\"ssl_stapling\" ignored, issuer certificate not found"); | |
328 X509_STORE_CTX_free(store_ctx); | |
329 return NGX_DECLINED; | |
330 } | |
331 | |
332 X509_STORE_CTX_free(store_ctx); | |
333 | |
334 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, | |
335 "SSL get issuer: found %p in cert store", issuer); | |
336 | |
337 staple->cert = cert; | |
338 staple->issuer = issuer; | |
339 | |
340 return NGX_OK; | |
341 } | |
342 | |
343 | |
344 static ngx_int_t | |
345 ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder) | |
346 { | |
347 ngx_url_t u; | |
348 char *s; | |
349 ngx_ssl_stapling_t *staple; | |
350 STACK_OF(OPENSSL_STRING) *aia; | |
351 | |
352 staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); | |
353 | |
354 if (responder->len == 0) { | |
355 | |
356 /* extract OCSP responder URL from certificate */ | |
357 | |
358 aia = X509_get1_ocsp(staple->cert); | |
359 if (aia == NULL) { | |
360 ngx_log_error(NGX_LOG_WARN, ssl->log, 0, | |
361 "\"ssl_stapling\" ignored, " | |
362 "no OCSP responder URL in the certificate"); | |
363 return NGX_DECLINED; | |
364 } | |
365 | |
366 #if OPENSSL_VERSION_NUMBER >= 0x10000000L | |
367 s = sk_OPENSSL_STRING_value(aia, 0); | |
368 #else | |
369 s = sk_value(aia, 0); | |
370 #endif | |
371 if (s == NULL) { | |
372 ngx_log_error(NGX_LOG_WARN, ssl->log, 0, | |
373 "\"ssl_stapling\" ignored, " | |
374 "no OCSP responder URL in the certificate"); | |
375 X509_email_free(aia); | |
376 return NGX_DECLINED; | |
377 } | |
378 | |
379 responder->len = ngx_strlen(s); | |
380 responder->data = ngx_palloc(cf->pool, responder->len); | |
381 if (responder->data == NULL) { | |
382 X509_email_free(aia); | |
383 return NGX_ERROR; | |
384 } | |
385 | |
386 ngx_memcpy(responder->data, s, responder->len); | |
387 X509_email_free(aia); | |
388 } | |
389 | |
390 ngx_memzero(&u, sizeof(ngx_url_t)); | |
391 | |
392 u.url = *responder; | |
393 u.default_port = 80; | |
394 u.uri_part = 1; | |
395 | |
396 if (u.url.len > 7 | |
397 && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) | |
398 { | |
399 u.url.len -= 7; | |
400 u.url.data += 7; | |
401 | |
402 } else { | |
403 ngx_log_error(NGX_LOG_WARN, ssl->log, 0, | |
404 "\"ssl_stapling\" ignored, " | |
405 "invalid URL prefix in OCSP responder \"%V\"", &u.url); | |
406 return NGX_DECLINED; | |
407 } | |
408 | |
409 if (ngx_parse_url(cf->pool, &u) != NGX_OK) { | |
410 if (u.err) { | |
411 ngx_log_error(NGX_LOG_WARN, ssl->log, 0, | |
412 "\"ssl_stapling\" ignored, " | |
413 "%s in OCSP responder \"%V\"", u.err, &u.url); | |
414 return NGX_DECLINED; | |
415 } | |
416 | |
417 return NGX_ERROR; | |
418 } | |
419 | |
420 staple->addrs = u.addrs; | |
421 staple->host = u.host; | |
422 staple->uri = u.uri; | |
423 staple->port = u.port; | |
424 | |
425 if (staple->uri.len == 0) { | |
426 ngx_str_set(&staple->uri, "/"); | |
427 } | |
428 | |
429 return NGX_OK; | |
430 } | |
431 | |
432 | |
433 ngx_int_t | |
434 ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
435 ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) | |
436 { | |
437 ngx_ssl_stapling_t *staple; | |
438 | |
439 staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index); | |
440 | |
441 staple->resolver = resolver; | |
442 staple->resolver_timeout = resolver_timeout; | |
443 | |
444 return NGX_OK; | |
445 } | |
446 | |
447 | |
448 static int | |
449 ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) | |
450 { | |
451 int rc; | |
452 u_char *p; | |
453 ngx_connection_t *c; | |
454 ngx_ssl_stapling_t *staple; | |
455 | |
456 c = ngx_ssl_get_connection(ssl_conn); | |
457 | |
458 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
459 "SSL certificate status callback"); | |
460 | |
461 staple = data; | |
462 rc = SSL_TLSEXT_ERR_NOACK; | |
463 | |
464 if (staple->staple.len) { | |
465 /* we have to copy ocsp response as OpenSSL will free it by itself */ | |
466 | |
467 p = OPENSSL_malloc(staple->staple.len); | |
468 if (p == NULL) { | |
469 ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed"); | |
470 return SSL_TLSEXT_ERR_NOACK; | |
471 } | |
472 | |
473 ngx_memcpy(p, staple->staple.data, staple->staple.len); | |
474 | |
475 SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len); | |
476 | |
477 rc = SSL_TLSEXT_ERR_OK; | |
478 } | |
479 | |
480 ngx_ssl_stapling_update(staple); | |
481 | |
482 return rc; | |
483 } | |
484 | |
485 | |
486 static void | |
487 ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple) | |
488 { | |
489 ngx_ssl_ocsp_ctx_t *ctx; | |
490 | |
491 if (staple->host.len == 0 | |
492 || staple->loading || staple->valid >= ngx_time()) | |
493 { | |
494 return; | |
495 } | |
496 | |
497 staple->loading = 1; | |
498 | |
499 ctx = ngx_ssl_ocsp_start(); | |
500 if (ctx == NULL) { | |
501 return; | |
502 } | |
503 | |
504 ctx->cert = staple->cert; | |
505 ctx->issuer = staple->issuer; | |
506 | |
507 ctx->addrs = staple->addrs; | |
508 ctx->host = staple->host; | |
509 ctx->uri = staple->uri; | |
510 ctx->port = staple->port; | |
511 ctx->timeout = staple->timeout; | |
512 | |
513 ctx->resolver = staple->resolver; | |
514 ctx->resolver_timeout = staple->resolver_timeout; | |
515 | |
516 ctx->handler = ngx_ssl_stapling_ocsp_handler; | |
517 ctx->data = staple; | |
518 | |
519 ngx_ssl_ocsp_request(ctx); | |
520 | |
521 return; | |
522 } | |
523 | |
524 | |
525 static void | |
526 ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) | |
527 { | |
528 #if OPENSSL_VERSION_NUMBER >= 0x0090707fL | |
529 const | |
530 #endif | |
531 u_char *p; | |
532 int n; | |
533 size_t len; | |
534 ngx_str_t response; | |
535 X509_STORE *store; | |
536 STACK_OF(X509) *chain; | |
537 OCSP_CERTID *id; | |
538 OCSP_RESPONSE *ocsp; | |
539 OCSP_BASICRESP *basic; | |
540 ngx_ssl_stapling_t *staple; | |
541 ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; | |
542 | |
543 staple = ctx->data; | |
544 ocsp = NULL; | |
545 basic = NULL; | |
546 id = NULL; | |
547 | |
548 if (ctx->code != 200) { | |
549 goto error; | |
550 } | |
551 | |
552 /* check the response */ | |
553 | |
554 len = ctx->response->last - ctx->response->pos; | |
555 p = ctx->response->pos; | |
556 | |
557 ocsp = d2i_OCSP_RESPONSE(NULL, &p, len); | |
558 if (ocsp == NULL) { | |
559 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
560 "d2i_OCSP_RESPONSE() failed"); | |
561 goto error; | |
562 } | |
563 | |
564 n = OCSP_response_status(ocsp); | |
565 | |
566 if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { | |
567 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
568 "OCSP response not successful (%d: %s)", | |
569 n, OCSP_response_status_str(n)); | |
570 goto error; | |
571 } | |
572 | |
573 basic = OCSP_response_get1_basic(ocsp); | |
574 if (basic == NULL) { | |
575 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
576 "OCSP_response_get1_basic() failed"); | |
577 goto error; | |
578 } | |
579 | |
580 store = SSL_CTX_get_cert_store(staple->ssl_ctx); | |
581 if (store == NULL) { | |
582 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
583 "SSL_CTX_get_cert_store() failed"); | |
584 goto error; | |
585 } | |
586 | |
587 #if OPENSSL_VERSION_NUMBER >= 0x10001000L | |
588 SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain); | |
589 #else | |
590 chain = staple->ssl_ctx->extra_certs; | |
591 #endif | |
592 | |
593 if (OCSP_basic_verify(basic, chain, store, | |
594 staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY) | |
595 != 1) | |
596 { | |
597 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
598 "OCSP_basic_verify() failed"); | |
599 goto error; | |
600 } | |
601 | |
602 id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); | |
603 if (id == NULL) { | |
604 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
605 "OCSP_cert_to_id() failed"); | |
606 goto error; | |
607 } | |
608 | |
609 if (OCSP_resp_find_status(basic, id, &n, NULL, NULL, | |
610 &thisupdate, &nextupdate) | |
611 != 1) | |
612 { | |
613 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
614 "certificate status not found in the OCSP response", | |
615 n, OCSP_response_status_str(n)); | |
616 goto error; | |
617 } | |
618 | |
619 if (n != V_OCSP_CERTSTATUS_GOOD) { | |
620 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
621 "certificate status \"%s\" in the OCSP response", | |
622 n, OCSP_cert_status_str(n)); | |
623 goto error; | |
624 } | |
625 | |
626 if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { | |
627 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
628 "OCSP_check_validity() failed"); | |
629 goto error; | |
630 } | |
631 | |
632 OCSP_CERTID_free(id); | |
633 OCSP_BASICRESP_free(basic); | |
634 OCSP_RESPONSE_free(ocsp); | |
635 | |
636 /* copy the response to memory not in ctx->pool */ | |
637 | |
638 response.len = len; | |
639 response.data = ngx_alloc(response.len, ctx->log); | |
640 | |
641 if (response.data == NULL) { | |
642 goto done; | |
643 } | |
644 | |
645 ngx_memcpy(response.data, ctx->response->pos, response.len); | |
646 | |
647 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
648 "ssl ocsp response, %s, %uz", | |
649 OCSP_cert_status_str(n), response.len); | |
650 | |
651 if (staple->staple.data) { | |
652 ngx_free(staple->staple.data); | |
653 } | |
654 | |
655 staple->staple = response; | |
656 | |
657 done: | |
658 | |
659 staple->loading = 0; | |
660 staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */ | |
661 | |
662 ngx_ssl_ocsp_done(ctx); | |
663 return; | |
664 | |
665 error: | |
666 | |
667 staple->loading = 0; | |
668 staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */ | |
669 | |
670 if (id) { | |
671 OCSP_CERTID_free(id); | |
672 } | |
673 | |
674 if (basic) { | |
675 OCSP_BASICRESP_free(basic); | |
676 } | |
677 | |
678 if (ocsp) { | |
679 OCSP_RESPONSE_free(ocsp); | |
680 } | |
681 | |
682 ngx_ssl_ocsp_done(ctx); | |
683 } | |
684 | |
685 | |
686 static void | |
687 ngx_ssl_stapling_cleanup(void *data) | |
688 { | |
689 ngx_ssl_stapling_t *staple = data; | |
690 | |
691 if (staple->issuer) { | |
692 X509_free(staple->issuer); | |
693 } | |
694 | |
695 if (staple->staple.data) { | |
696 ngx_free(staple->staple.data); | |
697 } | |
698 } | |
699 | |
700 | |
701 static ngx_ssl_ocsp_ctx_t * | |
702 ngx_ssl_ocsp_start(void) | |
703 { | |
704 ngx_log_t *log; | |
705 ngx_pool_t *pool; | |
706 ngx_ssl_ocsp_ctx_t *ctx; | |
707 | |
708 pool = ngx_create_pool(2048, ngx_cycle->log); | |
709 if (pool == NULL) { | |
710 return NULL; | |
711 } | |
712 | |
713 ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t)); | |
714 if (ctx == NULL) { | |
715 ngx_destroy_pool(pool); | |
716 return NULL; | |
717 } | |
718 | |
719 log = ngx_palloc(pool, sizeof(ngx_log_t)); | |
720 if (log == NULL) { | |
721 ngx_destroy_pool(pool); | |
722 return NULL; | |
723 } | |
724 | |
725 ctx->pool = pool; | |
726 | |
727 *log = *ctx->pool->log; | |
728 | |
729 ctx->pool->log = log; | |
730 ctx->log = log; | |
731 | |
732 log->handler = ngx_ssl_ocsp_log_error; | |
733 log->data = ctx; | |
734 log->action = "requesting certificate status"; | |
735 | |
736 return ctx; | |
737 } | |
738 | |
739 | |
740 static void | |
741 ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx) | |
742 { | |
743 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
744 "ssl ocsp done"); | |
745 | |
746 if (ctx->peer.connection) { | |
747 ngx_close_connection(ctx->peer.connection); | |
748 } | |
749 | |
750 ngx_destroy_pool(ctx->pool); | |
751 } | |
752 | |
753 | |
754 static void | |
755 ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx) | |
756 { | |
757 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
758 "ssl ocsp error"); | |
759 | |
760 ctx->code = 0; | |
761 ctx->handler(ctx); | |
762 } | |
763 | |
764 | |
765 static void | |
766 ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) | |
767 { | |
768 ngx_resolver_ctx_t *resolve, temp; | |
769 | |
770 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
771 "ssl ocsp request"); | |
772 | |
773 if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) { | |
774 ngx_ssl_ocsp_error(ctx); | |
775 return; | |
776 } | |
777 | |
778 if (ctx->resolver) { | |
779 /* resolve OCSP responder hostname */ | |
780 | |
781 temp.name = ctx->host; | |
782 | |
783 resolve = ngx_resolve_start(ctx->resolver, &temp); | |
784 if (resolve == NULL) { | |
785 ngx_ssl_ocsp_error(ctx); | |
786 return; | |
787 } | |
788 | |
789 if (resolve == NGX_NO_RESOLVER) { | |
790 ngx_log_error(NGX_LOG_WARN, ctx->log, 0, | |
791 "no resolver defined to resolve %V", &ctx->host); | |
792 goto connect; | |
793 } | |
794 | |
795 resolve->name = ctx->host; | |
796 resolve->type = NGX_RESOLVE_A; | |
797 resolve->handler = ngx_ssl_ocsp_resolve_handler; | |
798 resolve->data = ctx; | |
799 resolve->timeout = ctx->resolver_timeout; | |
800 | |
801 if (ngx_resolve_name(resolve) != NGX_OK) { | |
802 ngx_ssl_ocsp_error(ctx); | |
803 return; | |
804 } | |
805 | |
806 return; | |
807 } | |
808 | |
809 connect: | |
810 | |
811 ngx_ssl_ocsp_connect(ctx); | |
812 } | |
813 | |
814 | |
815 static void | |
816 ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) | |
817 { | |
818 ngx_ssl_ocsp_ctx_t *ctx = resolve->data; | |
819 | |
820 u_char *p; | |
821 size_t len; | |
822 in_port_t port; | |
823 ngx_uint_t i; | |
824 struct sockaddr_in *sin; | |
825 | |
826 ngx_log_debug0(NGX_LOG_ALERT, ctx->log, 0, | |
827 "ssl ocsp resolve handler"); | |
828 | |
829 if (resolve->state) { | |
830 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
831 "%V could not be resolved (%i: %s)", | |
832 &resolve->name, resolve->state, | |
833 ngx_resolver_strerror(resolve->state)); | |
834 goto failed; | |
835 } | |
836 | |
837 #if (NGX_DEBUG) | |
838 { | |
839 in_addr_t addr; | |
840 | |
841 for (i = 0; i < resolve->naddrs; i++) { | |
842 addr = ntohl(resolve->addrs[i]); | |
843 | |
844 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
845 "name was resolved to %ud.%ud.%ud.%ud", | |
846 (addr >> 24) & 0xff, (addr >> 16) & 0xff, | |
847 (addr >> 8) & 0xff, addr & 0xff); | |
848 } | |
849 } | |
850 #endif | |
851 | |
852 ctx->naddrs = resolve->naddrs; | |
853 ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t)); | |
854 | |
855 if (ctx->addrs == NULL) { | |
856 goto failed; | |
857 } | |
858 | |
859 port = htons(ctx->port); | |
860 | |
861 for (i = 0; i < resolve->naddrs; i++) { | |
862 | |
863 sin = ngx_pcalloc(ctx->pool, sizeof(struct sockaddr_in)); | |
864 if (sin == NULL) { | |
865 goto failed; | |
866 } | |
867 | |
868 sin->sin_family = AF_INET; | |
869 sin->sin_port = port; | |
870 sin->sin_addr.s_addr = resolve->addrs[i]; | |
871 | |
872 ctx->addrs[i].sockaddr = (struct sockaddr *) sin; | |
873 ctx->addrs[i].socklen = sizeof(struct sockaddr_in); | |
874 | |
875 len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; | |
876 | |
877 p = ngx_pnalloc(ctx->pool, len); | |
878 if (p == NULL) { | |
879 goto failed; | |
880 } | |
881 | |
882 len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1); | |
883 | |
884 ctx->addrs[i].name.len = len; | |
885 ctx->addrs[i].name.data = p; | |
886 } | |
887 | |
888 ngx_resolve_name_done(resolve); | |
889 | |
890 ngx_ssl_ocsp_connect(ctx); | |
891 return; | |
892 | |
893 failed: | |
894 | |
895 ngx_resolve_name_done(resolve); | |
896 ngx_ssl_ocsp_error(ctx); | |
897 } | |
898 | |
899 | |
900 static void | |
901 ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) | |
902 { | |
903 ngx_int_t rc; | |
904 | |
905 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
906 "ssl ocsp connect"); | |
907 | |
908 /* TODO: use all ip addresses */ | |
909 | |
910 ctx->peer.sockaddr = ctx->addrs[0].sockaddr; | |
911 ctx->peer.socklen = ctx->addrs[0].socklen; | |
912 ctx->peer.name = &ctx->addrs[0].name; | |
913 ctx->peer.get = ngx_event_get_peer; | |
914 ctx->peer.log = ctx->log; | |
915 ctx->peer.log_error = NGX_ERROR_ERR; | |
916 | |
917 rc = ngx_event_connect_peer(&ctx->peer); | |
918 | |
919 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
920 "ssl ocsp connect peer done"); | |
921 | |
922 if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { | |
923 ngx_ssl_ocsp_error(ctx); | |
924 return; | |
925 } | |
926 | |
927 ctx->peer.connection->data = ctx; | |
928 ctx->peer.connection->pool = ctx->pool; | |
929 | |
930 ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler; | |
931 ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler; | |
932 | |
933 ctx->process = ngx_ssl_ocsp_process_status_line; | |
934 | |
935 ngx_add_timer(ctx->peer.connection->read, ctx->timeout); | |
936 ngx_add_timer(ctx->peer.connection->write, ctx->timeout); | |
937 | |
938 if (rc == NGX_OK) { | |
939 ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); | |
940 return; | |
941 } | |
942 } | |
943 | |
944 | |
945 static void | |
946 ngx_ssl_ocsp_write_handler(ngx_event_t *wev) | |
947 { | |
948 ssize_t n, size; | |
949 ngx_connection_t *c; | |
950 ngx_ssl_ocsp_ctx_t *ctx; | |
951 | |
952 c = wev->data; | |
953 ctx = c->data; | |
954 | |
955 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, | |
956 "ssl ocsp write handler"); | |
957 | |
958 if (wev->timedout) { | |
959 ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, | |
960 "OCSP responder timed out"); | |
961 ngx_ssl_ocsp_error(ctx); | |
962 return; | |
963 } | |
964 | |
965 size = ctx->request->last - ctx->request->pos; | |
966 | |
967 n = ngx_send(c, ctx->request->pos, size); | |
968 | |
969 if (n == NGX_ERROR) { | |
970 ngx_ssl_ocsp_error(ctx); | |
971 return; | |
972 } | |
973 | |
974 if (n > 0) { | |
975 ctx->request->pos += n; | |
976 | |
977 if (n == size) { | |
978 wev->handler = ngx_ssl_ocsp_dummy_handler; | |
979 | |
980 if (wev->timer_set) { | |
981 ngx_del_timer(wev); | |
982 } | |
983 | |
984 if (ngx_handle_write_event(wev, 0) != NGX_OK) { | |
985 ngx_ssl_ocsp_error(ctx); | |
986 } | |
987 | |
988 return; | |
989 } | |
990 } | |
991 | |
992 if (!wev->timer_set) { | |
993 ngx_add_timer(wev, ctx->timeout); | |
994 } | |
995 } | |
996 | |
997 | |
998 static void | |
999 ngx_ssl_ocsp_read_handler(ngx_event_t *rev) | |
1000 { | |
1001 ssize_t n, size; | |
1002 ngx_int_t rc; | |
1003 ngx_ssl_ocsp_ctx_t *ctx; | |
1004 ngx_connection_t *c; | |
1005 | |
1006 c = rev->data; | |
1007 ctx = c->data; | |
1008 | |
1009 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, | |
1010 "ssl ocsp read handler"); | |
1011 | |
1012 if (rev->timedout) { | |
1013 ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, | |
1014 "OCSP responder timed out"); | |
1015 ngx_ssl_ocsp_error(ctx); | |
1016 return; | |
1017 } | |
1018 | |
1019 if (ctx->response == NULL) { | |
1020 ctx->response = ngx_create_temp_buf(ctx->pool, 16384); | |
1021 if (ctx->response == NULL) { | |
1022 ngx_ssl_ocsp_error(ctx); | |
1023 return; | |
1024 } | |
1025 } | |
1026 | |
1027 for ( ;; ) { | |
1028 | |
1029 size = ctx->response->end - ctx->response->last; | |
1030 | |
1031 n = ngx_recv(c, ctx->response->last, size); | |
1032 | |
1033 if (n > 0) { | |
1034 ctx->response->last += n; | |
1035 | |
1036 rc = ctx->process(ctx); | |
1037 | |
1038 if (rc == NGX_ERROR) { | |
1039 ngx_ssl_ocsp_error(ctx); | |
1040 return; | |
1041 } | |
1042 | |
1043 continue; | |
1044 } | |
1045 | |
1046 if (n == NGX_AGAIN) { | |
1047 | |
1048 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | |
1049 ngx_ssl_ocsp_error(ctx); | |
1050 } | |
1051 | |
1052 return; | |
1053 } | |
1054 | |
1055 break; | |
1056 } | |
1057 | |
1058 ctx->done = 1; | |
1059 | |
1060 rc = ctx->process(ctx); | |
1061 | |
1062 if (rc == NGX_DONE) { | |
1063 /* ctx->handler() was called */ | |
1064 return; | |
1065 } | |
1066 | |
1067 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
1068 "OCSP responder prematurely closed connection"); | |
1069 | |
1070 ngx_ssl_ocsp_error(ctx); | |
1071 } | |
1072 | |
1073 | |
1074 static void | |
1075 ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev) | |
1076 { | |
1077 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, | |
1078 "ssl ocsp dummy handler"); | |
1079 } | |
1080 | |
1081 | |
1082 static ngx_int_t | |
1083 ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx) | |
1084 { | |
1085 int len; | |
1086 u_char *p; | |
1087 uintptr_t escape; | |
1088 ngx_str_t binary, base64; | |
1089 ngx_buf_t *b; | |
1090 OCSP_CERTID *id; | |
1091 OCSP_REQUEST *ocsp; | |
1092 | |
1093 ocsp = OCSP_REQUEST_new(); | |
1094 if (ocsp == NULL) { | |
1095 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
1096 "OCSP_REQUEST_new() failed"); | |
1097 return NGX_ERROR; | |
1098 } | |
1099 | |
1100 id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); | |
1101 if (id == NULL) { | |
1102 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
1103 "OCSP_cert_to_id() failed"); | |
1104 goto failed; | |
1105 } | |
1106 | |
1107 if (OCSP_request_add0_id(ocsp, id) == NULL) { | |
1108 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
1109 "OCSP_request_add0_id() failed"); | |
1110 goto failed; | |
1111 } | |
1112 | |
1113 len = i2d_OCSP_REQUEST(ocsp, NULL); | |
1114 if (len <= 0) { | |
1115 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
1116 "i2d_OCSP_REQUEST() failed"); | |
1117 goto failed; | |
1118 } | |
1119 | |
1120 binary.len = len; | |
1121 binary.data = ngx_palloc(ctx->pool, len); | |
1122 if (binary.data == NULL) { | |
1123 goto failed; | |
1124 } | |
1125 | |
1126 p = binary.data; | |
1127 len = i2d_OCSP_REQUEST(ocsp, &p); | |
1128 if (len <= 0) { | |
1129 ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0, | |
1130 "i2d_OCSP_REQUEST() failed"); | |
1131 goto failed; | |
1132 } | |
1133 | |
1134 base64.len = ngx_base64_encoded_length(binary.len); | |
1135 base64.data = ngx_palloc(ctx->pool, base64.len); | |
1136 if (base64.data == NULL) { | |
1137 goto failed; | |
1138 } | |
1139 | |
1140 ngx_encode_base64(&base64, &binary); | |
1141 | |
1142 escape = ngx_escape_uri(NULL, base64.data, base64.len, | |
1143 NGX_ESCAPE_URI_COMPONENT); | |
1144 | |
1145 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1146 "ssl ocsp request length %z, escape %d", | |
1147 base64.len, escape); | |
1148 | |
1149 len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1 | |
1150 + base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1 | |
1151 + sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1 | |
1152 + sizeof(CRLF) - 1; | |
1153 | |
1154 b = ngx_create_temp_buf(ctx->pool, len); | |
1155 if (b == NULL) { | |
1156 goto failed; | |
1157 } | |
1158 | |
1159 p = b->last; | |
1160 | |
1161 p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1); | |
1162 p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len); | |
1163 | |
1164 if (ctx->uri.data[ctx->uri.len - 1] != '/') { | |
1165 *p++ = '/'; | |
1166 } | |
1167 | |
1168 if (escape == 0) { | |
1169 p = ngx_cpymem(p, base64.data, base64.len); | |
1170 | |
1171 } else { | |
1172 p = (u_char *) ngx_escape_uri(p, base64.data, base64.len, | |
1173 NGX_ESCAPE_URI_COMPONENT); | |
1174 } | |
1175 | |
1176 p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1); | |
1177 p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1); | |
1178 p = ngx_cpymem(p, ctx->host.data, ctx->host.len); | |
1179 *p++ = CR; *p++ = LF; | |
1180 | |
1181 /* add "\r\n" at the header end */ | |
1182 *p++ = CR; *p++ = LF; | |
1183 | |
1184 b->last = p; | |
1185 ctx->request = b; | |
1186 | |
1187 return NGX_OK; | |
1188 | |
1189 failed: | |
1190 | |
1191 OCSP_REQUEST_free(ocsp); | |
1192 | |
1193 return NGX_ERROR; | |
1194 } | |
1195 | |
1196 | |
1197 static ngx_int_t | |
1198 ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx) | |
1199 { | |
1200 ngx_int_t rc; | |
1201 | |
1202 rc = ngx_ssl_ocsp_parse_status_line(ctx); | |
1203 | |
1204 if (rc == NGX_OK) { | |
1205 #if 0 | |
1206 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1207 "ssl ocsp status line \"%*s\"", | |
1208 ctx->response->pos - ctx->response->start, | |
1209 ctx->response->start); | |
1210 #endif | |
1211 | |
1212 ctx->process = ngx_ssl_ocsp_process_headers; | |
1213 return ctx->process(ctx); | |
1214 } | |
1215 | |
1216 if (rc == NGX_AGAIN) { | |
1217 return NGX_AGAIN; | |
1218 } | |
1219 | |
1220 /* rc == NGX_ERROR */ | |
1221 | |
1222 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
1223 "OCSP responder sent invalid response"); | |
1224 | |
1225 return NGX_ERROR; | |
1226 } | |
1227 | |
1228 | |
1229 static ngx_int_t | |
1230 ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx) | |
1231 { | |
1232 u_char ch; | |
1233 u_char *p; | |
1234 ngx_buf_t *b; | |
1235 enum { | |
1236 sw_start = 0, | |
1237 sw_H, | |
1238 sw_HT, | |
1239 sw_HTT, | |
1240 sw_HTTP, | |
1241 sw_first_major_digit, | |
1242 sw_major_digit, | |
1243 sw_first_minor_digit, | |
1244 sw_minor_digit, | |
1245 sw_status, | |
1246 sw_space_after_status, | |
1247 sw_status_text, | |
1248 sw_almost_done | |
1249 } state; | |
1250 | |
1251 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1252 "ssl ocsp process status line"); | |
1253 | |
1254 state = ctx->state; | |
1255 b = ctx->response; | |
1256 | |
1257 for (p = b->pos; p < b->last; p++) { | |
1258 ch = *p; | |
1259 | |
1260 switch (state) { | |
1261 | |
1262 /* "HTTP/" */ | |
1263 case sw_start: | |
1264 switch (ch) { | |
1265 case 'H': | |
1266 state = sw_H; | |
1267 break; | |
1268 default: | |
1269 return NGX_ERROR; | |
1270 } | |
1271 break; | |
1272 | |
1273 case sw_H: | |
1274 switch (ch) { | |
1275 case 'T': | |
1276 state = sw_HT; | |
1277 break; | |
1278 default: | |
1279 return NGX_ERROR; | |
1280 } | |
1281 break; | |
1282 | |
1283 case sw_HT: | |
1284 switch (ch) { | |
1285 case 'T': | |
1286 state = sw_HTT; | |
1287 break; | |
1288 default: | |
1289 return NGX_ERROR; | |
1290 } | |
1291 break; | |
1292 | |
1293 case sw_HTT: | |
1294 switch (ch) { | |
1295 case 'P': | |
1296 state = sw_HTTP; | |
1297 break; | |
1298 default: | |
1299 return NGX_ERROR; | |
1300 } | |
1301 break; | |
1302 | |
1303 case sw_HTTP: | |
1304 switch (ch) { | |
1305 case '/': | |
1306 state = sw_first_major_digit; | |
1307 break; | |
1308 default: | |
1309 return NGX_ERROR; | |
1310 } | |
1311 break; | |
1312 | |
1313 /* the first digit of major HTTP version */ | |
1314 case sw_first_major_digit: | |
1315 if (ch < '1' || ch > '9') { | |
1316 return NGX_ERROR; | |
1317 } | |
1318 | |
1319 state = sw_major_digit; | |
1320 break; | |
1321 | |
1322 /* the major HTTP version or dot */ | |
1323 case sw_major_digit: | |
1324 if (ch == '.') { | |
1325 state = sw_first_minor_digit; | |
1326 break; | |
1327 } | |
1328 | |
1329 if (ch < '0' || ch > '9') { | |
1330 return NGX_ERROR; | |
1331 } | |
1332 | |
1333 break; | |
1334 | |
1335 /* the first digit of minor HTTP version */ | |
1336 case sw_first_minor_digit: | |
1337 if (ch < '0' || ch > '9') { | |
1338 return NGX_ERROR; | |
1339 } | |
1340 | |
1341 state = sw_minor_digit; | |
1342 break; | |
1343 | |
1344 /* the minor HTTP version or the end of the request line */ | |
1345 case sw_minor_digit: | |
1346 if (ch == ' ') { | |
1347 state = sw_status; | |
1348 break; | |
1349 } | |
1350 | |
1351 if (ch < '0' || ch > '9') { | |
1352 return NGX_ERROR; | |
1353 } | |
1354 | |
1355 break; | |
1356 | |
1357 /* HTTP status code */ | |
1358 case sw_status: | |
1359 if (ch == ' ') { | |
1360 break; | |
1361 } | |
1362 | |
1363 if (ch < '0' || ch > '9') { | |
1364 return NGX_ERROR; | |
1365 } | |
1366 | |
1367 ctx->code = ctx->code * 10 + ch - '0'; | |
1368 | |
1369 if (++ctx->count == 3) { | |
1370 state = sw_space_after_status; | |
1371 } | |
1372 | |
1373 break; | |
1374 | |
1375 /* space or end of line */ | |
1376 case sw_space_after_status: | |
1377 switch (ch) { | |
1378 case ' ': | |
1379 state = sw_status_text; | |
1380 break; | |
1381 case '.': /* IIS may send 403.1, 403.2, etc */ | |
1382 state = sw_status_text; | |
1383 break; | |
1384 case CR: | |
1385 state = sw_almost_done; | |
1386 break; | |
1387 case LF: | |
1388 goto done; | |
1389 default: | |
1390 return NGX_ERROR; | |
1391 } | |
1392 break; | |
1393 | |
1394 /* any text until end of line */ | |
1395 case sw_status_text: | |
1396 switch (ch) { | |
1397 case CR: | |
1398 state = sw_almost_done; | |
1399 break; | |
1400 case LF: | |
1401 goto done; | |
1402 } | |
1403 break; | |
1404 | |
1405 /* end of status line */ | |
1406 case sw_almost_done: | |
1407 switch (ch) { | |
1408 case LF: | |
1409 goto done; | |
1410 default: | |
1411 return NGX_ERROR; | |
1412 } | |
1413 } | |
1414 } | |
1415 | |
1416 b->pos = p; | |
1417 ctx->state = state; | |
1418 | |
1419 return NGX_AGAIN; | |
1420 | |
1421 done: | |
1422 | |
1423 b->pos = p + 1; | |
1424 ctx->state = sw_start; | |
1425 | |
1426 return NGX_OK; | |
1427 } | |
1428 | |
1429 | |
1430 static ngx_int_t | |
1431 ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx) | |
1432 { | |
1433 size_t len; | |
1434 ngx_int_t rc; | |
1435 | |
1436 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1437 "ssl ocsp process headers"); | |
1438 | |
1439 for ( ;; ) { | |
1440 rc = ngx_ssl_ocsp_parse_header_line(ctx); | |
1441 | |
1442 if (rc == NGX_OK) { | |
1443 | |
1444 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1445 "ssl ocsp header \"%*s: %*s\"", | |
1446 ctx->header_name_end - ctx->header_name_start, | |
1447 ctx->header_name_start, | |
1448 ctx->header_end - ctx->header_start, | |
1449 ctx->header_start); | |
1450 | |
1451 len = ctx->header_name_end - ctx->header_name_start; | |
1452 | |
1453 if (len == sizeof("Content-Type") - 1 | |
1454 && ngx_strncasecmp(ctx->header_name_start, | |
1455 (u_char *) "Content-Type", | |
1456 sizeof("Content-Type") - 1) | |
1457 == 0) | |
1458 { | |
1459 len = ctx->header_end - ctx->header_start; | |
1460 | |
1461 if (len != sizeof("application/ocsp-response") - 1 | |
1462 || ngx_strncasecmp(ctx->header_start, | |
1463 (u_char *) "application/ocsp-response", | |
1464 sizeof("application/ocsp-response") - 1) | |
1465 != 0) | |
1466 { | |
1467 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
1468 "OCSP responder sent invalid " | |
1469 "\"Content-Type\" header: \"%*s\"", | |
1470 ctx->header_end - ctx->header_start, | |
1471 ctx->header_start); | |
1472 return NGX_ERROR; | |
1473 } | |
1474 | |
1475 continue; | |
1476 } | |
1477 | |
1478 /* TODO: honor Content-Length */ | |
1479 | |
1480 continue; | |
1481 } | |
1482 | |
1483 if (rc == NGX_DONE) { | |
1484 break; | |
1485 } | |
1486 | |
1487 if (rc == NGX_AGAIN) { | |
1488 return NGX_AGAIN; | |
1489 } | |
1490 | |
1491 /* rc == NGX_ERROR */ | |
1492 | |
1493 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
1494 "OCSP responder sent invalid response"); | |
1495 | |
1496 return NGX_ERROR; | |
1497 } | |
1498 | |
1499 ctx->process = ngx_ssl_ocsp_process_body; | |
1500 return ctx->process(ctx); | |
1501 } | |
1502 | |
1503 static ngx_int_t | |
1504 ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx) | |
1505 { | |
1506 u_char c, ch, *p; | |
1507 enum { | |
1508 sw_start = 0, | |
1509 sw_name, | |
1510 sw_space_before_value, | |
1511 sw_value, | |
1512 sw_space_after_value, | |
1513 sw_almost_done, | |
1514 sw_header_almost_done | |
1515 } state; | |
1516 | |
1517 state = ctx->state; | |
1518 | |
1519 for (p = ctx->response->pos; p < ctx->response->last; p++) { | |
1520 ch = *p; | |
1521 | |
1522 #if 0 | |
1523 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1524 "s:%d in:'%02Xd:%c'", state, ch, ch); | |
1525 #endif | |
1526 | |
1527 switch (state) { | |
1528 | |
1529 /* first char */ | |
1530 case sw_start: | |
1531 | |
1532 switch (ch) { | |
1533 case CR: | |
1534 ctx->header_end = p; | |
1535 state = sw_header_almost_done; | |
1536 break; | |
1537 case LF: | |
1538 ctx->header_end = p; | |
1539 goto header_done; | |
1540 default: | |
1541 state = sw_name; | |
1542 ctx->header_name_start = p; | |
1543 | |
1544 c = (u_char) (ch | 0x20); | |
1545 if (c >= 'a' && c <= 'z') { | |
1546 break; | |
1547 } | |
1548 | |
1549 if (ch >= '0' && ch <= '9') { | |
1550 break; | |
1551 } | |
1552 | |
1553 return NGX_ERROR; | |
1554 } | |
1555 break; | |
1556 | |
1557 /* header name */ | |
1558 case sw_name: | |
1559 c = (u_char) (ch | 0x20); | |
1560 if (c >= 'a' && c <= 'z') { | |
1561 break; | |
1562 } | |
1563 | |
1564 if (ch == ':') { | |
1565 ctx->header_name_end = p; | |
1566 state = sw_space_before_value; | |
1567 break; | |
1568 } | |
1569 | |
1570 if (ch == '-') { | |
1571 break; | |
1572 } | |
1573 | |
1574 if (ch >= '0' && ch <= '9') { | |
1575 break; | |
1576 } | |
1577 | |
1578 if (ch == CR) { | |
1579 ctx->header_name_end = p; | |
1580 ctx->header_start = p; | |
1581 ctx->header_end = p; | |
1582 state = sw_almost_done; | |
1583 break; | |
1584 } | |
1585 | |
1586 if (ch == LF) { | |
1587 ctx->header_name_end = p; | |
1588 ctx->header_start = p; | |
1589 ctx->header_end = p; | |
1590 goto done; | |
1591 } | |
1592 | |
1593 return NGX_ERROR; | |
1594 | |
1595 /* space* before header value */ | |
1596 case sw_space_before_value: | |
1597 switch (ch) { | |
1598 case ' ': | |
1599 break; | |
1600 case CR: | |
1601 ctx->header_start = p; | |
1602 ctx->header_end = p; | |
1603 state = sw_almost_done; | |
1604 break; | |
1605 case LF: | |
1606 ctx->header_start = p; | |
1607 ctx->header_end = p; | |
1608 goto done; | |
1609 default: | |
1610 ctx->header_start = p; | |
1611 state = sw_value; | |
1612 break; | |
1613 } | |
1614 break; | |
1615 | |
1616 /* header value */ | |
1617 case sw_value: | |
1618 switch (ch) { | |
1619 case ' ': | |
1620 ctx->header_end = p; | |
1621 state = sw_space_after_value; | |
1622 break; | |
1623 case CR: | |
1624 ctx->header_end = p; | |
1625 state = sw_almost_done; | |
1626 break; | |
1627 case LF: | |
1628 ctx->header_end = p; | |
1629 goto done; | |
1630 } | |
1631 break; | |
1632 | |
1633 /* space* before end of header line */ | |
1634 case sw_space_after_value: | |
1635 switch (ch) { | |
1636 case ' ': | |
1637 break; | |
1638 case CR: | |
1639 state = sw_almost_done; | |
1640 break; | |
1641 case LF: | |
1642 goto done; | |
1643 default: | |
1644 state = sw_value; | |
1645 break; | |
1646 } | |
1647 break; | |
1648 | |
1649 /* end of header line */ | |
1650 case sw_almost_done: | |
1651 switch (ch) { | |
1652 case LF: | |
1653 goto done; | |
1654 default: | |
1655 return NGX_ERROR; | |
1656 } | |
1657 | |
1658 /* end of header */ | |
1659 case sw_header_almost_done: | |
1660 switch (ch) { | |
1661 case LF: | |
1662 goto header_done; | |
1663 default: | |
1664 return NGX_ERROR; | |
1665 } | |
1666 } | |
1667 } | |
1668 | |
1669 ctx->response->pos = p; | |
1670 ctx->state = state; | |
1671 | |
1672 return NGX_AGAIN; | |
1673 | |
1674 done: | |
1675 | |
1676 ctx->response->pos = p + 1; | |
1677 ctx->state = sw_start; | |
1678 | |
1679 return NGX_OK; | |
1680 | |
1681 header_done: | |
1682 | |
1683 ctx->response->pos = p + 1; | |
1684 ctx->state = sw_start; | |
1685 | |
1686 return NGX_DONE; | |
1687 } | |
1688 | |
1689 | |
1690 static ngx_int_t | |
1691 ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx) | |
1692 { | |
1693 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1694 "ssl ocsp process body"); | |
1695 | |
1696 if (ctx->done) { | |
1697 ctx->handler(ctx); | |
1698 return NGX_DONE; | |
1699 } | |
1700 | |
1701 return NGX_AGAIN; | |
1702 } | |
1703 | |
1704 | |
1705 static u_char * | |
1706 ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len) | |
1707 { | |
1708 u_char *p; | |
1709 ngx_ssl_ocsp_ctx_t *ctx; | |
1710 | |
1711 p = buf; | |
1712 | |
1713 if (log->action) { | |
1714 p = ngx_snprintf(buf, len, " while %s", log->action); | |
1715 len -= p - buf; | |
1716 } | |
1717 | |
1718 ctx = log->data; | |
1719 | |
1720 if (ctx) { | |
1721 p = ngx_snprintf(p, len, ", responder: %V", &ctx->host); | |
1722 } | |
1723 | |
1724 return p; | |
1725 } | |
1726 | |
1727 | |
1728 #else | |
1729 | |
1730 | |
1731 ngx_int_t | |
1732 ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, | |
1733 ngx_str_t *responder, ngx_uint_t verify) | |
1734 { | |
1735 ngx_log_error(NGX_LOG_WARN, ssl->log, 0, | |
1736 "\"ssl_stapling\" ignored, not supported"); | |
1737 | |
1738 return NGX_OK; | |
1739 } | |
1740 | |
1741 ngx_int_t | |
1742 ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
1743 ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) | |
1744 { | |
1745 return NGX_OK; | |
1746 } | |
1747 | |
1748 | |
1749 #endif |