From d961012799640ec49dc6c4ce274eff1b6f9fa718 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 14 Aug 2020 12:45:52 +0300 Subject: [PATCH 01/23] 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 10799a453..d23b59adf 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1019002 -#define NGINX_VERSION "1.19.2" +#define nginx_version 1019003 +#define NGINX_VERSION "1.19.3" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From 554916301c424f02b1cabc073845b64f8681099b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 29 Jul 2020 13:28:04 +0300 Subject: [PATCH 02/23] Cache: ignore stale-if-error for 4xx and 5xx codes. Previously the stale-if-error extension of the Cache-Control upstream header triggered the return of a stale response for all error conditions that can be specified in the proxy_cache_use_stale directive. The list of these errors includes both network/timeout/format errors, as well as some HTTP codes like 503, 504, 403, 429 etc. The latter prevented a cache entry from being updated by a response with any of these HTTP codes during the stale-if-error period. Now stale-if-error only works for network/timeout/format errors and ignores the upstream HTTP code. The return of a stale response for certain HTTP codes is still possible using the proxy_cache_use_stale directive. This change also applies to the stale-while-revalidate extension of the Cache-Control header, which triggers stale-if-error if it is missing. Reported at http://mailman.nginx.org/pipermail/nginx/2020-July/059723.html. --- src/http/ngx_http_upstream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 47f98ccb2..dda4046be 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2473,7 +2473,7 @@ ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u) #if (NGX_HTTP_CACHE) if (u->cache_status == NGX_HTTP_CACHE_EXPIRED - && ((u->conf->cache_use_stale & un->mask) || r->cache->stale_error)) + && (u->conf->cache_use_stale & un->mask)) { ngx_int_t rc; From b1274232db13e97c159272bf916366fca0c9feee Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 2 Sep 2020 23:13:36 +0300 Subject: [PATCH 03/23] HTTP/2: rejecting invalid stream identifiers with PROTOCOL_ERROR. Prodded by Xu Yang. --- src/http/v2/ngx_http_v2.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 51c8b0a71..1037e9d15 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -953,6 +953,13 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 DATA frame"); + if (h2c->state.sid == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent DATA frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + if (size > h2c->recv_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "client violated connection flow control: " @@ -2095,6 +2102,16 @@ static u_char * ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame"); + + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) { if (h2c->state.length != 0) { @@ -2118,9 +2135,6 @@ ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos, return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 SETTINGS frame"); - return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -2269,6 +2283,13 @@ ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 PING frame"); + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PING frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { return ngx_http_v2_state_skip(h2c, pos, end); } @@ -2310,6 +2331,13 @@ ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos, return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway); } + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent GOAWAY frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + #if (NGX_DEBUG) h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE; From ed0b19cdd4a9118cf8795ba2cf0a56684624bb41 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Fri, 4 Aug 2017 19:37:37 +0300 Subject: [PATCH 04/23] Cache: reset c->body_start when reading a variant on Vary mismatch. Previously, a variant not present in shared memory and stored on disk using a secondary key was read using c->body_start from a variant stored with a main key. This could result in critical errors "cache file .. has too long header". --- src/http/ngx_http_cache.h | 1 + src/http/ngx_http_file_cache.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h index cd0b4bbf8..d010a4ef0 100644 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -80,6 +80,7 @@ struct ngx_http_cache_s { ngx_str_t vary; u_char variant[NGX_HTTP_CACHE_KEY_LEN]; + size_t buffer_size; size_t header_start; size_t body_start; off_t length; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index e985f27b1..7a5b4ca5b 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -294,6 +294,8 @@ ngx_http_file_cache_open(ngx_http_request_t *r) cln->data = c; } + c->buffer_size = c->body_start; + rc = ngx_http_file_cache_exists(cache, c); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1230,7 +1232,7 @@ ngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c) c->secondary = 1; c->file.name.len = 0; - c->body_start = c->buf->end - c->buf->start; + c->body_start = c->buffer_size; ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN); From dc1b14126e0a7a45018d95b149ebdb29985f18f1 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Wed, 9 Sep 2020 19:26:27 +0300 Subject: [PATCH 05/23] Cache: keep c->body_start when Vary changes (ticket #2029). If the variant hash doesn't match one we used as a secondary cache key, we switch back to the original key. In this case, c->body_start was kept updated from an existing cache node overwriting the new response value. After file cache update, it led to discrepancy between a cache node and cache file seen as critical errors "file cache .. has too long header". --- src/http/ngx_http_cache.h | 1 + src/http/ngx_http_file_cache.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h index d010a4ef0..bb936c5fe 100644 --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -117,6 +117,7 @@ struct ngx_http_cache_s { unsigned purged:1; unsigned reading:1; unsigned secondary:1; + unsigned update_variant:1; unsigned background:1; unsigned stale_updating:1; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 7a5b4ca5b..c40093bca 100644 --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -854,7 +854,7 @@ ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) if (fcn->exists || fcn->uses >= c->min_uses) { c->exists = fcn->exists; - if (fcn->body_start) { + if (fcn->body_start && !c->update_variant) { c->body_start = fcn->body_start; } @@ -1339,6 +1339,7 @@ ngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c) ngx_shmtx_unlock(&cache->shpool->mutex); c->file.name.len = 0; + c->update_variant = 1; ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN); From 82cf625ab51dbed91bc38bdbc21ba192df2dd4d4 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 16 Sep 2020 18:26:22 +0300 Subject: [PATCH 06/23] SSL: workaround for incorrect SSL_write() errors in OpenSSL 1.1.1. OpenSSL 1.1.1 fails to return SSL_ERROR_SYSCALL if an error happens during SSL_write() after close_notify alert from the peer, and returns SSL_ERROR_ZERO_RETURN instead. Broken by this commit, which removes the "i == 0" check around the SSL_RECEIVED_SHUTDOWN one: https://git.openssl.org/?p=openssl.git;a=commitdiff;h=8051ab2 In particular, if a client closed the connection without reading the response but with properly sent close_notify alert, this resulted in unexpected "SSL_write() failed while ..." critical log message instead of correct "SSL_write() failed (32: Broken pipe)" at the info level. Since SSL_ERROR_ZERO_RETURN cannot be legitimately returned after SSL_write(), the fix is to convert all SSL_ERROR_ZERO_RETURN errors after SSL_write() to SSL_ERROR_SYSCALL. --- src/event/ngx_event_openssl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index f387c720d..62b475d77 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2573,6 +2573,18 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) sslerr = SSL_get_error(c->ssl->connection, n); + if (sslerr == SSL_ERROR_ZERO_RETURN) { + + /* + * OpenSSL 1.1.1 fails to return SSL_ERROR_SYSCALL if an error + * happens during SSL_write() after close_notify alert from the + * peer, and returns SSL_ERROR_ZERO_RETURN instead, + * https://git.openssl.org/?p=openssl.git;a=commitdiff;h=8051ab2 + */ + + sslerr = SSL_ERROR_SYSCALL; + } + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); From a1864c25861c8cb0b972815e4cbafcf227c4f8cb Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 16 Sep 2020 18:26:23 +0300 Subject: [PATCH 07/23] SSL: fixed event handling during shutdown. The c->read->ready and c->write->ready flags need to be cleared to ensure that appropriate read or write events will be reported by kernel. Without this, SSL shutdown might wait till the timeout after blocking on writing or reading even if there is a socket activity. --- src/event/ngx_event_openssl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 62b475d77..2f51b133a 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2865,6 +2865,13 @@ ngx_ssl_shutdown(ngx_connection_t *c) c->read->handler = ngx_ssl_shutdown_handler; c->write->handler = ngx_ssl_shutdown_handler; + if (sslerr == SSL_ERROR_WANT_READ) { + c->read->ready = 0; + + } else { + c->write->ready = 0; + } + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } From f6c28f93aff7caad3c178d4989b73a6ed3281bcd Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 16 Sep 2020 18:26:24 +0300 Subject: [PATCH 08/23] SSL: disabled shutdown after connection errors. This fixes "SSL_shutdown() failed (SSL: ... bad write retry)" errors as observed on the second SSL_shutdown() call after SSL shutdown fixes in 09fb2135a589 (1.19.2), notably when sending fails in ngx_http_test_expect(), similarly to ticket #1194. Note that there are some places where c->error is misused to prevent further output, such as ngx_http_v2_finalize_connection() if there are pending streams, or in filter finalization. These places seem to be extreme enough to don't care about missing shutdown though. For example, filter finalization currently prevents keepalive from being used. --- src/event/ngx_event_openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 2f51b133a..16dc55382 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2805,7 +2805,7 @@ ngx_ssl_shutdown(ngx_connection_t *c) return NGX_OK; } - if (c->timedout) { + if (c->timedout || c->error) { mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; SSL_set_quiet_shutdown(c->ssl->connection, 1); From e9a8612c13380beb7b313d3ce50b223abda3f90a Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 16 Sep 2020 18:26:25 +0300 Subject: [PATCH 09/23] SSL: disabled shutdown when there are buffered data. This fixes "SSL_shutdown() failed (SSL: ... bad write retry)" errors as observed on the second SSL_shutdown() call after SSL shutdown fixes in 09fb2135a589 (1.19.2), notably when HTTP/2 connections are closed due to read timeouts while there are incomplete writes. --- src/event/ngx_event_openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 16dc55382..da37b71df 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -2805,7 +2805,7 @@ ngx_ssl_shutdown(ngx_connection_t *c) return NGX_OK; } - if (c->timedout || c->error) { + if (c->timedout || c->error || c->buffered) { mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; SSL_set_quiet_shutdown(c->ssl->connection, 1); From 7c67ff73634acde729ceb151110f8db10322a33e Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 23 Sep 2020 19:50:49 +0300 Subject: [PATCH 10/23] HTTP/2: fixed segfault on DATA frames after 400 errors. If 400 errors were redirected to an upstream server using the error_page directive, DATA frames from the client might cause segmentation fault due to null pointer dereference. The bug had appeared in 6989:2c4dbcd6f2e4 (1.13.0). Fix is to skip such frames in ngx_http_v2_state_read_data() (similarly to 7561:9f1f9d6e056a). With the fix, behaviour of 400 errors in HTTP/2 is now similar to one in HTTP/1.x, that is, nginx doesn't try to read the request body. Note that proxying 400 errors, as well as other early stage errors, to upstream servers might not be a good idea anyway. These errors imply that reading and processing of the request (and the request headers) wasn't complete, and proxying of such incomplete request might lead to various errors. Reported by Chenglong Zhang. --- src/http/v2/ngx_http_v2.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 1037e9d15..9c994b8a3 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1084,6 +1084,13 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, return ngx_http_v2_state_skip_padded(h2c, pos, end); } + if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + size = end - pos; if (size >= h2c->state.length) { From 6c89d752c8ab3a3cc0832927484808b68153f8c4 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 23 Sep 2020 19:52:31 +0300 Subject: [PATCH 11/23] HTTP/2: run posted requests after reading body. HTTP/2 code failed to run posted requests after calling the request body handler, and this resulted in connection hang if a subrequest was created in the body handler and no other actions were made. --- src/http/v2/ngx_http_v2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 9c994b8a3..43a4fded5 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1058,6 +1058,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, size_t size; ngx_buf_t *buf; ngx_int_t rc; + ngx_connection_t *fc; ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; @@ -1076,6 +1077,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, } r = stream->request; + fc = r->connection; if (r->reading_body && !r->request_body_no_buffering) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, @@ -1108,6 +1110,8 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, ngx_http_finalize_request(r, rc); } + ngx_http_run_posted_requests(fc); + } else if (size) { buf = stream->preread; From 3bbeb1b8de857409c3dc9da45eb963d13b126537 Mon Sep 17 00:00:00 2001 From: Sergey Kandaurov Date: Thu, 24 Sep 2020 13:51:29 +0100 Subject: [PATCH 12/23] SSL: abort handshake on SSL_set_SSL_CTX() errors. In rare cases, such as memory allocation failure, SSL_set_SSL_CTX() returns NULL, which could mean that a different SSL configuration has not been set. Note that this new behaviour seemingly originated in OpenSSL-1.1.0 release. --- src/http/ngx_http_request.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index f80785d8f..2a0528c68 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -932,7 +932,10 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) c->ssl->buffer_size = sscf->buffer_size; if (sscf->ssl.ctx) { - SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); + if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) { + *ad = SSL_AD_INTERNAL_ERROR; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } /* * SSL_set_SSL_CTX() only changes certs as of 1.0.0d From c85d6fec217d1b17291779542de20ad77ae68661 Mon Sep 17 00:00:00 2001 From: Pavel Pautov Date: Fri, 28 Aug 2020 14:10:54 -0700 Subject: [PATCH 13/23] Stream: set module. Adds 'set' directive to the stream server context. --- auto/modules | 10 ++ auto/options | 3 + src/stream/ngx_stream_set_module.c | 226 +++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 src/stream/ngx_stream_set_module.c diff --git a/auto/modules b/auto/modules index d78e2823a..f1c63f3d5 100644 --- a/auto/modules +++ b/auto/modules @@ -1119,6 +1119,16 @@ if [ $STREAM != NO ]; then . auto/module fi + if [ $STREAM_SET = YES ]; then + ngx_module_name=ngx_stream_set_module + ngx_module_deps= + ngx_module_srcs=src/stream/ngx_stream_set_module.c + ngx_module_libs= + ngx_module_link=$STREAM_SET + + . auto/module + fi + if [ $STREAM_UPSTREAM_HASH = YES ]; then ngx_module_name=ngx_stream_upstream_hash_module ngx_module_deps= diff --git a/auto/options b/auto/options index 521c9768d..0b21def27 100644 --- a/auto/options +++ b/auto/options @@ -124,6 +124,7 @@ STREAM_GEOIP=NO STREAM_MAP=YES STREAM_SPLIT_CLIENTS=YES STREAM_RETURN=YES +STREAM_SET=YES STREAM_UPSTREAM_HASH=YES STREAM_UPSTREAM_LEAST_CONN=YES STREAM_UPSTREAM_RANDOM=YES @@ -324,6 +325,7 @@ use the \"--with-mail_ssl_module\" option instead" --without-stream_split_clients_module) STREAM_SPLIT_CLIENTS=NO ;; --without-stream_return_module) STREAM_RETURN=NO ;; + --without-stream_set_module) STREAM_SET=NO ;; --without-stream_upstream_hash_module) STREAM_UPSTREAM_HASH=NO ;; --without-stream_upstream_least_conn_module) @@ -538,6 +540,7 @@ cat << END --without-stream_split_clients_module disable ngx_stream_split_clients_module --without-stream_return_module disable ngx_stream_return_module + --without-stream_set_module disable ngx_stream_set_module --without-stream_upstream_hash_module disable ngx_stream_upstream_hash_module --without-stream_upstream_least_conn_module diff --git a/src/stream/ngx_stream_set_module.c b/src/stream/ngx_stream_set_module.c new file mode 100644 index 000000000..9b34a6099 --- /dev/null +++ b/src/stream/ngx_stream_set_module.c @@ -0,0 +1,226 @@ + +/* + * Copyright (C) Pavel Pautov + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_int_t index; + ngx_stream_set_variable_pt set_handler; + uintptr_t data; + ngx_stream_complex_value_t value; +} ngx_stream_set_cmd_t; + + +typedef struct { + ngx_array_t commands; +} ngx_stream_set_srv_conf_t; + + +static ngx_int_t ngx_stream_set_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_set_var(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_set_init(ngx_conf_t *cf); +static void *ngx_stream_set_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_stream_set_commands[] = { + + { ngx_string("set"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_stream_set, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_set_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_set_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_set_create_srv_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_set_module = { + NGX_MODULE_V1, + &ngx_stream_set_module_ctx, /* module context */ + ngx_stream_set_commands, /* module directives */ + NGX_STREAM_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_int_t +ngx_stream_set_handler(ngx_stream_session_t *s) +{ + ngx_str_t str; + ngx_uint_t i; + ngx_stream_set_cmd_t *cmds; + ngx_stream_set_srv_conf_t *scf; + ngx_stream_variable_value_t vv; + + scf = ngx_stream_get_module_srv_conf(s, ngx_stream_set_module); + cmds = scf->commands.elts; + vv = ngx_stream_variable_null_value; + + for (i = 0; i < scf->commands.nelts; i++) { + if (ngx_stream_complex_value(s, &cmds[i].value, &str) != NGX_OK) { + return NGX_ERROR; + } + + if (cmds[i].set_handler != NULL) { + vv.len = str.len; + vv.data = str.data; + cmds[i].set_handler(s, &vv, cmds[i].data); + + } else { + s->variables[cmds[i].index].len = str.len; + s->variables[cmds[i].index].valid = 1; + s->variables[cmds[i].index].no_cacheable = 0; + s->variables[cmds[i].index].not_found = 0; + s->variables[cmds[i].index].data = str.data; + } + } + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_stream_set_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, + uintptr_t data) +{ + *v = ngx_stream_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_set_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_set_handler; + + return NGX_OK; +} + + +static void * +ngx_stream_set_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_set_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_set_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->commands = { NULL }; + */ + + return conf; +} + + +static char * +ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_set_srv_conf_t *scf = conf; + + ngx_str_t *args; + ngx_int_t index; + ngx_stream_set_cmd_t *set_cmd; + ngx_stream_variable_t *v; + ngx_stream_compile_complex_value_t ccv; + + args = cf->args->elts; + + if (args[1].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &args[1]); + return NGX_CONF_ERROR; + } + + args[1].len--; + args[1].data++; + + v = ngx_stream_add_variable(cf, &args[1], + NGX_STREAM_VAR_CHANGEABLE|NGX_STREAM_VAR_WEAK); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + index = ngx_stream_get_variable_index(cf, &args[1]); + if (index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (v->get_handler == NULL) { + v->get_handler = ngx_stream_set_var; + } + + if (scf->commands.elts == NULL) { + if (ngx_array_init(&scf->commands, cf->pool, 1, + sizeof(ngx_stream_set_cmd_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + set_cmd = ngx_array_push(&scf->commands); + if (set_cmd == NULL) { + return NGX_CONF_ERROR; + } + + set_cmd->index = index; + set_cmd->set_handler = v->set_handler; + set_cmd->data = v->data; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &args[2]; + ccv.complex_value = &set_cmd->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} From b2b8f226f1cfaef8ca219b66374278af6fb4cf46 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Sun, 27 Sep 2020 23:21:09 +0300 Subject: [PATCH 14/23] Proxy: strengthen syntax checking for some directives. The "false" parameter of the proxy_redirect directive is deprecated. Warning has been emitted since c2230102df6f (0.7.54). The "off" parameter of the proxy_redirect, proxy_cookie_domain, and proxy_cookie_path directives tells nginx not to inherit the configuration from the previous configuration level. Previously, after specifying the directive with the "off" parameter, any other directives were ignored, and syntax checking was disabled. The syntax was enforced to allow either one directive with the "off" parameter, or several directives with other parameters. Also, specifying "proxy_redirect default foo" no longer works like "proxy_redirect default". --- src/http/modules/ngx_http_proxy_module.c | 32 +++++++++++++++--------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 6cf2cbde0..6bb3a6287 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -3766,7 +3766,7 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_http_compile_complex_value_t ccv; if (plcf->redirect == 0) { - return NGX_CONF_OK; + return "is duplicate"; } plcf->redirect = 1; @@ -3775,16 +3775,12 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (cf->args->nelts == 2) { if (ngx_strcmp(value[1].data, "off") == 0) { - plcf->redirect = 0; - plcf->redirects = NULL; - return NGX_CONF_OK; - } - if (ngx_strcmp(value[1].data, "false") == 0) { - ngx_conf_log_error(NGX_LOG_ERR, cf, 0, - "invalid parameter \"false\", use \"off\" instead"); + if (plcf->redirects) { + return "is duplicate"; + } + plcf->redirect = 0; - plcf->redirects = NULL; return NGX_CONF_OK; } @@ -3808,7 +3804,9 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - if (ngx_strcmp(value[1].data, "default") == 0) { + if (cf->args->nelts == 2 + && ngx_strcmp(value[1].data, "default") == 0) + { if (plcf->proxy_lengths) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_redirect default\" cannot be used " @@ -3911,7 +3909,7 @@ ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_http_compile_complex_value_t ccv; if (plcf->cookie_domains == NULL) { - return NGX_CONF_OK; + return "is duplicate"; } value = cf->args->elts; @@ -3919,6 +3917,11 @@ ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (cf->args->nelts == 2) { if (ngx_strcmp(value[1].data, "off") == 0) { + + if (plcf->cookie_domains != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + plcf->cookie_domains = NULL; return NGX_CONF_OK; } @@ -3998,7 +4001,7 @@ ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_http_compile_complex_value_t ccv; if (plcf->cookie_paths == NULL) { - return NGX_CONF_OK; + return "is duplicate"; } value = cf->args->elts; @@ -4006,6 +4009,11 @@ ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (cf->args->nelts == 2) { if (ngx_strcmp(value[1].data, "off") == 0) { + + if (plcf->cookie_paths != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + plcf->cookie_paths = NULL; return NGX_CONF_OK; } From 8b3f778cbc33aabd410381ce4a8bbfd193b23372 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Sun, 27 Sep 2020 23:21:10 +0300 Subject: [PATCH 15/23] Proxy: changed interface of some internal functions. This is in preparation for the next change. Also, moved optimization from ngx_http_proxy_rewrite_regex_handler() to ngx_http_proxy_rewrite(). --- src/http/modules/ngx_http_proxy_module.c | 62 ++++++++++++------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 6bb3a6287..7ebb55a8a 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -18,7 +18,7 @@ typedef struct { typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t; typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, + ngx_str_t *value, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr); struct ngx_http_proxy_rewrite_s { @@ -161,7 +161,7 @@ static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites); static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement); + ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement); static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf); static void *ngx_http_proxy_create_main_conf(ngx_conf_t *cf); @@ -2584,7 +2584,7 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, len = h->value.len - prefix; for (i = 0; i < plcf->redirects->nelts; i++) { - rc = pr[i].handler(r, h, prefix, len, &pr[i]); + rc = pr[i].handler(r, &h->value, prefix, len, &pr[i]); if (rc != NGX_DECLINED) { return rc; @@ -2669,7 +2669,7 @@ ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, pr = rewrites->elts; for (i = 0; i < rewrites->nelts; i++) { - rc = pr[i].handler(r, h, prefix, len, &pr[i]); + rc = pr[i].handler(r, &h->value, prefix, len, &pr[i]); if (rc != NGX_DECLINED) { return rc; @@ -2681,8 +2681,8 @@ ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t -ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) +ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, ngx_str_t *value, + size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { ngx_str_t pattern, replacement; @@ -2691,8 +2691,7 @@ ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, } if (pattern.len > len - || ngx_rstrncmp(h->value.data + prefix, pattern.data, - pattern.len) != 0) + || ngx_rstrncmp(value->data + prefix, pattern.data, pattern.len) != 0) { return NGX_DECLINED; } @@ -2701,20 +2700,20 @@ ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, return NGX_ERROR; } - return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement); + return ngx_http_proxy_rewrite(r, value, prefix, pattern.len, &replacement); } #if (NGX_PCRE) static ngx_int_t -ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h, +ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_str_t *value, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { ngx_str_t pattern, replacement; pattern.len = len; - pattern.data = h->value.data + prefix; + pattern.data = value->data + prefix; if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) { return NGX_DECLINED; @@ -2724,20 +2723,15 @@ ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h, return NGX_ERROR; } - if (prefix == 0 && h->value.len == len) { - h->value = replacement; - return NGX_OK; - } - - return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement); + return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement); } #endif static ngx_int_t -ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, - ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) +ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, ngx_str_t *value, + size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) { u_char *p; ngx_str_t pattern, replacement; @@ -2746,7 +2740,7 @@ ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, return NGX_ERROR; } - p = h->value.data + prefix; + p = value->data + prefix; if (p[0] == '.') { p++; @@ -2762,18 +2756,23 @@ ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, return NGX_ERROR; } - return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement); + return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement); } static ngx_int_t -ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix, +ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement) { u_char *p, *data; size_t new_len; - new_len = replacement->len + h->value.len - len; + if (len == value->len) { + *value = *replacement; + return NGX_OK; + } + + new_len = replacement->len + value->len - len; if (replacement->len > len) { @@ -2782,23 +2781,22 @@ ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix, return NGX_ERROR; } - p = ngx_copy(data, h->value.data, prefix); + p = ngx_copy(data, value->data, prefix); p = ngx_copy(p, replacement->data, replacement->len); - ngx_memcpy(p, h->value.data + prefix + len, - h->value.len - len - prefix + 1); + ngx_memcpy(p, value->data + prefix + len, + value->len - len - prefix + 1); - h->value.data = data; + value->data = data; } else { - p = ngx_copy(h->value.data + prefix, replacement->data, - replacement->len); + p = ngx_copy(value->data + prefix, replacement->data, replacement->len); - ngx_memmove(p, h->value.data + prefix + len, - h->value.len - len - prefix + 1); + ngx_memmove(p, value->data + prefix + len, + value->len - len - prefix + 1); } - h->value.len = new_len; + value->len = new_len; return NGX_OK; } From 21b903f8e31f722c104d425ce01c22614761fdd2 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Sun, 27 Sep 2020 23:21:11 +0300 Subject: [PATCH 16/23] Proxy: added the "proxy_cookie_flags" directive. --- src/http/modules/ngx_http_proxy_module.c | 582 +++++++++++++++++++++-- 1 file changed, 552 insertions(+), 30 deletions(-) diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 7ebb55a8a..17e4761c6 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -10,6 +10,19 @@ #include +#define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001 +#define NGX_HTTP_PROXY_COOKIE_SECURE_ON 0x0002 +#define NGX_HTTP_PROXY_COOKIE_SECURE_OFF 0x0004 +#define NGX_HTTP_PROXY_COOKIE_HTTPONLY 0x0008 +#define NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON 0x0010 +#define NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF 0x0020 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE 0x0040 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT 0x0080 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX 0x0100 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE 0x0200 +#define NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF 0x0400 + + typedef struct { ngx_array_t caches; /* ngx_http_file_cache_t * */ } ngx_http_proxy_main_conf_t; @@ -35,6 +48,19 @@ struct ngx_http_proxy_rewrite_s { }; +typedef struct { + union { + ngx_http_complex_value_t complex; +#if (NGX_PCRE) + ngx_http_regex_t *regex; +#endif + } cookie; + + ngx_uint_t flags; + ngx_uint_t regex; +} ngx_http_proxy_cookie_flags_t; + + typedef struct { ngx_str_t key_start; ngx_str_t schema; @@ -72,6 +98,7 @@ typedef struct { ngx_array_t *redirects; ngx_array_t *cookie_domains; ngx_array_t *cookie_paths; + ngx_array_t *cookie_flags; ngx_http_complex_value_t *method; ngx_str_t location; @@ -158,8 +185,14 @@ static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix); static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h); +static ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value, + ngx_array_t *attrs); static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, - ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites); + ngx_str_t *value, ngx_array_t *rewrites); +static ngx_int_t ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, + ngx_array_t *attrs, ngx_array_t *flags); +static ngx_int_t ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r, + ngx_array_t *attrs, ngx_uint_t flags); static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement); @@ -180,6 +213,8 @@ static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #if (NGX_HTTP_CACHE) @@ -282,6 +317,13 @@ static ngx_command_t ngx_http_proxy_commands[] = { 0, NULL }, + { ngx_string("proxy_cookie_flags"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, + ngx_http_proxy_cookie_flags, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("proxy_store"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_proxy_store, @@ -845,6 +887,36 @@ static ngx_path_init_t ngx_http_proxy_temp_path = { }; +static ngx_conf_bitmask_t ngx_http_proxy_cookie_flags_masks[] = { + + { ngx_string("secure"), + NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_ON }, + + { ngx_string("nosecure"), + NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_OFF }, + + { ngx_string("httponly"), + NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON }, + + { ngx_string("nohttponly"), + NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF }, + + { ngx_string("samesite=strict"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT }, + + { ngx_string("samesite=lax"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX }, + + { ngx_string("samesite=none"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE }, + + { ngx_string("nosamesite"), + NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF }, + + { ngx_null_string, 0 } +}; + + static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) { @@ -906,7 +978,7 @@ ngx_http_proxy_handler(ngx_http_request_t *r) u->rewrite_redirect = ngx_http_proxy_rewrite_redirect; } - if (plcf->cookie_domains || plcf->cookie_paths) { + if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) { u->rewrite_cookie = ngx_http_proxy_rewrite_cookie; } @@ -2598,27 +2670,41 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) { - size_t prefix; u_char *p; + size_t len; ngx_int_t rc, rv; + ngx_str_t *key, *value; + ngx_uint_t i; + ngx_array_t attrs; + ngx_keyval_t *attr; ngx_http_proxy_loc_conf_t *plcf; - p = (u_char *) ngx_strchr(h->value.data, ';'); - if (p == NULL) { - return NGX_DECLINED; + ngx_array_init(&attrs, r->pool, 2, sizeof(ngx_keyval_t)); + + if (ngx_http_proxy_parse_cookie(&h->value, &attrs) != NGX_OK) { + return NGX_ERROR; } - prefix = p + 1 - h->value.data; + attr = attrs.elts; + + if (attr[0].value.data == NULL) { + return NGX_DECLINED; + } rv = NGX_DECLINED; plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); - if (plcf->cookie_domains) { - p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1); + for (i = 1; i < attrs.nelts; i++) { - if (p) { - rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7, + key = &attr[i].key; + value = &attr[i].value; + + if (plcf->cookie_domains && key->len == 6 + && ngx_strncasecmp(key->data, (u_char *) "domain", 6) == 0 + && value->data) + { + rc = ngx_http_proxy_rewrite_cookie_value(r, value, plcf->cookie_domains); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -2628,13 +2714,12 @@ ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) rv = rc; } } - } - if (plcf->cookie_paths) { - p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1); - - if (p) { - rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5, + if (plcf->cookie_paths && key->len == 4 + && ngx_strncasecmp(key->data, (u_char *) "path", 4) == 0 + && value->data) + { + rc = ngx_http_proxy_rewrite_cookie_value(r, value, plcf->cookie_paths); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -2646,30 +2731,153 @@ ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) } } - return rv; + if (plcf->cookie_flags) { + rc = ngx_http_proxy_rewrite_cookie_flags(r, &attrs, + plcf->cookie_flags); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_DECLINED) { + rv = rc; + } + + attr = attrs.elts; + } + + if (rv != NGX_OK) { + return rv; + } + + len = 0; + + for (i = 0; i < attrs.nelts; i++) { + + if (attr[i].key.data == NULL) { + continue; + } + + if (i > 0) { + len += 2; + } + + len += attr[i].key.len; + + if (attr[i].value.data) { + len += 1 + attr[i].value.len; + } + } + + p = ngx_pnalloc(r->pool, len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + h->value.data = p; + h->value.len = len; + + for (i = 0; i < attrs.nelts; i++) { + + if (attr[i].key.data == NULL) { + continue; + } + + if (i > 0) { + *p++ = ';'; + *p++ = ' '; + } + + p = ngx_cpymem(p, attr[i].key.data, attr[i].key.len); + + if (attr[i].value.data) { + *p++ = '='; + p = ngx_cpymem(p, attr[i].value.data, attr[i].value.len); + } + } + + *p = '\0'; + + return NGX_OK; } static ngx_int_t -ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, - u_char *value, ngx_array_t *rewrites) +ngx_http_proxy_parse_cookie(ngx_str_t *value, ngx_array_t *attrs) +{ + u_char *start, *end, *p, *last; + ngx_str_t name, val; + ngx_keyval_t *attr; + + start = value->data; + end = value->data + value->len; + + for ( ;; ) { + + last = (u_char *) ngx_strchr(start, ';'); + + if (last == NULL) { + last = end; + } + + while (start < last && *start == ' ') { start++; } + + for (p = start; p < last && *p != '='; p++) { /* void */ } + + name.data = start; + name.len = p - start; + + while (name.len && name.data[name.len - 1] == ' ') { + name.len--; + } + + if (p < last) { + + p++; + + while (p < last && *p == ' ') { p++; } + + val.data = p; + val.len = last - val.data; + + while (val.len && val.data[val.len - 1] == ' ') { + val.len--; + } + + } else { + ngx_str_null(&val); + } + + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + attr->key = name; + attr->value = val; + + if (last == end) { + break; + } + + start = last + 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_str_t *value, + ngx_array_t *rewrites) { - size_t len, prefix; - u_char *p; ngx_int_t rc; ngx_uint_t i; ngx_http_proxy_rewrite_t *pr; - prefix = value - h->value.data; - - p = (u_char *) ngx_strchr(value, ';'); - - len = p ? (size_t) (p - value) : (h->value.len - prefix); - pr = rewrites->elts; for (i = 0; i < rewrites->nelts; i++) { - rc = pr[i].handler(r, &h->value, prefix, len, &pr[i]); + rc = pr[i].handler(r, value, 0, value->len, &pr[i]); if (rc != NGX_DECLINED) { return rc; @@ -2680,6 +2888,192 @@ ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, } +static ngx_int_t +ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs, + ngx_array_t *flags) +{ + ngx_str_t pattern; +#if (NGX_PCRE) + ngx_int_t rc; +#endif + ngx_uint_t i; + ngx_keyval_t *attr; + ngx_http_proxy_cookie_flags_t *pcf; + + attr = attrs->elts; + pcf = flags->elts; + + for (i = 0; i < flags->nelts; i++) { + +#if (NGX_PCRE) + if (pcf[i].regex) { + rc = ngx_http_regex_exec(r, pcf[i].cookie.regex, &attr[0].key); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_OK) { + break; + } + + /* NGX_DECLINED */ + + continue; + } +#endif + + if (ngx_http_complex_value(r, &pcf[i].cookie.complex, &pattern) + != NGX_OK) + { + return NGX_ERROR; + } + + if (pattern.len == attr[0].key.len + && ngx_strncasecmp(attr[0].key.data, pattern.data, pattern.len) + == 0) + { + break; + } + } + + if (i == flags->nelts) { + return NGX_DECLINED; + } + + return ngx_http_proxy_edit_cookie_flags(r, attrs, pcf[i].flags); +} + + +static ngx_int_t +ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs, + ngx_uint_t flags) +{ + ngx_str_t *key, *value; + ngx_uint_t i; + ngx_keyval_t *attr; + + attr = attrs->elts; + + for (i = 1; i < attrs->nelts; i++) { + key = &attr[i].key; + + if (key->len == 6 + && ngx_strncasecmp(key->data, (u_char *) "secure", 6) == 0) + { + if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SECURE_ON; + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_OFF) { + key->data = NULL; + } + + continue; + } + + if (key->len == 8 + && ngx_strncasecmp(key->data, (u_char *) "httponly", 8) == 0) + { + if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) { + flags &= ~NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON; + + } else if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF) { + key->data = NULL; + } + + continue; + } + + if (key->len == 8 + && ngx_strncasecmp(key->data, (u_char *) "samesite", 8) == 0) + { + value = &attr[i].value; + + if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT; + + if (value->len != 6 + || ngx_strncasecmp(value->data, (u_char *) "strict", 6) + != 0) + { + ngx_str_set(key, "SameSite"); + ngx_str_set(value, "Strict"); + } + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX; + + if (value->len != 3 + || ngx_strncasecmp(value->data, (u_char *) "lax", 3) != 0) + { + ngx_str_set(key, "SameSite"); + ngx_str_set(value, "Lax"); + } + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE) { + flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE; + + if (value->len != 4 + || ngx_strncasecmp(value->data, (u_char *) "none", 4) != 0) + { + ngx_str_set(key, "SameSite"); + ngx_str_set(value, "None"); + } + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF) { + key->data = NULL; + } + + continue; + } + } + + if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) { + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + ngx_str_set(&attr->key, "Secure"); + ngx_str_null(&attr->value); + } + + if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) { + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + ngx_str_set(&attr->key, "HttpOnly"); + ngx_str_null(&attr->value); + } + + if (flags & (NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT + |NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX + |NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE)) + { + attr = ngx_array_push(attrs); + if (attr == NULL) { + return NGX_ERROR; + } + + ngx_str_set(&attr->key, "SameSite"); + + if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) { + ngx_str_set(&attr->value, "Strict"); + + } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) { + ngx_str_set(&attr->value, "Lax"); + + } else { + ngx_str_set(&attr->value, "None"); + } + } + + return NGX_OK; +} + + static ngx_int_t ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, ngx_str_t *value, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr) @@ -2742,7 +3136,7 @@ ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, ngx_str_t *value, p = value->data + prefix; - if (p[0] == '.') { + if (len && p[0] == '.') { p++; prefix++; len--; @@ -2955,6 +3349,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->cookie_domains = NGX_CONF_UNSET_PTR; conf->cookie_paths = NGX_CONF_UNSET_PTR; + conf->cookie_flags = NGX_CONF_UNSET_PTR; conf->http_version = NGX_CONF_UNSET_UINT; @@ -3350,6 +3745,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL); + ngx_conf_merge_ptr_value(conf->cookie_flags, prev->cookie_flags, NULL); + ngx_conf_merge_uint_value(conf->http_version, prev->http_version, NGX_HTTP_VERSION_10); @@ -4081,6 +4478,131 @@ ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static char * +ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_proxy_loc_conf_t *plcf = conf; + + ngx_str_t *value; + ngx_uint_t i, m; + ngx_conf_bitmask_t *mask; + ngx_http_proxy_cookie_flags_t *pcf; + ngx_http_compile_complex_value_t ccv; +#if (NGX_PCRE) + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; +#endif + + if (plcf->cookie_flags == NULL) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (cf->args->nelts == 2) { + + if (ngx_strcmp(value[1].data, "off") == 0) { + + if (plcf->cookie_flags != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + plcf->cookie_flags = NULL; + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (plcf->cookie_flags == NGX_CONF_UNSET_PTR) { + plcf->cookie_flags = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_proxy_cookie_flags_t)); + if (plcf->cookie_flags == NULL) { + return NGX_CONF_ERROR; + } + } + + pcf = ngx_array_push(plcf->cookie_flags); + if (pcf == NULL) { + return NGX_CONF_ERROR; + } + + pcf->regex = 0; + + if (value[1].data[0] == '~') { + value[1].len--; + value[1].data++; + +#if (NGX_PCRE) + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + rc.pattern = value[1]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + rc.options = NGX_REGEX_CASELESS; + + pcf->cookie.regex = ngx_http_regex_compile(cf, &rc); + if (pcf->cookie.regex == NULL) { + return NGX_CONF_ERROR; + } + + pcf->regex = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "using regex \"%V\" requires PCRE library", + &value[1]); + return NGX_CONF_ERROR; +#endif + + } else { + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pcf->cookie.complex; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + mask = ngx_http_proxy_cookie_flags_masks; + pcf->flags = 0; + + for (i = 2; i < cf->args->nelts; i++) { + for (m = 0; mask[m].name.len != 0; m++) { + + if (mask[m].name.len != value[i].len + || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0) + { + continue; + } + + if (pcf->flags & mask[m].mask) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + pcf->flags |= mask[m].mask; + + break; + } + + if (mask[m].name.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless) From c511f3de3eb34641d85005b48683e4fc88f92ec5 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 28 Sep 2020 17:07:48 +0300 Subject: [PATCH 17/23] Userid: userid_flags directive to set cookie flags. --- .../modules/ngx_http_userid_filter_module.c | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c index 31cf402f4..db1643c30 100644 --- a/src/http/modules/ngx_http_userid_filter_module.c +++ b/src/http/modules/ngx_http_userid_filter_module.c @@ -15,12 +15,20 @@ #define NGX_HTTP_USERID_V1 2 #define NGX_HTTP_USERID_ON 3 +#define NGX_HTTP_USERID_COOKIE_SECURE 0x0001 +#define NGX_HTTP_USERID_COOKIE_HTTPONLY 0x0002 +#define NGX_HTTP_USERID_COOKIE_SAMESITE 0x0004 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT 0x0008 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_LAX 0x0010 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_NONE 0x0020 + /* 31 Dec 2037 23:55:55 GMT */ #define NGX_HTTP_USERID_MAX_EXPIRES 2145916555 typedef struct { ngx_uint_t enable; + ngx_uint_t flags; ngx_int_t service; @@ -88,6 +96,19 @@ static ngx_conf_enum_t ngx_http_userid_state[] = { }; +static ngx_conf_bitmask_t ngx_http_userid_flags[] = { + { ngx_string("secure"), NGX_HTTP_USERID_COOKIE_SECURE }, + { ngx_string("httponly"), NGX_HTTP_USERID_COOKIE_HTTPONLY }, + { ngx_string("samesite=strict"), + NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT }, + { ngx_string("samesite=lax"), + NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_LAX }, + { ngx_string("samesite=none"), + NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_NONE }, + { ngx_null_string, 0 } +}; + + static ngx_conf_post_handler_pt ngx_http_userid_domain_p = ngx_http_userid_domain; static ngx_conf_post_handler_pt ngx_http_userid_path_p = ngx_http_userid_path; @@ -138,6 +159,13 @@ static ngx_command_t ngx_http_userid_commands[] = { 0, NULL }, + { ngx_string("userid_flags"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_userid_conf_t, flags), + &ngx_http_userid_flags }, + { ngx_string("userid_p3p"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -383,6 +411,26 @@ ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, len += conf->domain.len; } + if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) { + len += sizeof("; secure") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) { + len += sizeof("; httponly") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) { + len += sizeof("; samesite=strict") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) { + len += sizeof("; samesite=lax") - 1; + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) { + len += sizeof("; samesite=none") - 1; + } + cookie = ngx_pnalloc(r->pool, len); if (cookie == NULL) { return NGX_ERROR; @@ -422,6 +470,26 @@ ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, p = ngx_copy(p, conf->path.data, conf->path.len); + if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) { + p = ngx_cpymem(p, "; secure", sizeof("; secure") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) { + p = ngx_cpymem(p, "; httponly", sizeof("; httponly") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) { + p = ngx_cpymem(p, "; samesite=strict", sizeof("; samesite=strict") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) { + p = ngx_cpymem(p, "; samesite=lax", sizeof("; samesite=lax") - 1); + } + + if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) { + p = ngx_cpymem(p, "; samesite=none", sizeof("; samesite=none") - 1); + } + set_cookie = ngx_list_push(&r->headers_out.headers); if (set_cookie == NULL) { return NGX_ERROR; @@ -658,6 +726,7 @@ ngx_http_userid_create_conf(ngx_conf_t *cf) /* * set by ngx_pcalloc(): * + * conf->flags = 0; * conf->name = { 0, NULL }; * conf->domain = { 0, NULL }; * conf->path = { 0, NULL }; @@ -682,6 +751,9 @@ ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->enable, prev->enable, NGX_HTTP_USERID_OFF); + ngx_conf_merge_bitmask_value(conf->flags, prev->flags, + NGX_CONF_BITMASK_SET); + ngx_conf_merge_str_value(conf->name, prev->name, "uid"); ngx_conf_merge_str_value(conf->domain, prev->domain, ""); ngx_conf_merge_str_value(conf->path, prev->path, "; path=/"); From 279efaab660df37502459967b6c7a92b4eccd850 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Mon, 28 Sep 2020 17:41:22 +0300 Subject: [PATCH 18/23] Resolver: improved error messages (ticket #2024). --- src/core/ngx_resolver.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index e51712c0a..5b716ee75 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -1918,7 +1918,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, if (rn == NULL) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -1930,7 +1930,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -1949,7 +1949,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, if (rn->query == NULL || rn->naddrs != (u_short) -1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -1964,7 +1964,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, - "wrong ident %ui response for %V, expect %ui", + "wrong ident %ui in DNS response for %V, expect %ui", ident, &name, qident); ngx_resolver_free(r, name.data); goto failed; @@ -2149,7 +2149,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, if (class != 1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected RR class %ui", class); + "unexpected RR class %ui in DNS response", class); goto failed; } @@ -2218,7 +2218,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, default: ngx_log_error(r->log_level, r->log, 0, - "unexpected RR type %ui", type); + "unexpected RR type %ui in DNS response", type); } i += len; @@ -2567,7 +2567,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n, if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -2581,7 +2581,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n, if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, - "wrong ident %ui response for %V, expect %ui", + "wrong ident %ui in DNS response for %V, expect %ui", ident, &name, qident); ngx_resolver_free(r, name.data); goto failed; @@ -2691,7 +2691,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n, if (class != 1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected RR class %ui", class); + "unexpected RR class %ui in DNS response", class); goto failed; } @@ -2734,7 +2734,7 @@ ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n, default: ngx_log_error(r->log_level, r->log, 0, - "unexpected RR type %ui", type); + "unexpected RR type %ui in DNS response", type); } i += len; @@ -3165,7 +3165,7 @@ valid: if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, - "unexpected response for %V", &name); + "unexpected DNS response for %V", &name); ngx_resolver_free(r, name.data); goto failed; } @@ -3174,7 +3174,7 @@ valid: if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, - "wrong ident %ui response for %V, expect %ui", + "wrong ident %ui in DNS response for %V, expect %ui", ident, &name, qident); ngx_resolver_free(r, name.data); goto failed; @@ -3256,7 +3256,7 @@ valid: if (class != 1) { ngx_log_error(r->log_level, r->log, 0, - "unexpected RR class %ui", class); + "unexpected RR class %ui in DNS response", class); goto failed; } @@ -3283,7 +3283,7 @@ valid: default: ngx_log_error(r->log_level, r->log, 0, - "unexpected RR type %ui", type); + "unexpected RR type %ui in DNS response", type); } i += len; @@ -3952,12 +3952,12 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src, } if (p >= last) { - err = "name is out of response"; + err = "name is out of DNS response"; goto invalid; } } - err = "compression pointers loop"; + err = "compression pointers loop in DNS response"; invalid: From 718d589091a1c653595f9b210bba132c43dd2c75 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 29 Sep 2020 15:52:18 +0300 Subject: [PATCH 19/23] Userid: userid_flags fixup. In 7717:e3e8b8234f05, the 1st bit was incorrectly used. It shouldn't be used for bitmask values, as it is used by NGX_CONF_BITMASK_SET. Additionally, special value "off" added to make it possible to clear inherited userid_flags value. --- src/http/modules/ngx_http_userid_filter_module.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c index db1643c30..1e33c5c96 100644 --- a/src/http/modules/ngx_http_userid_filter_module.c +++ b/src/http/modules/ngx_http_userid_filter_module.c @@ -15,12 +15,13 @@ #define NGX_HTTP_USERID_V1 2 #define NGX_HTTP_USERID_ON 3 -#define NGX_HTTP_USERID_COOKIE_SECURE 0x0001 -#define NGX_HTTP_USERID_COOKIE_HTTPONLY 0x0002 -#define NGX_HTTP_USERID_COOKIE_SAMESITE 0x0004 -#define NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT 0x0008 -#define NGX_HTTP_USERID_COOKIE_SAMESITE_LAX 0x0010 -#define NGX_HTTP_USERID_COOKIE_SAMESITE_NONE 0x0020 +#define NGX_HTTP_USERID_COOKIE_OFF 0x0002 +#define NGX_HTTP_USERID_COOKIE_SECURE 0x0004 +#define NGX_HTTP_USERID_COOKIE_HTTPONLY 0x0008 +#define NGX_HTTP_USERID_COOKIE_SAMESITE 0x0010 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT 0x0020 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_LAX 0x0040 +#define NGX_HTTP_USERID_COOKIE_SAMESITE_NONE 0x0080 /* 31 Dec 2037 23:55:55 GMT */ #define NGX_HTTP_USERID_MAX_EXPIRES 2145916555 @@ -97,6 +98,7 @@ static ngx_conf_enum_t ngx_http_userid_state[] = { static ngx_conf_bitmask_t ngx_http_userid_flags[] = { + { ngx_string("off"), NGX_HTTP_USERID_COOKIE_OFF }, { ngx_string("secure"), NGX_HTTP_USERID_COOKIE_SECURE }, { ngx_string("httponly"), NGX_HTTP_USERID_COOKIE_HTTPONLY }, { ngx_string("samesite=strict"), @@ -752,7 +754,7 @@ ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child) NGX_HTTP_USERID_OFF); ngx_conf_merge_bitmask_value(conf->flags, prev->flags, - NGX_CONF_BITMASK_SET); + (NGX_CONF_BITMASK_SET|NGX_HTTP_USERID_COOKIE_OFF)); ngx_conf_merge_str_value(conf->name, prev->name, "uid"); ngx_conf_merge_str_value(conf->domain, prev->domain, ""); From e64f7fe7c903e6994defb21db0b9667dbec7c20d Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 29 Sep 2020 15:54:09 +0300 Subject: [PATCH 20/23] Proxy: error checking for array init, missed in 7716:d6a5e14aa3e4. Found by Coverity (CID 1467637). --- src/http/modules/ngx_http_proxy_module.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 17e4761c6..c5c9e8587 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -2679,7 +2679,9 @@ ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) ngx_keyval_t *attr; ngx_http_proxy_loc_conf_t *plcf; - ngx_array_init(&attrs, r->pool, 2, sizeof(ngx_keyval_t)); + if (ngx_array_init(&attrs, r->pool, 2, sizeof(ngx_keyval_t)) != NGX_OK) { + return NGX_ERROR; + } if (ngx_http_proxy_parse_cookie(&h->value, &attrs) != NGX_OK) { return NGX_ERROR; From 249e126beb02f3b2d62e5969b6ae6765b4bf8027 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 29 Sep 2020 15:56:16 +0300 Subject: [PATCH 21/23] 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 4582e4460..80eb9b929 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-1.1.1g +OPENSSL = openssl-1.1.1h ZLIB = zlib-1.2.11 PCRE = pcre-8.44 From 3e299b15768e31ea31473b88c18a7ade86bfe157 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 29 Sep 2020 17:32:10 +0300 Subject: [PATCH 22/23] nginx-1.19.3-RELEASE --- docs/xml/nginx/changes.xml | 106 +++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index 2106c8789..976c9de92 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,112 @@ + + + + +модуль ngx_stream_set_module. + + +the ngx_stream_set_module. + + + + + +директива proxy_cookie_flags. + + +the "proxy_cookie_flags" directive. + + + + + +директива userid_flags. + + +the "userid_flags" directive. + + + + + +расширение управления кэшированием stale-if-error +ошибочно применялось, если бэкенд возвращал ответ +с кодом 500, 502, 503, 504, 403, 404 или 429. + + +the "stale-if-error" cache control extension +was erroneously applied if backend returned a response +with status code 500, 502, 503, 504, 403, 404, or 429. + + + + + +если использовалось кэширование +и бэкенд возвращал ответы с строкой заголовка Vary, +в логах могли появляться сообщения "[crit] cache file ... has too long header". + + +"[crit] cache file ... has too long header" messages might appear in logs +if caching was used +and the backend returned responses with the "Vary" header line. + + + + + +при использовании OpenSSL 1.1.1 +в логах могли появляться сообщения "[crit] SSL_write() failed". + + +"[crit] SSL_write() failed" messages might appear in logs +when using OpenSSL 1.1.1. + + + + + +в логах могли появляться сообщения +"SSL_shutdown() failed (SSL: ... bad write retry)"; +ошибка появилась в 1.19.2. + + +"SSL_shutdown() failed (SSL: ... bad write retry)" +messages might appear in logs; +the bug had appeared in 1.19.2. + + + + + +при использовании HTTP/2 +в рабочем процессе мог произойти segmentation fault, +если ошибки с кодом 400 с помощью директивы error_page +перенаправлялись в проксируемый location. + + +a segmentation fault might occur in a worker process +when using HTTP/2 +if errors with code 400 were redirected to a proxied location +using the "error_page" directive. + + + + + +утечки сокетов при использовании HTTP/2 и подзапросов в модуле njs. + + +socket leak when using HTTP/2 and subrequests in the njs module. + + + + + + From 135b707ab02d5921d7e789c231995f0cc11ae4a6 Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Tue, 29 Sep 2020 17:32:10 +0300 Subject: [PATCH 23/23] release-1.19.3 tag --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index edcbdac78..713fa4a01 100644 --- a/.hgtags +++ b/.hgtags @@ -452,3 +452,4 @@ c44970de01474f6f3e01b0adea85ec1d03e3a5f2 release-1.17.10 cbe6ba650211541310618849168631ce0b788f35 release-1.19.0 062920e2f3bf871ef7a3d8496edec1b3065faf80 release-1.19.1 a7b46539f507e6c64efa0efda69ad60b6f4ffbce release-1.19.2 +3cbc2602325f0ac08917a4397d76f5155c34b7b1 release-1.19.3