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