Mercurial > hg > nginx
comparison src/event/ngx_event_openssl_stapling.c @ 8411:7995cd199b52 quic
Merged with the default branch.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Tue, 26 May 2020 20:26:44 +0300 |
parents | bd4d1b9db0ee |
children | 1ece2ac2555a |
comparison
equal
deleted
inserted
replaced
8410:c7d1b500bd0a | 8411:7995cd199b52 |
---|---|
20 | 20 |
21 ngx_resolver_t *resolver; | 21 ngx_resolver_t *resolver; |
22 ngx_msec_t resolver_timeout; | 22 ngx_msec_t resolver_timeout; |
23 | 23 |
24 ngx_addr_t *addrs; | 24 ngx_addr_t *addrs; |
25 ngx_uint_t naddrs; | |
25 ngx_str_t host; | 26 ngx_str_t host; |
26 ngx_str_t uri; | 27 ngx_str_t uri; |
27 in_port_t port; | 28 in_port_t port; |
28 | 29 |
29 SSL_CTX *ssl_ctx; | 30 SSL_CTX *ssl_ctx; |
30 | 31 |
31 X509 *cert; | 32 X509 *cert; |
32 X509 *issuer; | 33 X509 *issuer; |
34 STACK_OF(X509) *chain; | |
33 | 35 |
34 u_char *name; | 36 u_char *name; |
35 | 37 |
36 time_t valid; | 38 time_t valid; |
37 time_t refresh; | 39 time_t refresh; |
39 unsigned verify:1; | 41 unsigned verify:1; |
40 unsigned loading:1; | 42 unsigned loading:1; |
41 } ngx_ssl_stapling_t; | 43 } ngx_ssl_stapling_t; |
42 | 44 |
43 | 45 |
46 typedef struct { | |
47 ngx_addr_t *addrs; | |
48 ngx_uint_t naddrs; | |
49 | |
50 ngx_str_t host; | |
51 ngx_str_t uri; | |
52 in_port_t port; | |
53 ngx_uint_t depth; | |
54 | |
55 ngx_shm_zone_t *shm_zone; | |
56 | |
57 ngx_resolver_t *resolver; | |
58 ngx_msec_t resolver_timeout; | |
59 } ngx_ssl_ocsp_conf_t; | |
60 | |
61 | |
62 typedef struct { | |
63 ngx_rbtree_t rbtree; | |
64 ngx_rbtree_node_t sentinel; | |
65 ngx_queue_t expire_queue; | |
66 } ngx_ssl_ocsp_cache_t; | |
67 | |
68 | |
69 typedef struct { | |
70 ngx_str_node_t node; | |
71 ngx_queue_t queue; | |
72 int status; | |
73 time_t valid; | |
74 } ngx_ssl_ocsp_cache_node_t; | |
75 | |
76 | |
44 typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t; | 77 typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t; |
45 | 78 |
79 | |
80 struct ngx_ssl_ocsp_s { | |
81 STACK_OF(X509) *certs; | |
82 ngx_uint_t ncert; | |
83 | |
84 int cert_status; | |
85 ngx_int_t status; | |
86 | |
87 ngx_ssl_ocsp_conf_t *conf; | |
88 ngx_ssl_ocsp_ctx_t *ctx; | |
89 }; | |
90 | |
91 | |
46 struct ngx_ssl_ocsp_ctx_s { | 92 struct ngx_ssl_ocsp_ctx_s { |
93 SSL_CTX *ssl_ctx; | |
94 | |
47 X509 *cert; | 95 X509 *cert; |
48 X509 *issuer; | 96 X509 *issuer; |
97 STACK_OF(X509) *chain; | |
98 | |
99 int status; | |
100 time_t valid; | |
49 | 101 |
50 u_char *name; | 102 u_char *name; |
51 | 103 |
52 ngx_uint_t naddrs; | 104 ngx_uint_t naddrs; |
105 ngx_uint_t naddr; | |
53 | 106 |
54 ngx_addr_t *addrs; | 107 ngx_addr_t *addrs; |
55 ngx_str_t host; | 108 ngx_str_t host; |
56 ngx_str_t uri; | 109 ngx_str_t uri; |
57 in_port_t port; | 110 in_port_t port; |
62 ngx_msec_t timeout; | 115 ngx_msec_t timeout; |
63 | 116 |
64 void (*handler)(ngx_ssl_ocsp_ctx_t *ctx); | 117 void (*handler)(ngx_ssl_ocsp_ctx_t *ctx); |
65 void *data; | 118 void *data; |
66 | 119 |
120 ngx_str_t key; | |
67 ngx_buf_t *request; | 121 ngx_buf_t *request; |
68 ngx_buf_t *response; | 122 ngx_buf_t *response; |
69 ngx_peer_connection_t peer; | 123 ngx_peer_connection_t peer; |
70 | 124 |
125 ngx_shm_zone_t *shm_zone; | |
126 | |
71 ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx); | 127 ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx); |
72 | 128 |
73 ngx_uint_t state; | 129 ngx_uint_t state; |
74 | 130 |
75 ngx_uint_t code; | 131 ngx_uint_t code; |
76 ngx_uint_t count; | 132 ngx_uint_t count; |
77 | 133 ngx_uint_t flags; |
78 ngx_uint_t done; | 134 ngx_uint_t done; |
79 | 135 |
80 u_char *header_name_start; | 136 u_char *header_name_start; |
81 u_char *header_name_end; | 137 u_char *header_name_end; |
82 u_char *header_start; | 138 u_char *header_start; |
103 | 159 |
104 static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time); | 160 static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time); |
105 | 161 |
106 static void ngx_ssl_stapling_cleanup(void *data); | 162 static void ngx_ssl_stapling_cleanup(void *data); |
107 | 163 |
108 static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void); | 164 static void ngx_ssl_ocsp_validate_next(ngx_connection_t *c); |
165 static void ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); | |
166 static ngx_int_t ngx_ssl_ocsp_responder(ngx_connection_t *c, | |
167 ngx_ssl_ocsp_ctx_t *ctx); | |
168 | |
169 static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(ngx_log_t *log); | |
109 static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx); | 170 static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx); |
171 static void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx); | |
110 static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx); | 172 static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx); |
111 static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve); | 173 static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve); |
112 static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx); | 174 static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx); |
113 static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev); | 175 static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev); |
114 static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev); | 176 static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev); |
118 static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx); | 180 static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx); |
119 static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx); | 181 static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx); |
120 static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx); | 182 static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx); |
121 static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx); | 183 static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx); |
122 static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx); | 184 static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx); |
185 static ngx_int_t ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx); | |
186 | |
187 static ngx_int_t ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx); | |
188 static ngx_int_t ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx); | |
189 static ngx_int_t ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx); | |
123 | 190 |
124 static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len); | 191 static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len); |
125 | 192 |
126 | 193 |
127 ngx_int_t | 194 ngx_int_t |
171 if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) { | 238 if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) { |
172 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); | 239 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); |
173 return NGX_ERROR; | 240 return NGX_ERROR; |
174 } | 241 } |
175 | 242 |
243 #ifdef SSL_CTRL_SELECT_CURRENT_CERT | |
244 /* OpenSSL 1.0.2+ */ | |
245 SSL_CTX_select_current_cert(ssl->ctx, cert); | |
246 #endif | |
247 | |
248 #ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS | |
249 /* OpenSSL 1.0.1+ */ | |
250 SSL_CTX_get_extra_chain_certs(ssl->ctx, &staple->chain); | |
251 #else | |
252 staple->chain = ssl->ctx->extra_certs; | |
253 #endif | |
254 | |
176 staple->ssl_ctx = ssl->ctx; | 255 staple->ssl_ctx = ssl->ctx; |
177 staple->timeout = 60000; | 256 staple->timeout = 60000; |
178 staple->verify = verify; | 257 staple->verify = verify; |
179 staple->cert = cert; | 258 staple->cert = cert; |
180 staple->name = X509_get_ex_data(staple->cert, | 259 staple->name = X509_get_ex_data(staple->cert, |
287 { | 366 { |
288 int i, n, rc; | 367 int i, n, rc; |
289 X509 *cert, *issuer; | 368 X509 *cert, *issuer; |
290 X509_STORE *store; | 369 X509_STORE *store; |
291 X509_STORE_CTX *store_ctx; | 370 X509_STORE_CTX *store_ctx; |
292 STACK_OF(X509) *chain; | |
293 | 371 |
294 cert = staple->cert; | 372 cert = staple->cert; |
295 | 373 |
296 #ifdef SSL_CTRL_SELECT_CURRENT_CERT | 374 n = sk_X509_num(staple->chain); |
297 /* OpenSSL 1.0.2+ */ | |
298 SSL_CTX_select_current_cert(ssl->ctx, cert); | |
299 #endif | |
300 | |
301 #ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS | |
302 /* OpenSSL 1.0.1+ */ | |
303 SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain); | |
304 #else | |
305 chain = ssl->ctx->extra_certs; | |
306 #endif | |
307 | |
308 n = sk_X509_num(chain); | |
309 | 375 |
310 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, | 376 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0, |
311 "SSL get issuer: %d extra certs", n); | 377 "SSL get issuer: %d extra certs", n); |
312 | 378 |
313 for (i = 0; i < n; i++) { | 379 for (i = 0; i < n; i++) { |
314 issuer = sk_X509_value(chain, i); | 380 issuer = sk_X509_value(staple->chain, i); |
315 if (X509_check_issued(issuer, cert) == X509_V_OK) { | 381 if (X509_check_issued(issuer, cert) == X509_V_OK) { |
316 #if OPENSSL_VERSION_NUMBER >= 0x10100001L | 382 #if OPENSSL_VERSION_NUMBER >= 0x10100001L |
317 X509_up_ref(issuer); | 383 X509_up_ref(issuer); |
318 #else | 384 #else |
319 CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509); | 385 CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509); |
460 | 526 |
461 return NGX_ERROR; | 527 return NGX_ERROR; |
462 } | 528 } |
463 | 529 |
464 staple->addrs = u.addrs; | 530 staple->addrs = u.addrs; |
531 staple->naddrs = u.naddrs; | |
465 staple->host = u.host; | 532 staple->host = u.host; |
466 staple->uri = u.uri; | 533 staple->uri = u.uri; |
467 staple->port = u.port; | 534 staple->port = u.port; |
468 | 535 |
469 if (staple->uri.len == 0) { | 536 if (staple->uri.len == 0) { |
557 return; | 624 return; |
558 } | 625 } |
559 | 626 |
560 staple->loading = 1; | 627 staple->loading = 1; |
561 | 628 |
562 ctx = ngx_ssl_ocsp_start(); | 629 ctx = ngx_ssl_ocsp_start(ngx_cycle->log); |
563 if (ctx == NULL) { | 630 if (ctx == NULL) { |
564 return; | 631 return; |
565 } | 632 } |
566 | 633 |
634 ctx->ssl_ctx = staple->ssl_ctx; | |
567 ctx->cert = staple->cert; | 635 ctx->cert = staple->cert; |
568 ctx->issuer = staple->issuer; | 636 ctx->issuer = staple->issuer; |
637 ctx->chain = staple->chain; | |
569 ctx->name = staple->name; | 638 ctx->name = staple->name; |
639 ctx->flags = (staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY); | |
570 | 640 |
571 ctx->addrs = staple->addrs; | 641 ctx->addrs = staple->addrs; |
642 ctx->naddrs = staple->naddrs; | |
572 ctx->host = staple->host; | 643 ctx->host = staple->host; |
573 ctx->uri = staple->uri; | 644 ctx->uri = staple->uri; |
574 ctx->port = staple->port; | 645 ctx->port = staple->port; |
575 ctx->timeout = staple->timeout; | 646 ctx->timeout = staple->timeout; |
576 | 647 |
587 | 658 |
588 | 659 |
589 static void | 660 static void |
590 ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) | 661 ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) |
591 { | 662 { |
592 int n; | 663 time_t now; |
593 size_t len; | 664 ngx_str_t response; |
594 time_t now, valid; | 665 ngx_ssl_stapling_t *staple; |
595 ngx_str_t response; | |
596 X509_STORE *store; | |
597 const u_char *p; | |
598 STACK_OF(X509) *chain; | |
599 OCSP_CERTID *id; | |
600 OCSP_RESPONSE *ocsp; | |
601 OCSP_BASICRESP *basic; | |
602 ngx_ssl_stapling_t *staple; | |
603 ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; | |
604 | 666 |
605 staple = ctx->data; | 667 staple = ctx->data; |
606 now = ngx_time(); | 668 now = ngx_time(); |
607 ocsp = NULL; | 669 |
608 basic = NULL; | 670 if (ngx_ssl_ocsp_verify(ctx) != NGX_OK) { |
609 id = NULL; | |
610 | |
611 if (ctx->code != 200) { | |
612 goto error; | 671 goto error; |
613 } | 672 } |
614 | 673 |
615 /* check the response */ | 674 if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { |
616 | |
617 len = ctx->response->last - ctx->response->pos; | |
618 p = ctx->response->pos; | |
619 | |
620 ocsp = d2i_OCSP_RESPONSE(NULL, &p, len); | |
621 if (ocsp == NULL) { | |
622 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
623 "d2i_OCSP_RESPONSE() failed"); | |
624 goto error; | |
625 } | |
626 | |
627 n = OCSP_response_status(ocsp); | |
628 | |
629 if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { | |
630 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
631 "OCSP response not successful (%d: %s)", | |
632 n, OCSP_response_status_str(n)); | |
633 goto error; | |
634 } | |
635 | |
636 basic = OCSP_response_get1_basic(ocsp); | |
637 if (basic == NULL) { | |
638 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
639 "OCSP_response_get1_basic() failed"); | |
640 goto error; | |
641 } | |
642 | |
643 store = SSL_CTX_get_cert_store(staple->ssl_ctx); | |
644 if (store == NULL) { | |
645 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
646 "SSL_CTX_get_cert_store() failed"); | |
647 goto error; | |
648 } | |
649 | |
650 #ifdef SSL_CTRL_SELECT_CURRENT_CERT | |
651 /* OpenSSL 1.0.2+ */ | |
652 SSL_CTX_select_current_cert(staple->ssl_ctx, ctx->cert); | |
653 #endif | |
654 | |
655 #ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS | |
656 /* OpenSSL 1.0.1+ */ | |
657 SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain); | |
658 #else | |
659 chain = staple->ssl_ctx->extra_certs; | |
660 #endif | |
661 | |
662 if (OCSP_basic_verify(basic, chain, store, | |
663 staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY) | |
664 != 1) | |
665 { | |
666 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
667 "OCSP_basic_verify() failed"); | |
668 goto error; | |
669 } | |
670 | |
671 id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); | |
672 if (id == NULL) { | |
673 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
674 "OCSP_cert_to_id() failed"); | |
675 goto error; | |
676 } | |
677 | |
678 if (OCSP_resp_find_status(basic, id, &n, NULL, NULL, | |
679 &thisupdate, &nextupdate) | |
680 != 1) | |
681 { | |
682 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
683 "certificate status not found in the OCSP response"); | |
684 goto error; | |
685 } | |
686 | |
687 if (n != V_OCSP_CERTSTATUS_GOOD) { | |
688 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | 675 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, |
689 "certificate status \"%s\" in the OCSP response", | 676 "certificate status \"%s\" in the OCSP response", |
690 OCSP_cert_status_str(n)); | 677 OCSP_cert_status_str(ctx->status)); |
691 goto error; | 678 goto error; |
692 } | 679 } |
693 | 680 |
694 if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { | |
695 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
696 "OCSP_check_validity() failed"); | |
697 goto error; | |
698 } | |
699 | |
700 if (nextupdate) { | |
701 valid = ngx_ssl_stapling_time(nextupdate); | |
702 if (valid == (time_t) NGX_ERROR) { | |
703 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
704 "invalid nextUpdate time in certificate status"); | |
705 goto error; | |
706 } | |
707 | |
708 } else { | |
709 valid = NGX_MAX_TIME_T_VALUE; | |
710 } | |
711 | |
712 OCSP_CERTID_free(id); | |
713 OCSP_BASICRESP_free(basic); | |
714 OCSP_RESPONSE_free(ocsp); | |
715 | |
716 id = NULL; | |
717 basic = NULL; | |
718 ocsp = NULL; | |
719 | |
720 /* copy the response to memory not in ctx->pool */ | 681 /* copy the response to memory not in ctx->pool */ |
721 | 682 |
722 response.len = len; | 683 response.len = ctx->response->last - ctx->response->pos; |
723 response.data = ngx_alloc(response.len, ctx->log); | 684 response.data = ngx_alloc(response.len, ctx->log); |
724 | 685 |
725 if (response.data == NULL) { | 686 if (response.data == NULL) { |
726 goto error; | 687 goto error; |
727 } | 688 } |
728 | 689 |
729 ngx_memcpy(response.data, ctx->response->pos, response.len); | 690 ngx_memcpy(response.data, ctx->response->pos, response.len); |
730 | 691 |
731 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
732 "ssl ocsp response, %s, %uz", | |
733 OCSP_cert_status_str(n), response.len); | |
734 | |
735 if (staple->staple.data) { | 692 if (staple->staple.data) { |
736 ngx_free(staple->staple.data); | 693 ngx_free(staple->staple.data); |
737 } | 694 } |
738 | 695 |
739 staple->staple = response; | 696 staple->staple = response; |
740 staple->valid = valid; | 697 staple->valid = ctx->valid; |
741 | 698 |
742 /* | 699 /* |
743 * refresh before the response expires, | 700 * refresh before the response expires, |
744 * but not earlier than in 5 minutes, and at least in an hour | 701 * but not earlier than in 5 minutes, and at least in an hour |
745 */ | 702 */ |
746 | 703 |
747 staple->loading = 0; | 704 staple->loading = 0; |
748 staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300); | 705 staple->refresh = ngx_max(ngx_min(ctx->valid - 300, now + 3600), now + 300); |
749 | 706 |
750 ngx_ssl_ocsp_done(ctx); | 707 ngx_ssl_ocsp_done(ctx); |
751 return; | 708 return; |
752 | 709 |
753 error: | 710 error: |
754 | 711 |
755 staple->loading = 0; | 712 staple->loading = 0; |
756 staple->refresh = now + 300; | 713 staple->refresh = now + 300; |
757 | |
758 if (id) { | |
759 OCSP_CERTID_free(id); | |
760 } | |
761 | |
762 if (basic) { | |
763 OCSP_BASICRESP_free(basic); | |
764 } | |
765 | |
766 if (ocsp) { | |
767 OCSP_RESPONSE_free(ocsp); | |
768 } | |
769 | 714 |
770 ngx_ssl_ocsp_done(ctx); | 715 ngx_ssl_ocsp_done(ctx); |
771 } | 716 } |
772 | 717 |
773 | 718 |
818 ngx_free(staple->staple.data); | 763 ngx_free(staple->staple.data); |
819 } | 764 } |
820 } | 765 } |
821 | 766 |
822 | 767 |
768 ngx_int_t | |
769 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, | |
770 ngx_uint_t depth, ngx_shm_zone_t *shm_zone) | |
771 { | |
772 ngx_url_t u; | |
773 ngx_ssl_ocsp_conf_t *ocf; | |
774 | |
775 ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t)); | |
776 if (ocf == NULL) { | |
777 return NGX_ERROR; | |
778 } | |
779 | |
780 ocf->depth = depth; | |
781 ocf->shm_zone = shm_zone; | |
782 | |
783 if (responder->len) { | |
784 ngx_memzero(&u, sizeof(ngx_url_t)); | |
785 | |
786 u.url = *responder; | |
787 u.default_port = 80; | |
788 u.uri_part = 1; | |
789 | |
790 if (u.url.len > 7 | |
791 && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) | |
792 { | |
793 u.url.len -= 7; | |
794 u.url.data += 7; | |
795 | |
796 } else { | |
797 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
798 "invalid URL prefix in OCSP responder \"%V\" " | |
799 "in \"ssl_ocsp_responder\"", &u.url); | |
800 return NGX_ERROR; | |
801 } | |
802 | |
803 if (ngx_parse_url(cf->pool, &u) != NGX_OK) { | |
804 if (u.err) { | |
805 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
806 "%s in OCSP responder \"%V\" " | |
807 "in \"ssl_ocsp_responder\"", u.err, &u.url); | |
808 } | |
809 | |
810 return NGX_ERROR; | |
811 } | |
812 | |
813 ocf->addrs = u.addrs; | |
814 ocf->naddrs = u.naddrs; | |
815 ocf->host = u.host; | |
816 ocf->uri = u.uri; | |
817 ocf->port = u.port; | |
818 } | |
819 | |
820 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ocsp_index, ocf) == 0) { | |
821 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, | |
822 "SSL_CTX_set_ex_data() failed"); | |
823 return NGX_ERROR; | |
824 } | |
825 | |
826 return NGX_OK; | |
827 } | |
828 | |
829 | |
830 ngx_int_t | |
831 ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
832 ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) | |
833 { | |
834 ngx_ssl_ocsp_conf_t *ocf; | |
835 | |
836 ocf = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_ocsp_index); | |
837 ocf->resolver = resolver; | |
838 ocf->resolver_timeout = resolver_timeout; | |
839 | |
840 return NGX_OK; | |
841 } | |
842 | |
843 | |
844 ngx_int_t | |
845 ngx_ssl_ocsp_validate(ngx_connection_t *c) | |
846 { | |
847 X509 *cert; | |
848 SSL_CTX *ssl_ctx; | |
849 ngx_int_t rc; | |
850 X509_STORE *store; | |
851 X509_STORE_CTX *store_ctx; | |
852 STACK_OF(X509) *chain; | |
853 ngx_ssl_ocsp_t *ocsp; | |
854 ngx_ssl_ocsp_conf_t *ocf; | |
855 | |
856 if (c->ssl->in_ocsp) { | |
857 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
858 return NGX_ERROR; | |
859 } | |
860 | |
861 if (ngx_handle_write_event(c->write, 0) != NGX_OK) { | |
862 return NGX_ERROR; | |
863 } | |
864 | |
865 return NGX_AGAIN; | |
866 } | |
867 | |
868 ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection); | |
869 | |
870 ocf = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ocsp_index); | |
871 if (ocf == NULL) { | |
872 return NGX_OK; | |
873 } | |
874 | |
875 if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { | |
876 return NGX_OK; | |
877 } | |
878 | |
879 cert = SSL_get_peer_certificate(c->ssl->connection); | |
880 if (cert == NULL) { | |
881 return NGX_OK; | |
882 } | |
883 | |
884 ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t)); | |
885 if (ocsp == NULL) { | |
886 return NGX_ERROR; | |
887 } | |
888 | |
889 c->ssl->ocsp = ocsp; | |
890 | |
891 ocsp->status = NGX_AGAIN; | |
892 ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD; | |
893 ocsp->conf = ocf; | |
894 | |
895 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER) | |
896 | |
897 ocsp->certs = SSL_get0_verified_chain(c->ssl->connection); | |
898 | |
899 if (ocsp->certs) { | |
900 ocsp->certs = X509_chain_up_ref(ocsp->certs); | |
901 if (ocsp->certs == NULL) { | |
902 return NGX_ERROR; | |
903 } | |
904 } | |
905 | |
906 #endif | |
907 | |
908 if (ocsp->certs == NULL) { | |
909 store = SSL_CTX_get_cert_store(ssl_ctx); | |
910 if (store == NULL) { | |
911 ngx_ssl_error(NGX_LOG_ERR, c->log, 0, | |
912 "SSL_CTX_get_cert_store() failed"); | |
913 return NGX_ERROR; | |
914 } | |
915 | |
916 store_ctx = X509_STORE_CTX_new(); | |
917 if (store_ctx == NULL) { | |
918 ngx_ssl_error(NGX_LOG_ERR, c->log, 0, | |
919 "X509_STORE_CTX_new() failed"); | |
920 return NGX_ERROR; | |
921 } | |
922 | |
923 chain = SSL_get_peer_cert_chain(c->ssl->connection); | |
924 | |
925 if (X509_STORE_CTX_init(store_ctx, store, cert, chain) == 0) { | |
926 ngx_ssl_error(NGX_LOG_ERR, c->log, 0, | |
927 "X509_STORE_CTX_init() failed"); | |
928 X509_STORE_CTX_free(store_ctx); | |
929 return NGX_ERROR; | |
930 } | |
931 | |
932 rc = X509_verify_cert(store_ctx); | |
933 if (rc <= 0) { | |
934 ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed"); | |
935 X509_STORE_CTX_free(store_ctx); | |
936 return NGX_ERROR; | |
937 } | |
938 | |
939 ocsp->certs = X509_STORE_CTX_get1_chain(store_ctx); | |
940 if (ocsp->certs == NULL) { | |
941 ngx_ssl_error(NGX_LOG_ERR, c->log, 0, | |
942 "X509_STORE_CTX_get1_chain() failed"); | |
943 X509_STORE_CTX_free(store_ctx); | |
944 return NGX_ERROR; | |
945 } | |
946 | |
947 X509_STORE_CTX_free(store_ctx); | |
948 } | |
949 | |
950 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
951 "ssl ocsp validate, certs:%d", sk_X509_num(ocsp->certs)); | |
952 | |
953 ngx_ssl_ocsp_validate_next(c); | |
954 | |
955 if (ocsp->status == NGX_AGAIN) { | |
956 c->ssl->in_ocsp = 1; | |
957 return NGX_AGAIN; | |
958 } | |
959 | |
960 return NGX_OK; | |
961 } | |
962 | |
963 | |
964 static void | |
965 ngx_ssl_ocsp_validate_next(ngx_connection_t *c) | |
966 { | |
967 ngx_int_t rc; | |
968 ngx_uint_t n; | |
969 ngx_ssl_ocsp_t *ocsp; | |
970 ngx_ssl_ocsp_ctx_t *ctx; | |
971 ngx_ssl_ocsp_conf_t *ocf; | |
972 | |
973 ocsp = c->ssl->ocsp; | |
974 ocf = ocsp->conf; | |
975 | |
976 n = sk_X509_num(ocsp->certs); | |
977 | |
978 for ( ;; ) { | |
979 | |
980 if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) { | |
981 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
982 "ssl ocsp validated, certs:%ui", ocsp->ncert); | |
983 goto done; | |
984 } | |
985 | |
986 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
987 "ssl ocsp validate cert:%ui", ocsp->ncert); | |
988 | |
989 ctx = ngx_ssl_ocsp_start(c->log); | |
990 if (ctx == NULL) { | |
991 goto failed; | |
992 } | |
993 | |
994 ocsp->ctx = ctx; | |
995 | |
996 ctx->ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection); | |
997 ctx->cert = sk_X509_value(ocsp->certs, ocsp->ncert); | |
998 ctx->issuer = sk_X509_value(ocsp->certs, ocsp->ncert + 1); | |
999 ctx->chain = ocsp->certs; | |
1000 | |
1001 ctx->resolver = ocf->resolver; | |
1002 ctx->resolver_timeout = ocf->resolver_timeout; | |
1003 | |
1004 ctx->handler = ngx_ssl_ocsp_handler; | |
1005 ctx->data = c; | |
1006 | |
1007 ctx->shm_zone = ocf->shm_zone; | |
1008 | |
1009 ctx->addrs = ocf->addrs; | |
1010 ctx->naddrs = ocf->naddrs; | |
1011 ctx->host = ocf->host; | |
1012 ctx->uri = ocf->uri; | |
1013 ctx->port = ocf->port; | |
1014 | |
1015 if (ngx_ssl_ocsp_responder(c, ctx) != NGX_OK) { | |
1016 goto failed; | |
1017 } | |
1018 | |
1019 if (ctx->uri.len == 0) { | |
1020 ngx_str_set(&ctx->uri, "/"); | |
1021 } | |
1022 | |
1023 ocsp->ncert++; | |
1024 | |
1025 rc = ngx_ssl_ocsp_cache_lookup(ctx); | |
1026 | |
1027 if (rc == NGX_ERROR) { | |
1028 goto failed; | |
1029 } | |
1030 | |
1031 if (rc == NGX_DECLINED) { | |
1032 break; | |
1033 } | |
1034 | |
1035 /* rc == NGX_OK */ | |
1036 | |
1037 if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { | |
1038 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1039 "ssl ocsp cached status \"%s\"", | |
1040 OCSP_cert_status_str(ctx->status)); | |
1041 ocsp->cert_status = ctx->status; | |
1042 goto done; | |
1043 } | |
1044 | |
1045 ocsp->ctx = NULL; | |
1046 ngx_ssl_ocsp_done(ctx); | |
1047 } | |
1048 | |
1049 ngx_ssl_ocsp_request(ctx); | |
1050 return; | |
1051 | |
1052 done: | |
1053 | |
1054 ocsp->status = NGX_OK; | |
1055 return; | |
1056 | |
1057 failed: | |
1058 | |
1059 ocsp->status = NGX_ERROR; | |
1060 } | |
1061 | |
1062 | |
1063 static void | |
1064 ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx) | |
1065 { | |
1066 ngx_int_t rc; | |
1067 ngx_ssl_ocsp_t *ocsp; | |
1068 ngx_connection_t *c; | |
1069 | |
1070 c = ctx->data; | |
1071 ocsp = c->ssl->ocsp; | |
1072 ocsp->ctx = NULL; | |
1073 | |
1074 rc = ngx_ssl_ocsp_verify(ctx); | |
1075 if (rc != NGX_OK) { | |
1076 ocsp->status = rc; | |
1077 ngx_ssl_ocsp_done(ctx); | |
1078 goto done; | |
1079 } | |
1080 | |
1081 rc = ngx_ssl_ocsp_cache_store(ctx); | |
1082 if (rc != NGX_OK) { | |
1083 ocsp->status = rc; | |
1084 ngx_ssl_ocsp_done(ctx); | |
1085 goto done; | |
1086 } | |
1087 | |
1088 if (ctx->status != V_OCSP_CERTSTATUS_GOOD) { | |
1089 ocsp->cert_status = ctx->status; | |
1090 ocsp->status = NGX_OK; | |
1091 ngx_ssl_ocsp_done(ctx); | |
1092 goto done; | |
1093 } | |
1094 | |
1095 ngx_ssl_ocsp_done(ctx); | |
1096 | |
1097 ngx_ssl_ocsp_validate_next(c); | |
1098 | |
1099 done: | |
1100 | |
1101 if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) { | |
1102 return; | |
1103 } | |
1104 | |
1105 c->ssl->handshaked = 1; | |
1106 | |
1107 c->ssl->handler(c); | |
1108 } | |
1109 | |
1110 | |
1111 static ngx_int_t | |
1112 ngx_ssl_ocsp_responder(ngx_connection_t *c, ngx_ssl_ocsp_ctx_t *ctx) | |
1113 { | |
1114 char *s; | |
1115 ngx_str_t responder; | |
1116 ngx_url_t u; | |
1117 STACK_OF(OPENSSL_STRING) *aia; | |
1118 | |
1119 if (ctx->host.len) { | |
1120 return NGX_OK; | |
1121 } | |
1122 | |
1123 /* extract OCSP responder URL from certificate */ | |
1124 | |
1125 aia = X509_get1_ocsp(ctx->cert); | |
1126 if (aia == NULL) { | |
1127 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
1128 "no OCSP responder URL in certificate"); | |
1129 return NGX_ERROR; | |
1130 } | |
1131 | |
1132 #if OPENSSL_VERSION_NUMBER >= 0x10000000L | |
1133 s = sk_OPENSSL_STRING_value(aia, 0); | |
1134 #else | |
1135 s = sk_value(aia, 0); | |
1136 #endif | |
1137 if (s == NULL) { | |
1138 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
1139 "no OCSP responder URL in certificate"); | |
1140 X509_email_free(aia); | |
1141 return NGX_ERROR; | |
1142 } | |
1143 | |
1144 responder.len = ngx_strlen(s); | |
1145 responder.data = ngx_palloc(ctx->pool, responder.len); | |
1146 if (responder.data == NULL) { | |
1147 X509_email_free(aia); | |
1148 return NGX_ERROR; | |
1149 } | |
1150 | |
1151 ngx_memcpy(responder.data, s, responder.len); | |
1152 X509_email_free(aia); | |
1153 | |
1154 ngx_memzero(&u, sizeof(ngx_url_t)); | |
1155 | |
1156 u.url = responder; | |
1157 u.default_port = 80; | |
1158 u.uri_part = 1; | |
1159 u.no_resolve = 1; | |
1160 | |
1161 if (u.url.len > 7 | |
1162 && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) | |
1163 { | |
1164 u.url.len -= 7; | |
1165 u.url.data += 7; | |
1166 | |
1167 } else { | |
1168 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
1169 "invalid URL prefix in OCSP responder \"%V\" " | |
1170 "in certificate", &u.url); | |
1171 return NGX_ERROR; | |
1172 } | |
1173 | |
1174 if (ngx_parse_url(ctx->pool, &u) != NGX_OK) { | |
1175 if (u.err) { | |
1176 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
1177 "%s in OCSP responder \"%V\" in certificate", | |
1178 u.err, &u.url); | |
1179 } | |
1180 | |
1181 return NGX_ERROR; | |
1182 } | |
1183 | |
1184 if (u.host.len == 0) { | |
1185 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
1186 "empty host in OCSP responder in certificate"); | |
1187 return NGX_ERROR; | |
1188 } | |
1189 | |
1190 ctx->addrs = u.addrs; | |
1191 ctx->naddrs = u.naddrs; | |
1192 ctx->host = u.host; | |
1193 ctx->uri = u.uri; | |
1194 ctx->port = u.port; | |
1195 | |
1196 return NGX_OK; | |
1197 } | |
1198 | |
1199 | |
1200 ngx_int_t | |
1201 ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s) | |
1202 { | |
1203 ngx_ssl_ocsp_t *ocsp; | |
1204 | |
1205 ocsp = c->ssl->ocsp; | |
1206 if (ocsp == NULL) { | |
1207 return NGX_OK; | |
1208 } | |
1209 | |
1210 if (ocsp->status == NGX_ERROR) { | |
1211 *s = "certificate status request failed"; | |
1212 return NGX_DECLINED; | |
1213 } | |
1214 | |
1215 switch (ocsp->cert_status) { | |
1216 | |
1217 case V_OCSP_CERTSTATUS_GOOD: | |
1218 return NGX_OK; | |
1219 | |
1220 case V_OCSP_CERTSTATUS_REVOKED: | |
1221 *s = "certificate revoked"; | |
1222 break; | |
1223 | |
1224 default: /* V_OCSP_CERTSTATUS_UNKNOWN */ | |
1225 *s = "certificate status unknown"; | |
1226 } | |
1227 | |
1228 return NGX_DECLINED; | |
1229 } | |
1230 | |
1231 | |
1232 void | |
1233 ngx_ssl_ocsp_cleanup(ngx_connection_t *c) | |
1234 { | |
1235 ngx_ssl_ocsp_t *ocsp; | |
1236 | |
1237 ocsp = c->ssl->ocsp; | |
1238 if (ocsp == NULL) { | |
1239 return; | |
1240 } | |
1241 | |
1242 if (ocsp->ctx) { | |
1243 ngx_ssl_ocsp_done(ocsp->ctx); | |
1244 ocsp->ctx = NULL; | |
1245 } | |
1246 | |
1247 if (ocsp->certs) { | |
1248 sk_X509_pop_free(ocsp->certs, X509_free); | |
1249 ocsp->certs = NULL; | |
1250 } | |
1251 } | |
1252 | |
1253 | |
823 static ngx_ssl_ocsp_ctx_t * | 1254 static ngx_ssl_ocsp_ctx_t * |
824 ngx_ssl_ocsp_start(void) | 1255 ngx_ssl_ocsp_start(ngx_log_t *log) |
825 { | 1256 { |
826 ngx_log_t *log; | |
827 ngx_pool_t *pool; | 1257 ngx_pool_t *pool; |
828 ngx_ssl_ocsp_ctx_t *ctx; | 1258 ngx_ssl_ocsp_ctx_t *ctx; |
829 | 1259 |
830 pool = ngx_create_pool(2048, ngx_cycle->log); | 1260 pool = ngx_create_pool(2048, log); |
831 if (pool == NULL) { | 1261 if (pool == NULL) { |
832 return NULL; | 1262 return NULL; |
833 } | 1263 } |
834 | 1264 |
835 ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t)); | 1265 ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t)); |
883 ctx->handler(ctx); | 1313 ctx->handler(ctx); |
884 } | 1314 } |
885 | 1315 |
886 | 1316 |
887 static void | 1317 static void |
1318 ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx) | |
1319 { | |
1320 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
1321 "ssl ocsp next"); | |
1322 | |
1323 if (++ctx->naddr >= ctx->naddrs) { | |
1324 ngx_ssl_ocsp_error(ctx); | |
1325 return; | |
1326 } | |
1327 | |
1328 ctx->request->pos = ctx->request->start; | |
1329 | |
1330 if (ctx->response) { | |
1331 ctx->response->last = ctx->response->pos; | |
1332 } | |
1333 | |
1334 if (ctx->peer.connection) { | |
1335 ngx_close_connection(ctx->peer.connection); | |
1336 ctx->peer.connection = NULL; | |
1337 } | |
1338 | |
1339 ctx->state = 0; | |
1340 ctx->count = 0; | |
1341 ctx->done = 0; | |
1342 | |
1343 ngx_ssl_ocsp_connect(ctx); | |
1344 } | |
1345 | |
1346 | |
1347 static void | |
888 ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) | 1348 ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) |
889 { | 1349 { |
890 ngx_resolver_ctx_t *resolve, temp; | 1350 ngx_resolver_ctx_t *resolve, temp; |
891 | 1351 |
892 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | 1352 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, |
907 ngx_ssl_ocsp_error(ctx); | 1367 ngx_ssl_ocsp_error(ctx); |
908 return; | 1368 return; |
909 } | 1369 } |
910 | 1370 |
911 if (resolve == NGX_NO_RESOLVER) { | 1371 if (resolve == NGX_NO_RESOLVER) { |
1372 if (ctx->naddrs == 0) { | |
1373 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
1374 "no resolver defined to resolve %V", &ctx->host); | |
1375 | |
1376 ngx_ssl_ocsp_error(ctx); | |
1377 return; | |
1378 } | |
1379 | |
912 ngx_log_error(NGX_LOG_WARN, ctx->log, 0, | 1380 ngx_log_error(NGX_LOG_WARN, ctx->log, 0, |
913 "no resolver defined to resolve %V", &ctx->host); | 1381 "no resolver defined to resolve %V", &ctx->host); |
914 goto connect; | 1382 goto connect; |
915 } | 1383 } |
916 | 1384 |
1020 | 1488 |
1021 | 1489 |
1022 static void | 1490 static void |
1023 ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) | 1491 ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx) |
1024 { | 1492 { |
1025 ngx_int_t rc; | 1493 ngx_int_t rc; |
1026 | 1494 ngx_addr_t *addr; |
1027 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | 1495 |
1028 "ssl ocsp connect"); | 1496 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, |
1029 | 1497 "ssl ocsp connect %ui/%ui", ctx->naddr, ctx->naddrs); |
1030 /* TODO: use all ip addresses */ | 1498 |
1031 | 1499 addr = &ctx->addrs[ctx->naddr]; |
1032 ctx->peer.sockaddr = ctx->addrs[0].sockaddr; | 1500 |
1033 ctx->peer.socklen = ctx->addrs[0].socklen; | 1501 ctx->peer.sockaddr = addr->sockaddr; |
1034 ctx->peer.name = &ctx->addrs[0].name; | 1502 ctx->peer.socklen = addr->socklen; |
1503 ctx->peer.name = &addr->name; | |
1035 ctx->peer.get = ngx_event_get_peer; | 1504 ctx->peer.get = ngx_event_get_peer; |
1036 ctx->peer.log = ctx->log; | 1505 ctx->peer.log = ctx->log; |
1037 ctx->peer.log_error = NGX_ERROR_ERR; | 1506 ctx->peer.log_error = NGX_ERROR_ERR; |
1038 | 1507 |
1039 rc = ngx_event_connect_peer(&ctx->peer); | 1508 rc = ngx_event_connect_peer(&ctx->peer); |
1040 | 1509 |
1041 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | 1510 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, |
1042 "ssl ocsp connect peer done"); | 1511 "ssl ocsp connect peer done"); |
1043 | 1512 |
1044 if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { | 1513 if (rc == NGX_ERROR) { |
1045 ngx_ssl_ocsp_error(ctx); | 1514 ngx_ssl_ocsp_error(ctx); |
1046 return; | 1515 return; |
1047 } | 1516 } |
1048 | 1517 |
1518 if (rc == NGX_BUSY || rc == NGX_DECLINED) { | |
1519 ngx_ssl_ocsp_next(ctx); | |
1520 return; | |
1521 } | |
1522 | |
1049 ctx->peer.connection->data = ctx; | 1523 ctx->peer.connection->data = ctx; |
1050 ctx->peer.connection->pool = ctx->pool; | 1524 ctx->peer.connection->pool = ctx->pool; |
1051 | 1525 |
1052 ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler; | 1526 ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler; |
1053 ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler; | 1527 ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler; |
1054 | 1528 |
1055 ctx->process = ngx_ssl_ocsp_process_status_line; | 1529 ctx->process = ngx_ssl_ocsp_process_status_line; |
1056 | 1530 |
1057 ngx_add_timer(ctx->peer.connection->read, ctx->timeout); | 1531 if (ctx->timeout) { |
1058 ngx_add_timer(ctx->peer.connection->write, ctx->timeout); | 1532 ngx_add_timer(ctx->peer.connection->read, ctx->timeout); |
1533 ngx_add_timer(ctx->peer.connection->write, ctx->timeout); | |
1534 } | |
1059 | 1535 |
1060 if (rc == NGX_OK) { | 1536 if (rc == NGX_OK) { |
1061 ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); | 1537 ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); |
1062 return; | 1538 return; |
1063 } | 1539 } |
1078 "ssl ocsp write handler"); | 1554 "ssl ocsp write handler"); |
1079 | 1555 |
1080 if (wev->timedout) { | 1556 if (wev->timedout) { |
1081 ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, | 1557 ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, |
1082 "OCSP responder timed out"); | 1558 "OCSP responder timed out"); |
1083 ngx_ssl_ocsp_error(ctx); | 1559 ngx_ssl_ocsp_next(ctx); |
1084 return; | 1560 return; |
1085 } | 1561 } |
1086 | 1562 |
1087 size = ctx->request->last - ctx->request->pos; | 1563 size = ctx->request->last - ctx->request->pos; |
1088 | 1564 |
1089 n = ngx_send(c, ctx->request->pos, size); | 1565 n = ngx_send(c, ctx->request->pos, size); |
1090 | 1566 |
1091 if (n == NGX_ERROR) { | 1567 if (n == NGX_ERROR) { |
1092 ngx_ssl_ocsp_error(ctx); | 1568 ngx_ssl_ocsp_next(ctx); |
1093 return; | 1569 return; |
1094 } | 1570 } |
1095 | 1571 |
1096 if (n > 0) { | 1572 if (n > 0) { |
1097 ctx->request->pos += n; | 1573 ctx->request->pos += n; |
1109 | 1585 |
1110 return; | 1586 return; |
1111 } | 1587 } |
1112 } | 1588 } |
1113 | 1589 |
1114 if (!wev->timer_set) { | 1590 if (!wev->timer_set && ctx->timeout) { |
1115 ngx_add_timer(wev, ctx->timeout); | 1591 ngx_add_timer(wev, ctx->timeout); |
1116 } | 1592 } |
1117 } | 1593 } |
1118 | 1594 |
1119 | 1595 |
1132 "ssl ocsp read handler"); | 1608 "ssl ocsp read handler"); |
1133 | 1609 |
1134 if (rev->timedout) { | 1610 if (rev->timedout) { |
1135 ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, | 1611 ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, |
1136 "OCSP responder timed out"); | 1612 "OCSP responder timed out"); |
1137 ngx_ssl_ocsp_error(ctx); | 1613 ngx_ssl_ocsp_next(ctx); |
1138 return; | 1614 return; |
1139 } | 1615 } |
1140 | 1616 |
1141 if (ctx->response == NULL) { | 1617 if (ctx->response == NULL) { |
1142 ctx->response = ngx_create_temp_buf(ctx->pool, 16384); | 1618 ctx->response = ngx_create_temp_buf(ctx->pool, 16384); |
1156 ctx->response->last += n; | 1632 ctx->response->last += n; |
1157 | 1633 |
1158 rc = ctx->process(ctx); | 1634 rc = ctx->process(ctx); |
1159 | 1635 |
1160 if (rc == NGX_ERROR) { | 1636 if (rc == NGX_ERROR) { |
1161 ngx_ssl_ocsp_error(ctx); | 1637 ngx_ssl_ocsp_next(ctx); |
1162 return; | 1638 return; |
1163 } | 1639 } |
1164 | 1640 |
1165 continue; | 1641 continue; |
1166 } | 1642 } |
1187 } | 1663 } |
1188 | 1664 |
1189 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | 1665 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, |
1190 "OCSP responder prematurely closed connection"); | 1666 "OCSP responder prematurely closed connection"); |
1191 | 1667 |
1192 ngx_ssl_ocsp_error(ctx); | 1668 ngx_ssl_ocsp_next(ctx); |
1193 } | 1669 } |
1194 | 1670 |
1195 | 1671 |
1196 static void | 1672 static void |
1197 ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev) | 1673 ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev) |
1829 | 2305 |
1830 return NGX_AGAIN; | 2306 return NGX_AGAIN; |
1831 } | 2307 } |
1832 | 2308 |
1833 | 2309 |
2310 static ngx_int_t | |
2311 ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx) | |
2312 { | |
2313 int n; | |
2314 size_t len; | |
2315 X509_STORE *store; | |
2316 const u_char *p; | |
2317 OCSP_CERTID *id; | |
2318 OCSP_RESPONSE *ocsp; | |
2319 OCSP_BASICRESP *basic; | |
2320 ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; | |
2321 | |
2322 ocsp = NULL; | |
2323 basic = NULL; | |
2324 id = NULL; | |
2325 | |
2326 if (ctx->code != 200) { | |
2327 goto error; | |
2328 } | |
2329 | |
2330 /* check the response */ | |
2331 | |
2332 len = ctx->response->last - ctx->response->pos; | |
2333 p = ctx->response->pos; | |
2334 | |
2335 ocsp = d2i_OCSP_RESPONSE(NULL, &p, len); | |
2336 if (ocsp == NULL) { | |
2337 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
2338 "d2i_OCSP_RESPONSE() failed"); | |
2339 goto error; | |
2340 } | |
2341 | |
2342 n = OCSP_response_status(ocsp); | |
2343 | |
2344 if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { | |
2345 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
2346 "OCSP response not successful (%d: %s)", | |
2347 n, OCSP_response_status_str(n)); | |
2348 goto error; | |
2349 } | |
2350 | |
2351 basic = OCSP_response_get1_basic(ocsp); | |
2352 if (basic == NULL) { | |
2353 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
2354 "OCSP_response_get1_basic() failed"); | |
2355 goto error; | |
2356 } | |
2357 | |
2358 store = SSL_CTX_get_cert_store(ctx->ssl_ctx); | |
2359 if (store == NULL) { | |
2360 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
2361 "SSL_CTX_get_cert_store() failed"); | |
2362 goto error; | |
2363 } | |
2364 | |
2365 if (OCSP_basic_verify(basic, ctx->chain, store, ctx->flags) != 1) { | |
2366 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
2367 "OCSP_basic_verify() failed"); | |
2368 goto error; | |
2369 } | |
2370 | |
2371 id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer); | |
2372 if (id == NULL) { | |
2373 ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0, | |
2374 "OCSP_cert_to_id() failed"); | |
2375 goto error; | |
2376 } | |
2377 | |
2378 if (OCSP_resp_find_status(basic, id, &ctx->status, NULL, NULL, | |
2379 &thisupdate, &nextupdate) | |
2380 != 1) | |
2381 { | |
2382 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
2383 "certificate status not found in the OCSP response"); | |
2384 goto error; | |
2385 } | |
2386 | |
2387 if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { | |
2388 ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0, | |
2389 "OCSP_check_validity() failed"); | |
2390 goto error; | |
2391 } | |
2392 | |
2393 if (nextupdate) { | |
2394 ctx->valid = ngx_ssl_stapling_time(nextupdate); | |
2395 if (ctx->valid == (time_t) NGX_ERROR) { | |
2396 ngx_log_error(NGX_LOG_ERR, ctx->log, 0, | |
2397 "invalid nextUpdate time in certificate status"); | |
2398 goto error; | |
2399 } | |
2400 | |
2401 } else { | |
2402 ctx->valid = NGX_MAX_TIME_T_VALUE; | |
2403 } | |
2404 | |
2405 OCSP_CERTID_free(id); | |
2406 OCSP_BASICRESP_free(basic); | |
2407 OCSP_RESPONSE_free(ocsp); | |
2408 | |
2409 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
2410 "ssl ocsp response, %s, %uz", | |
2411 OCSP_cert_status_str(ctx->status), len); | |
2412 | |
2413 return NGX_OK; | |
2414 | |
2415 error: | |
2416 | |
2417 if (id) { | |
2418 OCSP_CERTID_free(id); | |
2419 } | |
2420 | |
2421 if (basic) { | |
2422 OCSP_BASICRESP_free(basic); | |
2423 } | |
2424 | |
2425 if (ocsp) { | |
2426 OCSP_RESPONSE_free(ocsp); | |
2427 } | |
2428 | |
2429 return NGX_ERROR; | |
2430 } | |
2431 | |
2432 | |
2433 ngx_int_t | |
2434 ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data) | |
2435 { | |
2436 size_t len; | |
2437 ngx_slab_pool_t *shpool; | |
2438 ngx_ssl_ocsp_cache_t *cache; | |
2439 | |
2440 if (data) { | |
2441 shm_zone->data = data; | |
2442 return NGX_OK; | |
2443 } | |
2444 | |
2445 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | |
2446 | |
2447 if (shm_zone->shm.exists) { | |
2448 shm_zone->data = shpool->data; | |
2449 return NGX_OK; | |
2450 } | |
2451 | |
2452 cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_ocsp_cache_t)); | |
2453 if (cache == NULL) { | |
2454 return NGX_ERROR; | |
2455 } | |
2456 | |
2457 shpool->data = cache; | |
2458 shm_zone->data = cache; | |
2459 | |
2460 ngx_rbtree_init(&cache->rbtree, &cache->sentinel, | |
2461 ngx_str_rbtree_insert_value); | |
2462 | |
2463 ngx_queue_init(&cache->expire_queue); | |
2464 | |
2465 len = sizeof(" in OCSP cache \"\"") + shm_zone->shm.name.len; | |
2466 | |
2467 shpool->log_ctx = ngx_slab_alloc(shpool, len); | |
2468 if (shpool->log_ctx == NULL) { | |
2469 return NGX_ERROR; | |
2470 } | |
2471 | |
2472 ngx_sprintf(shpool->log_ctx, " in OCSP cache \"%V\"%Z", | |
2473 &shm_zone->shm.name); | |
2474 | |
2475 shpool->log_nomem = 0; | |
2476 | |
2477 return NGX_OK; | |
2478 } | |
2479 | |
2480 | |
2481 static ngx_int_t | |
2482 ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx) | |
2483 { | |
2484 uint32_t hash; | |
2485 ngx_shm_zone_t *shm_zone; | |
2486 ngx_slab_pool_t *shpool; | |
2487 ngx_ssl_ocsp_cache_t *cache; | |
2488 ngx_ssl_ocsp_cache_node_t *node; | |
2489 | |
2490 shm_zone = ctx->shm_zone; | |
2491 | |
2492 if (shm_zone == NULL) { | |
2493 return NGX_DECLINED; | |
2494 } | |
2495 | |
2496 if (ngx_ssl_ocsp_create_key(ctx) != NGX_OK) { | |
2497 return NGX_ERROR; | |
2498 } | |
2499 | |
2500 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache lookup"); | |
2501 | |
2502 cache = shm_zone->data; | |
2503 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | |
2504 hash = ngx_hash_key(ctx->key.data, ctx->key.len); | |
2505 | |
2506 ngx_shmtx_lock(&shpool->mutex); | |
2507 | |
2508 node = (ngx_ssl_ocsp_cache_node_t *) | |
2509 ngx_str_rbtree_lookup(&cache->rbtree, &ctx->key, hash); | |
2510 | |
2511 if (node) { | |
2512 if (node->valid > ngx_time()) { | |
2513 ctx->status = node->status; | |
2514 ngx_shmtx_unlock(&shpool->mutex); | |
2515 | |
2516 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
2517 "ssl ocsp cache hit, %s", | |
2518 OCSP_cert_status_str(ctx->status)); | |
2519 | |
2520 return NGX_OK; | |
2521 } | |
2522 | |
2523 ngx_queue_remove(&node->queue); | |
2524 ngx_rbtree_delete(&cache->rbtree, &node->node.node); | |
2525 ngx_slab_free_locked(shpool, node); | |
2526 | |
2527 ngx_shmtx_unlock(&shpool->mutex); | |
2528 | |
2529 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
2530 "ssl ocsp cache expired"); | |
2531 | |
2532 return NGX_DECLINED; | |
2533 } | |
2534 | |
2535 ngx_shmtx_unlock(&shpool->mutex); | |
2536 | |
2537 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp cache miss"); | |
2538 | |
2539 return NGX_DECLINED; | |
2540 } | |
2541 | |
2542 | |
2543 static ngx_int_t | |
2544 ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx) | |
2545 { | |
2546 time_t now, valid; | |
2547 uint32_t hash; | |
2548 ngx_queue_t *q; | |
2549 ngx_shm_zone_t *shm_zone; | |
2550 ngx_slab_pool_t *shpool; | |
2551 ngx_ssl_ocsp_cache_t *cache; | |
2552 ngx_ssl_ocsp_cache_node_t *node; | |
2553 | |
2554 shm_zone = ctx->shm_zone; | |
2555 | |
2556 if (shm_zone == NULL) { | |
2557 return NGX_OK; | |
2558 } | |
2559 | |
2560 valid = ctx->valid; | |
2561 | |
2562 now = ngx_time(); | |
2563 | |
2564 if (valid < now) { | |
2565 return NGX_OK; | |
2566 } | |
2567 | |
2568 if (valid == NGX_MAX_TIME_T_VALUE) { | |
2569 valid = now + 3600; | |
2570 } | |
2571 | |
2572 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
2573 "ssl ocsp cache store, valid:%T", valid - now); | |
2574 | |
2575 cache = shm_zone->data; | |
2576 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | |
2577 hash = ngx_hash_key(ctx->key.data, ctx->key.len); | |
2578 | |
2579 ngx_shmtx_lock(&shpool->mutex); | |
2580 | |
2581 node = ngx_slab_calloc_locked(shpool, | |
2582 sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len); | |
2583 if (node == NULL) { | |
2584 | |
2585 if (!ngx_queue_empty(&cache->expire_queue)) { | |
2586 q = ngx_queue_last(&cache->expire_queue); | |
2587 node = ngx_queue_data(q, ngx_ssl_ocsp_cache_node_t, queue); | |
2588 | |
2589 ngx_rbtree_delete(&cache->rbtree, &node->node.node); | |
2590 ngx_queue_remove(q); | |
2591 ngx_slab_free_locked(shpool, node); | |
2592 | |
2593 node = ngx_slab_alloc_locked(shpool, | |
2594 sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len); | |
2595 } | |
2596 | |
2597 if (node == NULL) { | |
2598 ngx_shmtx_unlock(&shpool->mutex); | |
2599 ngx_log_error(NGX_LOG_ALERT, ctx->log, 0, | |
2600 "could not allocate new entry%s", shpool->log_ctx); | |
2601 return NGX_ERROR; | |
2602 } | |
2603 } | |
2604 | |
2605 node->node.str.len = ctx->key.len; | |
2606 node->node.str.data = (u_char *) node + sizeof(ngx_ssl_ocsp_cache_node_t); | |
2607 ngx_memcpy(node->node.str.data, ctx->key.data, ctx->key.len); | |
2608 node->node.node.key = hash; | |
2609 node->status = ctx->status; | |
2610 node->valid = valid; | |
2611 | |
2612 ngx_rbtree_insert(&cache->rbtree, &node->node.node); | |
2613 ngx_queue_insert_head(&cache->expire_queue, &node->queue); | |
2614 | |
2615 ngx_shmtx_unlock(&shpool->mutex); | |
2616 | |
2617 return NGX_OK; | |
2618 } | |
2619 | |
2620 | |
2621 static ngx_int_t | |
2622 ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx) | |
2623 { | |
2624 u_char *p; | |
2625 X509_NAME *name; | |
2626 ASN1_INTEGER *serial; | |
2627 | |
2628 p = ngx_pnalloc(ctx->pool, 60); | |
2629 if (p == NULL) { | |
2630 return NGX_ERROR; | |
2631 } | |
2632 | |
2633 ctx->key.data = p; | |
2634 ctx->key.len = 60; | |
2635 | |
2636 name = X509_get_subject_name(ctx->issuer); | |
2637 if (X509_NAME_digest(name, EVP_sha1(), p, NULL) == 0) { | |
2638 return NGX_ERROR; | |
2639 } | |
2640 | |
2641 p += 20; | |
2642 | |
2643 if (X509_pubkey_digest(ctx->issuer, EVP_sha1(), p, NULL) == 0) { | |
2644 return NGX_ERROR; | |
2645 } | |
2646 | |
2647 p += 20; | |
2648 | |
2649 serial = X509_get_serialNumber(ctx->cert); | |
2650 if (serial->length > 20) { | |
2651 return NGX_ERROR; | |
2652 } | |
2653 | |
2654 p = ngx_cpymem(p, serial->data, serial->length); | |
2655 ngx_memzero(p, 20 - serial->length); | |
2656 | |
2657 #if (NGX_DEBUG) | |
2658 { | |
2659 u_char buf[120]; | |
2660 | |
2661 ngx_hex_dump(buf, ctx->key.data, ctx->key.len); | |
2662 | |
2663 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0, | |
2664 "ssl ocsp key %*s", sizeof(buf), buf); | |
2665 } | |
2666 #endif | |
2667 | |
2668 return NGX_OK; | |
2669 } | |
2670 | |
2671 | |
1834 static u_char * | 2672 static u_char * |
1835 ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len) | 2673 ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len) |
1836 { | 2674 { |
1837 u_char *p; | 2675 u_char *p; |
1838 ngx_ssl_ocsp_ctx_t *ctx; | 2676 ngx_ssl_ocsp_ctx_t *ctx; |
1889 { | 2727 { |
1890 return NGX_OK; | 2728 return NGX_OK; |
1891 } | 2729 } |
1892 | 2730 |
1893 | 2731 |
2732 ngx_int_t | |
2733 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, | |
2734 ngx_uint_t depth, ngx_shm_zone_t *shm_zone) | |
2735 { | |
2736 ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, | |
2737 "\"ssl_ocsp\" is not supported on this platform"); | |
2738 | |
2739 return NGX_ERROR; | |
2740 } | |
2741 | |
2742 | |
2743 ngx_int_t | |
2744 ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, | |
2745 ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) | |
2746 { | |
2747 return NGX_OK; | |
2748 } | |
2749 | |
2750 | |
2751 ngx_int_t | |
2752 ngx_ssl_ocsp_validate(ngx_connection_t *c) | |
2753 { | |
2754 return NGX_OK; | |
2755 } | |
2756 | |
2757 | |
2758 ngx_int_t | |
2759 ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s) | |
2760 { | |
2761 return NGX_OK; | |
2762 } | |
2763 | |
2764 | |
2765 void | |
2766 ngx_ssl_ocsp_cleanup(ngx_connection_t *c) | |
2767 { | |
2768 } | |
2769 | |
2770 | |
2771 ngx_int_t | |
2772 ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data) | |
2773 { | |
2774 return NGX_OK; | |
2775 } | |
2776 | |
2777 | |
1894 #endif | 2778 #endif |