From d42d1aa241903a3af5de107d1ad60cc64fe2ca6b Mon Sep 17 00:00:00 2001 From: Hakase Date: Mon, 3 Nov 2025 21:09:28 +0900 Subject: [PATCH] Latest update - GitHub fd0848b (1.29.3) --- .github/workflows/f5_cla.yml | 41 ++ docs/xml/nginx/changes.xml | 155 ++++++- misc/GNUmakefile | 4 +- misc/README | 2 +- src/core/nginx.h | 4 +- src/core/ngx_string.c | 32 +- src/core/ngx_string.h | 1 + src/event/ngx_event_openssl.c | 420 +++++++++++++++++- src/event/ngx_event_openssl.h | 31 +- src/event/ngx_event_openssl_cache.c | 17 +- src/event/quic/ngx_event_quic.h | 3 +- src/event/quic/ngx_event_quic_protection.c | 36 +- src/event/quic/ngx_event_quic_protection.h | 4 +- src/event/quic/ngx_event_quic_ssl.c | 31 +- .../modules/ngx_http_chunked_filter_module.c | 4 +- src/http/modules/ngx_http_geo_module.c | 12 + .../modules/ngx_http_headers_filter_module.c | 74 ++- src/http/modules/ngx_http_index_module.c | 2 +- src/http/modules/ngx_http_ssl_module.c | 22 +- src/http/ngx_http_core_module.h | 1 + src/http/ngx_http_parse.c | 45 +- src/http/ngx_http_request.c | 91 ++-- src/http/ngx_http_request.h | 2 + src/http/ngx_http_upstream.c | 83 ++-- src/http/ngx_http_upstream.h | 2 +- src/http/ngx_http_variables.c | 55 +++ src/http/v2/ngx_http_v2.c | 13 +- src/http/v3/ngx_http_v3_request.c | 10 + src/mail/ngx_mail_proxy_module.c | 14 +- src/stream/ngx_stream_geo_module.c | 12 + src/stream/ngx_stream_proxy_module.c | 2 + src/stream/ngx_stream_ssl_module.c | 89 ++-- 32 files changed, 1132 insertions(+), 182 deletions(-) create mode 100644 .github/workflows/f5_cla.yml diff --git a/.github/workflows/f5_cla.yml b/.github/workflows/f5_cla.yml new file mode 100644 index 0000000..43e473e --- /dev/null +++ b/.github/workflows/f5_cla.yml @@ -0,0 +1,41 @@ +--- +name: F5 CLA +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] +permissions: read-all +jobs: + f5-cla: + name: F5 CLA + runs-on: ubuntu-24.04 + permissions: + actions: write + pull-requests: write + statuses: write + steps: + - name: Run F5 Contributor License Agreement (CLA) assistant + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target' + uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 + with: + # Path to the CLA document. + path-to-document: https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md + # Custom CLA messages. + custom-notsigned-prcomment: 'πŸŽ‰ Thank you for your contribution! It appears you have not yet signed the [F5 Contributor License Agreement (CLA)](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md) and reply on a new comment with the following text to agree:' + custom-pr-sign-comment: 'I have hereby read the F5 CLA and agree to its terms' + custom-allsigned-prcomment: 'βœ… All required contributors have signed the F5 CLA for this PR. Thank you!' + # Remote repository storing CLA signatures. + remote-organization-name: f5 + remote-repository-name: f5-cla-data + # Branch where CLA signatures are stored. + branch: main + path-to-signatures: signatures/signatures.json + # Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA. + # NOTE: You will want to edit the usernames to suit your project needs. + allowlist: bot* + # Do not lock PRs after a merge. + lock-pullrequest-aftermerge: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }} diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index c07de09..42d28b4 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,157 @@ + + + + +Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Ρ‹ add_header_inherit ΠΈ add_trailer_inherit. + + +the "add_header_inherit" and "add_trailer_inherit" directives. + + + + + +ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ $request_port ΠΈ $is_request_port. + + +the $request_port and $is_request_port variables. + + + + + +ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ $ssl_sigalg ΠΈ $ssl_client_sigalg. + + +the $ssl_sigalg and $ssl_client_sigalg variables. + + + + + +ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ volatile Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Ρ‹ geo. + + +the "volatile" parameter of the "geo" directive. + + + + + +Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ сТатиС сСртификатов доступно с BoringSSL. + + +now certificate compression is available with BoringSSL. + + + + + +Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ сТатиС сСртификатов Π·Π°ΠΏΡ€Π΅Ρ‰Π΅Π½ΠΎ ΠΏΡ€ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Π½ΠΎΠΌ OCSP stapling. + + +now certificate compression is disabled with OCSP stapling. + + + + + + + + + + +Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ nginx ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ с AWS-LC.
+Бпасибо Samuel Chiang. +
+ +now nginx can be built with AWS-LC.
+Thanks Samuel Chiang. +
+
+ + + +Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Π° ssl_protocols Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ +Π² Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠΌ сСрвСрС, ΠΎΡ‚Π»ΠΈΡ‡Π½ΠΎΠΌ ΠΎΡ‚ сСрвСра ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, +ΠΏΡ€ΠΈ использовании OpenSSL 1.1.1 ΠΈ Π½ΠΎΠ²Π΅Π΅. + + +now the "ssl_protocols" directive works +in a virtual server different from the default server +when using OpenSSL 1.1.1 or newer. + + + + + +ΠΏΡ€ΠΈ использовании TLSv1.3 с OpenSSL ΠΈ клиСнтских сСртификатов +SSL handshake всСгда Π·Π°Π²Π΅Ρ€ΡˆΠ°Π»ΡΡ ошибкой +ΠΏΡ€ΠΈ восстановлСнии сСссии с Π΄Ρ€ΡƒΠ³ΠΈΠΌ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ SNI; +ошибка появилась Π² 1.27.4. + + +SSL handshake always failed +when using TLSv1.3 with OpenSSL and client certificates +and resuming a session with a different SNI value; +the bug had appeared in 1.27.4. + + + + + +ΠΏΡ€ΠΈ использовании QUIC ΠΈ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Ρ‹ ssl_reject_handshake +Π² Π»ΠΎΠ³Π°Ρ… ΠΌΠΎΠ³Π»ΠΈ ΠΏΠΎΡΠ²Π»ΡΡ‚ΡŒΡΡ сообщСния +"ignoring stale global SSL error"; +ошибка появилась Π² 1.29.0.
+Бпасибо Π’Π»Π°Π΄ΠΈΠΌΠΈΡ€Ρƒ Π₯ΠΎΠΌΡƒΡ‚ΠΎΠ²Ρƒ. +
+ +the "ignoring stale global SSL error" +alerts might appear in logs +when using QUIC and the "ssl_reject_handshake" directive; +the bug had appeared in 1.29.0.
+Thanks to Vladimir Homutov. +
+
+ + + +Π² ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ delta-seconds +Π² строкС "Cache-Control" Π² Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ΅ ΠΎΡ‚Π²Π΅Ρ‚Π° бэкСнда. + + +in delta-seconds processing +in the "Cache-Control" backend response header line. + + + + + +ΠΊΠΎΠΌΠ°Π½Π΄Π° XCLIENT Π½Π΅ использовала ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²ΠΊΡƒ xtext.
+Бпасибо Igor Morgenstern из Aisle Research. +
+ +an XCLIENT command didn't use the xtext encoding.
+Thanks to Igor Morgenstern of Aisle Research. +
+
+ + + +Π² ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ SSL-сСртификатов Π²ΠΎ врСмя ΠΏΠ΅Ρ€Π΅ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ. + + +in SSL certificate caching during reconfiguration. + + + +
+ + @@ -8736,7 +8887,7 @@ Thanks to Piotr Sikora. Бпасибо Piotr Sikora. -now nginx can be build with BoringSSL and LibreSSL.
+now nginx can be built with BoringSSL and LibreSSL.
Thanks to Piotr Sikora.
@@ -26019,7 +26170,7 @@ the ECONNABORTED error log level was changed to "error" from "crit". ΠΌΠΎΠ΄ΡƒΠ»ΡŒ ngx_http_perl_module Π½Π΅ собирался Π±Π΅Π· модуля ngx_http_ssi_filter_module. -the ngx_http_perl_module could not be build without +the ngx_http_perl_module could not be built without the ngx_http_ssi_filter_module. diff --git a/misc/GNUmakefile b/misc/GNUmakefile index 81b1921..e91e734 100644 --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,9 +6,9 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-3.5.2 +OPENSSL = openssl-3.5.4 ZLIB = zlib-1.3.1 -PCRE = pcre2-10.45 +PCRE = pcre2-10.46 release: export diff --git a/misc/README b/misc/README index 3f7b323..a5a88f2 100644 --- a/misc/README +++ b/misc/README @@ -3,7 +3,7 @@ make -f misc/GNUmakefile release the required tools: *) xsltproc to build CHANGES, -*) xslscript.pl ( http://hg.nginx.org/xslscript ) to build XSLTs +*) xslscript.pl ( https://github.com/nginx/xslscript ) to build XSLTs from XSLScript sources. diff --git a/src/core/nginx.h b/src/core/nginx.h index 1fdb800..87ebe9c 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1029002 -#define NGINX_VERSION "1.29.2" +#define nginx_version 1029003 +#define NGINX_VERSION "1.29.3" #define NGINX_VER "nginx/" NGINX_VERSION " by Hakase" #ifndef NGINX_SERVER diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c index f8f7384..10fe764 100644 --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -1494,8 +1494,9 @@ ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len) uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) { - ngx_uint_t n; + u_char prefix; uint32_t *escape; + ngx_uint_t n; static u_char hex[] = "0123456789ABCDEF"; /* @@ -1633,11 +1634,36 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) /* mail_auth is the same as memcached */ + /* " ", "+", "=", not allowed */ + + static uint32_t mail_xtext[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x20000801, /* 0010 0000 0000 0000 0000 1000 0000 0001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + static uint32_t *map[] = - { uri, args, uri_component, html, refresh, memcached, memcached }; + { uri, args, uri_component, html, refresh, memcached, memcached, + mail_xtext }; + + static u_char map_char[] = + { '%', '%', '%', '%', '%', '%', '%', '+' }; escape = map[type]; + prefix = map_char[type]; if (dst == NULL) { @@ -1658,7 +1684,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) while (size) { if (escape[*src >> 5] & (1U << (*src & 0x1f))) { - *dst++ = '%'; + *dst++ = prefix; *dst++ = hex[*src >> 4]; *dst++ = hex[*src & 0xf]; src++; diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h index 713eb42..183a205 100644 --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -203,6 +203,7 @@ u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); #define NGX_ESCAPE_REFRESH 4 #define NGX_ESCAPE_MEMCACHED 5 #define NGX_ESCAPE_MAIL_AUTH 6 +#define NGX_ESCAPE_MAIL_XTEXT 7 #define NGX_UNESCAPE_URI 1 #define NGX_UNESCAPE_REDIRECT 2 diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 9636384..ec12a94 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -9,6 +9,10 @@ #include #include +#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) +#include +#endif + #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 @@ -19,6 +23,13 @@ typedef struct { static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void); +#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) +static int ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, + CBB *out, const uint8_t *in, size_t in_len); +static void *ngx_ssl_cert_compression_alloc(void *opaque, u_int items, + u_int size); +static void ngx_ssl_cert_compression_free(void *opaque, void *address); +#endif static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret); @@ -128,6 +139,8 @@ int ngx_ssl_ticket_keys_index; int ngx_ssl_ocsp_index; int ngx_ssl_index; int ngx_ssl_certificate_name_index; +int ngx_ssl_certificate_comp_index; +int ngx_ssl_client_hello_arg_index; u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE]; @@ -270,6 +283,21 @@ ngx_ssl_init(ngx_log_t *log) return NGX_ERROR; } + ngx_ssl_certificate_comp_index = X509_get_ex_new_index(0, NULL, NULL, NULL, + NULL); + if (ngx_ssl_certificate_comp_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); + return NGX_ERROR; + } + + ngx_ssl_client_hello_arg_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, + NULL, NULL); + if (ngx_ssl_client_hello_arg_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "SSL_CTX_get_ex_new_index() failed"); + return NGX_ERROR; + } + return NGX_OK; } @@ -449,10 +477,18 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, { char *err; X509 *x509, **elm; + u_long n; EVP_PKEY *pkey; + ngx_uint_t mask; STACK_OF(X509) *chain; - chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT, &err, cert, NULL); + mask = 0; + elm = NULL; + +retry: + + chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT | mask, + &err, cert, NULL); if (chain == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, @@ -492,11 +528,16 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } } - elm = ngx_array_push(&ssl->certs); if (elm == NULL) { - X509_free(x509); - sk_X509_pop_free(chain, X509_free); - return NGX_ERROR; + elm = ngx_array_push(&ssl->certs); + if (elm == NULL) { + X509_free(x509); + sk_X509_pop_free(chain, X509_free); + return NGX_ERROR; + } + + } else { + X509_free(*elm); } *elm = x509; @@ -519,11 +560,21 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } #else - { - int n; /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */ +#ifdef SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS + /* OpenSSL 1.0.1+ */ + SSL_CTX_clear_extra_chain_certs(ssl->ctx); +#else + + if (ssl->ctx->extra_certs) { + sk_X509_pop_free(ssl->ctx->extra_certs, X509_free); + ssl->ctx->extra_certs = NULL; + } + +#endif + n = sk_X509_num(chain); while (n--) { @@ -539,10 +590,11 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } sk_X509_free(chain); - } + #endif - pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY, &err, key, passwords); + pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY | mask, + &err, key, passwords); if (pkey == NULL) { if (err != NULL) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, @@ -554,9 +606,23 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, } if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) { + EVP_PKEY_free(pkey); + + /* there can be mismatched pairs on uneven cache update */ + + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_X509 + && ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH + && mask == 0) + { + ERR_clear_error(); + mask = NGX_SSL_CACHE_INVALIDATE; + goto retry; + } + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_use_PrivateKey(\"%s\") failed", key->data); - EVP_PKEY_free(pkey); return NGX_ERROR; } @@ -682,6 +748,18 @@ ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl, SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION); +#elif (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) + + if (SSL_CTX_add_cert_compression_alg(ssl->ctx, TLSEXT_cert_compression_zlib, + ngx_ssl_cert_compression_callback, + NULL) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_add_cert_compression_alg() failed"); + return NGX_ERROR; + } + #else ngx_log_error(NGX_LOG_WARN, ssl->log, 0, @@ -694,6 +772,155 @@ ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl, } +#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib) + +static int +ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, CBB *out, + const uint8_t *in, size_t in_len) +{ + int rc; + X509 *cert; + u_char *p; + z_stream zstream; + ngx_str_t *comp, tmp; + ngx_pool_t *pool; + ngx_connection_t *c; + +#ifdef OPENSSL_IS_BORINGSSL + { + SSL_CTX *ssl_ctx; + ngx_ssl_t *ssl; + + /* BoringSSL doesn't have certificate slots, we take the last set */ + + ssl_ctx = SSL_get_SSL_CTX(ssl_conn); + ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index); + cert = ((X509 **) ssl->certs.elts)[ssl->certs.nelts - 1]; + } +#else + + /* + * AWS-LC saves leaf certificate in SSL to associate with SSL_CTX, + * see https://github.com/aws/aws-lc/commit/e1ba2b3e5 + */ + + cert = SSL_get_certificate(ssl_conn); + +#endif + + comp = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index); + + if (comp != NULL) { + return CBB_add_bytes(out, comp->data, comp->len); + } + + c = ngx_ssl_get_connection(ssl_conn); + + pool = ngx_create_pool(256, c->log); + if (pool == NULL) { + return 0; + } + + pool->log = c->log; + + ngx_memzero(&zstream, sizeof(z_stream)); + + zstream.zalloc = ngx_ssl_cert_compression_alloc; + zstream.zfree = ngx_ssl_cert_compression_free; + zstream.opaque = pool; + + rc = deflateInit(&zstream, Z_DEFAULT_COMPRESSION); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateInit() failed: %d", rc); + goto error; + } + + tmp.len = deflateBound(&zstream, in_len); + tmp.data = ngx_palloc(pool, tmp.len); + if (tmp.data == NULL) { + goto error; + } + + zstream.next_in = (u_char *) in; + zstream.avail_in = in_len; + zstream.next_out = tmp.data; + zstream.avail_out = tmp.len; + + rc = deflate(&zstream, Z_FINISH); + + if (rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "deflate(Z_FINISH) failed: %d", rc); + goto error; + } + + tmp.len -= zstream.avail_out; + + rc = deflateEnd(&zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateEnd() failed: %d", rc); + goto error; + } + + p = ngx_alloc(sizeof(ngx_str_t) + tmp.len, c->log); + if (p == NULL) { + goto error; + } + + comp = (ngx_str_t *) p; + + comp->len = tmp.len; + comp->data = p + sizeof(ngx_str_t); + + ngx_memcpy(comp->data, tmp.data, tmp.len); + + if (X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, p) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_set_ex_data() failed"); + ngx_free(p); + } + + rc = CBB_add_bytes(out, tmp.data, tmp.len); + + ngx_destroy_pool(pool); + + return rc; + +error: + + ngx_destroy_pool(pool); + + return 0; +} + + +static void * +ngx_ssl_cert_compression_alloc(void *opaque, u_int items, u_int size) +{ + ngx_pool_t *pool = opaque; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pool->log, 0, + "cert compression alloc: n:%ud s:%ud", items, size); + + return ngx_palloc(pool, items * size); +} + + +static void +ngx_ssl_cert_compression_free(void *opaque, void *address) +{ +#if 0 + ngx_pool_t *pool = opaque; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pool->log, 0, + "cert compression free: %p", address); +#endif +} + +#endif + + ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers) @@ -1645,6 +1872,118 @@ ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) } +void +ngx_ssl_set_client_hello_callback(SSL_CTX *ssl_ctx, + ngx_ssl_client_hello_arg *cb) +{ +#ifdef SSL_CLIENT_HELLO_SUCCESS + + SSL_CTX_set_client_hello_cb(ssl_ctx, ngx_ssl_client_hello_callback, NULL); + SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_client_hello_arg_index, cb); + +#elif defined OPENSSL_IS_BORINGSSL + + SSL_CTX_set_select_certificate_cb(ssl_ctx, ngx_ssl_select_certificate); + SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_client_hello_arg_index, cb); + +#endif +} + + +#ifdef SSL_CLIENT_HELLO_SUCCESS + +int +ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) +{ + u_char *p; + size_t len; + ngx_int_t rc; + ngx_str_t host; + ngx_connection_t *c; + ngx_ssl_client_hello_arg *cb; + + c = ngx_ssl_get_connection(ssl_conn); + cb = SSL_CTX_get_ex_data(c->ssl->session_ctx, + ngx_ssl_client_hello_arg_index); + + if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, + (const unsigned char **) &p, &len) + == 0) + { + ngx_str_null(&host); + goto done; + } + + /* + * RFC 6066 mandates non-zero HostName length, we follow OpenSSL. + * No more than one ServerName is expected. + */ + + if (len < 5 + || (size_t) (p[0] << 8) + p[1] + 2 != len + || p[2] != TLSEXT_NAMETYPE_host_name + || (size_t) (p[3] << 8) + p[4] + 2 + 3 != len) + { + *ad = SSL_AD_DECODE_ERROR; + return SSL_CLIENT_HELLO_ERROR; + } + + len -= 5; + p += 5; + + if (len > TLSEXT_MAXLEN_host_name || ngx_strlchr(p, p + len, '\0')) { + *ad = SSL_AD_UNRECOGNIZED_NAME; + return SSL_CLIENT_HELLO_ERROR; + } + + host.len = len; + host.data = p; + +done: + + rc = cb->servername(ssl_conn, ad, &host); + + if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) { + return SSL_CLIENT_HELLO_ERROR; + } + + return SSL_CLIENT_HELLO_SUCCESS; +} + +#elif defined OPENSSL_IS_BORINGSSL + +enum ssl_select_cert_result_t ngx_ssl_select_certificate( + const SSL_CLIENT_HELLO *client_hello) +{ + int ad; + ngx_int_t rc; + ngx_ssl_conn_t *ssl_conn; + ngx_connection_t *c; + ngx_ssl_client_hello_arg *cb; + + ssl_conn = client_hello->ssl; + c = ngx_ssl_get_connection(ssl_conn); + cb = SSL_CTX_get_ex_data(c->ssl->session_ctx, + ngx_ssl_client_hello_arg_index); + + /* + * BoringSSL sends a hardcoded "handshake_failure" alert on errors, + * we use it to map SSL_AD_INTERNAL_ERROR. To preserve other alert + * values, error handling is postponed to the servername callback. + */ + + rc = cb->servername(ssl_conn, &ad, NULL); + + if (rc == SSL_TLSEXT_ERR_ALERT_FATAL && ad == SSL_AD_INTERNAL_ERROR) { + return ssl_select_cert_error; + } + + return ssl_select_cert_success; +} + +#endif + + ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) { @@ -4831,10 +5170,19 @@ ngx_ssl_cleanup_ctx(void *data) ngx_ssl_t *ssl = data; X509 *cert; + u_char *p; ngx_uint_t i; for (i = 0; i < ssl->certs.nelts; i++) { cert = ((X509 **) ssl->certs.elts)[i]; + + p = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index); + + if (p) { + ngx_free(p); + X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, NULL); + } + X509_free(cert); } @@ -5231,6 +5579,32 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) } +ngx_int_t +ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ +#ifdef SSL_get0_signature_name + + const char *name; + + if (SSL_get0_signature_name(c->ssl->connection, &name)) { + s->len = ngx_strlen(name); + s->data = ngx_pnalloc(pool, s->len); + if (s->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->data, name, s->len); + + return NGX_OK; + } + +#endif + + s->len = 0; + return NGX_OK; +} + + ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { @@ -5976,6 +6350,32 @@ ngx_ssl_parse_time( } +ngx_int_t +ngx_ssl_get_client_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ +#ifdef SSL_get0_peer_signature_name + + const char *name; + + if (SSL_get0_peer_signature_name(c->ssl->connection, &name)) { + s->len = ngx_strlen(name); + s->data = ngx_pnalloc(pool, s->len); + if (s->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->data, name, s->len); + + return NGX_OK; + } + +#endif + + s->len = 0; + return NGX_OK; +} + + static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) { diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index fccb677..1134e19 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -26,14 +26,6 @@ #include #endif #include -#if (NGX_QUIC) -#ifdef OPENSSL_IS_BORINGSSL -#include -#include -#else -#include -#endif -#endif #include #ifndef OPENSSL_NO_OCSP #include @@ -161,6 +153,7 @@ struct ngx_ssl_connection_s { unsigned in_ocsp:1; unsigned early_preread:1; unsigned write_blocked:1; + unsigned sni_accepted:1; ngx_ssl_dyn_rec_t dyn_rec; ngx_msec_t dyn_rec_last_write; @@ -211,6 +204,13 @@ typedef struct { } ngx_ssl_session_cache_t; +typedef int (*ngx_ssl_servername_pt)(ngx_ssl_conn_t *, int *, void *); + +typedef struct { + ngx_ssl_servername_pt servername; +} ngx_ssl_client_hello_arg; + + #define NGX_SSL_SSLv2 0x0002 #define NGX_SSL_SSLv3 0x0004 #define NGX_SSL_TLSv1 0x0008 @@ -300,6 +300,15 @@ ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); +void ngx_ssl_set_client_hello_callback(SSL_CTX *ssl_ctx, + ngx_ssl_client_hello_arg *cb); +#ifdef SSL_CLIENT_HELLO_SUCCESS +int ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg); +#elif defined OPENSSL_IS_BORINGSSL +enum ssl_select_cert_result_t ngx_ssl_select_certificate( + const SSL_CLIENT_HELLO *client_hello); +#endif + ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); @@ -333,6 +342,8 @@ ngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, @@ -369,6 +380,8 @@ ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_client_sigalg(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); @@ -396,6 +409,8 @@ extern int ngx_ssl_ticket_keys_index; extern int ngx_ssl_ocsp_index; extern int ngx_ssl_index; extern int ngx_ssl_certificate_name_index; +extern int ngx_ssl_certificate_comp_index; +extern int ngx_ssl_client_hello_arg_index; extern u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE]; diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index 42f5e1c..61fceed 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -193,6 +193,7 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, time_t mtime; uint32_t hash; ngx_int_t rc; + ngx_uint_t invalidate; ngx_file_uniq_t uniq; ngx_file_info_t fi; ngx_ssl_cache_t *cache, *old_cache; @@ -202,10 +203,17 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, *err = NULL; + invalidate = index & NGX_SSL_CACHE_INVALIDATE; + index &= ~NGX_SSL_CACHE_INVALIDATE; + if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) { return NULL; } + if (id.type == NGX_SSL_CACHE_DATA) { + invalidate = 0; + } + cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx, ngx_openssl_cache_module); @@ -215,7 +223,12 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, cn = ngx_ssl_cache_lookup(cache, type, &id, hash); if (cn != NULL) { - return type->ref(err, cn->value); + if (!invalidate) { + return type->ref(err, cn->value); + } + + type->free(cn->value); + ngx_rbtree_delete(&cache->rbtree, &cn->node); } value = NULL; @@ -236,7 +249,7 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, old_cache = ngx_ssl_cache_get_old_conf(cf->cycle); - if (old_cache && old_cache->inheritable) { + if (old_cache && old_cache->inheritable && !invalidate) { cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash); if (cn != NULL) { diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h index bab085f..4f899ec 100644 --- a/src/event/quic/ngx_event_quic.h +++ b/src/event/quic/ngx_event_quic.h @@ -18,7 +18,8 @@ #elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION) #define NGX_QUIC_QUICTLS_API 1 -#elif (defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER) +#elif (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC \ + || defined LIBRESSL_VERSION_NUMBER) #define NGX_QUIC_BORINGSSL_API 1 #else diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c index 885843d..2f28737 100644 --- a/src/event/quic/ngx_event_quic_protection.c +++ b/src/event/quic/ngx_event_quic_protection.c @@ -8,6 +8,12 @@ #include #include #include +#if (NGX_QUIC_BORINGSSL_EVP_API) +#include +#include +#else +#include +#endif /* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */ @@ -33,7 +39,7 @@ static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask, static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); -#ifndef OPENSSL_IS_BORINGSSL +#if !(NGX_QUIC_BORINGSSL_EVP_API) static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); #endif @@ -58,7 +64,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers) switch (id) { case TLS1_3_CK_AES_128_GCM_SHA256: -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) ciphers->c = EVP_aead_aes_128_gcm(); #else ciphers->c = EVP_aes_128_gcm(); @@ -69,7 +75,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers) break; case TLS1_3_CK_AES_256_GCM_SHA384: -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) ciphers->c = EVP_aead_aes_256_gcm(); #else ciphers->c = EVP_aes_256_gcm(); @@ -80,12 +86,12 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers) break; case TLS1_3_CK_CHACHA20_POLY1305_SHA256: -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) ciphers->c = EVP_aead_chacha20_poly1305(); #else ciphers->c = EVP_chacha20_poly1305(); #endif -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305(); #else ciphers->hp = EVP_chacha20(); @@ -94,7 +100,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers) len = 32; break; -#ifndef OPENSSL_IS_BORINGSSL +#if !(NGX_QUIC_BORINGSSL_EVP_API) case TLS1_3_CK_AES_128_CCM_SHA256: ciphers->c = EVP_aes_128_ccm(); ciphers->hp = EVP_aes_128_ctr(); @@ -263,7 +269,7 @@ static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len) { -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len) == 0) @@ -325,7 +331,7 @@ ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest, const u_char *secret, size_t secret_len, const u_char *salt, size_t salt_len) { -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt, salt_len) @@ -388,7 +394,7 @@ ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log) { -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) EVP_AEAD_CTX *ctx; ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len, @@ -448,7 +454,7 @@ static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) { -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce, s->iv.len, in->data, in->len, ad->data, ad->len) != 1) @@ -468,7 +474,7 @@ ngx_int_t ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) { -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce, s->iv.len, in->data, in->len, ad->data, ad->len) != 1) @@ -484,7 +490,7 @@ ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, } -#ifndef OPENSSL_IS_BORINGSSL +#if !(NGX_QUIC_BORINGSSL_EVP_API) static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, @@ -563,7 +569,7 @@ void ngx_quic_crypto_cleanup(ngx_quic_secret_t *s) { if (s->ctx) { -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) EVP_AEAD_CTX_free(s->ctx); #else EVP_CIPHER_CTX_free(s->ctx); @@ -579,7 +585,7 @@ ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, ngx_quic_secret_t *s, { EVP_CIPHER_CTX *ctx; -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) { /* no EVP interface */ s->hp_ctx = NULL; @@ -615,7 +621,7 @@ ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in, ctx = s->hp_ctx; -#ifdef OPENSSL_IS_BORINGSSL +#if (NGX_QUIC_BORINGSSL_EVP_API) uint32_t cnt; if (ctx == NULL) { diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h index fddc608..7c5cf31 100644 --- a/src/event/quic/ngx_event_quic_protection.h +++ b/src/event/quic/ngx_event_quic_protection.h @@ -22,10 +22,12 @@ #define NGX_QUIC_MAX_MD_SIZE 48 -#ifdef OPENSSL_IS_BORINGSSL +#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC) +#define NGX_QUIC_BORINGSSL_EVP_API 1 #define ngx_quic_cipher_t EVP_AEAD #define ngx_quic_crypto_ctx_t EVP_AEAD_CTX #else +#define NGX_QUIC_BORINGSSL_EVP_API 0 #define ngx_quic_cipher_t EVP_CIPHER #define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX #endif diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c index e961c80..a502431 100644 --- a/src/event/quic/ngx_event_quic_ssl.c +++ b/src/event/quic/ngx_event_quic_ssl.c @@ -695,30 +695,35 @@ ngx_quic_handshake(ngx_connection_t *c) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); - if (qc->error) { - return NGX_ERROR; - } - if (n <= 0) { sslerr = SSL_get_error(ssl_conn, n); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); + if (c->ssl->handshake_rejected) { + ngx_connection_error(c, 0, "handshake rejected"); + ERR_clear_error(); + return NGX_ERROR; + } + + if (qc->error) { + ngx_connection_error(c, 0, "SSL_do_handshake() failed"); + ERR_clear_error(); + return NGX_ERROR; + } + if (sslerr != SSL_ERROR_WANT_READ) { - - if (c->ssl->handshake_rejected) { - ngx_connection_error(c, 0, "handshake rejected"); - ERR_clear_error(); - - return NGX_ERROR; - } - ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed"); return NGX_ERROR; } } + if (qc->error) { + ngx_connection_error(c, 0, "SSL_do_handshake() failed"); + return NGX_ERROR; + } + if (!SSL_is_init_finished(ssl_conn)) { if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0) && qc->client_tp_done) @@ -968,7 +973,7 @@ ngx_quic_init_connection(ngx_connection_t *c) } #endif -#ifdef OPENSSL_IS_BORINGSSL +#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC) if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) { ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "quic SSL_set_quic_early_data_context() failed"); diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c index 4d6fd3e..ea5cbe6 100644 --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -66,7 +66,9 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r) || r->headers_out.status == NGX_HTTP_NO_CONTENT || r->headers_out.status < NGX_HTTP_OK || r != r->main - || r->method == NGX_HTTP_HEAD) + || r->method == NGX_HTTP_HEAD + || (r->method == NGX_HTTP_CONNECT + && r->headers_out.status < NGX_HTTP_SPECIAL_RESPONSE)) { return ngx_http_next_header_filter(r); } diff --git a/src/http/modules/ngx_http_geo_module.c b/src/http/modules/ngx_http_geo_module.c index 75c0397..619a5fd 100644 --- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -63,6 +63,7 @@ typedef struct { unsigned allow_binary_include:1; unsigned binary_include:1; unsigned proxy_recursive:1; + unsigned no_cacheable:1; } ngx_http_geo_conf_ctx_t; @@ -463,6 +464,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) + sizeof(ngx_http_variable_value_t) + 0x10000 * sizeof(ngx_http_geo_range_t *); ctx.allow_binary_include = 1; + ctx.no_cacheable = 0; save = *cf; cf->pool = pool; @@ -478,6 +480,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) goto failed; } + if (ctx.no_cacheable) { + var->flags |= NGX_HTTP_VAR_NOCACHEABLE; + } + geo->proxies = ctx.proxies; geo->proxy_recursive = ctx.proxy_recursive; @@ -623,6 +629,12 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) rv = NGX_CONF_OK; goto done; } + + else if (ngx_strcmp(value[0].data, "volatile") == 0) { + ctx->no_cacheable = 1; + rv = NGX_CONF_OK; + goto done; + } } if (cf->args->nelts != 2) { diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c index 90e6da8..f720440 100644 --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -10,6 +10,11 @@ #include +#define NGX_HTTP_HEADERS_INHERIT_OFF 0 +#define NGX_HTTP_HEADERS_INHERIT_ON 1 +#define NGX_HTTP_HEADERS_INHERIT_MERGE 2 + + typedef struct ngx_http_header_val_s ngx_http_header_val_t; typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r, @@ -49,6 +54,8 @@ typedef struct { ngx_http_complex_value_t *expires_value; ngx_array_t *headers; ngx_array_t *trailers; + ngx_uint_t headers_inherit; + ngx_uint_t trailers_inherit; } ngx_http_headers_conf_t; @@ -97,6 +104,14 @@ static ngx_http_set_header_t ngx_http_set_headers[] = { }; +static ngx_conf_enum_t ngx_http_headers_inherit[] = { + { ngx_string("off"), NGX_HTTP_HEADERS_INHERIT_OFF }, + { ngx_string("on"), NGX_HTTP_HEADERS_INHERIT_ON }, + { ngx_string("merge"), NGX_HTTP_HEADERS_INHERIT_MERGE }, + { ngx_null_string, 0 } +}; + + static ngx_command_t ngx_http_headers_filter_commands[] = { { ngx_string("expires"), @@ -123,6 +138,22 @@ static ngx_command_t ngx_http_headers_filter_commands[] = { offsetof(ngx_http_headers_conf_t, trailers), NULL }, + { ngx_string("add_header_inherit"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_headers_conf_t, headers_inherit), + &ngx_http_headers_inherit }, + + { ngx_string("add_trailer_inherit"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_headers_conf_t, trailers_inherit), + &ngx_http_headers_inherit }, + ngx_null_command }; @@ -657,6 +688,8 @@ ngx_http_headers_create_conf(ngx_conf_t *cf) */ conf->expires = NGX_HTTP_EXPIRES_UNSET; + conf->headers_inherit = NGX_CONF_UNSET_UINT; + conf->trailers_inherit = NGX_CONF_UNSET_UINT; return conf; } @@ -668,6 +701,8 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_headers_conf_t *prev = parent; ngx_http_headers_conf_t *conf = child; + ngx_http_header_val_t *hv; + if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { conf->expires = prev->expires; conf->expires_time = prev->expires_time; @@ -678,12 +713,43 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child) } } - if (conf->headers == NULL) { - conf->headers = prev->headers; + ngx_conf_merge_uint_value(conf->headers_inherit, prev->headers_inherit, + NGX_HTTP_HEADERS_INHERIT_ON); + ngx_conf_merge_uint_value(conf->trailers_inherit, prev->trailers_inherit, + NGX_HTTP_HEADERS_INHERIT_ON); + + if (conf->headers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF + && prev->headers) + { + if (conf->headers == NULL) { + conf->headers = prev->headers; + + } else if (conf->headers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) { + hv = ngx_array_push_n(conf->headers, prev->headers->nelts); + if (hv == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(hv, prev->headers->elts, + sizeof(ngx_http_header_val_t) * prev->headers->nelts); + } } - if (conf->trailers == NULL) { - conf->trailers = prev->trailers; + if (conf->trailers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF + && prev->trailers) + { + if (conf->trailers == NULL) { + conf->trailers = prev->trailers; + + } else if (conf->trailers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) { + hv = ngx_array_push_n(conf->trailers, prev->trailers->nelts); + if (hv == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(hv, prev->trailers->elts, + sizeof(ngx_http_header_val_t) * prev->trailers->nelts); + } } return NGX_CONF_OK; diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c index 2ee1dd5..18e7049 100644 --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -490,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) if (value[i].len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "index \"%V\" in \"index\" directive is invalid", - &value[1]); + &value[i]); return NGX_CONF_ERROR; } diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 6af252f..ccd7b71 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -237,7 +237,7 @@ static ngx_command_t ngx_http_ssl_commands[] = { NULL }, { ngx_string("ssl_ocsp"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, ocsp), @@ -393,6 +393,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_sigalg"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 }, @@ -450,6 +453,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = { { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_sigalg"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_client_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 }, + ngx_http_null_variable }; @@ -803,6 +809,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) cln->data = &conf->ssl; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + { + static ngx_ssl_client_hello_arg cb = { ngx_http_ssl_servername }; + + ngx_ssl_set_client_hello_callback(conf->ssl.ctx, &cb); if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx, ngx_http_ssl_servername) @@ -813,7 +823,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) "dynamically to an OpenSSL library which has no tlsext support, " "therefore SNI is not available"); } - + } #endif #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation @@ -956,13 +966,19 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->stapling) { + if (conf->certificate_compression) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_stapling\" is incompatible with " + "\"ssl_certificate_compression\""); + return NGX_CONF_ERROR; + } + if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, &conf->stapling_responder, conf->stapling_verify) != NGX_OK) { return NGX_CONF_ERROR; } - } if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) { diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index a794144..9be5653 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -206,6 +206,7 @@ typedef struct { #if (NGX_PCRE) unsigned captures:1; #endif + unsigned allow_connect:1; ngx_http_core_loc_conf_t **named_locations; } ngx_http_core_srv_conf_t; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index f7e5038..ae921f9 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -111,6 +111,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) sw_schema, sw_schema_slash, sw_schema_slash_slash, + sw_spaces_before_host, sw_host_start, sw_host, sw_host_end, @@ -158,6 +159,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (ch == ' ') { r->method_end = p - 1; m = r->request_start; + state = sw_spaces_before_uri; switch (p - m) { @@ -247,6 +249,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' ')) { r->method = NGX_HTTP_CONNECT; + state = sw_spaces_before_host; } break; @@ -269,7 +272,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } - state = sw_spaces_before_uri; break; } @@ -345,6 +347,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) } break; + case sw_spaces_before_host: + + if (ch == ' ') { + break; + } + + /* fall through */ + case sw_host_start: r->host_start = p; @@ -375,6 +385,15 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) r->host_end = p; + if (r->method == NGX_HTTP_CONNECT) { + if (ch == ':') { + state = sw_port; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + switch (ch) { case ':': state = sw_port; @@ -446,9 +465,23 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_port: if (ch >= '0' && ch <= '9') { + if (r->port >= 6553 && (r->port > 6553 || (ch - '0') > 5)) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->port = r->port * 10 + (ch - '0'); break; } + if (r->method == NGX_HTTP_CONNECT) { + if (ch == ' ') { + state = sw_http_09; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + switch (ch) { case '/': r->uri_start = p; @@ -684,6 +717,16 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_http_HTTP: switch (ch) { case '/': + + /* + * use single "/" from request line to preserve pointers, + * if request line will be copied to large client buffer + */ + if (r->method == NGX_HTTP_CONNECT) { + r->uri_start = p; + r->uri_end = p + 1; + } + state = sw_first_major_digit; break; default: diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 95cb1a1..533af45 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -891,27 +891,46 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) return SSL_TLSEXT_ERR_ALERT_FATAL; } + if (c->ssl->sni_accepted) { + return SSL_TLSEXT_ERR_OK; + } + + if (c->ssl->handshake_rejected) { + *ad = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + hc = c->data; - servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + if (arg != NULL) { + host = *(ngx_str_t *) arg; - if (servername == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "SSL server name: null"); - goto done; + if (host.data == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "SSL server name: null"); + goto done; + } + + } else { + servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + + if (servername == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "SSL server name: null"); + goto done; + } + + host.len = ngx_strlen(servername); + host.data = (u_char *) servername; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "SSL server name: \"%s\"", servername); - - host.len = ngx_strlen(servername); + "SSL server name: \"%V\"", &host); if (host.len == 0) { goto done; } - host.data = (u_char *) servername; - rc = ngx_http_validate_host(&host, c->pool, 1); if (rc == NGX_ERROR) { @@ -933,31 +952,6 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) goto done; } - sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module); - -#if (defined TLS1_3_VERSION \ - && !defined LIBRESSL_VERSION_NUMBER && !defined OPENSSL_IS_BORINGSSL) - - /* - * SSL_SESSION_get0_hostname() is only available in OpenSSL 1.1.1+, - * but servername being negotiated in every TLSv1.3 handshake - * is only returned in OpenSSL 1.1.1+ as well - */ - - if (sscf->verify) { - const char *hostname; - - hostname = SSL_SESSION_get0_hostname(SSL_get0_session(ssl_conn)); - - if (hostname != NULL && ngx_strcmp(hostname, servername) != 0) { - c->ssl->handshake_rejected = 1; - *ad = SSL_AD_ACCESS_DENIED; - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - } - -#endif - hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t)); if (hc->ssl_servername == NULL) { goto error; @@ -971,6 +965,8 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) ngx_set_connection_log(c, clcf->error_log); + sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module); + c->ssl->buffer_size = sscf->buffer_size; if (sscf->ssl.ctx) { @@ -1019,6 +1015,7 @@ done: return SSL_TLSEXT_ERR_ALERT_FATAL; } + c->ssl->sni_accepted = 1; return SSL_TLSEXT_ERR_OK; error: @@ -1849,8 +1846,9 @@ static ngx_int_t ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { - ngx_int_t rc; - ngx_str_t host; + u_char *p; + ngx_int_t rc; + ngx_str_t host; if (r->headers_in.host) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, @@ -1891,6 +1889,17 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h, r->headers_in.server = host; + p = ngx_strlchr(h->value.data + host.len, + h->value.data + h->value.len, ':'); + + if (p) { + rc = ngx_atoi(p + 1, h->value.data + h->value.len - p - 1); + + if (rc > 0 && rc < 65536) { + r->port = rc; + } + } + return NGX_OK; } @@ -1988,6 +1997,8 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) { + ngx_http_core_srv_conf_t *cscf; + if (r->headers_in.server.len == 0 && ngx_http_set_virtual_server(r, &r->headers_in.server) == NGX_ERROR) @@ -2056,7 +2067,11 @@ ngx_http_process_request_header(ngx_http_request_t *r) } } - if (r->method == NGX_HTTP_CONNECT) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->method == NGX_HTTP_CONNECT + && (r->http_version != NGX_HTTP_VERSION_11 || !cscf->allow_connect)) + { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent CONNECT method"); ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index ad11f14..1b012f8 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -461,6 +461,8 @@ struct ngx_http_request_s { ngx_http_cleanup_t *cleanup; + in_port_t port; + unsigned count:16; unsigned subrequests:8; unsigned blocked:8; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index de0f92a..6616354 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -116,6 +116,10 @@ static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, static ngx_int_t ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +#if (NGX_HTTP_CACHE) +static ngx_int_t ngx_http_upstream_process_delta_seconds(u_char *p, + u_char *last); +#endif static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r, @@ -5066,18 +5070,9 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r, } if (p) { - n = 0; - - for (p += offset; p < last; p++) { - if (*p == ',' || *p == ';' || *p == ' ') { - break; - } - - if (*p >= '0' && *p <= '9') { - n = n * 10 + (*p - '0'); - continue; - } + n = ngx_http_upstream_process_delta_seconds(p + offset, last); + if (n == NGX_ERROR) { u->cacheable = 0; return NGX_OK; } @@ -5087,7 +5082,8 @@ ngx_http_upstream_process_cache_control(ngx_http_request_t *r, return NGX_OK; } - r->cache->valid_sec = ngx_time() + n; + r->cache->valid_sec = ngx_min((ngx_uint_t) ngx_time() + n, + NGX_MAX_INT_T_VALUE); u->headers_in.expired = 0; } @@ -5097,18 +5093,9 @@ extensions: 23 - 1); if (p) { - n = 0; - - for (p += 23; p < last; p++) { - if (*p == ',' || *p == ';' || *p == ' ') { - break; - } - - if (*p >= '0' && *p <= '9') { - n = n * 10 + (*p - '0'); - continue; - } + n = ngx_http_upstream_process_delta_seconds(p + 23, last); + if (n == NGX_ERROR) { u->cacheable = 0; return NGX_OK; } @@ -5120,18 +5107,9 @@ extensions: p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1); if (p) { - n = 0; - - for (p += 15; p < last; p++) { - if (*p == ',' || *p == ';' || *p == ' ') { - break; - } - - if (*p >= '0' && *p <= '9') { - n = n * 10 + (*p - '0'); - continue; - } + n = ngx_http_upstream_process_delta_seconds(p + 15, last); + if (n == NGX_ERROR) { u->cacheable = 0; return NGX_OK; } @@ -5145,6 +5123,41 @@ extensions: } +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_upstream_process_delta_seconds(u_char *p, u_char *last) +{ + ngx_int_t n, cutoff, cutlim; + + cutoff = NGX_MAX_INT_T_VALUE / 10; + cutlim = NGX_MAX_INT_T_VALUE % 10; + + n = 0; + + for ( /* void */ ; p < last; p++) { + if (*p == ',' || *p == ';' || *p == ' ') { + break; + } + + if (*p < '0' || *p > '9') { + return NGX_ERROR; + } + + if (n >= cutoff && (n > cutoff || *p - '0' > cutlim)) { + n = NGX_MAX_INT_T_VALUE; + break; + } + + n = n * 10 + (*p - '0'); + } + + return n; +} + +#endif + + static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) @@ -6885,6 +6898,7 @@ ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u, } if (val.len == 0) { + u->peer.local = NULL; return NGX_OK; } @@ -6901,6 +6915,7 @@ ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u, if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid local address \"%V\"", &val); + u->peer.local = NULL; return NGX_OK; } diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index f3e9f79..64d70cf 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -253,7 +253,7 @@ typedef struct { ngx_str_t module; - NGX_COMPAT_BEGIN(2) + NGX_COMPAT_BEGIN(6) NGX_COMPAT_END } ngx_http_upstream_conf_t; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index 4f0bd0e..dd69bcf 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -71,6 +71,10 @@ static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_is_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static void ngx_http_variable_set_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, @@ -231,6 +235,12 @@ static ngx_http_variable_t ngx_http_core_variables[] = { { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 }, + { ngx_string("request_port"), NULL, + ngx_http_variable_request_port, 0, 0, 0 }, + + { ngx_string("is_request_port"), NULL, + ngx_http_variable_is_request_port, 0, 0, 0 }, + { ngx_string("request_uri"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, unparsed_uri), 0, 0 }, @@ -1540,6 +1550,51 @@ ngx_http_variable_https(ngx_http_request_t *r, } +static ngx_int_t +ngx_http_variable_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = r->port; + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_is_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->port == 0) { + *v = ngx_http_variable_null_value; + return NGX_OK; + } + + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) ":"; + + return NGX_OK; +} + + static void ngx_http_variable_set_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 23fb7b2..67f24bf 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -3527,7 +3527,8 @@ ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value) static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) { - ngx_int_t rc; + u_char *p; + ngx_int_t rc; if (r->host_start) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, @@ -3561,6 +3562,16 @@ ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) r->headers_in.server = *value; + p = ngx_strlchr(r->host_start + value->len, r->host_end, ':'); + + if (p) { + rc = ngx_atoi(p + 1, r->host_end - p - 1); + + if (rc > 0 && rc < 65536) { + r->port = rc; + } + } + return NGX_OK; } diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c index 844a400..77c55bf 100644 --- a/src/http/v3/ngx_http_v3_request.c +++ b/src/http/v3/ngx_http_v3_request.c @@ -979,6 +979,16 @@ ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) } r->headers_in.server = host; + + p = ngx_strlchr(r->host_start + host.len, r->host_end, ':'); + + if (p) { + rc = ngx_atoi(p + 1, r->host_end - p - 1); + + if (rc > 0 && rc < 65536) { + r->port = rc; + } + } } if (ngx_list_init(&r->headers_in.headers, r->pool, 20, diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c index 1c6d037..84a7f61 100644 --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -531,6 +531,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev) ngx_int_t rc; ngx_str_t line, auth, encoded; ngx_buf_t *b; + uintptr_t n; ngx_connection_t *c; ngx_mail_session_t *s; ngx_mail_proxy_conf_t *pcf; @@ -627,6 +628,10 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev) CRLF) - 1 + s->connection->addr_text.len + s->login.len + s->host.len; + n = ngx_escape_uri(NULL, s->login.data, s->login.len, + NGX_ESCAPE_MAIL_XTEXT); + line.len += n * 2; + #if (NGX_HAVE_INET6) if (s->connection->sockaddr->sa_family == AF_INET6) { line.len += sizeof("IPV6:") - 1; @@ -654,7 +659,14 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev) if (s->login.len && !pcf->smtp_auth) { p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1); - p = ngx_copy(p, s->login.data, s->login.len); + + if (n == 0) { + p = ngx_copy(p, s->login.data, s->login.len); + + } else { + p = (u_char *) ngx_escape_uri(p, s->login.data, s->login.len, + NGX_ESCAPE_MAIL_XTEXT); + } } p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1); diff --git a/src/stream/ngx_stream_geo_module.c b/src/stream/ngx_stream_geo_module.c index 2324bef..c109dc7 100644 --- a/src/stream/ngx_stream_geo_module.c +++ b/src/stream/ngx_stream_geo_module.c @@ -61,6 +61,7 @@ typedef struct { unsigned outside_entries:1; unsigned allow_binary_include:1; unsigned binary_include:1; + unsigned no_cacheable:1; } ngx_stream_geo_conf_ctx_t; @@ -433,6 +434,7 @@ ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) + sizeof(ngx_stream_variable_value_t) + 0x10000 * sizeof(ngx_stream_geo_range_t *); ctx.allow_binary_include = 1; + ctx.no_cacheable = 0; save = *cf; cf->pool = pool; @@ -448,6 +450,10 @@ ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) goto failed; } + if (ctx.no_cacheable) { + var->flags |= NGX_STREAM_VAR_NOCACHEABLE; + } + if (ctx.ranges) { if (ctx.high.low && !ctx.binary_include) { @@ -583,6 +589,12 @@ ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) goto done; } + + else if (ngx_strcmp(value[0].data, "volatile") == 0) { + ctx->no_cacheable = 1; + rv = NGX_CONF_OK; + goto done; + } } if (cf->args->nelts != 2) { diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 6e51585..300bdf6 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -680,6 +680,7 @@ ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u, } if (val.len == 0) { + u->peer.local = NULL; return NGX_OK; } @@ -696,6 +697,7 @@ ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u, if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "invalid local address \"%V\"", &val); + u->peer.local = NULL; return NGX_OK; } diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 7207e3d..90c0d04 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -239,7 +239,7 @@ static ngx_command_t ngx_stream_ssl_commands[] = { NULL }, { ngx_string("ssl_ocsp"), - NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_ssl_srv_conf_t, ocsp), @@ -357,6 +357,9 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = { { ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable, (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_sigalg"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable, (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 }, @@ -404,6 +407,9 @@ static ngx_stream_variable_t ngx_stream_ssl_vars[] = { { ngx_string("ssl_client_v_remain"), NULL, ngx_stream_ssl_variable, (uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_sigalg"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_client_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 }, + ngx_stream_null_variable }; @@ -555,27 +561,46 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) return SSL_TLSEXT_ERR_ALERT_FATAL; } + if (c->ssl->sni_accepted) { + return SSL_TLSEXT_ERR_OK; + } + + if (c->ssl->handshake_rejected) { + *ad = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + s = c->data; - servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + if (arg) { + host = *(ngx_str_t *) arg; - if (servername == NULL) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, - "SSL server name: null"); - goto done; + if (host.data == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL server name: null"); + goto done; + } + + } else { + servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + + if (servername == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL server name: null"); + goto done; + } + + host.len = ngx_strlen(servername); + host.data = (u_char *) servername; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, - "SSL server name: \"%s\"", servername); - - host.len = ngx_strlen(servername); + "SSL server name: \"%V\"", &host); if (host.len == 0) { goto done; } - host.data = (u_char *) servername; - rc = ngx_stream_validate_host(&host, c->pool, 1); if (rc == NGX_ERROR) { @@ -596,35 +621,12 @@ ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) goto done; } - sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module); - -#if (defined TLS1_3_VERSION \ - && !defined LIBRESSL_VERSION_NUMBER && !defined OPENSSL_IS_BORINGSSL) - - /* - * SSL_SESSION_get0_hostname() is only available in OpenSSL 1.1.1+, - * but servername being negotiated in every TLSv1.3 handshake - * is only returned in OpenSSL 1.1.1+ as well - */ - - if (sscf->verify) { - const char *hostname; - - hostname = SSL_SESSION_get0_hostname(SSL_get0_session(ssl_conn)); - - if (hostname != NULL && ngx_strcmp(hostname, servername) != 0) { - c->ssl->handshake_rejected = 1; - *ad = SSL_AD_ACCESS_DENIED; - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - } - -#endif - s->srv_conf = cscf->ctx->srv_conf; ngx_set_connection_log(c, cscf->error_log); + sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module); + if (sscf->ssl.ctx) { if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) { goto error; @@ -663,6 +665,7 @@ done: return SSL_TLSEXT_ERR_ALERT_FATAL; } + c->ssl->sni_accepted = 1; return SSL_TLSEXT_ERR_OK; error: @@ -1002,8 +1005,14 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) cln->data = &conf->ssl; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + { + static ngx_ssl_client_hello_arg cb = { ngx_stream_ssl_servername }; + + ngx_ssl_set_client_hello_callback(conf->ssl.ctx, &cb); + SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx, ngx_stream_ssl_servername); + } #endif #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation @@ -1148,13 +1157,19 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) if (conf->stapling) { + if (conf->certificate_compression) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_stapling\" is incompatible with " + "\"ssl_certificate_compression\""); + return NGX_CONF_ERROR; + } + if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, &conf->stapling_responder, conf->stapling_verify) != NGX_OK) { return NGX_CONF_ERROR; } - } if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {