From f8394db6fe9c73858032bd202bf0809d459a2f2f Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 3 Aug 2021 20:50:08 +0300 Subject: [PATCH 01/30] 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 c4cab7b7c..51155410b 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1021001 -#define NGINX_VERSION "1.21.1" +#define nginx_version 1021002 +#define NGINX_VERSION "1.21.2" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From 15769c3918578dfa601303afa40d7acf9c36e4d9 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 3 Aug 2021 20:50:30 +0300 Subject: [PATCH 02/30] SSL: set events ready flags after handshake. The c->read->ready and c->write->ready flags might be reset during the handshake, and not set again if the handshake was finished on the other event. At the same time, some data might be read from the socket during the handshake, so missing c->read->ready flag might result in a connection hang, for example, when waiting for an SMTP greeting (which was already received during the handshake). Found by Sergey Kandaurov. --- src/event/ngx_event_openssl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 396cc22b3..60cc35876 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1740,6 +1740,9 @@ ngx_ssl_handshake(ngx_connection_t *c) c->recv_chain = ngx_ssl_recv_chain; c->send_chain = ngx_ssl_send_chain; + c->read->ready = 1; + c->write->ready = 1; + #ifndef SSL_OP_NO_RENEGOTIATION #if OPENSSL_VERSION_NUMBER < 0x10100000L #ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS @@ -1885,6 +1888,9 @@ ngx_ssl_try_early_data(ngx_connection_t *c) c->recv_chain = ngx_ssl_recv_chain; c->send_chain = ngx_ssl_send_chain; + c->read->ready = 1; + c->write->ready = 1; + rc = ngx_ssl_ocsp_validate(c); if (rc == NGX_ERROR) { From 02bd43d05b6f7803597d8453d9848b767dc4a323 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 4 Aug 2021 21:27:51 +0300 Subject: [PATCH 03/30] SSL: SSL_CTX_set_tmp_dh() error handling. For example, it can fail due to weak DH parameters. --- src/event/ngx_event_openssl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 60cc35876..2a0d0054f 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1376,7 +1376,13 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) return NGX_ERROR; } - SSL_CTX_set_tmp_dh(ssl->ctx, dh); + if (SSL_CTX_set_tmp_dh(ssl->ctx, dh) != 1) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_tmp_dh(\"%s\") failed", file->data); + DH_free(dh); + BIO_free(bio); + return NGX_ERROR; + } DH_free(dh); BIO_free(bio); From 7bcb50c0610a18bf43bef0062b2d2dc550823b53 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 9 Aug 2021 18:12:12 +0300 Subject: [PATCH 04/30] Disabled HTTP/1.0 requests with Transfer-Encoding. The latest HTTP/1.1 draft describes Transfer-Encoding in HTTP/1.0 as having potentially faulty message framing as that could have been forwarded without handling of the chunked encoding, and forbids processing subsequest requests over that connection: https://github.com/httpwg/http-core/issues/879. While handling of such requests is permitted, the most secure approach seems to reject them. --- src/http/ngx_http_request.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 2d1845d02..bf931bf35 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1983,6 +1983,14 @@ ngx_http_process_request_header(ngx_http_request_t *r) } if (r->headers_in.transfer_encoding) { + if (r->http_version < NGX_HTTP_VERSION_11) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP/1.0 request with " + "\"Transfer-Encoding\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + if (r->headers_in.transfer_encoding->value.len == 7 && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data, (u_char *) "chunked", 7) == 0) From 36af236d6e7102f8dfaacc7ea42112bbdeb76def Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:42:59 +0300 Subject: [PATCH 05/30] SSL: RSA data type is deprecated in OpenSSL 3.0. The only consumer is a callback function for SSL_CTX_set_tmp_rsa_callback() deprecated in OpenSSL 1.1.0. Now the function is conditionally compiled too. --- src/event/ngx_event_openssl.c | 6 +++++- src/event/ngx_event_openssl.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 2a0d0054f..7c262d964 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1116,6 +1116,8 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret) } +#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) + RSA * ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, int key_length) @@ -1126,7 +1128,7 @@ ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, return NULL; } -#if (OPENSSL_VERSION_NUMBER < 0x10100003L && !defined OPENSSL_NO_DEPRECATED) +#ifndef OPENSSL_NO_DEPRECATED if (key == NULL) { key = RSA_generate_key(512, RSA_F4, NULL, NULL); @@ -1137,6 +1139,8 @@ ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, return key; } +#endif + 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 a415b4bda..7fb4c5f8c 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -196,8 +196,10 @@ ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c); 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); +#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, int key_length); +#endif 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); From ccc9bbad32962f2c4cdba7942a69f8ca80507bc7 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:16 +0300 Subject: [PATCH 06/30] SSL: SSL_get_peer_certificate() is deprecated in OpenSSL 3.0. Switch to SSL_get1_peer_certificate() when building with OpenSSL 3.0 and OPENSSL_NO_DEPRECATED defined. --- src/event/ngx_event_openssl.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 7fb4c5f8c..a70b86221 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -64,6 +64,11 @@ #endif +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined SSL_get_peer_certificate) +#define SSL_get_peer_certificate(s) SSL_get1_peer_certificate(s) +#endif + + typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; From b26858aa3c4055f9f85d09854ece15ad27c792fb Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:16 +0300 Subject: [PATCH 07/30] SSL: using SSL_CTX_set0_tmp_dh_pkey() with OpenSSL 3.0 in dhparam. Using PEM_read_bio_DHparams() and SSL_CTX_set_tmp_dh() is deprecated as part of deprecating the low level DH functions in favor of EVP_PKEY: https://git.openssl.org/?p=openssl.git;a=commitdiff;h=163f6dc --- src/event/ngx_event_openssl.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 7c262d964..10a76473e 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1354,7 +1354,6 @@ ngx_ssl_passwords_cleanup(void *data) ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) { - DH *dh; BIO *bio; if (file->len == 0) { @@ -1372,6 +1371,10 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) return NGX_ERROR; } +#ifdef SSL_CTX_set_tmp_dh + { + DH *dh; + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); if (dh == NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, @@ -1389,6 +1392,33 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) } DH_free(dh); + } +#else + { + EVP_PKEY *dh; + + /* + * PEM_read_bio_DHparams() and SSL_CTX_set_tmp_dh() + * are deprecated in OpenSSL 3.0 + */ + + dh = PEM_read_bio_Parameters(bio, NULL); + if (dh == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PEM_read_bio_Parameters(\"%s\") failed", file->data); + BIO_free(bio); + return NGX_ERROR; + } + + if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set0_tmp_dh_pkey(\%s\") failed", file->data); + BIO_free(bio); + return NGX_ERROR; + } + } +#endif + BIO_free(bio); return NGX_OK; From 9609288e7ce4504188b0c8389bbc2f382eed14f5 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:16 +0300 Subject: [PATCH 08/30] SSL: ERR_peek_error_line_data() compatibility with OpenSSL 3.0. ERR_peek_error_line_data() was deprecated in favour of ERR_peek_error_all(). Here we use the ERR_peek_error_data() helper to pass only used arguments. --- src/event/ngx_event_openssl.c | 2 +- src/event/ngx_event_openssl.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 10a76473e..72dd20649 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -3280,7 +3280,7 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) for ( ;; ) { - n = ERR_peek_error_line_data(NULL, NULL, &data, &flags); + n = ERR_peek_error_data(&data, &flags); if (n == 0) { break; diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index a70b86221..a54a86a4e 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -69,6 +69,11 @@ #endif +#if (OPENSSL_VERSION_NUMBER < 0x30000000L && !defined ERR_peek_error_data) +#define ERR_peek_error_data(d, f) ERR_peek_error_line_data(NULL, NULL, d, f) +#endif + + typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t; From 3df7efd34b26328c94149b6828ea22bb73ce00a2 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:16 +0300 Subject: [PATCH 09/30] SSL: silenced warnings when building with OpenSSL 3.0. The OPENSSL_SUPPRESS_DEPRECATED macro is used to suppress deprecation warnings. This covers Session Tickets keys, SSL Engine, DH low level API for DHE ciphers. Unlike OPENSSL_API_COMPAT, it works well with OpenSSL built with no-deprecated. In particular, it doesn't unhide various macros in OpenSSL includes, which are meant to be hidden under OPENSSL_NO_DEPRECATED. --- src/event/ngx_event_openssl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index a54a86a4e..81b87d7cb 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -12,6 +12,8 @@ #include #include +#define OPENSSL_SUPPRESS_DEPRECATED + #include #include #include From 5155845ce4453a07d60e2ce43946c9181bc311fa Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:17 +0300 Subject: [PATCH 10/30] SSL: use of the SSL_OP_IGNORE_UNEXPECTED_EOF option. A new behaviour was introduced in OpenSSL 1.1.1e, when a peer does not send close_notify before closing the connection. Previously, it was to return SSL_ERROR_SYSCALL with errno 0, known since at least OpenSSL 0.9.7, and is handled gracefully in nginx. Now it returns SSL_ERROR_SSL with a distinct reason SSL_R_UNEXPECTED_EOF_WHILE_READING ("unexpected eof while reading"). This leads to critical errors seen in nginx within various routines such as SSL_do_handshake(), SSL_read(), SSL_shutdown(). The behaviour was restored in OpenSSL 1.1.1f, but presents in OpenSSL 3.0 by default. Use of the SSL_OP_IGNORE_UNEXPECTED_EOF option added in OpenSSL 3.0 allows to set a compatible behaviour to return SSL_ERROR_ZERO_RETURN: https://git.openssl.org/?p=openssl.git;a=commitdiff;h=09b90e0 See for additional details: https://github.com/openssl/openssl/issues/11381 --- src/event/ngx_event_openssl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 72dd20649..ce2a566cd 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -378,6 +378,10 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_CLIENT_RENEGOTIATION); #endif +#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF + SSL_CTX_set_options(ssl->ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); +#endif + #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS); #endif From 9e4e7a4e4202b5b61b785998e143c6ae0a39883c Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:17 +0300 Subject: [PATCH 11/30] SSL: removed export ciphers support. Export ciphers are forbidden to negotiate in TLS 1.1 and later protocol modes. They are disabled since OpenSSL 1.0.2g by default unless explicitly configured with "enable-weak-ssl-ciphers", and completely removed in OpenSSL 1.1.0. --- src/event/ngx_event_openssl.c | 31 ------------------------------- src/event/ngx_event_openssl.h | 5 ----- 2 files changed, 36 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index ce2a566cd..3705f5eaf 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -863,11 +863,6 @@ ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, SSL_CTX_set_options(ssl->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); } -#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) - /* a temporary 512-bit RSA key is required for export versions of MSIE */ - SSL_CTX_set_tmp_rsa_callback(ssl->ctx, ngx_ssl_rsa512_key_callback); -#endif - return NGX_OK; } @@ -1120,32 +1115,6 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret) } -#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) - -RSA * -ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, - int key_length) -{ - static RSA *key; - - if (key_length != 512) { - return NULL; - } - -#ifndef OPENSSL_NO_DEPRECATED - - if (key == NULL) { - key = RSA_generate_key(512, RSA_F4, NULL, NULL); - } - -#endif - - return key; -} - -#endif - - 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 81b87d7cb..68e970b89 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -29,7 +29,6 @@ #include #endif #include -#include #include #include @@ -208,10 +207,6 @@ ngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c); 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); -#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER) -RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, - int key_length); -#endif 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); From 926e0aa70acd58d2a13451f40fc19aeb5393aa7e Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Tue, 10 Aug 2021 23:43:17 +0300 Subject: [PATCH 12/30] SSL: removed use of the SSL_OP_MSIE_SSLV2_RSA_PADDING option. It has no effect since OpenSSL 0.9.7h and 0.9.8a. --- src/event/ngx_event_openssl.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 3705f5eaf..c087884ce 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -299,11 +299,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); #endif -#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING - /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */ - SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING); -#endif - #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG); #endif From e455bcedf73ad8aff8b0acd1101b3f8cbde9ff37 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 16 Aug 2021 16:36:06 +0300 Subject: [PATCH 13/30] Welcome and 50x error pages style. Indentation of the CSS code removed to match style of the HTML code. --- docs/html/50x.html | 7 ++----- docs/html/index.html | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/html/50x.html b/docs/html/50x.html index 9071e0a24..7dd7c909c 100644 --- a/docs/html/50x.html +++ b/docs/html/50x.html @@ -3,11 +3,8 @@ Error diff --git a/docs/html/index.html b/docs/html/index.html index 2ca3b9543..09638f717 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -3,11 +3,8 @@ Welcome to nginx! From b381cbc085502b76a1db3f4439ccec5bb01b5a54 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 16 Aug 2021 16:36:08 +0300 Subject: [PATCH 14/30] Dark mode support in welcome and 50x error pages. Prodded by Duncan Lock. --- docs/html/50x.html | 1 + docs/html/index.html | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/html/50x.html b/docs/html/50x.html index 7dd7c909c..a57c2f93d 100644 --- a/docs/html/50x.html +++ b/docs/html/50x.html @@ -3,6 +3,7 @@ Error diff --git a/docs/html/index.html b/docs/html/index.html index 09638f717..e8f562255 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -3,6 +3,7 @@ Welcome to nginx! From ce5996cdd1b2e150f645efbc337e5a681dbe241c Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 16 Aug 2021 22:40:31 +0300 Subject: [PATCH 15/30] SSL: ciphers now set before loading certificates (ticket #2035). To load old/weak server or client certificates it might be needed to adjust the security level, as introduced in OpenSSL 1.1.0. This change ensures that ciphers are set before loading the certificates, so security level changes via the cipher string apply to certificate loading. --- src/http/modules/ngx_http_grpc_module.c | 12 ++++++------ src/http/modules/ngx_http_proxy_module.c | 12 ++++++------ src/http/modules/ngx_http_ssl_module.c | 14 +++++++------- src/http/modules/ngx_http_uwsgi_module.c | 12 ++++++------ src/mail/ngx_mail_ssl_module.c | 14 +++++++------- src/stream/ngx_stream_proxy_module.c | 8 ++++---- src/stream/ngx_stream_ssl_module.c | 14 +++++++------- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c index 65bd1e6c3..6842b7c6e 100644 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -4896,6 +4896,12 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf) cln->handler = ngx_ssl_cleanup_ctx; cln->data = glcf->upstream.ssl; + if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0) + != NGX_OK) + { + return NGX_ERROR; + } + if (glcf->upstream.ssl_certificate) { if (glcf->upstream.ssl_certificate_key == NULL) { @@ -4927,12 +4933,6 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf) } } - if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0) - != NGX_OK) - { - return NGX_ERROR; - } - if (glcf->upstream.ssl_verify) { if (glcf->ssl_trusted_certificate.len == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 368297e77..084462746 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -4944,6 +4944,12 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) cln->handler = ngx_ssl_cleanup_ctx; cln->data = plcf->upstream.ssl; + if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0) + != NGX_OK) + { + return NGX_ERROR; + } + if (plcf->upstream.ssl_certificate) { if (plcf->upstream.ssl_certificate_key == NULL) { @@ -4975,12 +4981,6 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) } } - if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0) - != NGX_OK) - { - return NGX_ERROR; - } - if (plcf->upstream.ssl_verify) { if (plcf->ssl_trusted_certificate.len == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index a47d6963a..1a744fff1 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -797,6 +797,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_ssl_npn_advertised, NULL); #endif + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + if (ngx_http_ssl_compile_certificates(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -829,13 +836,6 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } - if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, - conf->prefer_server_ciphers) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - conf->ssl.buffer_size = conf->buffer_size; if (conf->verify) { diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 40a06c78e..4f9c349c2 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -2432,6 +2432,12 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf) cln->handler = ngx_ssl_cleanup_ctx; cln->data = uwcf->upstream.ssl; + if (ngx_ssl_ciphers(cf, uwcf->upstream.ssl, &uwcf->ssl_ciphers, 0) + != NGX_OK) + { + return NGX_ERROR; + } + if (uwcf->upstream.ssl_certificate) { if (uwcf->upstream.ssl_certificate_key == NULL) { @@ -2463,12 +2469,6 @@ ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf) } } - if (ngx_ssl_ciphers(cf, uwcf->upstream.ssl, &uwcf->ssl_ciphers, 0) - != NGX_OK) - { - return NGX_ERROR; - } - if (uwcf->upstream.ssl_verify) { if (uwcf->ssl_trusted_certificate.len == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 7eae83e25..09cc425d6 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -394,6 +394,13 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, conf->certificate_keys, conf->passwords) != NGX_OK) @@ -430,13 +437,6 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) } } - if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, - conf->prefer_server_ciphers) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { return NGX_CONF_ERROR; } diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 8c686ab20..1275cf225 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -2185,6 +2185,10 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf) cln->handler = ngx_ssl_cleanup_ctx; cln->data = pscf->ssl; + if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) { + return NGX_ERROR; + } + if (pscf->ssl_certificate) { if (pscf->ssl_certificate_key == NULL) { @@ -2216,10 +2220,6 @@ ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf) } } - if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) { - return NGX_ERROR; - } - if (pscf->ssl_verify) { if (pscf->ssl_trusted_certificate.len == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index d8c0471ea..b7350002c 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -720,6 +720,13 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_stream_ssl_servername); #endif + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + if (ngx_stream_ssl_compile_certificates(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -752,13 +759,6 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) } } - if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, - conf->prefer_server_ciphers) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - if (conf->verify) { if (conf->client_certificate.len == 0 && conf->verify != 3) { From d4dad02e5ecd523f896a87a08a6582853d83a14d Mon Sep 17 00:00:00 2001 From: Rob Mueller Date: Fri, 13 Aug 2021 03:57:47 -0400 Subject: [PATCH 16/30] Mail: Auth-SSL-Protocol and Auth-SSL-Cipher headers (ticket #2134). This adds new Auth-SSL-Protocol and Auth-SSL-Cipher headers to the mail proxy auth protocol when SSL is enabled. This can be useful for detecting users using older clients that negotiate old ciphers when you want to upgrade to newer TLS versions of remove suppport for old and insecure ciphers. You can use your auth backend to notify these users before the upgrade that they either need to upgrade their client software or contact your support team to work out an upgrade path. --- src/mail/ngx_mail_auth_http_module.c | 41 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/mail/ngx_mail_auth_http_module.c b/src/mail/ngx_mail_auth_http_module.c index 2a198f494..27f64b92e 100644 --- a/src/mail/ngx_mail_auth_http_module.c +++ b/src/mail/ngx_mail_auth_http_module.c @@ -1137,8 +1137,8 @@ ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, ngx_str_t login, passwd; ngx_connection_t *c; #if (NGX_MAIL_SSL) - ngx_str_t verify, subject, issuer, serial, fingerprint, - raw_cert, cert; + ngx_str_t protocol, cipher, verify, subject, issuer, + serial, fingerprint, raw_cert, cert; ngx_mail_ssl_conf_t *sslcf; #endif ngx_mail_core_srv_conf_t *cscf; @@ -1155,6 +1155,25 @@ ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, #if (NGX_MAIL_SSL) + if (c->ssl) { + + if (ngx_ssl_get_protocol(c, pool, &protocol) != NGX_OK) { + return NULL; + } + + protocol.len = ngx_strlen(protocol.data); + + if (ngx_ssl_get_cipher_name(c, pool, &cipher) != NGX_OK) { + return NULL; + } + + cipher.len = ngx_strlen(cipher.data); + + } else { + ngx_str_null(&protocol); + ngx_str_null(&cipher); + } + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (c->ssl && sslcf->verify) { @@ -1252,6 +1271,10 @@ ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, if (c->ssl) { len += sizeof("Auth-SSL: on" CRLF) - 1 + + sizeof("Auth-SSL-Protocol: ") - 1 + protocol.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Cipher: ") - 1 + cipher.len + + sizeof(CRLF) - 1 + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + sizeof(CRLF) - 1 + sizeof("Auth-SSL-Subject: ") - 1 + subject.len @@ -1373,6 +1396,20 @@ ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, b->last = ngx_cpymem(b->last, "Auth-SSL: on" CRLF, sizeof("Auth-SSL: on" CRLF) - 1); + if (protocol.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Protocol: ", + sizeof("Auth-SSL-Protocol: ") - 1); + b->last = ngx_copy(b->last, protocol.data, protocol.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (cipher.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Cipher: ", + sizeof("Auth-SSL-Cipher: ") - 1); + b->last = ngx_copy(b->last, cipher.data, cipher.len); + *b->last++ = CR; *b->last++ = LF; + } + if (verify.len) { b->last = ngx_cpymem(b->last, "Auth-SSL-Verify: ", sizeof("Auth-SSL-Verify: ") - 1); From c231640eba9e26e963460c83f2907ac6f9abf3fc Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Fri, 20 Aug 2021 03:53:56 +0300 Subject: [PATCH 17/30] Upstream: fixed timeouts with gRPC, SSL and select (ticket #2229). With SSL it is possible that an established connection is ready for reading after the handshake. Further, events might be already disabled in case of level-triggered event methods. If this happens and ngx_http_upstream_send_request() blocks waiting for some data from the upstream, such as flow control in case of gRPC, the connection will time out due to no read events on the upstream connection. Fix is to explicitly check the c->read->ready flag if sending request blocks and post a read event if it is set. Note that while it is possible to modify ngx_ssl_handshake() to keep read events active, this won't completely resolve the issue, since there can be data already received during the SSL handshake (see 573bd30e46b4). --- src/http/ngx_http_upstream.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 61fad5ca3..01c7877e6 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2113,6 +2113,10 @@ ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; } + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); + } + return; } From 301efb8a73b7f14e41e2238cfef50b2b98137eab Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:20:34 +0300 Subject: [PATCH 18/30] HTTP/2: improved body reading logging. --- src/http/v2/ngx_http_v2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 3bef002bd..ac10c2ab6 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4154,6 +4154,9 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, rb = r->request_body; buf = rb->buf; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 process request body"); + if (size) { if (buf->sync) { buf->pos = buf->start = pos; @@ -4364,6 +4367,9 @@ ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r) stream = r->stream; fc = r->connection; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 read unbuffered request body"); + if (fc->read->timedout) { if (stream->recv_window) { stream->skip_data = 1; From 78d9a3af91a31bb77ad74aca7286f1ef2dad89d6 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:20:36 +0300 Subject: [PATCH 19/30] HTTP/2: reworked body reading to better match HTTP/1.x code. In particular, now the code always uses a buffer limited by client_body_buffer_size. At the cost of an additional copy it ensures that small DATA frames are not directly mapped to small write() syscalls, but rather buffered in memory before writing. Further, requests without Content-Length are no longer forced to use temporary files. --- src/http/v2/ngx_http_v2.c | 168 +++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 67 deletions(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index ac10c2ab6..461bbff84 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4032,11 +4032,11 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) len = r->headers_in.content_length_n; - if (r->request_body_no_buffering && !stream->in_closed) { + if (len < 0 || len > (off_t) clcf->client_body_buffer_size) { + len = clcf->client_body_buffer_size; + } - if (len < 0 || len > (off_t) clcf->client_body_buffer_size) { - len = clcf->client_body_buffer_size; - } + if (r->request_body_no_buffering && !stream->in_closed) { /* * We need a room to store data up to the stream's initial window size, @@ -4050,22 +4050,10 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) if (len > NGX_HTTP_V2_MAX_WINDOW) { len = NGX_HTTP_V2_MAX_WINDOW; } - - rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); - - } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size - && !r->request_body_in_file_only) - { - rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); - - } else { - rb->buf = ngx_calloc_buf(r->pool); - - if (rb->buf != NULL) { - rb->buf->sync = 1; - } } + rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); + if (rb->buf == NULL) { stream->skip_data = 1; return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -4144,7 +4132,7 @@ static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, size_t size, ngx_uint_t last) { - ngx_buf_t *buf; + size_t n; ngx_int_t rc; ngx_connection_t *fc; ngx_http_request_body_t *rb; @@ -4152,80 +4140,126 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, fc = r->connection; rb = r->request_body; - buf = rb->buf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 process request body"); - if (size) { - if (buf->sync) { - buf->pos = buf->start = pos; - buf->last = buf->end = pos + size; + if (size == 0 && !last) { + return NGX_OK; + } - r->request_body_in_file_only = 1; + for ( ;; ) { + for ( ;; ) { + if (rb->buf->last == rb->buf->end && size) { - } else { - if (size > (size_t) (buf->end - buf->last)) { - ngx_log_error(NGX_LOG_INFO, fc->log, 0, - "client intended to send body data " - "larger than declared"); + if (r->request_body_no_buffering) { - return NGX_HTTP_BAD_REQUEST; + /* should never happen due to flow control */ + + ngx_log_error(NGX_LOG_ALERT, fc->log, 0, + "no space in http2 body buffer"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* update chains */ + + ngx_log_error(NGX_LOG_DEBUG, fc->log, 0, + "http2 body update chains"); + + rc = ngx_http_v2_filter_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->busy != NULL) { + ngx_log_error(NGX_LOG_ALERT, fc->log, 0, + "busy buffers after request body flush"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->buf->pos = rb->buf->start; + rb->buf->last = rb->buf->start; } - buf->last = ngx_cpymem(buf->last, pos, size); - } - } + /* copy body data to the buffer */ - if (last) { - rb->rest = 0; + n = rb->buf->end - rb->buf->last; - if (fc->read->timer_set) { - ngx_del_timer(fc->read); + if (n > size) { + n = size; + } + + rb->buf->last = ngx_cpymem(rb->buf->last, pos, n); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 request body recv %uz", n); + + pos += n; + size -= n; + + if (size == 0 && last) { + rb->rest = 0; + } + + if (r->request_body_no_buffering) { + break; + } + + /* pass buffer to request body filter chain */ + + rc = ngx_http_v2_filter_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->rest == 0) { + break; + } + + if (size == 0) { + break; + } } - if (r->request_body_no_buffering) { - ngx_post_event(fc->read, &ngx_posted_events); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 request body rest %O", rb->rest); + + if (rb->rest == 0) { + break; + } + + if (size == 0) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_add_timer(fc->read, clcf->client_body_timeout); + + if (r->request_body_no_buffering) { + ngx_post_event(fc->read, &ngx_posted_events); + return NGX_OK; + } + return NGX_OK; } - - rc = ngx_http_v2_filter_request_body(r); - - if (rc != NGX_OK) { - return rc; - } - - if (buf->sync) { - /* prevent reusing this buffer in the upstream module */ - rb->buf = NULL; - } - - if (r->headers_in.chunked) { - r->headers_in.content_length_n = rb->received; - } - - r->read_event_handler = ngx_http_block_reading; - rb->post_handler(r); - - return NGX_OK; } - if (size == 0) { - return NGX_OK; + if (fc->read->timer_set) { + ngx_del_timer(fc->read); } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_add_timer(fc->read, clcf->client_body_timeout); - if (r->request_body_no_buffering) { ngx_post_event(fc->read, &ngx_posted_events); return NGX_OK; } - if (buf->sync) { - return ngx_http_v2_filter_request_body(r); + if (r->headers_in.chunked) { + r->headers_in.content_length_n = rb->received; } + r->read_event_handler = ngx_http_block_reading; + rb->post_handler(r); + return NGX_OK; } From 9f90d11cf52ccb0a8d086525aa7e4218d31d529a Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:20:38 +0300 Subject: [PATCH 20/30] HTTP/2: improved handling of END_STREAM in a separate DATA frame. The save body filter saves the request body to disk once the buffer is full. Yet in HTTP/2 this might happen even if there is no need to save anything to disk, notably when content length is known and the END_STREAM flag is sent in a separate empty DATA frame. Workaround is to provide additional byte in the buffer, so saving the request body won't be triggered. This fixes unexpected request body disk buffering in HTTP/2 observed after the previous change when content length is known and the END_STREAM flag is sent in a separate empty DATA frame. --- src/http/v2/ngx_http_v2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 461bbff84..a037e7a52 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4034,6 +4034,9 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) if (len < 0 || len > (off_t) clcf->client_body_buffer_size) { len = clcf->client_body_buffer_size; + + } else { + len++; } if (r->request_body_no_buffering && !stream->in_closed) { From 2862eb40e86190bf4d337a6483150f4385fb5358 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:20:44 +0300 Subject: [PATCH 21/30] HTTP/2: improved handling of preread unbuffered requests. Previously, fully preread unbuffered requests larger than client body buffer size were saved to disk, despite the fact that "unbuffered" is expected to imply no disk buffering. --- src/http/v2/ngx_http_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index a037e7a52..9e248758d 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4039,7 +4039,7 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) len++; } - if (r->request_body_no_buffering && !stream->in_closed) { + if (r->request_body_no_buffering) { /* * We need a room to store data up to the stream's initial window size, From aa02695f5ea70f6628317b56f93e7e8733d4a029 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:20:49 +0300 Subject: [PATCH 22/30] Request body: missing comments about initialization. --- src/http/ngx_http_request_body.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 0cae88f77..b72614ff9 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -62,11 +62,13 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, /* * set by ngx_pcalloc(): * + * rb->temp_file = NULL; * rb->bufs = NULL; * rb->buf = NULL; * rb->free = NULL; * rb->busy = NULL; * rb->chunked = NULL; + * rb->received = 0; */ rb->rest = -1; From fd9d43b087b4247f8cb5f91845be46bd624c175a Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:20:54 +0300 Subject: [PATCH 23/30] Request body: added alert to catch duplicate body saving. If due to an error ngx_http_request_body_save_filter() is called more than once with rb->rest == 0, this used to result in a segmentation fault. Added an alert to catch such errors, just in case. --- src/http/ngx_http_request_body.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index b72614ff9..c5401fd42 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -1246,6 +1246,12 @@ ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) if (rb->temp_file || r->request_body_in_file_only) { + if (rb->bufs && rb->bufs->buf->in_file) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "body already in file"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + if (ngx_http_write_request_body(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } From 2a709213800fd3fd2809881374eb110562b53c08 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:21:03 +0300 Subject: [PATCH 24/30] Request body: introduced rb->last_saved flag. It indicates that the last buffer was received by the save filter, and can be used to check this at higher levels. To be used in the following changes. --- src/http/ngx_http_request.h | 1 + src/http/ngx_http_request_body.c | 72 ++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 63576274e..896657890 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -302,6 +302,7 @@ typedef struct { ngx_chain_t *busy; ngx_http_chunked_t *chunked; ngx_http_client_body_handler_pt post_handler; + unsigned last_saved:1; } ngx_http_request_body_t; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index c5401fd42..4a31db0bc 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -69,6 +69,7 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, * rb->busy = NULL; * rb->chunked = NULL; * rb->received = 0; + * rb->last_saved = 0; */ rb->rest = -1; @@ -941,15 +942,32 @@ ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) rb = r->request_body; + out = NULL; + ll = &out; + if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http request body content length filter"); rb->rest = r->headers_in.content_length_n; - } - out = NULL; - ll = &out; + if (rb->rest == 0) { + + tl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (tl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b = tl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->last_buf = 1; + + *ll = tl; + ll = &tl->next; + } + } for (cl = in; cl; cl = cl->next) { @@ -1013,6 +1031,9 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) rb = r->request_body; + out = NULL; + ll = &out; + if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1029,9 +1050,6 @@ ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) rb->rest = cscf->large_client_header_buffers.size; } - out = NULL; - ll = &out; - for (cl = in; cl; cl = cl->next) { b = NULL; @@ -1188,15 +1206,16 @@ ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_buf_t *b; - ngx_chain_t *cl; + ngx_chain_t *cl, *tl, **ll; ngx_http_request_body_t *rb; rb = r->request_body; -#if (NGX_DEBUG) + ll = &rb->bufs; + + for (cl = rb->bufs; cl; cl = cl->next) { #if 0 - for (cl = rb->bufs; cl; cl = cl->next) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, "http body old buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %O", @@ -1205,10 +1224,13 @@ ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); - } #endif + ll = &cl->next; + } + for (cl = in; cl; cl = cl->next) { + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, "http body new buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %O", @@ -1217,15 +1239,31 @@ ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); + + if (cl->buf->last_buf) { + + if (rb->last_saved) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "duplicate last buf in save filter"); + *ll = NULL; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->last_saved = 1; + } + + tl = ngx_alloc_chain_link(r->pool); + if (tl == NULL) { + *ll = NULL; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + tl->buf = cl->buf; + *ll = tl; + ll = &tl->next; } -#endif - - /* TODO: coalesce neighbouring buffers */ - - if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + *ll = NULL; if (r->request_body_no_buffering) { return NGX_OK; From 67d160bf25e02ba6679bb6c3b9cbdfeb29b759de Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Sun, 29 Aug 2021 22:22:02 +0300 Subject: [PATCH 25/30] Request body: reading body buffering in filters. If a filter wants to buffer the request body during reading (for example, to check an external scanner), it can now do so. To make it possible, the code now checks rb->last_saved (introduced in the previous change) along with rb->rest == 0. Since in HTTP/2 this requires flow control to avoid overflowing the request body buffer, so filters which need buffering have to set the rb->filter_need_buffering flag on the first filter call. (Note that each filter is expected to call the next filter, so all filters will be able set the flag if needed.) --- src/http/ngx_http_request.h | 2 + src/http/ngx_http_request_body.c | 48 +++++++++- src/http/v2/ngx_http_v2.c | 158 ++++++++++++++++++++++++++----- 3 files changed, 182 insertions(+), 26 deletions(-) diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 896657890..b1269d22d 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -302,6 +302,8 @@ typedef struct { ngx_chain_t *busy; ngx_http_chunked_t *chunked; ngx_http_client_body_handler_pt post_handler; + unsigned filter_need_buffering:1; + unsigned last_sent:1; unsigned last_saved:1; } ngx_http_request_body_t; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c index 4a31db0bc..89a4c7492 100644 --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -69,6 +69,8 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, * rb->busy = NULL; * rb->chunked = NULL; * rb->received = 0; + * rb->filter_need_buffering = 0; + * rb->last_sent = 0; * rb->last_saved = 0; */ @@ -147,7 +149,7 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, } } - if (rb->rest == 0) { + if (rb->rest == 0 && rb->last_saved) { /* the whole request body was pre-read */ r->request_body_no_buffering = 0; post_handler(r); @@ -175,6 +177,10 @@ ngx_http_read_client_request_body(ngx_http_request_t *r, size += preread; } + if (size == 0) { + size++; + } + } else { size = clcf->client_body_buffer_size; } @@ -273,6 +279,7 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) size_t size; ssize_t n; ngx_int_t rc; + ngx_uint_t flush; ngx_chain_t out; ngx_connection_t *c; ngx_http_request_body_t *rb; @@ -280,12 +287,17 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) c = r->connection; rb = r->request_body; + flush = 1; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http read client request body"); for ( ;; ) { for ( ;; ) { + if (rb->rest == 0) { + break; + } + if (rb->buf->last == rb->buf->end) { /* update chains */ @@ -309,12 +321,25 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) return NGX_AGAIN; } + if (rb->filter_need_buffering) { + clcf = ngx_http_get_module_loc_conf(r, + ngx_http_core_module); + ngx_add_timer(c->read, clcf->client_body_timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "busy buffers after request body flush"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } + flush = 0; rb->buf->pos = rb->buf->start; rb->buf->last = rb->buf->start; } @@ -326,6 +351,10 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) size = (size_t) rest; } + if (size == 0) { + break; + } + n = c->recv(c, rb->buf->last, size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, @@ -350,6 +379,7 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) /* pass buffer to request body filter chain */ + flush = 0; out.buf = rb->buf; out.next = NULL; @@ -371,11 +401,19 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http client request body rest %O", rb->rest); - if (rb->rest == 0) { + if (flush) { + rc = ngx_http_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + return rc; + } + } + + if (rb->rest == 0 && rb->last_saved) { break; } - if (!c->read->ready) { + if (!c->read->ready || rb->rest == 0) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_add_timer(c->read, clcf->client_body_timeout); @@ -1280,7 +1318,9 @@ ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) return NGX_OK; } - /* rb->rest == 0 */ + if (!rb->last_saved) { + return NGX_OK; + } if (rb->temp_file || r->request_body_in_file_only) { diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 9e248758d..5ccb36360 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -173,7 +173,7 @@ static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); static void ngx_http_v2_run_request(ngx_http_request_t *r); static void ngx_http_v2_run_request_handler(ngx_event_t *ev); static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, - u_char *pos, size_t size, ngx_uint_t last); + u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush); static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r); static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); @@ -1092,7 +1092,7 @@ static u_char * ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - size_t size; + size_t size, window; ngx_buf_t *buf; ngx_int_t rc; ngx_connection_t *fc; @@ -1140,13 +1140,40 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, h2c->payload_bytes += size; if (r->request_body) { - rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); + rc = ngx_http_v2_process_request_body(r, pos, size, + stream->in_closed, 0); - if (rc != NGX_OK) { + if (rc != NGX_OK && rc != NGX_AGAIN) { stream->skip_data = 1; ngx_http_finalize_request(r, rc); } + if (rc == NGX_AGAIN && !stream->no_flow_control) { + buf = r->request_body->buf; + window = buf->end - buf->last; + + window -= h2c->state.length - size; + + if (window < stream->recv_window) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "http2 negative window update"); + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + if (window > stream->recv_window) { + if (ngx_http_v2_send_window_update(h2c, stream->node->id, + window - stream->recv_window) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->recv_window = window; + } + } + ngx_http_run_posted_requests(fc); } else if (size) { @@ -4027,6 +4054,17 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) return NGX_OK; } + rb->rest = 1; + + /* set rb->filter_need_buffering */ + + rc = ngx_http_top_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + stream->skip_data = 1; + return rc; + } + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -4039,7 +4077,7 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) len++; } - if (r->request_body_no_buffering) { + if (r->request_body_no_buffering || rb->filter_need_buffering) { /* * We need a room to store data up to the stream's initial window size, @@ -4062,36 +4100,45 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) return NGX_HTTP_INTERNAL_SERVER_ERROR; } - rb->rest = 1; - buf = stream->preread; if (stream->in_closed) { - r->request_body_no_buffering = 0; + if (!rb->filter_need_buffering) { + r->request_body_no_buffering = 0; + } if (buf) { rc = ngx_http_v2_process_request_body(r, buf->pos, - buf->last - buf->pos, 1); + buf->last - buf->pos, 1, 0); ngx_pfree(r->pool, buf->start); + + } else { + rc = ngx_http_v2_process_request_body(r, NULL, 0, 1, 0); + } + + if (rc != NGX_AGAIN) { return rc; } - return ngx_http_v2_process_request_body(r, NULL, 0, 1); + r->read_event_handler = ngx_http_v2_read_client_request_body_handler; + r->write_event_handler = ngx_http_request_empty_handler; + + return NGX_AGAIN; } if (buf) { rc = ngx_http_v2_process_request_body(r, buf->pos, - buf->last - buf->pos, 0); + buf->last - buf->pos, 0, 0); ngx_pfree(r->pool, buf->start); - if (rc != NGX_OK) { + if (rc != NGX_OK && rc != NGX_AGAIN) { stream->skip_data = 1; return rc; } } - if (r->request_body_no_buffering) { + if (r->request_body_no_buffering || rb->filter_need_buffering) { size = (size_t) len - h2scf->preread_size; } else { @@ -4133,7 +4180,7 @@ ngx_http_v2_read_request_body(ngx_http_request_t *r) static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, - size_t size, ngx_uint_t last) + size_t size, ngx_uint_t last, ngx_uint_t flush) { size_t n; ngx_int_t rc; @@ -4147,8 +4194,8 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 process request body"); - if (size == 0 && !last) { - return NGX_OK; + if (size == 0 && !last && !flush) { + return NGX_AGAIN; } for ( ;; ) { @@ -4230,7 +4277,7 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 request body rest %O", rb->rest); - if (rb->rest == 0) { + if (rb->rest == 0 && rb->last_saved) { break; } @@ -4240,10 +4287,10 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, if (r->request_body_no_buffering) { ngx_post_event(fc->read, &ngx_posted_events); - return NGX_OK; + return NGX_AGAIN; } - return NGX_OK; + return NGX_AGAIN; } } @@ -4279,7 +4326,7 @@ ngx_http_v2_filter_request_body(ngx_http_request_t *r) rb = r->request_body; buf = rb->buf; - if (buf->pos == buf->last && rb->rest) { + if (buf->pos == buf->last && (rb->rest || rb->last_sent)) { cl = NULL; goto update; } @@ -4342,6 +4389,7 @@ ngx_http_v2_filter_request_body(ngx_http_request_t *r) } b->last_buf = 1; + rb->last_sent = 1; } b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; @@ -4361,7 +4409,12 @@ update: static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) { - ngx_connection_t *fc; + size_t window; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_connection_t *h2c; fc = r->connection; @@ -4387,6 +4440,63 @@ ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); return; } + + rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1); + + if (rc != NGX_OK && rc != NGX_AGAIN) { + r->stream->skip_data = 1; + ngx_http_finalize_request(r, rc); + return; + } + + if (rc == NGX_OK) { + return; + } + + if (r->request_body->rest == 0) { + return; + } + + stream = r->stream; + h2c = stream->connection; + + buf = r->request_body->buf; + window = buf->end - buf->start; + + if (h2c->state.stream == stream) { + window -= h2c->state.length; + } + + if (window <= stream->recv_window) { + if (window < stream->recv_window) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "http2 negative window update"); + + stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + + if (ngx_http_v2_send_window_update(h2c, stream->node->id, + window - stream->recv_window) + == NGX_ERROR) + { + stream->skip_data = 1; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + stream->recv_window = window; + + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + stream->skip_data = 1; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } } @@ -4430,10 +4540,14 @@ ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r) return rc; } - if (!r->request_body->rest) { + if (r->request_body->rest == 0 && r->request_body->last_saved) { return NGX_OK; } + if (r->request_body->rest == 0) { + return NGX_AGAIN; + } + if (r->request_body->busy != NULL) { return NGX_AGAIN; } From 1e8c0d4e0673f50863230eda989867d3012fb349 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Mon, 30 Aug 2021 14:45:21 +0300 Subject: [PATCH 26/30] Give GCC atomics precedence over deprecated Darwin atomic(3). This allows to build nginx on macOS with -Wdeprecated-declarations. --- src/os/unix/ngx_atomic.h | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/os/unix/ngx_atomic.h b/src/os/unix/ngx_atomic.h index 74b8b7f84..fcab2d6a0 100644 --- a/src/os/unix/ngx_atomic.h +++ b/src/os/unix/ngx_atomic.h @@ -38,6 +38,39 @@ typedef volatile ngx_atomic_uint_t ngx_atomic_t; #define ngx_cpu_pause() +#elif (NGX_HAVE_GCC_ATOMIC) + +/* GCC 4.1 builtin atomic operations */ + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef long ngx_atomic_int_t; +typedef unsigned long ngx_atomic_uint_t; + +#if (NGX_PTR_SIZE == 8) +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) +#else +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) +#endif + +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + + +#define ngx_atomic_cmp_set(lock, old, set) \ + __sync_bool_compare_and_swap(lock, old, set) + +#define ngx_atomic_fetch_add(value, add) \ + __sync_fetch_and_add(value, add) + +#define ngx_memory_barrier() __sync_synchronize() + +#if ( __i386__ || __i386 || __amd64__ || __amd64 ) +#define ngx_cpu_pause() __asm__ ("pause") +#else +#define ngx_cpu_pause() +#endif + + #elif (NGX_DARWIN_ATOMIC) /* @@ -88,39 +121,6 @@ typedef uint32_t ngx_atomic_uint_t; typedef volatile ngx_atomic_uint_t ngx_atomic_t; -#elif (NGX_HAVE_GCC_ATOMIC) - -/* GCC 4.1 builtin atomic operations */ - -#define NGX_HAVE_ATOMIC_OPS 1 - -typedef long ngx_atomic_int_t; -typedef unsigned long ngx_atomic_uint_t; - -#if (NGX_PTR_SIZE == 8) -#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) -#else -#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) -#endif - -typedef volatile ngx_atomic_uint_t ngx_atomic_t; - - -#define ngx_atomic_cmp_set(lock, old, set) \ - __sync_bool_compare_and_swap(lock, old, set) - -#define ngx_atomic_fetch_add(value, add) \ - __sync_fetch_and_add(value, add) - -#define ngx_memory_barrier() __sync_synchronize() - -#if ( __i386__ || __i386 || __amd64__ || __amd64 ) -#define ngx_cpu_pause() __asm__ ("pause") -#else -#define ngx_cpu_pause() -#endif - - #elif ( __i386__ || __i386 ) typedef int32_t ngx_atomic_int_t; From 15bf6d8cc950d3aec2d1c3152b39f62be4939025 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 31 Aug 2021 16:44:13 +0300 Subject: [PATCH 27/30] HTTP/2: avoid memcpy() with NULL source and zero length. Prodded by Clang Static Analyzer. --- src/http/v2/ngx_http_v2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 5ccb36360..79c4f17c2 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -4241,7 +4241,9 @@ ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, n = size; } - rb->buf->last = ngx_cpymem(rb->buf->last, pos, n); + if (n > 0) { + rb->buf->last = ngx_cpymem(rb->buf->last, pos, n); + } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 request body recv %uz", n); From 7e1667ca753a7c73c6f7371eb2c0a9153280953f Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 31 Aug 2021 17:54:54 +0300 Subject: [PATCH 28/30] 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 c5fde1f04..36dd63836 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-1.1.1k +OPENSSL = openssl-1.1.1l ZLIB = zlib-1.2.11 PCRE = pcre-8.44 From d16245213a7072ff14f050bff227b75d59c49bec Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 31 Aug 2021 18:13:46 +0300 Subject: [PATCH 29/30] nginx-1.21.2-RELEASE --- docs/xml/nginx/changes.xml | 107 +++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 1fb7634cb..fb64a4732 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,113 @@ + + + + +теперь nginx возвращает ошибку, +если в запросе по протоколу HTTP/1.0 присутствует +строка заголовка "Transfer-Encoding". + + +now nginx rejects HTTP/1.0 requests +with the "Transfer-Encoding" header line. + + + + + +экспортные шифры больше не поддерживаются. + + +export ciphers are no longer supported. + + + + + +совместимость с OpenSSL 3.0. + + +OpenSSL 3.0 compatibility. + + + + + +теперь серверу аутентификации почтового прокси-сервера +передаются строки заголовка "Auth-SSL-Protocol" и "Auth-SSL-Cipher".
+Спасибо Rob Mueller. +
+ +the "Auth-SSL-Protocol" and "Auth-SSL-Cipher" header lines +are now passed to the mail proxy authentication server.
+Thanks to Rob Mueller. +
+
+ + + +API для обработки тела запроса +теперь позволяет буферизировать обрабатываемые данные. + + +request body filters API +now permits buffering of the data being processed. + + + + + +SSL-соединения к бэкендам в модуле stream +могли зависать после SSL handshake. + + +backend SSL connections in the stream module +might hang after an SSL handshake. + + + + + +уровень безопасности, доступный в OpenSSL 1.1.0 и новее, +не учитывался при загрузке сертификатов сервера, +если был задан через "@SECLEVEL=N" в директиве ssl_ciphers. + + +the security level, which is available in OpenSSL 1.1.0 or newer, +did not affect loading of the server certificates +when set with "@SECLEVEL=N" in the "ssl_ciphers" directive. + + + + + +SSL-соединения с gRPC-бэкендами могли зависать, +если использовались методы select, poll или /dev/poll. + + +SSL connections with gRPC backends might hang +if select, poll, or /dev/poll methods were used. + + + + + +при использовании HTTP/2 +тело запроса всегда записывалось на диск, +если в запросе не было строки заголовка "Content-Length". + + +when using HTTP/2 +client request body was always written to disk +if the "Content-Length" header line was not present in the request. + + + +
+ + From a80a0601b95f58146381d8f208d4de293da7a239 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 31 Aug 2021 18:13:47 +0300 Subject: [PATCH 30/30] release-1.21.2 tag --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index ecf429ec9..e9aa48d74 100644 --- a/.hgtags +++ b/.hgtags @@ -462,3 +462,4 @@ da571b8eaf8f30f36c43b3c9b25e01e31f47149c release-1.19.9 ffcbb9980ee2bad27b4d7b1cd680b14ff47b29aa release-1.19.10 df34dcc9ac072ffd0945e5a1f3eb7987e8275375 release-1.21.0 a68ac0677f8553b1f84d357bc9da114731ab5f47 release-1.21.1 +bfbc52374adcbf2f9060afd62de940f6fab3bba5 release-1.21.2