[nginx] SSL: caching CRLs.

noreply at nginx.com noreply at nginx.com
Tue Oct 1 14:00:02 UTC 2024


details:   https://github.com/nginx/nginx/commit/61314518de74fcb3af954ea6e6cb2820307676d0
branches:  master
commit:    61314518de74fcb3af954ea6e6cb2820307676d0
user:      Sergey Kandaurov <pluknet at nginx.com>
date:      Mon, 9 Sep 2024 19:05:31 +0400
description:
SSL: caching CRLs.

Based on previous work by Mini Hawthorne.

---
 src/event/ngx_event_openssl.c       |  72 ++++++++++++++++++------
 src/event/ngx_event_openssl.h       |   1 +
 src/event/ngx_event_openssl_cache.c | 108 +++++++++++++++++++++++++++++++++++-
 3 files changed, 164 insertions(+), 17 deletions(-)

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 1a6ca18ad..72189e1a4 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -18,6 +18,7 @@ typedef struct {
 } ngx_openssl_conf_t;
 
 
+static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void);
 static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
 static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
     int ret);
@@ -739,17 +740,16 @@ ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
 ngx_int_t
 ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
 {
-    X509_STORE   *store;
-    X509_LOOKUP  *lookup;
+    int                  n, i;
+    char                *err;
+    X509_CRL            *x509;
+    X509_STORE          *store;
+    STACK_OF(X509_CRL)  *chain;
 
     if (crl->len == 0) {
         return NGX_OK;
     }
 
-    if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {
-        return NGX_ERROR;
-    }
-
     store = SSL_CTX_get_cert_store(ssl->ctx);
 
     if (store == NULL) {
@@ -758,22 +758,36 @@ ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
         return NGX_ERROR;
     }
 
-    lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+    chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CRL, &err, crl, NULL);
+    if (chain == NULL) {
+        if (err != NULL) {
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "cannot load CRL \"%s\": %s", crl->data, err);
+        }
 
-    if (lookup == NULL) {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "X509_STORE_add_lookup() failed");
         return NGX_ERROR;
     }
 
-    if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)
-        == 0)
-    {
-        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
-                      "X509_LOOKUP_load_file(\"%s\") failed", crl->data);
-        return NGX_ERROR;
+    n = sk_X509_CRL_num(chain);
+
+    for (i = 0; i < n; i++) {
+        x509 = sk_X509_CRL_value(chain, i);
+
+        if (X509_STORE_add_crl(store, x509) != 1) {
+
+            if (ngx_ssl_cert_already_in_hash()) {
+                continue;
+            }
+
+            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                          "X509_STORE_add_crl(\"%s\") failed", crl->data);
+            sk_X509_CRL_pop_free(chain, X509_CRL_free);
+            return NGX_ERROR;
+        }
     }
 
+    sk_X509_CRL_pop_free(chain, X509_CRL_free);
+
     X509_STORE_set_flags(store,
                          X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
 
@@ -781,6 +795,32 @@ ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
 }
 
 
+static ngx_inline ngx_int_t
+ngx_ssl_cert_already_in_hash(void)
+{
+#if !(OPENSSL_VERSION_NUMBER >= 0x1010009fL \
+      || LIBRESSL_VERSION_NUMBER >= 0x3050000fL)
+    u_long  error;
+
+    /*
+     * OpenSSL prior to 1.1.0i doesn't ignore duplicate certificate entries,
+     * see https://github.com/openssl/openssl/commit/c0452248
+     */
+
+    error = ERR_peek_last_error();
+
+    if (ERR_GET_LIB(error) == ERR_LIB_X509
+        && ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE)
+    {
+        ERR_clear_error();
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+
+
 static int
 ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
 {
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 5e36fb5e0..e2f0e5821 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -195,6 +195,7 @@ typedef struct {
 
 #define NGX_SSL_CACHE_CERT  0
 #define NGX_SSL_CACHE_PKEY  1
+#define NGX_SSL_CACHE_CRL   2
 
 
 ngx_int_t ngx_ssl_init(ngx_log_t *log);
diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
index f5bb9a241..b2615d2bf 100644
--- a/src/event/ngx_event_openssl_cache.c
+++ b/src/event/ngx_event_openssl_cache.c
@@ -65,6 +65,11 @@ static int ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag,
 static void ngx_ssl_cache_pkey_free(void *data);
 static void *ngx_ssl_cache_pkey_ref(char **err, void *data);
 
+static void *ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err,
+    void *data);
+static void ngx_ssl_cache_crl_free(void *data);
+static void *ngx_ssl_cache_crl_ref(char **err, void *data);
+
 static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err);
 
 static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle);
@@ -107,6 +112,11 @@ static ngx_ssl_cache_type_t  ngx_ssl_cache_types[] = {
     { ngx_ssl_cache_pkey_create,
       ngx_ssl_cache_pkey_free,
       ngx_ssl_cache_pkey_ref },
+
+    /* NGX_SSL_CACHE_CRL */
+    { ngx_ssl_cache_crl_create,
+      ngx_ssl_cache_crl_free,
+      ngx_ssl_cache_crl_ref },
 };
 
 
@@ -177,7 +187,9 @@ static ngx_int_t
 ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path,
     ngx_ssl_cache_key_t *id)
 {
-    if (ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) {
+    if (index <= NGX_SSL_CACHE_PKEY
+        && ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0)
+    {
         id->type = NGX_SSL_CACHE_DATA;
 
     } else if (index == NGX_SSL_CACHE_PKEY
@@ -517,6 +529,100 @@ ngx_ssl_cache_pkey_ref(char **err, void *data)
 }
 
 
+static void *
+ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err, void *data)
+{
+    BIO                 *bio;
+    u_long               n;
+    X509_CRL            *x509;
+    STACK_OF(X509_CRL)  *chain;
+
+    chain = sk_X509_CRL_new_null();
+    if (chain == NULL) {
+        *err = "sk_X509_CRL_new_null() failed";
+        return NULL;
+    }
+
+    bio = ngx_ssl_cache_create_bio(id, err);
+    if (bio == NULL) {
+        sk_X509_CRL_pop_free(chain, X509_CRL_free);
+        return NULL;
+    }
+
+    for ( ;; ) {
+
+        x509 = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
+        if (x509 == NULL) {
+            n = ERR_peek_last_error();
+
+            if (ERR_GET_LIB(n) == ERR_LIB_PEM
+                && ERR_GET_REASON(n) == PEM_R_NO_START_LINE
+                && sk_X509_CRL_num(chain) > 0)
+            {
+                /* end of file */
+                ERR_clear_error();
+                break;
+            }
+
+            /* some real error */
+
+            *err = "PEM_read_bio_X509_CRL() failed";
+            BIO_free(bio);
+            sk_X509_CRL_pop_free(chain, X509_CRL_free);
+            return NULL;
+        }
+
+        if (sk_X509_CRL_push(chain, x509) == 0) {
+            *err = "sk_X509_CRL_push() failed";
+            BIO_free(bio);
+            X509_CRL_free(x509);
+            sk_X509_CRL_pop_free(chain, X509_CRL_free);
+            return NULL;
+        }
+    }
+
+    BIO_free(bio);
+
+    return chain;
+}
+
+
+static void
+ngx_ssl_cache_crl_free(void *data)
+{
+    sk_X509_CRL_pop_free(data, X509_CRL_free);
+}
+
+
+static void *
+ngx_ssl_cache_crl_ref(char **err, void *data)
+{
+    int                  n, i;
+    X509_CRL            *x509;
+    STACK_OF(X509_CRL)  *chain;
+
+    chain = sk_X509_CRL_dup(data);
+    if (chain == NULL) {
+        *err = "sk_X509_CRL_dup() failed";
+        return NULL;
+    }
+
+    n = sk_X509_CRL_num(chain);
+
+    for (i = 0; i < n; i++) {
+        x509 = sk_X509_CRL_value(chain, i);
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+        X509_CRL_up_ref(x509);
+#else
+        CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509_CRL);
+#endif
+    }
+
+    return chain;
+}
+
+
 static BIO *
 ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err)
 {


More information about the nginx-devel mailing list