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