Mercurial > hg > nginx
comparison src/event/ngx_event_openssl_stapling.c @ 7654:b56f725dd4bb
OCSP: certificate status cache.
When enabled, certificate status is stored in cache and is used to validate
the certificate in future requests.
New directive ssl_ocsp_cache is added to configure the cache.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Fri, 22 May 2020 17:25:27 +0300 |
parents | 8409f9df6219 |
children | bd4d1b9db0ee |
comparison
equal
deleted
inserted
replaced
7653:8409f9df6219 | 7654:b56f725dd4bb |
---|---|
50 ngx_str_t host; | 50 ngx_str_t host; |
51 ngx_str_t uri; | 51 ngx_str_t uri; |
52 in_port_t port; | 52 in_port_t port; |
53 ngx_uint_t depth; | 53 ngx_uint_t depth; |
54 | 54 |
55 ngx_shm_zone_t *shm_zone; | |
56 | |
55 ngx_resolver_t *resolver; | 57 ngx_resolver_t *resolver; |
56 ngx_msec_t resolver_timeout; | 58 ngx_msec_t resolver_timeout; |
57 } ngx_ssl_ocsp_conf_t; | 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; | |
58 | 75 |
59 | 76 |
60 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; |
61 | 78 |
62 | 79 |
98 ngx_msec_t timeout; | 115 ngx_msec_t timeout; |
99 | 116 |
100 void (*handler)(ngx_ssl_ocsp_ctx_t *ctx); | 117 void (*handler)(ngx_ssl_ocsp_ctx_t *ctx); |
101 void *data; | 118 void *data; |
102 | 119 |
120 ngx_str_t key; | |
103 ngx_buf_t *request; | 121 ngx_buf_t *request; |
104 ngx_buf_t *response; | 122 ngx_buf_t *response; |
105 ngx_peer_connection_t peer; | 123 ngx_peer_connection_t peer; |
124 | |
125 ngx_shm_zone_t *shm_zone; | |
106 | 126 |
107 ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx); | 127 ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx); |
108 | 128 |
109 ngx_uint_t state; | 129 ngx_uint_t state; |
110 | 130 |
161 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); |
162 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); |
163 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); |
164 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); |
165 static ngx_int_t ngx_ssl_ocsp_verify(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); | |
166 | 190 |
167 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); |
168 | 192 |
169 | 193 |
170 ngx_int_t | 194 ngx_int_t |
741 } | 765 } |
742 | 766 |
743 | 767 |
744 ngx_int_t | 768 ngx_int_t |
745 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, | 769 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, |
746 ngx_uint_t depth) | 770 ngx_uint_t depth, ngx_shm_zone_t *shm_zone) |
747 { | 771 { |
748 ngx_url_t u; | 772 ngx_url_t u; |
749 ngx_ssl_ocsp_conf_t *ocf; | 773 ngx_ssl_ocsp_conf_t *ocf; |
750 | 774 |
751 ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t)); | 775 ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t)); |
752 if (ocf == NULL) { | 776 if (ocf == NULL) { |
753 return NGX_ERROR; | 777 return NGX_ERROR; |
754 } | 778 } |
755 | 779 |
756 ocf->depth = depth; | 780 ocf->depth = depth; |
781 ocf->shm_zone = shm_zone; | |
757 | 782 |
758 if (responder->len) { | 783 if (responder->len) { |
759 ngx_memzero(&u, sizeof(ngx_url_t)); | 784 ngx_memzero(&u, sizeof(ngx_url_t)); |
760 | 785 |
761 u.url = *responder; | 786 u.url = *responder; |
937 | 962 |
938 | 963 |
939 static void | 964 static void |
940 ngx_ssl_ocsp_validate_next(ngx_connection_t *c) | 965 ngx_ssl_ocsp_validate_next(ngx_connection_t *c) |
941 { | 966 { |
967 ngx_int_t rc; | |
942 ngx_uint_t n; | 968 ngx_uint_t n; |
943 ngx_ssl_ocsp_t *ocsp; | 969 ngx_ssl_ocsp_t *ocsp; |
944 ngx_ssl_ocsp_ctx_t *ctx; | 970 ngx_ssl_ocsp_ctx_t *ctx; |
945 ngx_ssl_ocsp_conf_t *ocf; | 971 ngx_ssl_ocsp_conf_t *ocf; |
946 | 972 |
975 ctx->resolver = ocf->resolver; | 1001 ctx->resolver = ocf->resolver; |
976 ctx->resolver_timeout = ocf->resolver_timeout; | 1002 ctx->resolver_timeout = ocf->resolver_timeout; |
977 | 1003 |
978 ctx->handler = ngx_ssl_ocsp_handler; | 1004 ctx->handler = ngx_ssl_ocsp_handler; |
979 ctx->data = c; | 1005 ctx->data = c; |
1006 | |
1007 ctx->shm_zone = ocf->shm_zone; | |
980 | 1008 |
981 ctx->addrs = ocf->addrs; | 1009 ctx->addrs = ocf->addrs; |
982 ctx->naddrs = ocf->naddrs; | 1010 ctx->naddrs = ocf->naddrs; |
983 ctx->host = ocf->host; | 1011 ctx->host = ocf->host; |
984 ctx->uri = ocf->uri; | 1012 ctx->uri = ocf->uri; |
992 ngx_str_set(&ctx->uri, "/"); | 1020 ngx_str_set(&ctx->uri, "/"); |
993 } | 1021 } |
994 | 1022 |
995 ocsp->ncert++; | 1023 ocsp->ncert++; |
996 | 1024 |
997 break; | 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); | |
998 } | 1047 } |
999 | 1048 |
1000 ngx_ssl_ocsp_request(ctx); | 1049 ngx_ssl_ocsp_request(ctx); |
1001 return; | 1050 return; |
1002 | 1051 |
1021 c = ctx->data; | 1070 c = ctx->data; |
1022 ocsp = c->ssl->ocsp; | 1071 ocsp = c->ssl->ocsp; |
1023 ocsp->ctx = NULL; | 1072 ocsp->ctx = NULL; |
1024 | 1073 |
1025 rc = ngx_ssl_ocsp_verify(ctx); | 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); | |
1026 if (rc != NGX_OK) { | 1082 if (rc != NGX_OK) { |
1027 ocsp->status = rc; | 1083 ocsp->status = rc; |
1028 ngx_ssl_ocsp_done(ctx); | 1084 ngx_ssl_ocsp_done(ctx); |
1029 goto done; | 1085 goto done; |
1030 } | 1086 } |
2372 | 2428 |
2373 return NGX_ERROR; | 2429 return NGX_ERROR; |
2374 } | 2430 } |
2375 | 2431 |
2376 | 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", 120, buf); | |
2665 } | |
2666 #endif | |
2667 | |
2668 return NGX_OK; | |
2669 } | |
2670 | |
2671 | |
2377 static u_char * | 2672 static u_char * |
2378 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) |
2379 { | 2674 { |
2380 u_char *p; | 2675 u_char *p; |
2381 ngx_ssl_ocsp_ctx_t *ctx; | 2676 ngx_ssl_ocsp_ctx_t *ctx; |
2434 } | 2729 } |
2435 | 2730 |
2436 | 2731 |
2437 ngx_int_t | 2732 ngx_int_t |
2438 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, | 2733 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder, |
2439 ngx_uint_t depth) | 2734 ngx_uint_t depth, ngx_shm_zone_t *shm_zone) |
2440 { | 2735 { |
2441 ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, | 2736 ngx_log_error(NGX_LOG_EMERG, ssl->log, 0, |
2442 "\"ssl_ocsp\" is not supported on this platform"); | 2737 "\"ssl_ocsp\" is not supported on this platform"); |
2443 | 2738 |
2444 return NGX_ERROR; | 2739 return NGX_ERROR; |
2471 ngx_ssl_ocsp_cleanup(ngx_connection_t *c) | 2766 ngx_ssl_ocsp_cleanup(ngx_connection_t *c) |
2472 { | 2767 { |
2473 } | 2768 } |
2474 | 2769 |
2475 | 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 | |
2476 #endif | 2778 #endif |