From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching CRLs. Message-ID: <20241001140002.9C9A34777F@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/61314518de74fcb3af954ea6e6cb2820307676d0 branches: master commit: 61314518de74fcb3af954ea6e6cb2820307676d0 user: Sergey Kandaurov 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) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching certificates. Message-ID: <20241001140002.8F0A14777D@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/78ed123e71aab17ffecfa0b2b27a349cfb4b2502 branches: master commit: 78ed123e71aab17ffecfa0b2b27a349cfb4b2502 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:03:52 +0400 description: SSL: caching certificates. Certificate chains are now loaded once. The certificate cache provides each chain as a unique stack of reference counted elements. This shallow copy is required because OpenSSL stacks aren't reference counted. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 105 ++--------------------- src/event/ngx_event_openssl.h | 3 + src/event/ngx_event_openssl_cache.c | 167 ++++++++++++++++++++++++++++++++++-- 3 files changed, 173 insertions(+), 102 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 3ed003062..018d03016 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -18,8 +18,6 @@ typedef struct { } ngx_openssl_conf_t; -static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, - ngx_str_t *cert, STACK_OF(X509) **chain); static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, ngx_str_t *key, ngx_array_t *passwords); static int ngx_ssl_password_callback(char *buf, int size, int rwflag, @@ -449,8 +447,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, EVP_PKEY *pkey; STACK_OF(X509) *chain; - x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain); - if (x509 == NULL) { + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT, &err, cert, NULL); + if (chain == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "cannot load certificate \"%s\": %s", @@ -460,6 +458,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } + x509 = sk_X509_shift(chain); + if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_use_certificate(\"%s\") failed", cert->data); @@ -570,8 +570,9 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, EVP_PKEY *pkey; STACK_OF(X509) *chain; - x509 = ngx_ssl_load_certificate(pool, &err, cert, &chain); - if (x509 == NULL) { + chain = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_CERT, &err, + cert, NULL); + if (chain == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "cannot load certificate \"%s\": %s", @@ -581,6 +582,8 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, return NGX_ERROR; } + x509 = sk_X509_shift(chain); + if (SSL_use_certificate(c->ssl->connection, x509) == 0) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_use_certificate(\"%s\") failed", cert->data); @@ -632,96 +635,6 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, } -static X509 * -ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert, - STACK_OF(X509) **chain) -{ - BIO *bio; - X509 *x509, *temp; - u_long n; - - if (ngx_strncmp(cert->data, "data:", sizeof("data:") - 1) == 0) { - - bio = BIO_new_mem_buf(cert->data + sizeof("data:") - 1, - cert->len - (sizeof("data:") - 1)); - if (bio == NULL) { - *err = "BIO_new_mem_buf() failed"; - return NULL; - } - - } else { - - if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert) - != NGX_OK) - { - *err = NULL; - return NULL; - } - - bio = BIO_new_file((char *) cert->data, "r"); - if (bio == NULL) { - *err = "BIO_new_file() failed"; - return NULL; - } - } - - /* certificate itself */ - - x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); - if (x509 == NULL) { - *err = "PEM_read_bio_X509_AUX() failed"; - BIO_free(bio); - return NULL; - } - - /* rest of the chain */ - - *chain = sk_X509_new_null(); - if (*chain == NULL) { - *err = "sk_X509_new_null() failed"; - BIO_free(bio); - X509_free(x509); - return NULL; - } - - for ( ;; ) { - - temp = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (temp == NULL) { - n = ERR_peek_last_error(); - - if (ERR_GET_LIB(n) == ERR_LIB_PEM - && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) - { - /* end of file */ - ERR_clear_error(); - break; - } - - /* some real error */ - - *err = "PEM_read_bio_X509() failed"; - BIO_free(bio); - X509_free(x509); - sk_X509_pop_free(*chain, X509_free); - return NULL; - } - - if (sk_X509_push(*chain, temp) == 0) { - *err = "sk_X509_push() failed"; - BIO_free(bio); - X509_free(x509); - sk_X509_pop_free(*chain, X509_free); - return NULL; - } - } - - BIO_free(bio); - - return x509; -} - - static EVP_PKEY * ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, ngx_str_t *key, ngx_array_t *passwords) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index fa2282ce8..addc7b4d3 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -193,6 +193,9 @@ typedef struct { #define NGX_SSL_BUFSIZE 16384 +#define NGX_SSL_CACHE_CERT 0 + + ngx_int_t ngx_ssl_init(ngx_log_t *log); ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index 98867de50..20b87b79a 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -10,6 +10,7 @@ #define NGX_SSL_CACHE_PATH 0 +#define NGX_SSL_CACHE_DATA 1 typedef struct { @@ -51,6 +52,13 @@ static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash); +static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, + void *data); +static void ngx_ssl_cache_cert_free(void *data); +static void *ngx_ssl_cache_cert_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); static void ngx_ssl_cache_cleanup(void *data); static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp, @@ -82,6 +90,10 @@ ngx_module_t ngx_openssl_cache_module = { static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { + /* NGX_SSL_CACHE_CERT */ + { ngx_ssl_cache_cert_create, + ngx_ssl_cache_cert_free, + ngx_ssl_cache_cert_ref }, }; @@ -152,13 +164,18 @@ 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_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) - != NGX_OK) - { - return NGX_ERROR; - } + if (ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) { + id->type = NGX_SSL_CACHE_DATA; + + } else { + if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) + != NGX_OK) + { + return NGX_ERROR; + } - id->type = NGX_SSL_CACHE_PATH; + id->type = NGX_SSL_CACHE_PATH; + } id->len = path->len; id->data = path->data; @@ -219,6 +236,144 @@ ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type, } +static void * +ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + BIO *bio; + X509 *x509; + u_long n; + STACK_OF(X509) *chain; + + chain = sk_X509_new_null(); + if (chain == NULL) { + *err = "sk_X509_new_null() failed"; + return NULL; + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + /* certificate itself */ + + x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (x509 == NULL) { + *err = "PEM_read_bio_X509_AUX() failed"; + BIO_free(bio); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + BIO_free(bio); + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + /* rest of the chain */ + + for ( ;; ) { + + x509 = PEM_read_bio_X509(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) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509() failed"; + BIO_free(bio); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + BIO_free(bio); + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + +static void +ngx_ssl_cache_cert_free(void *data) +{ + sk_X509_pop_free(data, X509_free); +} + + +static void * +ngx_ssl_cache_cert_ref(char **err, void *data) +{ + int n, i; + X509 *x509; + STACK_OF(X509) *chain; + + chain = sk_X509_dup(data); + if (chain == NULL) { + *err = "sk_X509_dup() failed"; + return NULL; + } + + n = sk_X509_num(chain); + + for (i = 0; i < n; i++) { + x509 = sk_X509_value(chain, i); + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + X509_up_ref(x509); +#else + CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509); +#endif + } + + return chain; +} + + +static BIO * +ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) +{ + BIO *bio; + + if (id->type == NGX_SSL_CACHE_DATA) { + + bio = BIO_new_mem_buf(id->data + sizeof("data:") - 1, + id->len - (sizeof("data:") - 1)); + if (bio == NULL) { + *err = "BIO_new_mem_buf() failed"; + } + + return bio; + } + + bio = BIO_new_file((char *) id->data, "r"); + if (bio == NULL) { + *err = "BIO_new_file() failed"; + } + + return bio; +} + + static void * ngx_openssl_cache_create_conf(ngx_cycle_t *cycle) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching certificate keys. Message-ID: <20241001140002.9625F4777E@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/7ea2fb6cb197925c7c0e35def9ece12d11b09bb9 branches: master commit: 7ea2fb6cb197925c7c0e35def9ece12d11b09bb9 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:04:18 +0400 description: SSL: caching certificate keys. EVP_KEY objects are a reference-counted container for key material, shallow copies and OpenSSL stack management aren't needed as with certificates. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 154 +-------------------------------- src/event/ngx_event_openssl.h | 1 + src/event/ngx_event_openssl_cache.c | 168 ++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 151 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 018d03016..1a6ca18ad 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -18,10 +18,6 @@ typedef struct { } ngx_openssl_conf_t; -static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, - ngx_str_t *key, ngx_array_t *passwords); -static int ngx_ssl_password_callback(char *buf, int size, int rwflag, - void *userdata); 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); @@ -537,7 +533,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } #endif - pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords); + pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY, &err, key, passwords); if (pkey == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, @@ -611,7 +607,8 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, #endif - pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords); + pkey = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_PKEY, &err, + key, passwords); if (pkey == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_ERR, c->log, 0, @@ -635,151 +632,6 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool, } -static EVP_PKEY * -ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err, - ngx_str_t *key, ngx_array_t *passwords) -{ - BIO *bio; - EVP_PKEY *pkey; - ngx_str_t *pwd; - ngx_uint_t tries; - pem_password_cb *cb; - - if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) { - -#ifndef OPENSSL_NO_ENGINE - - u_char *p, *last; - ENGINE *engine; - - p = key->data + sizeof("engine:") - 1; - last = (u_char *) ngx_strchr(p, ':'); - - if (last == NULL) { - *err = "invalid syntax"; - return NULL; - } - - *last = '\0'; - - engine = ENGINE_by_id((char *) p); - - *last++ = ':'; - - if (engine == NULL) { - *err = "ENGINE_by_id() failed"; - return NULL; - } - - pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0); - - if (pkey == NULL) { - *err = "ENGINE_load_private_key() failed"; - ENGINE_free(engine); - return NULL; - } - - ENGINE_free(engine); - - return pkey; - -#else - - *err = "loading \"engine:...\" certificate keys is not supported"; - return NULL; - -#endif - } - - if (ngx_strncmp(key->data, "data:", sizeof("data:") - 1) == 0) { - - bio = BIO_new_mem_buf(key->data + sizeof("data:") - 1, - key->len - (sizeof("data:") - 1)); - if (bio == NULL) { - *err = "BIO_new_mem_buf() failed"; - return NULL; - } - - } else { - - if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key) - != NGX_OK) - { - *err = NULL; - return NULL; - } - - bio = BIO_new_file((char *) key->data, "r"); - if (bio == NULL) { - *err = "BIO_new_file() failed"; - return NULL; - } - } - - if (passwords) { - tries = passwords->nelts; - pwd = passwords->elts; - cb = ngx_ssl_password_callback; - - } else { - tries = 1; - pwd = NULL; - cb = NULL; - } - - for ( ;; ) { - - pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd); - if (pkey != NULL) { - break; - } - - if (tries-- > 1) { - ERR_clear_error(); - (void) BIO_reset(bio); - pwd++; - continue; - } - - *err = "PEM_read_bio_PrivateKey() failed"; - BIO_free(bio); - return NULL; - } - - BIO_free(bio); - - return pkey; -} - - -static int -ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata) -{ - ngx_str_t *pwd = userdata; - - if (rwflag) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_ssl_password_callback() is called for encryption"); - return 0; - } - - if (pwd == NULL) { - return 0; - } - - if (pwd->len > (size_t) size) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, - "password is truncated to %d bytes", size); - } else { - size = pwd->len; - } - - ngx_memcpy(buf, pwd->data, size); - - return size; -} - - ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index addc7b4d3..5e36fb5e0 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -194,6 +194,7 @@ typedef struct { #define NGX_SSL_CACHE_CERT 0 +#define NGX_SSL_CACHE_PKEY 1 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 20b87b79a..f5bb9a241 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -11,6 +11,7 @@ #define NGX_SSL_CACHE_PATH 0 #define NGX_SSL_CACHE_DATA 1 +#define NGX_SSL_CACHE_ENGINE 2 typedef struct { @@ -57,6 +58,13 @@ static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, static void ngx_ssl_cache_cert_free(void *data); static void *ngx_ssl_cache_cert_ref(char **err, void *data); +static void *ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, + void *data); +static int ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag, + void *userdata); +static void ngx_ssl_cache_pkey_free(void *data); +static void *ngx_ssl_cache_pkey_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); @@ -94,6 +102,11 @@ static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { { ngx_ssl_cache_cert_create, ngx_ssl_cache_cert_free, ngx_ssl_cache_cert_ref }, + + /* NGX_SSL_CACHE_PKEY */ + { ngx_ssl_cache_pkey_create, + ngx_ssl_cache_pkey_free, + ngx_ssl_cache_pkey_ref }, }; @@ -167,6 +180,11 @@ ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path, if (ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0) { id->type = NGX_SSL_CACHE_DATA; + } else if (index == NGX_SSL_CACHE_PKEY + && ngx_strncmp(path->data, "engine:", sizeof("engine:") - 1) == 0) + { + id->type = NGX_SSL_CACHE_ENGINE; + } else { if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) != NGX_OK) @@ -349,6 +367,156 @@ ngx_ssl_cache_cert_ref(char **err, void *data) } +static void * +ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + ngx_array_t *passwords = data; + + BIO *bio; + EVP_PKEY *pkey; + ngx_str_t *pwd; + ngx_uint_t tries; + pem_password_cb *cb; + + if (id->type == NGX_SSL_CACHE_ENGINE) { + +#ifndef OPENSSL_NO_ENGINE + + u_char *p, *last; + ENGINE *engine; + + p = id->data + sizeof("engine:") - 1; + last = (u_char *) ngx_strchr(p, ':'); + + if (last == NULL) { + *err = "invalid syntax"; + return NULL; + } + + *last = '\0'; + + engine = ENGINE_by_id((char *) p); + + *last++ = ':'; + + if (engine == NULL) { + *err = "ENGINE_by_id() failed"; + return NULL; + } + + pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0); + + if (pkey == NULL) { + *err = "ENGINE_load_private_key() failed"; + ENGINE_free(engine); + return NULL; + } + + ENGINE_free(engine); + + return pkey; + +#else + + *err = "loading \"engine:...\" certificate keys is not supported"; + return NULL; + +#endif + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + return NULL; + } + + if (passwords) { + tries = passwords->nelts; + pwd = passwords->elts; + cb = ngx_ssl_cache_pkey_password_callback; + + } else { + tries = 1; + pwd = NULL; + cb = NULL; + } + + for ( ;; ) { + + pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd); + if (pkey != NULL) { + break; + } + + if (tries-- > 1) { + ERR_clear_error(); + (void) BIO_reset(bio); + pwd++; + continue; + } + + *err = "PEM_read_bio_PrivateKey() failed"; + BIO_free(bio); + return NULL; + } + + BIO_free(bio); + + return pkey; +} + + +static int +ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag, + void *userdata) +{ + ngx_str_t *pwd = userdata; + + if (rwflag) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "ngx_ssl_cache_pkey_password_callback() is called " + "for encryption"); + return 0; + } + + if (pwd == NULL) { + return 0; + } + + if (pwd->len > (size_t) size) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "password is truncated to %d bytes", size); + } else { + size = pwd->len; + } + + ngx_memcpy(buf, pwd->data, size); + + return size; +} + + +static void +ngx_ssl_cache_pkey_free(void *data) +{ + EVP_PKEY_free(data); +} + + +static void * +ngx_ssl_cache_pkey_ref(char **err, void *data) +{ + EVP_PKEY *pkey = data; + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + EVP_PKEY_up_ref(pkey); +#else + CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); +#endif + + return data; +} + + static BIO * ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: moved certificate storage out of exdata. Message-ID: <20241001140002.83DBA4777B@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/f36ff3550a7271a618edb119f064dddd086cc380 branches: master commit: f36ff3550a7271a618edb119f064dddd086cc380 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:02:27 +0400 description: SSL: moved certificate storage out of exdata. Instead of cross-linking the objects using exdata, pointers to configured certificates are now stored in ngx_ssl_t, and OCSP staples are now accessed with rbtree in it. This allows sharing these objects between SSL contexts. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 78 +++++++++++++--------------------- src/event/ngx_event_openssl.h | 9 ++-- src/event/ngx_event_openssl_stapling.c | 68 ++++++++++++++++++++++------- 3 files changed, 89 insertions(+), 66 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 8d1f5695c..3ed003062 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -131,10 +131,8 @@ int ngx_ssl_server_conf_index; int ngx_ssl_session_cache_index; int ngx_ssl_ticket_keys_index; int ngx_ssl_ocsp_index; -int ngx_ssl_certificate_index; -int ngx_ssl_next_certificate_index; +int ngx_ssl_index; int ngx_ssl_certificate_name_index; -int ngx_ssl_stapling_index; ngx_int_t @@ -258,21 +256,14 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } - ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, - NULL); - if (ngx_ssl_certificate_index == -1) { + ngx_ssl_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_ssl_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } - ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL, - NULL); - if (ngx_ssl_next_certificate_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); - return NGX_ERROR; - } - ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); @@ -281,13 +272,6 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } - ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); - - if (ngx_ssl_stapling_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); - return NGX_ERROR; - } - return NGX_OK; } @@ -308,12 +292,15 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) return NGX_ERROR; } - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) { + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_index, ssl) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_set_ex_data() failed"); return NGX_ERROR; } + ngx_rbtree_init(&ssl->staple_rbtree, &ssl->staple_sentinel, + ngx_rbtree_insert_value); + ssl->buffer_size = NGX_SSL_BUFSIZE; /* client side options */ @@ -458,7 +445,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords) { char *err; - X509 *x509; + X509 *x509, **elm; EVP_PKEY *pkey; STACK_OF(X509) *chain; @@ -490,29 +477,29 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_ERROR; } - if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index, - SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index)) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); - X509_free(x509); - sk_X509_pop_free(chain, X509_free); - return NGX_ERROR; + if (ssl->certs.elts == NULL) { + if (ngx_array_init(&ssl->certs, cf->pool, 1, sizeof(X509 *)) + != NGX_OK) + { + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } } - if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_set_ex_data() failed"); + elm = ngx_array_push(&ssl->certs); + if (elm == NULL) { X509_free(x509); sk_X509_pop_free(chain, X509_free); return NGX_ERROR; } + *elm = x509; + /* * Note that x509 is not freed here, but will be instead freed in * ngx_ssl_cleanup_ctx(). This is because we need to preserve all - * certificates to be able to iterate all of them through exdata - * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index), + * certificates to be able to iterate all of them through ssl->certs, * while OpenSSL can free a certificate if it is replaced with another * certificate of the same type. */ @@ -3820,10 +3807,9 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, goto failed; } - for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); - cert; - cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) - { + for (k = 0; k < ssl->certs.nelts; k++) { + cert = ((X509 **) ssl->certs.elts)[k]; + if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_digest() failed"); @@ -3837,9 +3823,7 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, } } - if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL - && certificates != NULL) - { + if (ssl->certs.nelts == 0 && certificates != NULL) { /* * If certificates are loaded dynamically, we use certificate * names as specified in the configuration (with variables). @@ -4851,14 +4835,12 @@ ngx_ssl_cleanup_ctx(void *data) { ngx_ssl_t *ssl = data; - X509 *cert, *next; - - cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + X509 *cert; + ngx_uint_t i; - while (cert) { - next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index); + for (i = 0; i < ssl->certs.nelts; i++) { + cert = ((X509 **) ssl->certs.elts)[i]; X509_free(cert); - cert = next; } SSL_CTX_free(ssl->ctx); diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index ebb2c35bf..41566422a 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -90,6 +90,11 @@ struct ngx_ssl_s { SSL_CTX *ctx; ngx_log_t *log; size_t buffer_size; + + ngx_array_t certs; + + ngx_rbtree_t staple_rbtree; + ngx_rbtree_node_t staple_sentinel; }; @@ -330,10 +335,8 @@ extern int ngx_ssl_server_conf_index; extern int ngx_ssl_session_cache_index; extern int ngx_ssl_ticket_keys_index; extern int ngx_ssl_ocsp_index; -extern int ngx_ssl_certificate_index; -extern int ngx_ssl_next_certificate_index; +extern int ngx_ssl_index; extern int ngx_ssl_certificate_name_index; -extern int ngx_ssl_stapling_index; #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c index e9bb8354e..a0a8031c7 100644 --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -15,6 +15,8 @@ typedef struct { + ngx_rbtree_node_t node; + ngx_str_t staple; ngx_msec_t timeout; @@ -154,6 +156,7 @@ static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data); +static ngx_ssl_stapling_t *ngx_ssl_stapling_lookup(ngx_ssl_t *ssl, X509 *cert); static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple); static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx); @@ -195,12 +198,12 @@ ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify) { - X509 *cert; + X509 *cert; + ngx_uint_t k; + + for (k = 0; k < ssl->certs.nelts; k++) { + cert = ((X509 **) ssl->certs.elts)[k]; - for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); - cert; - cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) - { if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify) != NGX_OK) { @@ -235,10 +238,9 @@ ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert, cln->handler = ngx_ssl_stapling_cleanup; cln->data = staple; - if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed"); - return NGX_ERROR; - } + staple->node.key = (ngx_rbtree_key_t) cert; + + ngx_rbtree_insert(&ssl->staple_rbtree, &staple->node); #ifdef SSL_CTRL_SELECT_CURRENT_CERT /* OpenSSL 1.0.2+ */ @@ -545,14 +547,21 @@ ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_resolver_t *resolver, ngx_msec_t resolver_timeout) { - X509 *cert; + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; ngx_ssl_stapling_t *staple; - for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); - cert; - cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index)) + tree = &ssl->staple_rbtree; + + if (tree->root == tree->sentinel) { + return NGX_OK; + } + + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) { - staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + staple = ngx_rbtree_data(node, ngx_ssl_stapling_t, node); staple->resolver = resolver; staple->resolver_timeout = resolver_timeout; } @@ -567,6 +576,8 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) int rc; X509 *cert; u_char *p; + SSL_CTX *ssl_ctx; + ngx_ssl_t *ssl; ngx_connection_t *c; ngx_ssl_stapling_t *staple; @@ -583,7 +594,10 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) return rc; } - staple = X509_get_ex_data(cert, ngx_ssl_stapling_index); + ssl_ctx = SSL_get_SSL_CTX(ssl_conn); + ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index); + + staple = ngx_ssl_stapling_lookup(ssl, cert); if (staple == NULL) { return rc; @@ -613,6 +627,30 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) } +static ngx_ssl_stapling_t * +ngx_ssl_stapling_lookup(ngx_ssl_t *ssl, X509 *cert) +{ + ngx_rbtree_key_t key; + ngx_rbtree_node_t *node, *sentinel; + + node = ssl->staple_rbtree.root; + sentinel = ssl->staple_rbtree.sentinel; + key = (ngx_rbtree_key_t) cert; + + while (node != sentinel) { + + if (key != node->key) { + node = (key < node->key) ? node->left : node->right; + continue; + } + + return ngx_rbtree_data(node, ngx_ssl_stapling_t, node); + } + + return NULL; +} + + static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple) { From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: object caching. Message-ID: <20241001140002.883794777C@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/7d7e8d2cb8d16e409e0d4c777b30f1d8d7838c7b branches: master commit: 7d7e8d2cb8d16e409e0d4c777b30f1d8d7838c7b user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:03:20 +0400 description: SSL: object caching. Added ngx_openssl_cache_module, which indexes a type-aware object cache. It maps an id to a unique instance, and provides references to it, which are dropped when the cycle's pool is destroyed. The cache will be used in subsequent patches. Based on previous work by Mini Hawthorne. --- auto/modules | 3 +- src/event/ngx_event_openssl.h | 5 + src/event/ngx_event_openssl_cache.c | 311 ++++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+), 1 deletion(-) diff --git a/auto/modules b/auto/modules index 1a5e4212d..38b3aba78 100644 --- a/auto/modules +++ b/auto/modules @@ -1307,10 +1307,11 @@ fi if [ $USE_OPENSSL = YES ]; then ngx_module_type=CORE - ngx_module_name=ngx_openssl_module + ngx_module_name="ngx_openssl_module ngx_openssl_cache_module" ngx_module_incs= ngx_module_deps=src/event/ngx_event_openssl.h ngx_module_srcs="src/event/ngx_event_openssl.c + src/event/ngx_event_openssl_cache.c src/event/ngx_event_openssl_stapling.c" ngx_module_libs= ngx_module_link=YES diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 41566422a..fa2282ce8 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -224,6 +224,11 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s); void ngx_ssl_ocsp_cleanup(ngx_connection_t *c); ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data); +void *ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, + ngx_str_t *path, void *data); +void *ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, + char **err, ngx_str_t *path, void *data); + ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file); ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf, ngx_array_t *passwords); diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c new file mode 100644 index 000000000..98867de50 --- /dev/null +++ b/src/event/ngx_event_openssl_cache.c @@ -0,0 +1,311 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_SSL_CACHE_PATH 0 + + +typedef struct { + unsigned type:2; + unsigned len:30; + u_char *data; +} ngx_ssl_cache_key_t; + + +typedef void *(*ngx_ssl_cache_create_pt)(ngx_ssl_cache_key_t *id, char **err, + void *data); +typedef void (*ngx_ssl_cache_free_pt)(void *data); +typedef void *(*ngx_ssl_cache_ref_pt)(char **err, void *data); + + +typedef struct { + ngx_ssl_cache_create_pt create; + ngx_ssl_cache_free_pt free; + ngx_ssl_cache_ref_pt ref; +} ngx_ssl_cache_type_t; + + +typedef struct { + ngx_rbtree_node_t node; + ngx_ssl_cache_key_t id; + ngx_ssl_cache_type_t *type; + void *value; +} ngx_ssl_cache_node_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_ssl_cache_t; + + +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); +static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, + ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash); + +static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle); +static void ngx_ssl_cache_cleanup(void *data); +static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + + +static ngx_core_module_t ngx_openssl_cache_module_ctx = { + ngx_string("openssl_cache"), + ngx_openssl_cache_create_conf, + NULL +}; + + +ngx_module_t ngx_openssl_cache_module = { + NGX_MODULE_V1, + &ngx_openssl_cache_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { + +}; + + +void * +ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, + ngx_str_t *path, void *data) +{ + uint32_t hash; + ngx_ssl_cache_t *cache; + ngx_ssl_cache_key_t id; + ngx_ssl_cache_type_t *type; + ngx_ssl_cache_node_t *cn; + + if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) { + return NULL; + } + + cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx, + ngx_openssl_cache_module); + + type = &ngx_ssl_cache_types[index]; + hash = ngx_murmur_hash2(id.data, id.len); + + cn = ngx_ssl_cache_lookup(cache, type, &id, hash); + if (cn != NULL) { + return type->ref(err, cn->value); + } + + cn = ngx_palloc(cf->pool, sizeof(ngx_ssl_cache_node_t) + id.len + 1); + if (cn == NULL) { + return NULL; + } + + cn->node.key = hash; + cn->id.data = (u_char *)(cn + 1); + cn->id.len = id.len; + cn->id.type = id.type; + cn->type = type; + + ngx_cpystrn(cn->id.data, id.data, id.len + 1); + + cn->value = type->create(&id, err, data); + if (cn->value == NULL) { + return NULL; + } + + ngx_rbtree_insert(&cache->rbtree, &cn->node); + + return type->ref(err, cn->value); +} + + +void * +ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err, + ngx_str_t *path, void *data) +{ + ngx_ssl_cache_key_t id; + + if (ngx_ssl_cache_init_key(pool, index, path, &id) != NGX_OK) { + return NULL; + } + + return ngx_ssl_cache_types[index].create(&id, err, data); +} + + +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_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path) + != NGX_OK) + { + return NGX_ERROR; + } + + id->type = NGX_SSL_CACHE_PATH; + + id->len = path->len; + id->data = path->data; + + return NGX_OK; +} + + +static ngx_ssl_cache_node_t * +ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type, + ngx_ssl_cache_key_t *id, uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_ssl_cache_node_t *cn; + + node = cache->rbtree.root; + sentinel = cache->rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + cn = (ngx_ssl_cache_node_t *) node; + + if (type < cn->type) { + node = node->left; + continue; + } + + if (type > cn->type) { + node = node->right; + continue; + } + + /* type == cn->type */ + + rc = ngx_memn2cmp(id->data, cn->id.data, id->len, cn->id.len); + + if (rc == 0) { + return cn; + } + + node = (rc < 0) ? node->left : node->right; + } + + return NULL; +} + + +static void * +ngx_openssl_cache_create_conf(ngx_cycle_t *cycle) +{ + ngx_ssl_cache_t *cache; + ngx_pool_cleanup_t *cln; + + cache = ngx_pcalloc(cycle->pool, sizeof(ngx_ssl_cache_t)); + if (cache == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_ssl_cache_cleanup; + cln->data = cache; + + ngx_rbtree_init(&cache->rbtree, &cache->sentinel, + ngx_ssl_cache_node_insert); + + return cache; +} + + +static void +ngx_ssl_cache_cleanup(void *data) +{ + ngx_ssl_cache_t *cache = data; + + ngx_rbtree_t *tree; + ngx_rbtree_node_t *node; + ngx_ssl_cache_node_t *cn; + + tree = &cache->rbtree; + + if (tree->root == tree->sentinel) { + return; + } + + for (node = ngx_rbtree_min(tree->root, tree->sentinel); + node; + node = ngx_rbtree_next(tree, node)) + { + cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node); + cn->type->free(cn->value); + } +} + + +static void +ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_ssl_cache_node_t *n, *t; + + for ( ;; ) { + + n = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node); + t = ngx_rbtree_data(temp, ngx_ssl_cache_node_t, node); + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (n->type != t->type) { + + p = (n->type < t->type) ? &temp->left : &temp->right; + + } else { + + p = (ngx_memn2cmp(n->id.data, t->id.data, n->id.len, t->id.len) + < 0) ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} From noreply at nginx.com Tue Oct 1 14:00:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 14:00:02 +0000 (UTC) Subject: [nginx] SSL: caching CA certificates. Message-ID: <20241001140002.A360547780@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/5917e9de5a45bb7288c1c433db840d1a4c6290f3 branches: master commit: 5917e9de5a45bb7288c1c433db840d1a4c6290f3 user: Sergey Kandaurov date: Mon, 9 Sep 2024 19:05:58 +0400 description: SSL: caching CA certificates. This can potentially provide a large amount of savings, because CA certificates can be quite large. Based on previous work by Mini Hawthorne. --- src/event/ngx_event_openssl.c | 146 +++++++++++++++++++++++++++++------- src/event/ngx_event_openssl.h | 1 + src/event/ngx_event_openssl_cache.c | 66 ++++++++++++++++ 3 files changed, 185 insertions(+), 28 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 72189e1a4..2b1d107df 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -22,6 +22,8 @@ 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); +static int ngx_ssl_cmp_x509_name(const X509_NAME *const *a, + const X509_NAME *const *b); static void ngx_ssl_passwords_cleanup(void *data); static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess); @@ -656,6 +658,12 @@ ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { + int n, i; + char *err; + X509 *x509; + X509_NAME *name; + X509_STORE *store; + STACK_OF(X509) *chain; STACK_OF(X509_NAME) *list; SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback); @@ -666,34 +674,84 @@ ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { + list = sk_X509_NAME_new(ngx_ssl_cmp_x509_name); + if (list == NULL) { return NGX_ERROR; } - if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL) - == 0) - { + store = SSL_CTX_get_cert_store(ssl->ctx); + + if (store == NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_load_verify_locations(\"%s\") failed", - cert->data); + "SSL_CTX_get_cert_store() failed"); return NGX_ERROR; } - /* - * SSL_CTX_load_verify_locations() may leave errors in the error queue - * while returning success - */ + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL); + if (chain == NULL) { + if (err != NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "cannot load certificate \"%s\": %s", + cert->data, err); + } - ERR_clear_error(); + sk_X509_NAME_pop_free(list, X509_NAME_free); + return NGX_ERROR; + } - list = SSL_load_client_CA_file((char *) cert->data); + n = sk_X509_num(chain); - if (list == NULL) { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_load_client_CA_file(\"%s\") failed", cert->data); - return NGX_ERROR; + for (i = 0; i < n; i++) { + x509 = sk_X509_value(chain, i); + + if (X509_STORE_add_cert(store, x509) != 1) { + + if (ngx_ssl_cert_already_in_hash()) { + continue; + } + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_STORE_add_cert(\"%s\") failed", cert->data); + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + + name = X509_get_subject_name(x509); + if (name == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_get_subject_name(\"%s\") failed", cert->data); + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + + name = X509_NAME_dup(name); + if (name == NULL) { + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + +#ifdef OPENSSL_IS_BORINGSSL + if (sk_X509_NAME_find(list, NULL, name) > 0) { +#else + if (sk_X509_NAME_find(list, name) >= 0) { +#endif + X509_NAME_free(name); + continue; + } + + if (sk_X509_NAME_push(list, name) == 0) { + sk_X509_NAME_pop_free(list, X509_NAME_free); + sk_X509_pop_free(chain, X509_free); + X509_NAME_free(name); + return NGX_ERROR; + } } + sk_X509_pop_free(chain, X509_free); + SSL_CTX_set_client_CA_list(ssl->ctx, list); return NGX_OK; @@ -704,6 +762,12 @@ ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { + int i, n; + char *err; + X509 *x509; + X509_STORE *store; + STACK_OF(X509) *chain; + SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx), ngx_ssl_verify_callback); @@ -713,25 +777,44 @@ ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { + store = SSL_CTX_get_cert_store(ssl->ctx); + + if (store == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_get_cert_store() failed"); return NGX_ERROR; } - if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL) - == 0) - { - ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_load_verify_locations(\"%s\") failed", - cert->data); + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL); + if (chain == NULL) { + if (err != NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "cannot load certificate \"%s\": %s", + cert->data, err); + } + return NGX_ERROR; } - /* - * SSL_CTX_load_verify_locations() may leave errors in the error queue - * while returning success - */ + n = sk_X509_num(chain); - ERR_clear_error(); + for (i = 0; i < n; i++) { + x509 = sk_X509_value(chain, i); + + if (X509_STORE_add_cert(store, x509) != 1) { + + if (ngx_ssl_cert_already_in_hash()) { + continue; + } + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_STORE_add_cert(\"%s\") failed", cert->data); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + } + + sk_X509_pop_free(chain, X509_free); return NGX_OK; } @@ -987,6 +1070,13 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret) } +static int +ngx_ssl_cmp_x509_name(const X509_NAME *const *a, const X509_NAME *const *b) +{ + return (X509_NAME_cmp(*a, *b)); +} + + ngx_array_t * ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index e2f0e5821..6d171229c 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -196,6 +196,7 @@ typedef struct { #define NGX_SSL_CACHE_CERT 0 #define NGX_SSL_CACHE_PKEY 1 #define NGX_SSL_CACHE_CRL 2 +#define NGX_SSL_CACHE_CA 3 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 b2615d2bf..f43bdb5e7 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -70,6 +70,9 @@ static void *ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err, static void ngx_ssl_cache_crl_free(void *data); static void *ngx_ssl_cache_crl_ref(char **err, void *data); +static void *ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, 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); @@ -117,6 +120,11 @@ static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = { { ngx_ssl_cache_crl_create, ngx_ssl_cache_crl_free, ngx_ssl_cache_crl_ref }, + + /* NGX_SSL_CACHE_CA */ + { ngx_ssl_cache_ca_create, + ngx_ssl_cache_cert_free, + ngx_ssl_cache_cert_ref } }; @@ -623,6 +631,64 @@ ngx_ssl_cache_crl_ref(char **err, void *data) } +static void * +ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err, void *data) +{ + BIO *bio; + X509 *x509; + u_long n; + STACK_OF(X509) *chain; + + chain = sk_X509_new_null(); + if (chain == NULL) { + *err = "sk_X509_new_null() failed"; + return NULL; + } + + bio = ngx_ssl_cache_create_bio(id, err); + if (bio == NULL) { + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + for ( ;; ) { + + x509 = PEM_read_bio_X509_AUX(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_num(chain) > 0) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + *err = "PEM_read_bio_X509_AUX() failed"; + BIO_free(bio); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + + if (sk_X509_push(chain, x509) == 0) { + *err = "sk_X509_push() failed"; + BIO_free(bio); + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NULL; + } + } + + BIO_free(bio); + + return chain; +} + + static BIO * ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err) { From noreply at nginx.com Tue Oct 1 23:31:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 23:31:02 +0000 (UTC) Subject: [njs] Version 0.8.6. Message-ID: <20241001233102.6C97247782@pubserv1.nginx> details: https://github.com/nginx/njs/commit/c5a29a7af8894ee1ec44ebda71ef0ea1f2a31af6 branches: master commit: c5a29a7af8894ee1ec44ebda71ef0ea1f2a31af6 user: Dmitry Volyntsev date: Thu, 26 Sep 2024 17:47:07 -0700 description: Version 0.8.6. --- CHANGES | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGES b/CHANGES index 32eb840f..859ae077 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,41 @@ +Changes with njs 0.8.6 02 Oct 2024 + + nginx modules: + + *) Feature: introduced QuickJS engine. + + *) Feature: added optional nocache flag for js_set directive. + Thanks to Thomas P. + + *) Feature: exposed capture group variables in HTTP module. + Thanks to Thomas P. + + Core: + + *) Feature: added Buffer module for QuickJS engine. + + *) Bugfix: fixed handling of empty labelled statement in a function. + + *) Bugfix: fixed Function constructor handling when called without + arguments. + + *) Bugfix: fixed Buffer.prototype.writeInt8() and friends. + + *) Bugfix: fixed Buffer.prototype.writeFloat() and friends. + + *) Bugfix: fixed Buffer.prototype.lastIndexOf(). + + *) Bugfix: fixed Buffer.prototype.write(). + + *) Bugfix: fixed maybe-uninitialized warnings in error creation. + + *) Bugfix: fixed 'ctx.codepoint' initialization in UTF-8 decoding. + + *) Bugfix: fixed 'length' initialization in Array.prototype.pop(). + + *) Bugfix: fixed handling of encode arg in fs.readdir() and + fs.realpath(). + Changes with njs 0.8.5 25 Jun 2024 nginx modules: From noreply at nginx.com Tue Oct 1 23:38:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 1 Oct 2024 23:38:02 +0000 (UTC) Subject: [njs] Annotated tag created: 0.8.6 Message-ID: <20241001233802.683A847784@pubserv1.nginx> details: https://github.com/nginx/njs/releases/tag/0.8.6 branches: commit: c5a29a7af8894ee1ec44ebda71ef0ea1f2a31af6 user: Dmitry Volyntsev date: Tue Oct 1 16:36:28 2024 -0700 description: Version 0.8.6. From noreply at nginx.com Wed Oct 2 15:14:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 15:14:02 +0000 (UTC) Subject: [nginx] Updated OpenSSL used for win32 builds. Message-ID: <20241002151402.E65DD477AD@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/0e7c9ddb275a496b24adc3f1de8eebe70a40ee26 branches: master commit: 0e7c9ddb275a496b24adc3f1de8eebe70a40ee26 user: Sergey Kandaurov date: Tue, 1 Oct 2024 19:23:45 +0400 description: Updated OpenSSL used for win32 builds. --- misc/GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/GNUmakefile b/misc/GNUmakefile index fcf34ec34..e18a86a63 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-3.0.14 +OPENSSL = openssl-3.0.15 ZLIB = zlib-1.3.1 PCRE = pcre2-10.39 From noreply at nginx.com Wed Oct 2 15:14:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 15:14:02 +0000 (UTC) Subject: [nginx] nginx-1.27.2-RELEASE Message-ID: <20241002151402.F1F14477AE@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/e24f7ccc161f1a2a759eb27263ec9af4fc7c8e96 branches: master commit: e24f7ccc161f1a2a759eb27263ec9af4fc7c8e96 user: Sergey Kandaurov date: Tue, 1 Oct 2024 23:59:48 +0400 description: nginx-1.27.2-RELEASE --- docs/xml/nginx/changes.xml | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 9c2f317bc..c18dedff4 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,71 @@ + + + + +SSL-сертификаты, секретные ключи и списки CRL теперь кешируются +на старте или во время переконфигурации. + + +SSL certificates, secret keys, and CRLs are now cached +on start or during reconfiguration. + + + + + +проверка клиентских сертификатов с помощью OCSP в модуле stream. + + +client certificate validation with OCSP in the stream module. + + + + + +поддержка OCSP stapling в модуле stream. + + +OCSP stapling support in the stream module. + + + + + +директива proxy_pass_trailers в модуле ngx_http_proxy_module. + + +the "proxy_pass_trailers" directive in the ngx_http_proxy_module. + + + + + +директива ssl_client_certificate теперь поддерживает сертификаты +с дополнительными данными. + + +the "ssl_client_certificate" directive now supports certificates +with auxiliary information. + + + + + +теперь наличие директивы ssl_client_certificate не обязательно +для проверки клиентских SSL-сертификатов. + + +now the "ssl_client_certificate" directive is not required +for client SSL certificates verification. + + + + + + From noreply at nginx.com Wed Oct 2 15:15:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 15:15:03 +0000 (UTC) Subject: [nginx] Annotated tag created: release-1.27.2 Message-ID: <20241002151503.23565477AE@pubserv1.nginx> details: https://github.com/nginx/nginx/releases/tag/release-1.27.2 branches: commit: b06e59c73ca45df937c5d93c0cc473e2bdcf4607 user: Sergey Kandaurov date: Wed Oct 2 18:57:34 2024 +0400 description: release-1.27.2 tag From noreply at nginx.com Wed Oct 2 16:45:17 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 2 Oct 2024 16:45:17 +0000 (UTC) Subject: [nginx] Annotated tag created: release-1.27.2 Message-ID: <20241002164517.74D88477AF@pubserv1.nginx> details: https://github.com/nginx/nginx/releases/tag/release-1.27.2 branches: commit: e24f7ccc161f1a2a759eb27263ec9af4fc7c8e96 user: Sergey Kandaurov date: Wed Oct 2 19:15:47 2024 +0400 description: release-1.27.2 tag From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Version bump. Message-ID: <20241008043502.8C0FF4783D@pubserv1.nginx> details: https://github.com/nginx/njs/commit/f2e18cde418f99b0cdb5f8965a8eda8c7020f462 branches: master commit: f2e18cde418f99b0cdb5f8965a8eda8c7020f462 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 18:09:47 -0700 description: Version bump. --- src/njs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/njs.h b/src/njs.h index 8950a0db..3b42ae61 100644 --- a/src/njs.h +++ b/src/njs.h @@ -11,8 +11,8 @@ #include -#define NJS_VERSION "0.8.6" -#define NJS_VERSION_NUMBER 0x000806 +#define NJS_VERSION "0.8.7" +#define NJS_VERSION_NUMBER 0x000807 #include From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Fixed dead store assignment in TextEncoder.encodeInto(). Message-ID: <20241008043502.A0AB547840@pubserv1.nginx> details: https://github.com/nginx/njs/commit/34e322521508cba30bafc5cc60063a82ca358312 branches: master commit: 34e322521508cba30bafc5cc60063a82ca358312 user: Dmitry Volyntsev date: Tue, 24 Sep 2024 22:07:44 -0700 description: Fixed dead store assignment in TextEncoder.encodeInto(). Found by Clang static analyzer. --- src/njs_encoding.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/njs_encoding.c b/src/njs_encoding.c index 98a73338..f70c084d 100644 --- a/src/njs_encoding.c +++ b/src/njs_encoding.c @@ -204,7 +204,6 @@ njs_text_encoder_encode_into(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, to = njs_typed_array_start(array); to_end = to + array->byte_length; - cp = 0; njs_set_number(&read, 0); njs_set_number(&written, 0); From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Tests: external code for unit tests is rewritten using public API. Message-ID: <20241008043502.9C6A84783F@pubserv1.nginx> details: https://github.com/nginx/njs/commit/0279dff2fcd3e288b3a0d1698cee2204b9ec6de0 branches: master commit: 0279dff2fcd3e288b3a0d1698cee2204b9ec6de0 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 17:57:08 -0700 description: Tests: external code for unit tests is rewritten using public API. This part was missed in 24b56ef88 (0.8.0). --- src/test/njs_externals_test.c | 127 +++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c index f91c919a..7d2e82af 100644 --- a/src/test/njs_externals_test.c +++ b/src/test/njs_externals_test.c @@ -4,7 +4,10 @@ * Copyright (C) NGINX, Inc. */ -#include +#include +#include +#include +#include #include "njs_externals_test.h" @@ -21,8 +24,8 @@ typedef struct { typedef struct { - njs_value_t name; - njs_value_t value; + njs_str_t name; + njs_opaque_value_t value; } njs_unit_test_prop_t; @@ -59,7 +62,7 @@ lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data) njs_unit_test_prop_t *prop; prop = data; - njs_string_get(&prop->name, &name); + name = prop->name; if (name.length != lhq->key.length) { return NJS_DECLINED; @@ -96,18 +99,21 @@ static const njs_lvlhsh_proto_t lvlhsh_proto njs_aligned(64) = { static njs_unit_test_prop_t * -lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_value_t *name, +lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_str_t *name, const njs_value_t *value) { njs_unit_test_prop_t *prop; - prop = njs_mp_alloc(pool, sizeof(njs_unit_test_prop_t)); + prop = njs_mp_alloc(pool, sizeof(njs_unit_test_prop_t) + name->length); if (prop == NULL) { return NULL; } - prop->name = *name; - prop->value = *value; + prop->name.length = name->length; + prop->name.start = (u_char *) prop + sizeof(njs_unit_test_prop_t); + memcpy(prop->name.start, name->start, name->length); + + njs_value_assign(&prop->value, value); return prop; } @@ -119,7 +125,7 @@ lvlhsh_unit_test_add(njs_mp_t *pool, njs_unit_test_req_t *r, { njs_lvlhsh_query_t lhq; - njs_string_get(&prop->name, &lhq.key); + lhq.key = prop->name; lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.replace = 1; @@ -231,7 +237,6 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; - njs_value_t name; njs_lvlhsh_query_t lhq; njs_unit_test_req_t *r; njs_unit_test_prop_t *prop; @@ -264,14 +269,13 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, if (setval != NULL) { /* Set. */ - njs_vm_value_string_create(vm, &name, lhq.key.start, lhq.key.length); - prop = lvlhsh_unit_test_alloc(vm->mem_pool, &name, setval); + prop = lvlhsh_unit_test_alloc(njs_vm_memory_pool(vm), &lhq.key, setval); if (prop == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } - ret = lvlhsh_unit_test_add(vm->mem_pool, r, prop); + ret = lvlhsh_unit_test_add(njs_vm_memory_pool(vm), r, prop); if (ret != NJS_OK) { njs_vm_error(vm, "lvlhsh_unit_test_add() failed"); return NJS_ERROR; @@ -291,12 +295,12 @@ njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, if (ret == NJS_OK) { if (retval == NULL) { - njs_set_invalid(&prop->value); + njs_value_invalid_set(njs_value_arg(&prop->value)); return NJS_OK; } - if (njs_is_valid(&prop->value)) { - *retval = prop->value; + if (njs_value_is_valid(njs_value_arg(&prop->value))) { + njs_value_assign(retval, njs_value_arg(&prop->value)); return NJS_OK; } } @@ -377,7 +381,7 @@ njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } @@ -387,7 +391,7 @@ njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r->uri.length); } - njs_set_undefined(retval); + njs_value_undefined_set(retval); return NJS_OK; } @@ -414,25 +418,27 @@ njs_unit_test_r_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; - njs_value_t value, *argument, *select; + njs_value_t *argument, *select; njs_function_t *callback; njs_external_ev_t *ev; njs_external_env_t *env; + njs_opaque_value_t value; njs_unit_test_req_t *r; r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ev = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_external_ev_t)); if (ev == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } - ret = njs_vm_promise_create(vm, &value, njs_value_arg(&ev->callbacks[0])); + ret = njs_vm_promise_create(vm, njs_value_arg(&value), + njs_value_arg(&ev->callbacks[0])); if (ret != NJS_OK) { return NJS_ERROR; } @@ -449,10 +455,10 @@ njs_unit_test_r_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, ev->function = callback; ev->data = r; ev->nargs = 2; - njs_value_assign(&ev->args[0], &ev->callbacks[!!njs_bool(select)]); + njs_value_assign(&ev->args[0], &ev->callbacks[!!njs_value_bool(select)]); njs_value_assign(&ev->args[1], argument); - env = vm->external; + env = njs_vm_external_ptr(vm); njs_queue_insert_tail(&env->events, &ev->link); @@ -468,11 +474,11 @@ njs_unit_test_r_retval(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, { njs_external_env_t *env; - env = vm->external; + env = njs_vm_external_ptr(vm); njs_value_assign(&env->retval, njs_arg(args, nargs, 1)); - njs_set_undefined(retval); + njs_value_undefined_set(retval); return NJS_OK; } @@ -497,11 +503,11 @@ njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } - sr = njs_mp_zalloc(vm->mem_pool, sizeof(njs_unit_test_req_t)); + sr = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_unit_test_req_t)); if (sr == NULL) { goto memory_error; } @@ -522,7 +528,7 @@ njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, memory_error: - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } @@ -537,7 +543,7 @@ njs_unit_test_r_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { - njs_type_error(vm, "\"this\" is not an external"); + njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } @@ -559,7 +565,7 @@ njs_unit_test_null_get(njs_vm_t *vm, njs_value_t *args, this = njs_argument(args, 0); if (!njs_value_is_external(this, njs_external_null_proto_id)) { - njs_type_error(vm, "\"this\" is not a null external"); + njs_vm_type_error(vm, "\"this\" is not a null external"); return NJS_ERROR; } @@ -586,16 +592,16 @@ njs_unit_test_null_set(njs_vm_t *vm, njs_value_t *args, this = njs_argument(args, 0); if (!njs_value_is_external(this, njs_external_null_proto_id)) { - njs_type_error(vm, "\"this\" is not a null external"); + njs_vm_type_error(vm, "\"this\" is not a null external"); return NJS_ERROR; } d = njs_value_external(this); if (d == NULL) { - d = njs_mp_alloc(vm->mem_pool, sizeof(double)); + d = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(double)); if (d == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } } @@ -616,9 +622,9 @@ njs_unit_test_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, { njs_unit_test_req_t *sr; - sr = njs_mp_zalloc(vm->mem_pool, sizeof(njs_unit_test_req_t)); + sr = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_unit_test_req_t)); if (sr == NULL) { - njs_memory_error(vm); + njs_vm_memory_error(vm); return NJS_ERROR; } @@ -1017,9 +1023,15 @@ static njs_external_t njs_unit_test_proto_props[] = { typedef struct { - njs_str_t name; - njs_unit_test_req_t request; - njs_unit_test_prop_t props[2]; + njs_str_t name; + njs_str_t value; +} njs_unit_test_prop_init_t; + + +typedef struct { + njs_str_t name; + njs_unit_test_req_t request; + njs_unit_test_prop_init_t props[2]; } njs_unit_test_req_init_t; @@ -1032,8 +1044,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 13, }, { - { njs_string("r"), njs_string("rval") }, - { njs_string("r2"), njs_string("r2val") }, + { njs_str("r"), njs_str("rval") }, + { njs_str("r2"), njs_str("r2val") }, } }, @@ -1044,8 +1056,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 1024, }, { - { njs_string("p"), njs_string("pval") }, - { njs_string("p2"), njs_string("p2val") }, + { njs_str("p"), njs_str("pval") }, + { njs_str("p2"), njs_str("p2val") }, } }, @@ -1056,8 +1068,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 1025, }, { - { njs_string("q"), njs_string("qval") }, - { njs_string("q2"), njs_string("q2val") }, + { njs_str("q"), njs_str("qval") }, + { njs_str("q2"), njs_str("q2val") }, } }, @@ -1068,8 +1080,8 @@ static njs_unit_test_req_init_t njs_test_requests[] = { .d = 1026, }, { - { njs_string("k"), njs_string("kval") }, - { njs_string("k2"), njs_string("k2val") }, + { njs_str("k"), njs_str("kval") }, + { njs_str("k2"), njs_str("k2val") }, } }, }; @@ -1081,10 +1093,12 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init, { njs_int_t ret; njs_uint_t i, j; + njs_opaque_value_t value; njs_unit_test_req_t *requests; njs_unit_test_prop_t *prop; - requests = njs_mp_zalloc(vm->mem_pool, n * sizeof(njs_unit_test_req_t)); + requests = njs_mp_zalloc(njs_vm_memory_pool(vm), + n * sizeof(njs_unit_test_req_t)); if (njs_slow_path(requests == NULL)) { return NJS_ERROR; } @@ -1108,15 +1122,24 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init, } for (j = 0; j < njs_nitems(init[i].props); j++) { - prop = lvlhsh_unit_test_alloc(vm->mem_pool, &init[i].props[j].name, - &init[i].props[j].value); + ret = njs_vm_value_string_create(vm, njs_value_arg(&value), + init[i].props[j].value.start, + init[i].props[j].value.length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + prop = lvlhsh_unit_test_alloc(njs_vm_memory_pool(vm), + &init[i].props[j].name, + njs_value_arg(&value)); if (njs_slow_path(prop == NULL)) { njs_printf("lvlhsh_unit_test_alloc() failed\n"); return NJS_ERROR; } - ret = lvlhsh_unit_test_add(vm->mem_pool, &requests[i], prop); + ret = lvlhsh_unit_test_add(njs_vm_memory_pool(vm), &requests[i], + prop); if (njs_slow_path(ret != NJS_OK)) { njs_printf("lvlhsh_unit_test_add() failed\n"); return NJS_ERROR; From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Style. Message-ID: <20241008043502.A4B6647841@pubserv1.nginx> details: https://github.com/nginx/njs/commit/4bf2d068fee6ebf09b33954f00ae94ec8b741f3e branches: master commit: 4bf2d068fee6ebf09b33954f00ae94ec8b741f3e user: Dmitry Volyntsev date: Mon, 7 Oct 2024 18:12:12 -0700 description: Style. --- src/njs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/njs.h b/src/njs.h index 3b42ae61..466932d8 100644 --- a/src/njs.h +++ b/src/njs.h @@ -295,7 +295,7 @@ NJS_EXPORT void njs_vm_destroy(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end); NJS_EXPORT void njs_vm_set_module_loader(njs_vm_t *vm, - njs_module_loader_t module_loader, void *opaque); + njs_module_loader_t module_loader, void *opaque); NJS_EXPORT njs_mod_t *njs_vm_add_module(njs_vm_t *vm, njs_str_t *name, njs_value_t *value); NJS_EXPORT njs_mod_t *njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, @@ -314,7 +314,7 @@ NJS_EXPORT njs_int_t njs_vm_execute_pending_job(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_pending(njs_vm_t *vm); NJS_EXPORT void njs_vm_set_rejection_tracker(njs_vm_t *vm, - njs_rejection_tracker_t rejection_tracker, void *opaque); + njs_rejection_tracker_t rejection_tracker, void *opaque); /* * Runs the specified function with provided arguments. From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Removed deprecated $262.byteString(). Message-ID: <20241008043502.977784783E@pubserv1.nginx> details: https://github.com/nginx/njs/commit/efa5e7d653548505d05d66eaed609db9042b834e branches: master commit: efa5e7d653548505d05d66eaed609db9042b834e user: Dmitry Volyntsev date: Mon, 7 Oct 2024 17:50:19 -0700 description: Removed deprecated $262.byteString(). --- src/test/njs_externals_test.c | 173 ------------------------------------------ src/test/njs_unit_test.c | 55 +------------- 2 files changed, 1 insertion(+), 227 deletions(-) diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c index 7dcad90b..f91c919a 100644 --- a/src/test/njs_externals_test.c +++ b/src/test/njs_externals_test.c @@ -650,168 +650,6 @@ njs_unit_test_error_message(njs_vm_t *vm, njs_object_prop_t *prop, } -static njs_int_t -njs_262_bytes_from_array_like(njs_vm_t *vm, njs_value_t *value, - njs_value_t *retval) -{ - u_char *p; - int64_t length; - uint32_t u32; - njs_int_t ret; - njs_array_t *array; - njs_value_t *octet, index, prop; - njs_array_buffer_t *buffer; - - array = NULL; - buffer = NULL; - - switch (value->type) { - case NJS_ARRAY: - array = njs_array(value); - length = array->length; - break; - - case NJS_ARRAY_BUFFER: - case NJS_TYPED_ARRAY: - - if (njs_is_typed_array(value)) { - buffer = njs_typed_array(value)->buffer; - - } else { - buffer = njs_array_buffer(value); - } - - length = buffer->size; - break; - - default: - ret = njs_object_length(vm, value, &length); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - } - - p = njs_string_alloc(vm, retval, length, 0); - if (njs_slow_path(p == NULL)) { - return NJS_ERROR; - } - - if (array != NULL) { - octet = array->start; - - while (length != 0) { - ret = njs_value_to_uint32(vm, octet, &u32); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - *p++ = (u_char) u32; - octet++; - length--; - } - - } else if (buffer != NULL) { - memcpy(p, buffer->u.u8, length); - - } else { - p += length - 1; - - while (length != 0) { - njs_set_number(&index, length - 1); - - ret = njs_value_property(vm, value, &index, &prop); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } - - ret = njs_value_to_uint32(vm, &prop, &u32); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - *p-- = (u_char) u32; - length--; - } - } - - return NJS_OK; -} - - -static njs_int_t -njs_262_bytes_from_string(njs_vm_t *vm, const njs_value_t *string, - const njs_value_t *encoding, njs_value_t *retval) -{ - njs_str_t enc, str; - - if (!njs_is_string(encoding)) { - njs_type_error(vm, "\"encoding\" must be a string"); - return NJS_ERROR; - } - - njs_string_get(encoding, &enc); - njs_string_get(string, &str); - - if (enc.length == 3 && memcmp(enc.start, "hex", 3) == 0) { - return njs_string_decode_hex(vm, retval, &str); - - } else if (enc.length == 6 && memcmp(enc.start, "base64", 6) == 0) { - return njs_string_decode_base64(vm, retval, &str); - - } else if (enc.length == 9 && memcmp(enc.start, "base64url", 9) == 0) { - return njs_string_decode_base64url(vm, retval, &str); - } - - njs_type_error(vm, "Unknown encoding: \"%V\"", &enc); - - return NJS_ERROR; -} - - -/* - * $262.byteString(array-like). - * Converts an array-like object containing octets into a byte string. - * - * $262.byteString(string[, encoding]). - * Converts a string using provided encoding: hex, base64, base64url to - * a byte string. - * - * Note: the function produces a byte string, and byte strings are deprecated. - * The function is provided for testing of existing code which works with - * byte strings. When code working with byte strings is removed - * the function will be removed as well. - */ - -static njs_int_t -njs_262_byte_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_index_t unused, njs_value_t *retval) -{ - njs_value_t *value; - - value = njs_arg(args, nargs, 1); - - if (njs_is_string(value)) { - return njs_262_bytes_from_string(vm, value, njs_arg(args, nargs, 2), - retval); - - } else if (njs_is_object(value)) { - - if (njs_is_object_string(value)) { - value = njs_object_value(value); - return njs_262_bytes_from_string(vm, value, - njs_arg(args, nargs, 2), - retval); - } - - return njs_262_bytes_from_array_like(vm, value, retval); - } - - njs_type_error(vm, "value must be a string or array-like object"); - - return NJS_ERROR; -} - - static njs_external_t njs_unit_test_262_external[] = { { @@ -833,17 +671,6 @@ static njs_external_t njs_unit_test_262_external[] = { } }, - { - .flags = NJS_EXTERN_METHOD, - .name.string = njs_str("byteString"), - .writable = 1, - .configurable = 1, - .enumerable = 1, - .u.method = { - .native = njs_262_byte_string, - } - }, - }; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 10ee6c1e..c52753a7 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -10028,9 +10028,6 @@ static njs_unit_test_t njs_test[] = { njs_str("decodeURI('%D0%B0%D0%B1%D0%B2').length"), njs_str("3")}, - { njs_str("decodeURI($262.byteString([0x80,0x80]))"), - njs_str("URIError: malformed URI")}, - { njs_str("[" " '%'," " '%0'," @@ -10074,11 +10071,8 @@ static njs_unit_test_t njs_test[] = " String.fromCodePoint(0x100)," " String.fromCodePoint(0x00, 0x100)," " String.fromCodePoint(0x00, 0x01, 0x100)," - " $262.byteString([0x80])," - " $262.byteString([0x60, 0x80])," - " $262.byteString([0x60, 0x60, 0x80])," "].map(v => { try { return btoa(v); } catch (e) { return '#'} })"), - njs_str("dW5kZWZpbmVk,,AA==,AAE=,AAEC,AP7/,#,#,#,#,#,#")}, + njs_str("dW5kZWZpbmVk,,AA==,AAE=,AAEC,AP7/,#,#,#")}, /* atob() */ @@ -19359,17 +19353,6 @@ static njs_unit_test_t njs_test[] = { njs_str("var en = new TextEncoder(); var res = en.encode('α1α'); res[2]"), njs_str("49") }, - { njs_str("var en = new TextEncoder(); en.encode($262.byteString([0xCE]))"), - njs_str("239,191,189") }, - - { njs_str("var en = new TextEncoder();" - "en.encode($262.byteString([0xCE, 0xB1, 0xCE]))"), - njs_str("206,177,239,191,189") }, - - { njs_str("var en = new TextEncoder();" - "en.encode($262.byteString([0xCE, 0xCE, 0xB1]))"), - njs_str("239,191,189,206,177") }, - { njs_str("var en = new TextEncoder(); en.encoding"), njs_str("utf-8") }, @@ -19391,33 +19374,6 @@ static njs_unit_test_t njs_test[] = "en.encodeInto('ααααα', utf8.subarray(2)); utf8[0]"), njs_str("0") }, - { njs_str("var str = $262.byteString([0xCE]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(3);" - "var res = en.encodeInto(str, utf8); " - "[njs.dump(res), utf8]"), - njs_str("{read:1,written:3},239,191,189") }, - - { njs_str("var str = $262.byteString([0xCE]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(5);" - "en.encodeInto(str, utf8); utf8"), - njs_str("239,191,189,0,0") }, - - { njs_str("var str = $262.byteString([0xCE, 0xB1, 0xCE]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(5);" - "var res = en.encodeInto(str, utf8);" - "[njs.dump(res), utf8]"), - njs_str("{read:2,written:5},206,177,239,191,189") }, - - { njs_str("var str = $262.byteString([0xCE, 0xCE, 0xB1]);" - "var en = new TextEncoder();" - "var utf8 = new Uint8Array(5);" - "var res = en.encodeInto(str, utf8);" - "[njs.dump(res), utf8]"), - njs_str("{read:2,written:5},239,191,189,206,177") }, - { njs_str("TextEncoder.prototype.encodeInto.apply({}, [])"), njs_str("TypeError: \"this\" is not a TextEncoder") }, @@ -20901,11 +20857,6 @@ static njs_unit_test_t njs_querystring_module_test[] = "out.join('; ')"), njs_str("baz; fuz; muz; tax") }, - { njs_str("var qs = require('querystring'); " - "qs.stringify({a: 'b'}, null, null, " - " {encodeURIComponent: () => $262.byteString([0x9d])})"), - njs_str("InternalError: invalid UTF-8 string") }, - { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, null, null, {encodeURIComponent: 123});" "out.join('; ')"), @@ -20955,10 +20906,6 @@ static njs_unit_test_t njs_querystring_module_test[] = "qs.stringify(123)"), njs_str("") }, - { njs_str("var qs = require('querystring');" - "qs.stringify({X: $262.byteString(Array(4).fill(0x9d))})"), - njs_str("X=%9D%9D%9D%9D") }, - { njs_str("var qs = require('querystring');" "qs.stringify({X:{toString(){return 3}}})"), njs_str("X=") }, From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Benchmark: code is rewritten using public API. Message-ID: <20241008043502.AFC9747843@pubserv1.nginx> details: https://github.com/nginx/njs/commit/30a965832bb68b232fa2007919267f8827661800 branches: master commit: 30a965832bb68b232fa2007919267f8827661800 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 19:08:53 -0700 description: Benchmark: code is rewritten using public API. --- src/test/njs_benchmark.c | 99 +++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c index 71de6b57..6ca83e82 100644 --- a/src/test/njs_benchmark.c +++ b/src/test/njs_benchmark.c @@ -4,7 +4,8 @@ * Copyright (C) NGINX, Inc. */ -#include +#include +#include #include "njs_externals_test.h" @@ -98,19 +99,20 @@ static njs_int_t njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, njs_benchmark_test_t *test) { - u_char *start; - njs_vm_t *vm, *nvm; - uint64_t ns; - njs_int_t ret; - njs_str_t s, *expected; - njs_uint_t i, n; - njs_bool_t success; - njs_value_t *result, retval, name, usec, times; - njs_vm_opt_t options; - - static const njs_value_t name_key = njs_string("name"); - static const njs_value_t usec_key = njs_string("usec"); - static const njs_value_t times_key = njs_string("times"); + u_char *start; + njs_vm_t *vm, *nvm; + uint64_t ns; + njs_int_t ret; + njs_str_t s, *expected; + njs_uint_t i, n; + njs_bool_t success; + njs_value_t *result; + njs_vm_opt_t options; + njs_opaque_value_t retval, name, usec, times; + + static const njs_str_t name_key = njs_str("name"); + static const njs_str_t usec_key = njs_str("usec"); + static const njs_str_t times_key = njs_str("times"); njs_vm_opt_init(&options); @@ -148,9 +150,9 @@ njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, goto done; } - (void) njs_vm_start(nvm, &retval); + (void) njs_vm_start(nvm, njs_value_arg(&retval)); - if (njs_vm_value_string(nvm, &s, &retval) != NJS_OK) { + if (njs_vm_value_string(nvm, &s, njs_value_arg(&retval)) != NJS_OK) { njs_printf("njs_vm_value_string() failed\n"); goto done; } @@ -188,23 +190,41 @@ njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, goto done; } - ret = njs_vm_value_string_create(parent, &name, (u_char *) test->name, + ret = njs_vm_value_string_create(parent, njs_value_arg(&name), + (u_char *) test->name, njs_strlen(test->name)); if (ret != NJS_OK) { njs_printf("njs_vm_value_string_create() failed\n"); goto done; } - njs_value_number_set(&usec, 1000 * ns); - njs_value_number_set(×, n); + njs_value_number_set(njs_value_arg(&usec), 1000 * ns); + njs_value_number_set(njs_value_arg(×), n); - ret = njs_vm_object_alloc(parent, result, &name_key, &name, - &usec_key, &usec, ×_key, ×, NULL); + ret = njs_vm_object_alloc(parent, result, NULL); if (ret != NJS_OK) { njs_printf("njs_vm_object_alloc() failed\n"); goto done; } + ret = njs_vm_object_prop_set(parent, result, &name_key, &name); + if (ret != NJS_OK) { + njs_printf("njs_vm_object_prop_set() failed\n"); + goto done; + } + + ret = njs_vm_object_prop_set(parent, result, &usec_key, &usec); + if (ret != NJS_OK) { + njs_printf("njs_vm_object_prop_set() failed\n"); + goto done; + } + + ret = njs_vm_object_prop_set(parent, result, ×_key, ×); + if (ret != NJS_OK) { + njs_printf("njs_vm_object_prop_set() failed\n"); + goto done; + } + ret = NJS_OK; done: @@ -483,8 +503,8 @@ main(int argc, char **argv) njs_str_t out; njs_uint_t i; njs_opts_t opts; - njs_value_t args[2], report, retval; njs_vm_opt_t options; + njs_opaque_value_t args[2], report, retval; njs_benchmark_test_t *test; static const char help[] = @@ -563,9 +583,9 @@ main(int argc, char **argv) goto done; } - njs_vm_start(vm, &retval); + njs_vm_start(vm, njs_value_arg(&retval)); - ret = njs_vm_array_alloc(vm, &report, 8); + ret = njs_vm_array_alloc(vm, njs_value_arg(&report), 8); if (ret != NJS_OK) { njs_printf("njs_vm_array_alloc() failed\n"); goto done; @@ -581,7 +601,7 @@ main(int argc, char **argv) if (strncmp(test->name, opts.prefix, njs_min(strlen(test->name), strlen(opts.prefix))) == 0) { - ret = njs_benchmark_test(vm, &opts, &report, test); + ret = njs_benchmark_test(vm, &opts, njs_value_arg(&report), test); if (ret != NJS_OK) { goto done; @@ -590,19 +610,20 @@ main(int argc, char **argv) } if (opts.previous) { - ret = njs_vm_value_string_create(vm, &args[0], (u_char *) opts.previous, + ret = njs_vm_value_string_create(vm, njs_value_arg(&args[0]), + (u_char *) opts.previous, njs_strlen(opts.previous)); if (ret != NJS_OK) { njs_printf("njs_vm_value_string_create() failed\n"); goto done; } - args[1] = report; + njs_value_assign(&args[1], &report); njs_vm_invoke(vm, njs_vm_function(vm, &compare), njs_value_arg(&args), - 2, &retval); + 2, njs_value_arg(&retval)); - ret = njs_vm_value_dump(vm, &out, &retval, 1, 1); + ret = njs_vm_value_dump(vm, &out, njs_value_arg(&retval), 1, 1); if (ret != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; @@ -614,13 +635,14 @@ main(int argc, char **argv) } if (opts.dump_report) { - ret = njs_vm_json_stringify(vm, &report, 1, &retval); + ret = njs_vm_json_stringify(vm, njs_value_arg(&report), 1, + njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_json_stringify() failed\n"); goto done; } - ret = njs_vm_value_dump(vm, &out, &retval, 1, 1); + ret = njs_vm_value_dump(vm, &out, njs_value_arg(&retval), 1, 1); if (ret != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; @@ -690,10 +712,10 @@ static njs_int_t njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - int64_t i, n; - njs_chb_t chain; - njs_str_t s, mode; - njs_value_t value; + int64_t i, n; + njs_chb_t chain; + njs_str_t s, mode; + njs_opaque_value_t value; njs_value_string_get(njs_arg(args, nargs, 1), &mode); @@ -706,7 +728,8 @@ njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, if (memcmp(mode.start, "create", 6) == 0) { for (i = 0; i < n; i++) { - njs_string_create(vm, &value, s.start, s.length); + njs_vm_value_string_create(vm, njs_value_arg(&value), s.start, + s.length); } } else if (memcmp(mode.start, "chb", 3) == 0) { @@ -721,13 +744,13 @@ njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_chb_append(&chain, s.start, s.length); for (i = 0; i < n; i++) { - njs_string_create_chb(vm, &value, &chain); + njs_vm_value_string_create_chb(vm, njs_value_arg(&value), &chain); } njs_chb_destroy(&chain); } else { - njs_type_error(vm, "unknown mode \"%V\"", &mode); + njs_vm_type_error(vm, "unknown mode \"%V\"", &mode); return NJS_ERROR; } From noreply at nginx.com Tue Oct 8 04:35:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 04:35:02 +0000 (UTC) Subject: [njs] Removed deprecated njs_string_set(). Message-ID: <20241008043502.AAD5447842@pubserv1.nginx> details: https://github.com/nginx/njs/commit/cf889c11f2fca991452dbdc7fe40cc4e05b708d4 branches: master commit: cf889c11f2fca991452dbdc7fe40cc4e05b708d4 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 18:51:03 -0700 description: Removed deprecated njs_string_set(). --- src/njs_string.c | 52 ------------------------------------------------ src/njs_string.h | 3 --- src/test/njs_benchmark.c | 27 +------------------------ 3 files changed, 1 insertion(+), 81 deletions(-) diff --git a/src/njs_string.c b/src/njs_string.c index ea22755b..9405a8db 100644 --- a/src/njs_string.c +++ b/src/njs_string.c @@ -74,58 +74,6 @@ static njs_int_t njs_string_match_multiple(njs_vm_t *vm, njs_value_t *args, #define njs_base64_decoded_length(len, pad) (((len / 4) * 3) - pad) -njs_int_t -njs_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, - uint32_t size) -{ - u_char *dst; - const u_char *src; - njs_string_t *string; - - value->type = NJS_STRING; - njs_string_truth(value, size); - - if (size <= NJS_STRING_SHORT) { - value->short_string.size = size; - value->short_string.length = 0; - - dst = value->short_string.start; - src = start; - - while (size != 0) { - /* The maximum size is just 14 bytes. */ - njs_pragma_loop_disable_vectorization; - - *dst++ = *src++; - size--; - } - - } else { - /* - * Setting UTF-8 length is not required here, it just allows - * to store the constant in whole byte instead of bit twiddling. - */ - value->short_string.size = NJS_STRING_LONG; - value->short_string.length = 0; - value->long_string.external = 0xff; - value->long_string.size = size; - - string = njs_mp_alloc(vm->mem_pool, sizeof(njs_string_t)); - if (njs_slow_path(string == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } - - value->long_string.data = string; - - string->start = (u_char *) start; - string->length = 0; - } - - return NJS_OK; -} - - njs_int_t njs_string_create(njs_vm_t *vm, njs_value_t *value, const u_char *src, size_t size) diff --git a/src/njs_string.h b/src/njs_string.h index 9b478875..686a5a74 100644 --- a/src/njs_string.h +++ b/src/njs_string.h @@ -102,9 +102,6 @@ typedef enum { } njs_trim_t; -/* njs_string_set() Is deprecated. */ -njs_int_t njs_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, - uint32_t size); u_char *njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint64_t size, uint64_t length); njs_int_t njs_string_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, diff --git a/src/test/njs_benchmark.c b/src/test/njs_benchmark.c index f957dc56..71de6b57 100644 --- a/src/test/njs_benchmark.c +++ b/src/test/njs_benchmark.c @@ -265,26 +265,6 @@ static njs_benchmark_test_t njs_test[] = njs_str("4"), 100000 }, - { "string set 'abcdefABCDEF'", - njs_str("benchmark.string('set', 'abcdef', 1000000)"), - njs_str("undefined"), - 1 }, - - { "string set 'АБВГДЕ'", - njs_str("benchmark.string('set', 'АБВГДЕ', 1000000)"), - njs_str("undefined"), - 1 }, - - { "string set 'x'.repeat(24)", - njs_str("benchmark.string('set', 'x'.repeat(32), 1000000)"), - njs_str("undefined"), - 1 }, - - { "string set 'Д'.repeat(12)", - njs_str("benchmark.string('set', 'А'.repeat(16), 1000000)"), - njs_str("undefined"), - 1 }, - { "string create 'abcdefABCDEF'", njs_str("benchmark.string('create', 'abcdef', 1000000)"), njs_str("undefined"), @@ -723,12 +703,7 @@ njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - if (memcmp(mode.start, "set", 3) == 0) { - for (i = 0; i < n; i++) { - njs_string_set(vm, &value, s.start, s.length); - } - - } else if (memcmp(mode.start, "create", 6) == 0) { + if (memcmp(mode.start, "create", 6) == 0) { for (i = 0; i < n; i++) { njs_string_create(vm, &value, s.start, s.length); From noreply at nginx.com Tue Oct 8 14:30:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 14:30:03 +0000 (UTC) Subject: [nginx] Version bump. Message-ID: <20241008143003.A754A47845@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/144778aee6e377c9bcd84368848b2e27871d570f branches: master commit: 144778aee6e377c9bcd84368848b2e27871d570f user: Sergey Kandaurov date: Mon, 7 Oct 2024 19:10:39 +0400 description: Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/nginx.h b/src/core/nginx.h index 140a29408..fb6fa4737 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1027002 -#define NGINX_VERSION "1.27.2" +#define nginx_version 1027003 +#define NGINX_VERSION "1.27.3" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From noreply at nginx.com Tue Oct 8 15:56:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 15:56:02 +0000 (UTC) Subject: [nginx] QUIC: prevent deleted stream frame retransmissions. Message-ID: <20241008155602.A514547846@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/3f6d94d8881ca6755b1baa4dd6248a7b7ed15735 branches: master commit: 3f6d94d8881ca6755b1baa4dd6248a7b7ed15735 user: nandsky date: Mon, 30 Sep 2024 20:51:17 +0800 description: QUIC: prevent deleted stream frame retransmissions. Since a2a513b93cae, stream frames no longer need to be retransmitted after it was deleted. The frames which were retransmitted before, could be stream data frames sent prior to a RESET_STREAM. Such retransmissions are explicitly prohibited by RFC 9000, Section 19.4. --- src/event/quic/ngx_event_quic_ack.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c index c7ffd44dd..c953b8042 100644 --- a/src/event/quic/ngx_event_quic_ack.c +++ b/src/event/quic/ngx_event_quic_ack.c @@ -631,13 +631,12 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx) case NGX_QUIC_FT_STREAM: qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id); - if (qs) { - if (qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT - || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD) - { - ngx_quic_free_frame(c, f); - break; - } + if (qs == NULL + || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT + || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD) + { + ngx_quic_free_frame(c, f); + break; } /* fall through */ From noreply at nginx.com Tue Oct 8 23:04:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 23:04:01 +0000 (UTC) Subject: [njs] Optimized qjs_to_bytes(). Message-ID: <20241008230401.F389C47855@pubserv1.nginx> details: https://github.com/nginx/njs/commit/39a2d4bf212346d1487e4d27383453cafefa17ea branches: master commit: 39a2d4bf212346d1487e4d27383453cafefa17ea user: Dmitry Volyntsev date: Mon, 7 Oct 2024 22:46:40 -0700 description: Optimized qjs_to_bytes(). Doing JS_IsString() check first before a heavy-weight call to JS_GetTypedArrayBuffer() which throws an exception when argument is not a typed array. --- src/qjs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qjs.c b/src/qjs.c index e7653569..3d378fcc 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -175,6 +175,10 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) size_t byte_offset, byte_length; JSValue val; + if (JS_IsString(value)) { + goto string; + } + val = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, NULL); if (!JS_IsException(val)) { bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, val); @@ -195,8 +199,6 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) return 0; } - bytes->tag = JS_TAG_STRING; - if (!JS_IsString(value)) { val = JS_ToString(ctx, value); @@ -209,6 +211,9 @@ qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) } } +string: + + bytes->tag = JS_TAG_STRING; bytes->start = (u_char *) JS_ToCStringLen(ctx, &bytes->length, value); return (bytes->start != NULL) ? 0 : -1; From noreply at nginx.com Tue Oct 8 23:04:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 8 Oct 2024 23:04:01 +0000 (UTC) Subject: [njs] Optimized ngx_qjs_string(). Message-ID: <20241008230401.F059047854@pubserv1.nginx> details: https://github.com/nginx/njs/commit/3a4349e6b2b001001c64dd48f7863c5820cdf3e6 branches: master commit: 3a4349e6b2b001001c64dd48f7863c5820cdf3e6 user: Dmitry Volyntsev date: Mon, 7 Oct 2024 22:41:24 -0700 description: Optimized ngx_qjs_string(). Doing JS_IsString() check first before a heavy-weight call to JS_GetTypedArrayBuffer() which throws an exception when argument is not a typed array. --- nginx/ngx_js.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 5fe3dc84..98e48dc2 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1472,6 +1472,10 @@ ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) cx = e->u.qjs.ctx; + if (JS_IsString(val)) { + goto string; + } + buffer = JS_GetTypedArrayBuffer(cx, val, &byte_offset, &byte_length, NULL); if (!JS_IsException(buffer)) { start = JS_GetArrayBuffer(cx, &dst->len, buffer); @@ -1492,6 +1496,8 @@ ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) } } +string: + str = JS_ToCString(cx, val); if (str == NULL) { return NGX_ERROR; From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Fixed heap-buffer-overflow in Buffer.prototype.indexOf(). Message-ID: <20241010164402.E17FF4786A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6c8084b666cfe5db5d9401e7dff7981b5b2eb100 branches: master commit: 6c8084b666cfe5db5d9401e7dff7981b5b2eb100 user: Dmitry Volyntsev date: Wed, 9 Oct 2024 17:32:11 -0700 description: Fixed heap-buffer-overflow in Buffer.prototype.indexOf(). Previously, when `from` argument was provided heap-buffer-overflow might happen due to lack of boundary check. `to = njs_min(to, length)` statement was also removed because it has no effect, `to` is equal to `length` here. The issue was introduced in 5d15a8d6 (0.8.5). This closes #794 issue on Github. --- src/njs_buffer.c | 5 ++++- src/qjs_buffer.c | 7 ++++++- test/buffer.t.js | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 07054bf0..0bd5b896 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2228,7 +2228,10 @@ encoding: } else { to -= str.length - 1; - to = njs_min(to, length); + + if (from > to) { + goto done; + } } for (i = from; i != to; i += increment) { diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 2487c633..5def5e63 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -1098,7 +1098,10 @@ encoding: } else { to -= str.length - 1; - to = njs_min(to, length); + + if (from > to) { + goto done; + } } for (i = from; i != to; i += increment) { @@ -1108,6 +1111,8 @@ encoding: } } +done: + JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, -1); } diff --git a/test/buffer.t.js b/test/buffer.t.js index 55227b3a..f47c62f7 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -473,6 +473,7 @@ let indexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: -1 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'def', offset: -3, expected: 3 }, + { buf: Buffer.from('abcdef'), value: 'efgh', offset: 4, expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Fixed Buffer.prototype.lastIndexOf() when `from` is provided. Message-ID: <20241010164402.E7FE94786B@pubserv1.nginx> details: https://github.com/nginx/njs/commit/e0563f4dd6a2ab52b4796b5cc536ce14e8ad6483 branches: master commit: e0563f4dd6a2ab52b4796b5cc536ce14e8ad6483 user: Dmitry Volyntsev date: Wed, 9 Oct 2024 17:59:26 -0700 description: Fixed Buffer.prototype.lastIndexOf() when `from` is provided. Previous incorrect fix was introduced in 5d15a8d6 (0.8.5). --- src/njs_buffer.c | 15 +++++++++------ src/qjs_buffer.c | 16 +++++++++------- test/buffer.t.js | 2 ++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 0bd5b896..20618d24 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2218,13 +2218,12 @@ encoding: goto done; } - if (str.length > (size_t) length) { - goto done; - } - if (last) { - from -= str.length - 1; - from = njs_max(from, 0); + from = njs_min(from, length - (int64_t) str.length); + + if (to > from) { + goto done; + } } else { to -= str.length - 1; @@ -2246,6 +2245,10 @@ encoding: case NJS_NUMBER: byte = njs_number_to_uint32(njs_number(value)); + if (last) { + from = njs_min(from, length - 1); + } + for (i = from; i != to; i += increment) { if (u8[i] == byte) { index = i; diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 5def5e63..8a7b10ff 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -1047,6 +1047,10 @@ qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, return JS_EXCEPTION; } + if (last) { + from = njs_min(from, length - 1); + } + for (i = from; i != to; i += increment) { if (self.start[i] == (uint8_t) byte) { return JS_NewInt32(ctx, i); @@ -1087,14 +1091,12 @@ encoding: return JS_NewInt32(ctx, (last) ? length : 0); } - if (str.length > (size_t) length) { - JS_FreeValue(ctx, buffer); - return JS_NewInt32(ctx, -1); - } - if (last) { - from -= str.length - 1; - from = njs_max(from, 0); + from = njs_min(from, length - (int64_t) str.length); + + if (to > from) { + goto done; + } } else { to -= str.length - 1; diff --git a/test/buffer.t.js b/test/buffer.t.js index f47c62f7..0b8db0d3 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -583,6 +583,7 @@ let lastIndexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: -1 }, + { buf: Buffer.from('xxxABCx'), value: 'ABC', offset: 3, expected: 3 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdef', expected: 0 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdefg', expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, @@ -590,6 +591,7 @@ let lastIndexOf_tsuite = { exception: 'TypeError: "utf-128" encoding is not supported' }, { buf: Buffer.from('abcabc'), value: 0x61, expected: 3 }, { buf: Buffer.from('abcabc'), value: 0x61, offset: 1, expected: 0 }, + { buf: Buffer.from('ab'), value: 7, offset: 2, expected: -1 }, { buf: Buffer.from('abcdef'), value: Buffer.from('def'), expected: 3 }, { buf: Buffer.from('abcdef'), value: Buffer.from(new Uint8Array([0x60, 0x62, 0x63]).buffer, 1), expected: 1 }, { buf: Buffer.from('abcdef'), value: {}, From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Test262: reporting name of the testsuite for a failed test. Message-ID: <20241010164402.DC5C747864@pubserv1.nginx> details: https://github.com/nginx/njs/commit/7b9ae6607b1bc9dae126a1a81ce1041bc79d8734 branches: master commit: 7b9ae6607b1bc9dae126a1a81ce1041bc79d8734 user: Dmitry Volyntsev date: Wed, 9 Oct 2024 16:05:26 -0700 description: Test262: reporting name of the testsuite for a failed test. --- test/harness/runTsuite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/harness/runTsuite.js b/test/harness/runTsuite.js index aa3f5c0f..8103f661 100644 --- a/test/harness/runTsuite.js +++ b/test/harness/runTsuite.js @@ -30,7 +30,7 @@ async function run(tlist) { let r = results.map((r, i) => validate(ts.tests, r, i)); r.forEach((v, i) => { - assert.sameValue(v, true, `FAILED ${i}: ${JSON.stringify(ts.tests[i])}\n with reason: ${results[i].reason}`); + assert.sameValue(v, true, `FAILED ${ts.name}: ${JSON.stringify(ts.tests[i])}\n with reason: ${results[i].reason}`); }) } } From noreply at nginx.com Thu Oct 10 16:44:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 10 Oct 2024 16:44:02 +0000 (UTC) Subject: [njs] Fixed Buffer.prototype.indexOf() and friends. Message-ID: <20241010164402.EE6EE4786C@pubserv1.nginx> details: https://github.com/nginx/njs/commit/5a8bb2d172ad080efc0d719e7df609ac5355992e branches: master commit: 5a8bb2d172ad080efc0d719e7df609ac5355992e user: Dmitry Volyntsev date: Wed, 9 Oct 2024 18:25:52 -0700 description: Fixed Buffer.prototype.indexOf() and friends. With empty buffers in both the self and search cases. --- src/njs_buffer.c | 39 ++++++++------------------------------- src/qjs_buffer.c | 39 ++++++++------------------------------- test/buffer.t.js | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 62 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 20618d24..2f0fc620 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2117,10 +2117,6 @@ njs_buffer_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, index = -1; - if (njs_slow_path(array->byte_length == 0)) { - goto done; - } - length = array->byte_length; if (last) { @@ -2145,30 +2141,11 @@ njs_buffer_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } - if (last) { - if (from >= 0) { - from = njs_min(from, length - 1); - - } else if (from < 0) { - from += length; - } - - if (from <= to) { - goto done; - } + if (from >= 0) { + from = njs_min(from, length); } else { - if (from < 0) { - from += length; - - if (from < 0) { - from = 0; - } - } - - if (from >= to) { - goto done; - } + from = njs_max(0, length + from); } } @@ -2213,11 +2190,6 @@ encoding: str.length = src->byte_length; } - if (njs_slow_path(str.length == 0)) { - index = (last) ? length : 0; - goto done; - } - if (last) { from = njs_min(from, length - (int64_t) str.length); @@ -2233,6 +2205,11 @@ encoding: } } + if (from == to && str.length == 0) { + index = 0; + goto done; + } + for (i = from; i != to; i += increment) { if (memcmp(&u8[i], str.start, str.length) == 0) { index = i; diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 8a7b10ff..02618387 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -988,10 +988,6 @@ qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, length = self.length; - if (length == 0) { - return JS_NewInt32(ctx, -1); - } - if (last) { from = length - 1; to = -1; @@ -1015,30 +1011,11 @@ qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, return JS_EXCEPTION; } - if (last) { - if (from >= 0) { - from = njs_min(from, length - 1); - - } else if (from < 0) { - from += length; - } - - if (from <= to) { - return JS_NewInt32(ctx, -1); - } + if (from >= 0) { + from = njs_min(from, length); } else { - if (from < 0) { - from += length; - - if (from < 0) { - from = 0; - } - } - - if (from >= to) { - return JS_NewInt32(ctx, -1); - } + from = njs_max(0, length + from); } } @@ -1086,11 +1063,6 @@ encoding: "or Buffer-like object"); } - if (str.length == 0) { - JS_FreeValue(ctx, buffer); - return JS_NewInt32(ctx, (last) ? length : 0); - } - if (last) { from = njs_min(from, length - (int64_t) str.length); @@ -1106,6 +1078,11 @@ encoding: } } + if (from == to && str.length == 0) { + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, 0); + } + for (i = from; i != to; i += increment) { if (memcmp(&self.start[i], str.start, str.length) == 0) { JS_FreeValue(ctx, buffer); diff --git a/test/buffer.t.js b/test/buffer.t.js index 0b8db0d3..9becf487 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -474,6 +474,19 @@ let indexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'def', offset: -3, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'efgh', offset: 4, expected: -1 }, + { buf: Buffer.from(''), value: '', expected: 0 }, + { buf: Buffer.from(''), value: '', offset: -1, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 1, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -4, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -3, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -2, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: -1, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: 1, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: 2, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 3, expected: 3 }, + { buf: Buffer.from('abc'), value: '', offset: 4, expected: 3 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, @@ -584,6 +597,19 @@ let lastIndexOf_tsuite = { { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: -1 }, { buf: Buffer.from('xxxABCx'), value: 'ABC', offset: 3, expected: 3 }, + { buf: Buffer.from(''), value: '', expected: 0 }, + { buf: Buffer.from(''), value: '', offset: -1, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from(''), value: '', offset: 1, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -4, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -3, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: -2, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: -1, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 0, expected: 0 }, + { buf: Buffer.from('abc'), value: '', offset: 1, expected: 1 }, + { buf: Buffer.from('abc'), value: '', offset: 2, expected: 2 }, + { buf: Buffer.from('abc'), value: '', offset: 3, expected: 3 }, + { buf: Buffer.from('abc'), value: '', offset: 4, expected: 3 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdef', expected: 0 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdefg', expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, From noreply at nginx.com Fri Oct 11 01:28:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 11 Oct 2024 01:28:02 +0000 (UTC) Subject: [njs] Fixed Buffer.prototype.indexOf() on 32bits platforms. Message-ID: <20241011012802.41BDA47864@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6902aaa17d8a2f30c44f2ed0ed165605233a2c6d branches: master commit: 6902aaa17d8a2f30c44f2ed0ed165605233a2c6d user: Dmitry Volyntsev date: Thu, 10 Oct 2024 16:33:13 -0700 description: Fixed Buffer.prototype.indexOf() on 32bits platforms. --- src/njs_buffer.c | 2 +- src/qjs_buffer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 2f0fc620..3ce1b90a 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -2198,7 +2198,7 @@ encoding: } } else { - to -= str.length - 1; + to -= (int64_t) str.length - 1; if (from > to) { goto done; diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 02618387..9f451e26 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -1071,7 +1071,7 @@ encoding: } } else { - to -= str.length - 1; + to -= (int64_t) str.length - 1; if (from > to) { goto done; From noreply at nginx.com Fri Oct 11 01:28:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 11 Oct 2024 01:28:02 +0000 (UTC) Subject: [njs] CI: added 32bit test in check-pr job. Message-ID: <20241011012802.3D7B947851@pubserv1.nginx> details: https://github.com/nginx/njs/commit/78a34bf74fea40e14c25bfc09f86cc96a81e5c5c branches: master commit: 78a34bf74fea40e14c25bfc09f86cc96a81e5c5c user: Dmitry Volyntsev date: Thu, 10 Oct 2024 18:03:38 -0700 description: CI: added 32bit test in check-pr job. --- .github/workflows/check-pr.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 0160c1fd..6440e8ce 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -28,6 +28,12 @@ jobs: libgd-dev libxml2-dev libedit-dev libperl-dev libtest-harness-perl \ libgd-perl libgeoip-dev expect + - name: Install x86 build dependencies + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y gcc-multilib libc6:i386 libpcre2-dev:i386 zlib1g-dev:i386 + - name: Check out nginx run: | git clone https://github.com/nginx/nginx nginx-source @@ -55,6 +61,19 @@ jobs: $MAKE_UTILITY test $MAKE_UTILITY clean + - name: Configure and make njs, 32-bit + run: | + ./configure \ + --cc-opt="$CC_OPT -m32" \ + --ld-opt="$LD_OPT" \ + || cat build/autoconf.err + $MAKE_UTILITY -j$(nproc) + + - name: Test njs, 32-bit + run: | + $MAKE_UTILITY test + $MAKE_UTILITY clean + - name: Configure and make njs with quickjs run: | ./configure \