From mdounin at mdounin.ru Sat Oct 4 12:11:51 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 04 Oct 2025 15:11:51 +0300 Subject: [PATCH] Tests: adjusted mp4 tests to use mpeg4 encoder Message-ID: <15e16e42f9621a475464.1759579911@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1759579773 -10800 # Sat Oct 04 15:09:33 2025 +0300 # Node ID 15e16e42f9621a475464a3b009445b0abeee19bf # Parent b090b08f52e48a288b93bd98cacb79d034424c11 Tests: adjusted mp4 tests to use mpeg4 encoder. It turns out libopenh264 encoder as provided on Fedora is not stable, and, in particular, mp4 tests fail due to this on current Fedora Rawhide. As a workaround, switched to using mpeg4 internal encoder instead, which is always available. diff --git a/mp4.t b/mp4.t --- a/mp4.t +++ b/mp4.t @@ -51,18 +51,16 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' . '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 ' - . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v h264 ' + . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v mpeg4 ' . "${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' . '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 ' - . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v h264 ' + . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v mpeg4 ' . '-movflags +faststart ' . "${\($t->testdir())}/no_mdat.mp4") == 0 or die "Can't create mp4 file: $!"; diff --git a/mp4_directio.t b/mp4_directio.t --- a/mp4_directio.t +++ b/mp4_directio.t @@ -50,11 +50,9 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' - . '-g 15 -c:v h264 ' + . '-g 15 -c:v mpeg4 ' . "${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; diff --git a/mp4_ssi.t b/mp4_ssi.t --- a/mp4_ssi.t +++ b/mp4_ssi.t @@ -52,12 +52,10 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' . '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 ' - . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v h264 ' + . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v mpeg4 ' . "${\($t->testdir())}/ssi.mp4") == 0 or die "Can't create mp4 file: $!"; diff --git a/mp4_start_key_frame.t b/mp4_start_key_frame.t --- a/mp4_start_key_frame.t +++ b/mp4_start_key_frame.t @@ -56,11 +56,9 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' - . '-pix_fmt yuv420p -g 15 -c:v h264 ' + . '-pix_fmt yuv420p -g 15 -c:v mpeg4 ' . "${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; $t->run()->plan(4); diff --git a/range_mp4.t b/range_mp4.t --- a/range_mp4.t +++ b/range_mp4.t @@ -49,11 +49,9 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' - . "-pix_fmt yuv420p -c:v h264 ${\($t->testdir())}/test.mp4") == 0 + . "-pix_fmt yuv420p -c:v mpeg4 ${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; $t->run()->plan(13); From mdounin at mdounin.ru Sat Oct 4 12:20:22 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 04 Oct 2025 15:20:22 +0300 Subject: [PATCH] SSL: loading certificates and keys via OpenSSL STORE API Message-ID: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1759439438 -10800 # Fri Oct 03 00:10:38 2025 +0300 # Node ID 245798d0af7f180811ebc2d326b618f6b69aa175 # Parent 841909a0388fee2fb391632c65b70d730afde792 SSL: loading certificates and keys via OpenSSL STORE API. This makes it possible to load keys from various cryptographic tokens without engines, which are deprecated by OpenSSL 3.x and already removed in some distributions (notably Fedora). Known OpenSSL 3.x providers implementing OpenSSL STORE API include libp11 pkcs11prov (which replaces pkcs11 engine) and pkcs11-provider, both providing access to PKCS#11 modules. Additionally, this makes it possible to load certificates, which wasn't possible with engines, and simplifies configuration in some cases. When loading keys, relevant passwords or PINs can be supplied with the ssl_password_file directive, similarly to loading keys from PEM files. Note though that pkcs11-provider needs to be explicitly configured to cache PINs to properly work across forks (or PIN needs to be set in the provider configuration). Note that AWS-LC defines ERR_LIB_OSSL_STORE, which is the most logical choice for conditional testing for the STORE API. As such, we instead use the ERR_R_OSSL_STORE_LIB generic error code, which was introduced in the same commit. diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -662,6 +662,117 @@ ngx_ssl_load_certificate(ngx_pool_t *poo X509 *x509, *temp; u_long n; + if (ngx_strncmp(cert->data, "store:", sizeof("store:") - 1) == 0) { + +#ifdef OSSL_STORE_INFO_CERT + + u_char *p; + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + + p = cert->data + sizeof("store:") - 1; + + store = OSSL_STORE_open((char *) p, UI_null(), NULL, NULL, NULL); + if (store == NULL) { + *err = "OSSL_STORE_open() failed"; + return NULL; + } + + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) { + *err = "OSSL_STORE_expect() failed"; + OSSL_STORE_close(store); + return NULL; + } + + /* certificate itself */ + + for ( ;; ) { + info = OSSL_STORE_load(store); + if (info == NULL) { + + /* + * OSSL_STORE_load() may return an error, for example, + * if it encounters an encrypted key, even when expected + * object type is explicitly set with with OSSL_STORE_expect(). + * We ignore such errors as long as there are additional + * objects to load, and report accumulated errors from + * the error queue if nothing was loaded. + */ + + if (!OSSL_STORE_eof(store)) { + continue; + } + + *err = "OSSL_STORE_load() failed"; + OSSL_STORE_close(store); + return NULL; + } + + ERR_clear_error(); + + x509 = OSSL_STORE_INFO_get1_CERT(info); + + OSSL_STORE_INFO_free(info); + break; + } + + /* rest of the chain */ + + *chain = sk_X509_new_null(); + if (*chain == NULL) { + *err = "sk_X509_new_null() failed"; + OSSL_STORE_close(store); + X509_free(x509); + return NULL; + } + + for ( ;; ) { + info = OSSL_STORE_load(store); + if (info == NULL) { + + /* ignore errors */ + + if (!OSSL_STORE_eof(store)) { + ERR_clear_error(); + continue; + } + + break; + } + + temp = OSSL_STORE_INFO_get1_CERT(info); + if (temp == NULL) { + *err = "OSSL_STORE_INFO_get1_CERT() failed"; + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(store); + X509_free(x509); + sk_X509_pop_free(*chain, X509_free); + return NULL; + } + + if (sk_X509_push(*chain, temp) == 0) { + *err = "sk_X509_push() failed"; + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(store); + X509_free(x509); + sk_X509_pop_free(*chain, X509_free); + return NULL; + } + + OSSL_STORE_INFO_free(info); + } + + OSSL_STORE_close(store); + return x509; + +#else + + *err = "loading \"store:...\" certificates is not supported"; + return NULL; + +#endif + } + if (ngx_strncmp(cert->data, "data:", sizeof("data:") - 1) == 0) { bio = BIO_new_mem_buf(cert->data + sizeof("data:") - 1, @@ -800,6 +911,107 @@ ngx_ssl_load_certificate_key(ngx_pool_t #endif } + if (ngx_strncmp(key->data, "store:", sizeof("store:") - 1) == 0) { + +#ifdef OSSL_STORE_INFO_PKEY + + u_char *p; + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + UI_METHOD *wrap; + const UI_METHOD *ui; + + p = key->data + sizeof("store:") - 1; + + if (passwords) { + wrap = UI_UTIL_wrap_read_pem_callback(ngx_ssl_password_callback, 0); + if (wrap == NULL) { + *err = "UI_UTIL_wrap_read_pem_callback() failed"; + return NULL; + } + + ui = wrap; + tries = passwords->nelts; + pwd = passwords->elts; + + } else { + wrap = NULL; + ui = UI_get_default_method(); + tries = 1; + pwd = NULL; + } + + for ( ;; ) { + + store = OSSL_STORE_open((char *) p, ui, pwd, NULL, NULL); + if (store == NULL) { + *err = "OSSL_STORE_open() failed"; + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) { + *err = "OSSL_STORE_expect() failed"; + OSSL_STORE_close(store); + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + info = OSSL_STORE_load(store); + if (info == NULL) { + + /* retry if multiple passwords available */ + + if (tries-- > 1) { + OSSL_STORE_close(store); + ERR_clear_error(); + pwd++; + continue; + } + + *err = "OSSL_STORE_load() failed"; + OSSL_STORE_close(store); + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + pkey = OSSL_STORE_INFO_get1_PKEY(info); + if (pkey == NULL) { + *err = "OSSL_STORE_INFO_get1_PKEY() failed"; + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(store); + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + OSSL_STORE_INFO_free(info); + break; + } + + OSSL_STORE_close(store); + + if (wrap) { + UI_destroy_method(wrap); + } + + return pkey; + +#else + + *err = "loading \"store:...\" certificate keys is not supported"; + return NULL; + +#endif + } + if (ngx_strncmp(key->data, "data:", sizeof("data:") - 1) == 0) { bio = BIO_new_mem_buf(key->data + sizeof("data:") - 1, diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -42,6 +42,10 @@ #ifdef SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG #include #endif +#ifdef ERR_R_OSSL_STORE_LIB +#include +#include +#endif #define NGX_SSL_NAME "OpenSSL" From mdounin at mdounin.ru Sat Oct 4 12:22:38 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 04 Oct 2025 15:22:38 +0300 Subject: [PATCH 1 of 4] Tests: loading certificates and keys via OpenSSL STORE API In-Reply-To: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> References: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> Message-ID: <847d878436a23b4ed2a8.1759580558@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1759579949 -10800 # Sat Oct 04 15:12:29 2025 +0300 # Node ID 847d878436a23b4ed2a80d33cdab99249ce8f772 # Parent 15e16e42f9621a475464a3b009445b0abeee19bf Tests: loading certificates and keys via OpenSSL STORE API. diff --git a/ssl_store.t b/ssl_store.t new file mode 100644 --- /dev/null +++ b/ssl_store.t @@ -0,0 +1,201 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for http ssl module, loading "store:..." certificate and keys. +# Uses internal "file:" scheme for testing, as available in OpenSSL 1.1.1 +# and later. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http proxy http_ssl openssl:1.1.1 sni socket_ssl_sni/) + ->has_daemon('openssl'); + +plan(skip_all => 'no store:... keys') + unless $t->has_version('1.29.3'); +plan(skip_all => 'no store:... keys in BoringSSL') + if $t->has_module('BoringSSL'); + +$t->write_file_expand('nginx.conf', <<'EOF')->plan(10); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8443 ssl; + server_name localhost; + + ssl_certificate store:file:%%TESTDIR%%/localhost.crt; + ssl_certificate_key store:file:%%TESTDIR%%/localhost.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name crt_key; + + ssl_certificate store:file:%%TESTDIR%%/localhost.crt_key; + ssl_certificate_key store:file:%%TESTDIR%%/localhost.crt_key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name key_crt; + + ssl_certificate store:file:%%TESTDIR%%/localhost.key_crt; + ssl_certificate_key store:file:%%TESTDIR%%/localhost.key_crt; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name encrypted; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/encrypted.crt; + ssl_certificate_key store:file:%%TESTDIR%%/encrypted.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name encrypted_crt_key; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/encrypted.crt_key; + ssl_certificate_key store:file:%%TESTDIR%%/encrypted.crt_key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name encrypted_key_crt; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/encrypted.key_crt; + ssl_certificate_key store:file:%%TESTDIR%%/encrypted.key_crt; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name dynamic no_password; + + ssl_certificate store:file:%%TESTDIR%%/$ssl_server_name.crt; + ssl_certificate_key store:file:%%TESTDIR%%/$ssl_server_name.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name password; + + ssl_password_file password; + ssl_certificate store:file:%%TESTDIR%%/$ssl_server_name.crt; + ssl_certificate_key store:file:%%TESTDIR%%/$ssl_server_name.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name multiple; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/$ssl_server_name.crt; + ssl_certificate_key store:file:%%TESTDIR%%/$ssl_server_name.key; + } +} + +EOF + +$t->write_file('openssl.conf', <testdir(); + +foreach my $name ('localhost', 'dynamic') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . "-nodes " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +foreach my $name ('encrypted', 'password', 'multiple') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . "-passout pass:secret " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +$t->write_file('localhost.crt_key', + $t->read_file('localhost.crt') . $t->read_file('localhost.key')); +$t->write_file('localhost.key_crt', + $t->read_file('localhost.key') . $t->read_file('localhost.crt')); + +$t->write_file('encrypted.crt_key', + $t->read_file('encrypted.crt') . $t->read_file('encrypted.key')); +$t->write_file('encrypted.key_crt', + $t->read_file('encrypted.key') . $t->read_file('encrypted.crt')); + +$t->write_file('index.html', ''); +$t->write_file('password', "secret\n"); +$t->write_file('passwords', "foo\nbar\nsecret\n"); + +$t->run(); + +############################################################################### + +like(http_get('/', SSL => 1), qr/200 OK/, 'ssl store'); + +like(http_get('/', SSL => 1, SSL_hostname => 'crt_key'), qr/200 OK/, + 'ssl store crt and key'); + +like(http_get('/', SSL => 1, SSL_hostname => 'key_crt'), qr/200 OK/, + 'ssl store key and crt'); + +like(http_get('/', SSL => 1, SSL_hostname => 'encrypted'), qr/200 OK/, + 'ssl store encrypted'); + +like(http_get('/', SSL => 1, SSL_hostname => 'encrypted_crt_key'), qr/200 OK/, + 'ssl store encrypted crt and key'); + +like(http_get('/', SSL => 1, SSL_hostname => 'encrypted_key_crt'), qr/200 OK/, + 'ssl store encrypted key and crt'); + +like(http_get('/', SSL => 1, SSL_hostname => 'dynamic'), qr/200 OK/, + 'ssl store variable'); + +is(http_get('/', SSL => 1, SSL_hostname => 'no_password'), undef, + 'ssl store encrypted no password'); + +like(http_get('/', SSL => 1, SSL_hostname => 'password'), qr/200 OK/, + 'ssl store encrypted one password'); + +like(http_get('/', SSL => 1, SSL_hostname => 'multiple'), qr/200 OK/, + 'ssl store encrypted multiple passwords'); + +############################################################################### From mdounin at mdounin.ru Sat Oct 4 12:22:39 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 04 Oct 2025 15:22:39 +0300 Subject: [PATCH 2 of 4] Tests: improved ssl_engine_keys.t In-Reply-To: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> References: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> Message-ID: <38338626b7c2526ff6cc.1759580559@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1759579985 -10800 # Sat Oct 04 15:13:05 2025 +0300 # Node ID 38338626b7c2526ff6cc2ea0ea38b934b4fdb21e # Parent 847d878436a23b4ed2a80d33cdab99249ce8f772 Tests: improved ssl_engine_keys.t. Notably, this change adds some detection of library paths for pkcs11 engine and libsofthsm2 on various systems, improves error handling, switches to pkcs11-tool long arguments to improve readability, and clarifies that the test is currently marked unsafe mostly because the engine found might not match OpenSSL being used. diff --git a/ssl_engine_keys.t b/ssl_engine_keys.t --- a/ssl_engine_keys.t +++ b/ssl_engine_keys.t @@ -1,5 +1,6 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Sergey Kandaurov # (C) Nginx, Inc. @@ -22,13 +23,17 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - -plan(skip_all => 'may not work, leaves coredump') +plan(skip_all => 'may not work') unless $ENV{TEST_NGINX_UNSAFE}; -my $t = Test::Nginx->new()->has(qw/http proxy http_ssl/)->has_daemon('openssl') - ->has_daemon('softhsm2-util')->has_daemon('pkcs11-tool')->plan(2); +my $t = Test::Nginx->new() + ->has(qw/http proxy http_ssl/) + ->has_daemon('openssl') + ->has_daemon('softhsm2-util') + ->has_daemon('pkcs11-tool'); + +plan(skip_all => 'no engine:... keys') + unless $t->has_module('OpenSSL') and !$t->has_module('BoringSSL'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -86,8 +91,29 @@ EOF # # http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html # -# Note that library paths may differ on different systems, -# and may need to be adjusted. +# Note that library paths are different on different systems. We try +# to detect some known ones. +# +# Still, detected libraries might not match OpenSSL library used when +# building nginx, or the "openssl" tool in path, so everything will fail. +# As such, this test is marked unsafe. + +# Libraries on various systems: FreeBSD, Alpine, Ubuntu + +my ($engine) = grep { -e $_ } qw! + /usr/local/lib/engines/pkcs11.so + /usr/lib/engines-3/pkcs11.so + /usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so +!; + +my ($softhsm) = grep { -e $_ } qw! + /usr/local/lib/softhsm/libsofthsm2.so + /usr/lib/softhsm/libsofthsm2.so + /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so +!; + +plan(skip_all => 'no libp11 pkcs11 engine') unless $engine; +plan(skip_all => 'no softhsm2') unless $softhsm; $t->write_file('openssl.conf', <>$d/openssl.out 2>&1"); + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't initialize softhsm token: $!\n"; - system('pkcs11-tool --module=/usr/local/lib/softhsm/libsofthsm2.so ' - . '-p 1234 -l -k -d 0 -a nx_key_0 --key-type rsa:2048 ' - . ">>$d/openssl.out 2>&1"); + system('pkcs11-tool ' + . "--module=$softhsm " + . '--token-label token0 --pin 1234 --login ' + . '--keypairgen --id 0 --label key0 --key-type rsa:2048 ' + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't generate pkcs11 keypair: $!\n"; system('openssl req -x509 -new ' . "-subj /CN=$name/ -out $d/$name.crt -text " @@ -140,7 +170,7 @@ foreach my $name ('localhost') { or die "Can't create certificate for $name: $!\n"; } -$t->run(); +$t->run()->plan(2); $t->write_file('index.html', ''); From mdounin at mdounin.ru Sat Oct 4 12:22:40 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 04 Oct 2025 15:22:40 +0300 Subject: [PATCH 3 of 4] Tests: loading keys from libp11 pkcs11prov provider In-Reply-To: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> References: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1759580289 -10800 # Sat Oct 04 15:18:09 2025 +0300 # Node ID a940924b9f40b94a9f0e064c763ade4cc02268ae # Parent 38338626b7c2526ff6cc2ea0ea38b934b4fdb21e Tests: loading keys from libp11 pkcs11prov provider. diff --git a/ssl_engine_keys.t b/ssl_store_libp11.t copy from ssl_engine_keys.t copy to ssl_store_libp11.t --- a/ssl_engine_keys.t +++ b/ssl_store_libp11.t @@ -4,7 +4,8 @@ # (C) Sergey Kandaurov # (C) Nginx, Inc. -# Tests for http ssl module, loading "engine:..." keys. +# Tests for http ssl module, loading "store:..." certificates and keys +# from libp11 pkcs11prov OpenSSL provider (https://github.com/OpenSC/libp11). ############################################################################### @@ -32,7 +33,7 @@ my $t = Test::Nginx->new() ->has_daemon('softhsm2-util') ->has_daemon('pkcs11-tool'); -plan(skip_all => 'no engine:... keys') +plan(skip_all => 'no store:... keys') unless $t->has_module('OpenSSL') and !$t->has_module('BoringSSL'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -52,8 +53,8 @@ http { listen 127.0.0.1:8080; server_name localhost; - ssl_certificate localhost.crt; - ssl_certificate_key engine:pkcs11:id_00; + ssl_certificate store:pkcs11:object=cert-localhost; + ssl_certificate_key store:pkcs11:object=key0; location / { # index index.html by default @@ -74,8 +75,8 @@ http { listen 127.0.0.1:8082 ssl; server_name localhost; - ssl_certificate $ssl_server_name.crt; - ssl_certificate_key engine:pkcs11:id_00; + ssl_certificate store:pkcs11:object=cert-$ssl_server_name; + ssl_certificate_key store:pkcs11:object=key0; location / { # index index.html by default @@ -86,11 +87,15 @@ http { EOF # Create a SoftHSM token with a secret key, and configure OpenSSL -# to access it using the pkcs11 engine, see detailed example +# to access it using the libp11 pkcs11prov provider. See detailed example # posted by Dmitrii Pichulin here: # # http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html # +# Adapted to provider usage based on libp11 documentation, see here: +# +# https://github.com/OpenSC/libp11 +# # Note that library paths are different on different systems. We try # to detect some known ones. # @@ -98,12 +103,12 @@ EOF # building nginx, or the "openssl" tool in path, so everything will fail. # As such, this test is marked unsafe. -# Libraries on various systems: FreeBSD, Alpine, Ubuntu +# Libraries on various systems: FreeBSD, Alpine, Debian -my ($engine) = grep { -e $_ } qw! - /usr/local/lib/engines/pkcs11.so - /usr/lib/engines-3/pkcs11.so - /usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so +my ($provider) = grep { -e $_ } qw! + /usr/local/lib/ossl-modules/pkcs11prov.so + /usr/lib/ossl-modules/pkcs11prov.so + /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11prov.so !; my ($softhsm) = grep { -e $_ } qw! @@ -112,24 +117,29 @@ my ($softhsm) = grep { -e $_ } qw! /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so !; -plan(skip_all => 'no libp11 pkcs11 engine') unless $engine; +plan(skip_all => 'no libp11 pkcs11prov provider') unless $provider; plan(skip_all => 'no softhsm2') unless $softhsm; $t->write_file('openssl.conf', <>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; + + system('pkcs11-tool ' + . "--module=$softhsm " + . '--token-label token0 --pin 1234 --login ' + . "--write-object $d/$name.crt --type cert --label cert-$name " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't store certificate to pkcs11 token: $!\n"; } $t->run()->plan(2); @@ -176,7 +193,7 @@ foreach my $name ('localhost') { ############################################################################### -like(http_get('/proxy'), qr/200 OK/, 'ssl engine keys'); +like(http_get('/proxy'), qr/200 OK/, 'ssl store libp11 pkcs11prov'); like(http_get('/var'), qr/200 OK/, 'ssl_certificate with variable'); ############################################################################### From mdounin at mdounin.ru Sat Oct 4 12:22:41 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 04 Oct 2025 15:22:41 +0300 Subject: [PATCH 4 of 4] Tests: loading keys from pkcs11-provider In-Reply-To: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> References: <245798d0af7f180811eb.1759580422@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1759580295 -10800 # Sat Oct 04 15:18:15 2025 +0300 # Node ID fd2d4a72c3e1140f67ec96b2ae96a82b67240336 # Parent a940924b9f40b94a9f0e064c763ade4cc02268ae Tests: loading keys from pkcs11-provider. diff --git a/ssl_store_libp11.t b/ssl_store_pkcs11.t copy from ssl_store_libp11.t copy to ssl_store_pkcs11.t --- a/ssl_store_libp11.t +++ b/ssl_store_pkcs11.t @@ -5,7 +5,7 @@ # (C) Nginx, Inc. # Tests for http ssl module, loading "store:..." certificates and keys -# from libp11 pkcs11prov OpenSSL provider (https://github.com/OpenSC/libp11). +# from pkcs11-provider (https://github.com/latchset/pkcs11-provider). ############################################################################### @@ -42,6 +42,11 @@ plan(skip_all => 'no store:... keys') daemon off; +# pkcs11-provider tries to reinitialize softhsm after fork(), +# so we need softhsm2 environment variable in worker processes + +env SOFTHSM2_CONF; + events { } @@ -87,14 +92,16 @@ http { EOF # Create a SoftHSM token with a secret key, and configure OpenSSL -# to access it using the libp11 pkcs11prov provider. See detailed example -# posted by Dmitrii Pichulin here: +# to access it using pkcs11-provider. See detailed example posted +# by Dmitrii Pichulin here: # # http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html # -# Adapted to provider usage based on libp11 documentation, see here: +# Adapted to provider usage based on libp11 documentation and +# pkcs11-provider documentation, see here: # # https://github.com/OpenSC/libp11 +# https://github.com/latchset/pkcs11-provider # # Note that library paths are different on different systems. We try # to detect some known ones. @@ -102,22 +109,41 @@ EOF # Still, detected libraries might not match OpenSSL library used when # building nginx, or the "openssl" tool in path, so everything will fail. # As such, this test is marked unsafe. +# +# Note well that pkcs11-provider asks for PIN after fork() via the default +# user interface (not the one explicitly passed to OSSL_STORE_open()) +# if PIN is not explicitly provided in the provider configuration with +# "pkcs11-module-token-pin = ..." and/or PIN caching is not explicitly +# enabled with "pkcs11-module-cache-pins = cache". Even "pin-value=..." in +# PKCS#11 URI is not enough. We use PIN in the configuration explicitly +# set with "pkcs11-module-token-pin = 1234". +# +# Additionally, old versions of pkcs11-provider need various quirks +# to work with SoftHSM. In particular, pkcs11-provider 0.3 as seen +# on Ubuntu 24.04 needs at least: +# +# pkcs11-module-load-behavior = early +# pkcs11-module-quirks = no-operation-state +# +# No quirks are needed with pkcs11-provider 1.0+. -# Libraries on various systems: FreeBSD, Alpine, Debian +# Libraries on various systems: FreeBSD, Alpine, Debian, Fedora my ($provider) = grep { -e $_ } qw! - /usr/local/lib/ossl-modules/pkcs11prov.so - /usr/lib/ossl-modules/pkcs11prov.so - /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11prov.so + /usr/local/lib/ossl-modules/pkcs11.so + /usr/lib/ossl-modules/pkcs11.so + /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so + /usr/lib64/ossl-modules/pkcs11.so !; my ($softhsm) = grep { -e $_ } qw! /usr/local/lib/softhsm/libsofthsm2.so /usr/lib/softhsm/libsofthsm2.so /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so + /usr/lib64/pkcs11/libsofthsm2.so !; -plan(skip_all => 'no libp11 pkcs11prov provider') unless $provider; +plan(skip_all => 'no pkcs11-provider') unless $provider; plan(skip_all => 'no softhsm2') unless $softhsm; $t->write_file('openssl.conf', < details: http://freenginx.org/hg/nginx-tests/rev/6e663bd67bc4 branches: changeset: 2027:6e663bd67bc4 user: Maxim Dounin date: Wed Oct 15 02:32:07 2025 +0300 description: Tests: adjusted mp4 tests to use mpeg4 encoder. It turns out libopenh264 encoder as provided on Fedora is not stable, and, in particular, mp4 tests fail due to this on current Fedora Rawhide. As a workaround, switched to using mpeg4 internal encoder instead, which is always available. diffstat: mp4.t | 6 ++---- mp4_directio.t | 4 +--- mp4_ssi.t | 4 +--- mp4_start_key_frame.t | 4 +--- range_mp4.t | 4 +--- 5 files changed, 6 insertions(+), 16 deletions(-) diffs (89 lines): diff --git a/mp4.t b/mp4.t --- a/mp4.t +++ b/mp4.t @@ -51,18 +51,16 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' . '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 ' - . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v h264 ' + . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v mpeg4 ' . "${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' . '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 ' - . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v h264 ' + . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v mpeg4 ' . '-movflags +faststart ' . "${\($t->testdir())}/no_mdat.mp4") == 0 or die "Can't create mp4 file: $!"; diff --git a/mp4_directio.t b/mp4_directio.t --- a/mp4_directio.t +++ b/mp4_directio.t @@ -50,11 +50,9 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' - . '-g 15 -c:v h264 ' + . '-g 15 -c:v mpeg4 ' . "${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; diff --git a/mp4_ssi.t b/mp4_ssi.t --- a/mp4_ssi.t +++ b/mp4_ssi.t @@ -52,12 +52,10 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' . '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 ' - . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v h264 ' + . '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v mpeg4 ' . "${\($t->testdir())}/ssi.mp4") == 0 or die "Can't create mp4 file: $!"; diff --git a/mp4_start_key_frame.t b/mp4_start_key_frame.t --- a/mp4_start_key_frame.t +++ b/mp4_start_key_frame.t @@ -56,11 +56,9 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' - . '-pix_fmt yuv420p -g 15 -c:v h264 ' + . '-pix_fmt yuv420p -g 15 -c:v mpeg4 ' . "${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; $t->run()->plan(4); diff --git a/range_mp4.t b/range_mp4.t --- a/range_mp4.t +++ b/range_mp4.t @@ -49,11 +49,9 @@ EOF plan(skip_all => 'no lavfi') unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`; -plan(skip_all => 'no libx264 or libopenh264') - unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`; system('ffmpeg -nostdin -loglevel quiet -y ' . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 ' - . "-pix_fmt yuv420p -c:v h264 ${\($t->testdir())}/test.mp4") == 0 + . "-pix_fmt yuv420p -c:v mpeg4 ${\($t->testdir())}/test.mp4") == 0 or die "Can't create mp4 file: $!"; $t->run()->plan(13); From mdounin at mdounin.ru Tue Oct 14 23:57:21 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 15 Oct 2025 02:57:21 +0300 Subject: [nginx] Version bump. Message-ID: details: http://freenginx.org/hg/nginx/rev/850d5eebddbc branches: changeset: 9427:850d5eebddbc user: Maxim Dounin date: Wed Oct 15 02:35:04 2025 +0300 description: Version bump. diffstat: src/core/nginx.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff --git a/src/core/nginx.h b/src/core/nginx.h --- 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 freenginx 1 From mdounin at mdounin.ru Tue Oct 14 23:57:21 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 15 Oct 2025 02:57:21 +0300 Subject: [nginx] SSL: loading certificates and keys via OpenSSL STORE API. Message-ID: details: http://freenginx.org/hg/nginx/rev/96e7c245c14d branches: changeset: 9428:96e7c245c14d user: Maxim Dounin date: Wed Oct 15 02:35:19 2025 +0300 description: SSL: loading certificates and keys via OpenSSL STORE API. This makes it possible to load keys from various cryptographic tokens without engines, which are deprecated by OpenSSL 3.x and already removed in some distributions (notably Fedora). Known OpenSSL 3.x providers implementing OpenSSL STORE API include libp11 pkcs11prov (which replaces pkcs11 engine) and pkcs11-provider, both providing access to PKCS#11 modules. Additionally, this makes it possible to load certificates, which wasn't possible with engines, and simplifies configuration in some cases. When loading keys, relevant passwords or PINs can be supplied with the ssl_password_file directive, similarly to loading keys from PEM files. Note though that pkcs11-provider needs to be explicitly configured to cache PINs to properly work across forks (or PIN needs to be set in the provider configuration). Note that AWS-LC defines ERR_LIB_OSSL_STORE, which is the most logical choice for conditional testing for the STORE API. As such, we instead use the ERR_R_OSSL_STORE_LIB generic error code, which was introduced in the same commit. diffstat: src/event/ngx_event_openssl.c | 212 ++++++++++++++++++++++++++++++++++++++++++ src/event/ngx_event_openssl.h | 4 + 2 files changed, 216 insertions(+), 0 deletions(-) diffs (243 lines): diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -662,6 +662,117 @@ ngx_ssl_load_certificate(ngx_pool_t *poo X509 *x509, *temp; u_long n; + if (ngx_strncmp(cert->data, "store:", sizeof("store:") - 1) == 0) { + +#ifdef OSSL_STORE_INFO_CERT + + u_char *p; + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + + p = cert->data + sizeof("store:") - 1; + + store = OSSL_STORE_open((char *) p, UI_null(), NULL, NULL, NULL); + if (store == NULL) { + *err = "OSSL_STORE_open() failed"; + return NULL; + } + + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) { + *err = "OSSL_STORE_expect() failed"; + OSSL_STORE_close(store); + return NULL; + } + + /* certificate itself */ + + for ( ;; ) { + info = OSSL_STORE_load(store); + if (info == NULL) { + + /* + * OSSL_STORE_load() may return an error, for example, + * if it encounters an encrypted key, even when expected + * object type is explicitly set with with OSSL_STORE_expect(). + * We ignore such errors as long as there are additional + * objects to load, and report accumulated errors from + * the error queue if nothing was loaded. + */ + + if (!OSSL_STORE_eof(store)) { + continue; + } + + *err = "OSSL_STORE_load() failed"; + OSSL_STORE_close(store); + return NULL; + } + + ERR_clear_error(); + + x509 = OSSL_STORE_INFO_get1_CERT(info); + + OSSL_STORE_INFO_free(info); + break; + } + + /* rest of the chain */ + + *chain = sk_X509_new_null(); + if (*chain == NULL) { + *err = "sk_X509_new_null() failed"; + OSSL_STORE_close(store); + X509_free(x509); + return NULL; + } + + for ( ;; ) { + info = OSSL_STORE_load(store); + if (info == NULL) { + + /* ignore errors */ + + if (!OSSL_STORE_eof(store)) { + ERR_clear_error(); + continue; + } + + break; + } + + temp = OSSL_STORE_INFO_get1_CERT(info); + if (temp == NULL) { + *err = "OSSL_STORE_INFO_get1_CERT() failed"; + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(store); + X509_free(x509); + sk_X509_pop_free(*chain, X509_free); + return NULL; + } + + if (sk_X509_push(*chain, temp) == 0) { + *err = "sk_X509_push() failed"; + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(store); + X509_free(x509); + sk_X509_pop_free(*chain, X509_free); + return NULL; + } + + OSSL_STORE_INFO_free(info); + } + + OSSL_STORE_close(store); + return x509; + +#else + + *err = "loading \"store:...\" certificates is not supported"; + return NULL; + +#endif + } + if (ngx_strncmp(cert->data, "data:", sizeof("data:") - 1) == 0) { bio = BIO_new_mem_buf(cert->data + sizeof("data:") - 1, @@ -800,6 +911,107 @@ ngx_ssl_load_certificate_key(ngx_pool_t #endif } + if (ngx_strncmp(key->data, "store:", sizeof("store:") - 1) == 0) { + +#ifdef OSSL_STORE_INFO_PKEY + + u_char *p; + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + UI_METHOD *wrap; + const UI_METHOD *ui; + + p = key->data + sizeof("store:") - 1; + + if (passwords) { + wrap = UI_UTIL_wrap_read_pem_callback(ngx_ssl_password_callback, 0); + if (wrap == NULL) { + *err = "UI_UTIL_wrap_read_pem_callback() failed"; + return NULL; + } + + ui = wrap; + tries = passwords->nelts; + pwd = passwords->elts; + + } else { + wrap = NULL; + ui = UI_get_default_method(); + tries = 1; + pwd = NULL; + } + + for ( ;; ) { + + store = OSSL_STORE_open((char *) p, ui, pwd, NULL, NULL); + if (store == NULL) { + *err = "OSSL_STORE_open() failed"; + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) { + *err = "OSSL_STORE_expect() failed"; + OSSL_STORE_close(store); + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + info = OSSL_STORE_load(store); + if (info == NULL) { + + /* retry if multiple passwords available */ + + if (tries-- > 1) { + OSSL_STORE_close(store); + ERR_clear_error(); + pwd++; + continue; + } + + *err = "OSSL_STORE_load() failed"; + OSSL_STORE_close(store); + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + pkey = OSSL_STORE_INFO_get1_PKEY(info); + if (pkey == NULL) { + *err = "OSSL_STORE_INFO_get1_PKEY() failed"; + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(store); + if (wrap) { + UI_destroy_method(wrap); + } + return NULL; + } + + OSSL_STORE_INFO_free(info); + break; + } + + OSSL_STORE_close(store); + + if (wrap) { + UI_destroy_method(wrap); + } + + return pkey; + +#else + + *err = "loading \"store:...\" certificate keys is not supported"; + return NULL; + +#endif + } + if (ngx_strncmp(key->data, "data:", sizeof("data:") - 1) == 0) { bio = BIO_new_mem_buf(key->data + sizeof("data:") - 1, diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -42,6 +42,10 @@ #ifdef SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG #include #endif +#ifdef ERR_R_OSSL_STORE_LIB +#include +#include +#endif #define NGX_SSL_NAME "OpenSSL" From mdounin at mdounin.ru Tue Oct 14 23:57:53 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 15 Oct 2025 02:57:53 +0300 Subject: [nginx-tests] Tests: loading certificates and keys via OpenSSL S... Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/1d18f874376d branches: changeset: 2028:1d18f874376d user: Maxim Dounin date: Wed Oct 15 02:38:11 2025 +0300 description: Tests: loading certificates and keys via OpenSSL STORE API. diffstat: ssl_store.t | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 201 insertions(+), 0 deletions(-) diffs (206 lines): diff --git a/ssl_store.t b/ssl_store.t new file mode 100644 --- /dev/null +++ b/ssl_store.t @@ -0,0 +1,201 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for http ssl module, loading "store:..." certificate and keys. +# Uses internal "file:" scheme for testing, as available in OpenSSL 1.1.1 +# and later. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new() + ->has(qw/http proxy http_ssl openssl:1.1.1 sni socket_ssl_sni/) + ->has_daemon('openssl'); + +plan(skip_all => 'no store:... keys') + unless $t->has_version('1.29.3'); +plan(skip_all => 'no store:... keys in BoringSSL') + if $t->has_module('BoringSSL'); + +$t->write_file_expand('nginx.conf', <<'EOF')->plan(10); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8443 ssl; + server_name localhost; + + ssl_certificate store:file:%%TESTDIR%%/localhost.crt; + ssl_certificate_key store:file:%%TESTDIR%%/localhost.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name crt_key; + + ssl_certificate store:file:%%TESTDIR%%/localhost.crt_key; + ssl_certificate_key store:file:%%TESTDIR%%/localhost.crt_key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name key_crt; + + ssl_certificate store:file:%%TESTDIR%%/localhost.key_crt; + ssl_certificate_key store:file:%%TESTDIR%%/localhost.key_crt; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name encrypted; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/encrypted.crt; + ssl_certificate_key store:file:%%TESTDIR%%/encrypted.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name encrypted_crt_key; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/encrypted.crt_key; + ssl_certificate_key store:file:%%TESTDIR%%/encrypted.crt_key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name encrypted_key_crt; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/encrypted.key_crt; + ssl_certificate_key store:file:%%TESTDIR%%/encrypted.key_crt; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name dynamic no_password; + + ssl_certificate store:file:%%TESTDIR%%/$ssl_server_name.crt; + ssl_certificate_key store:file:%%TESTDIR%%/$ssl_server_name.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name password; + + ssl_password_file password; + ssl_certificate store:file:%%TESTDIR%%/$ssl_server_name.crt; + ssl_certificate_key store:file:%%TESTDIR%%/$ssl_server_name.key; + } + + server { + listen 127.0.0.1:8443 ssl; + server_name multiple; + + ssl_password_file passwords; + ssl_certificate store:file:%%TESTDIR%%/$ssl_server_name.crt; + ssl_certificate_key store:file:%%TESTDIR%%/$ssl_server_name.key; + } +} + +EOF + +$t->write_file('openssl.conf', <testdir(); + +foreach my $name ('localhost', 'dynamic') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . "-nodes " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +foreach my $name ('encrypted', 'password', 'multiple') { + system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=$name/ " + . "-out $d/$name.crt -keyout $d/$name.key " + . "-passout pass:secret " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for $name: $!\n"; +} + +$t->write_file('localhost.crt_key', + $t->read_file('localhost.crt') . $t->read_file('localhost.key')); +$t->write_file('localhost.key_crt', + $t->read_file('localhost.key') . $t->read_file('localhost.crt')); + +$t->write_file('encrypted.crt_key', + $t->read_file('encrypted.crt') . $t->read_file('encrypted.key')); +$t->write_file('encrypted.key_crt', + $t->read_file('encrypted.key') . $t->read_file('encrypted.crt')); + +$t->write_file('index.html', ''); +$t->write_file('password', "secret\n"); +$t->write_file('passwords', "foo\nbar\nsecret\n"); + +$t->run(); + +############################################################################### + +like(http_get('/', SSL => 1), qr/200 OK/, 'ssl store'); + +like(http_get('/', SSL => 1, SSL_hostname => 'crt_key'), qr/200 OK/, + 'ssl store crt and key'); + +like(http_get('/', SSL => 1, SSL_hostname => 'key_crt'), qr/200 OK/, + 'ssl store key and crt'); + +like(http_get('/', SSL => 1, SSL_hostname => 'encrypted'), qr/200 OK/, + 'ssl store encrypted'); + +like(http_get('/', SSL => 1, SSL_hostname => 'encrypted_crt_key'), qr/200 OK/, + 'ssl store encrypted crt and key'); + +like(http_get('/', SSL => 1, SSL_hostname => 'encrypted_key_crt'), qr/200 OK/, + 'ssl store encrypted key and crt'); + +like(http_get('/', SSL => 1, SSL_hostname => 'dynamic'), qr/200 OK/, + 'ssl store variable'); + +is(http_get('/', SSL => 1, SSL_hostname => 'no_password'), undef, + 'ssl store encrypted no password'); + +like(http_get('/', SSL => 1, SSL_hostname => 'password'), qr/200 OK/, + 'ssl store encrypted one password'); + +like(http_get('/', SSL => 1, SSL_hostname => 'multiple'), qr/200 OK/, + 'ssl store encrypted multiple passwords'); + +############################################################################### From mdounin at mdounin.ru Tue Oct 14 23:57:53 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 15 Oct 2025 02:57:53 +0300 Subject: [nginx-tests] Tests: improved ssl_engine_keys.t. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/d95ec4a96edd branches: changeset: 2029:d95ec4a96edd user: Maxim Dounin date: Wed Oct 15 02:38:16 2025 +0300 description: Tests: improved ssl_engine_keys.t. Notably, this change adds some detection of library paths for pkcs11 engine and libsofthsm2 on various systems, improves error handling, switches to pkcs11-tool long arguments to improve readability, and clarifies that the test is currently marked unsafe mostly because the engine found might not match OpenSSL being used. diffstat: ssl_engine_keys.t | 60 +++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 45 insertions(+), 15 deletions(-) diffs (108 lines): diff --git a/ssl_engine_keys.t b/ssl_engine_keys.t --- a/ssl_engine_keys.t +++ b/ssl_engine_keys.t @@ -1,5 +1,6 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Sergey Kandaurov # (C) Nginx, Inc. @@ -22,13 +23,17 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - -plan(skip_all => 'may not work, leaves coredump') +plan(skip_all => 'may not work') unless $ENV{TEST_NGINX_UNSAFE}; -my $t = Test::Nginx->new()->has(qw/http proxy http_ssl/)->has_daemon('openssl') - ->has_daemon('softhsm2-util')->has_daemon('pkcs11-tool')->plan(2); +my $t = Test::Nginx->new() + ->has(qw/http proxy http_ssl/) + ->has_daemon('openssl') + ->has_daemon('softhsm2-util') + ->has_daemon('pkcs11-tool'); + +plan(skip_all => 'no engine:... keys') + unless $t->has_module('OpenSSL') and !$t->has_module('BoringSSL'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -86,8 +91,29 @@ EOF # # http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html # -# Note that library paths may differ on different systems, -# and may need to be adjusted. +# Note that library paths are different on different systems. We try +# to detect some known ones. +# +# Still, detected libraries might not match OpenSSL library used when +# building nginx, or the "openssl" tool in path, so everything will fail. +# As such, this test is marked unsafe. + +# Libraries on various systems: FreeBSD, Alpine, Ubuntu + +my ($engine) = grep { -e $_ } qw! + /usr/local/lib/engines/pkcs11.so + /usr/lib/engines-3/pkcs11.so + /usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so +!; + +my ($softhsm) = grep { -e $_ } qw! + /usr/local/lib/softhsm/libsofthsm2.so + /usr/lib/softhsm/libsofthsm2.so + /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so +!; + +plan(skip_all => 'no libp11 pkcs11 engine') unless $engine; +plan(skip_all => 'no softhsm2') unless $softhsm; $t->write_file('openssl.conf', <>$d/openssl.out 2>&1"); + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't initialize softhsm token: $!\n"; - system('pkcs11-tool --module=/usr/local/lib/softhsm/libsofthsm2.so ' - . '-p 1234 -l -k -d 0 -a nx_key_0 --key-type rsa:2048 ' - . ">>$d/openssl.out 2>&1"); + system('pkcs11-tool ' + . "--module=$softhsm " + . '--token-label token0 --pin 1234 --login ' + . '--keypairgen --id 0 --label key0 --key-type rsa:2048 ' + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't generate pkcs11 keypair: $!\n"; system('openssl req -x509 -new ' . "-subj /CN=$name/ -out $d/$name.crt -text " @@ -140,7 +170,7 @@ foreach my $name ('localhost') { or die "Can't create certificate for $name: $!\n"; } -$t->run(); +$t->run()->plan(2); $t->write_file('index.html', ''); From mdounin at mdounin.ru Tue Oct 14 23:57:53 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 15 Oct 2025 02:57:53 +0300 Subject: [nginx-tests] Tests: loading keys from libp11 pkcs11prov provider. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/a7221352da4f branches: changeset: 2030:a7221352da4f user: Maxim Dounin date: Wed Oct 15 02:39:06 2025 +0300 description: Tests: loading keys from libp11 pkcs11prov provider. diffstat: ssl_store_libp11.t | 65 ++++++++++++++++++++++++++++++++++------------------- 1 files changed, 41 insertions(+), 24 deletions(-) diffs (148 lines): diff --git a/ssl_engine_keys.t b/ssl_store_libp11.t copy from ssl_engine_keys.t copy to ssl_store_libp11.t --- a/ssl_engine_keys.t +++ b/ssl_store_libp11.t @@ -4,7 +4,8 @@ # (C) Sergey Kandaurov # (C) Nginx, Inc. -# Tests for http ssl module, loading "engine:..." keys. +# Tests for http ssl module, loading "store:..." certificates and keys +# from libp11 pkcs11prov OpenSSL provider (https://github.com/OpenSC/libp11). ############################################################################### @@ -32,7 +33,7 @@ my $t = Test::Nginx->new() ->has_daemon('softhsm2-util') ->has_daemon('pkcs11-tool'); -plan(skip_all => 'no engine:... keys') +plan(skip_all => 'no store:... keys') unless $t->has_module('OpenSSL') and !$t->has_module('BoringSSL'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -52,8 +53,8 @@ http { listen 127.0.0.1:8080; server_name localhost; - ssl_certificate localhost.crt; - ssl_certificate_key engine:pkcs11:id_00; + ssl_certificate store:pkcs11:object=cert-localhost; + ssl_certificate_key store:pkcs11:object=key0; location / { # index index.html by default @@ -74,8 +75,8 @@ http { listen 127.0.0.1:8082 ssl; server_name localhost; - ssl_certificate $ssl_server_name.crt; - ssl_certificate_key engine:pkcs11:id_00; + ssl_certificate store:pkcs11:object=cert-$ssl_server_name; + ssl_certificate_key store:pkcs11:object=key0; location / { # index index.html by default @@ -86,11 +87,15 @@ http { EOF # Create a SoftHSM token with a secret key, and configure OpenSSL -# to access it using the pkcs11 engine, see detailed example +# to access it using the libp11 pkcs11prov provider. See detailed example # posted by Dmitrii Pichulin here: # # http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html # +# Adapted to provider usage based on libp11 documentation, see here: +# +# https://github.com/OpenSC/libp11 +# # Note that library paths are different on different systems. We try # to detect some known ones. # @@ -98,12 +103,12 @@ EOF # building nginx, or the "openssl" tool in path, so everything will fail. # As such, this test is marked unsafe. -# Libraries on various systems: FreeBSD, Alpine, Ubuntu +# Libraries on various systems: FreeBSD, Alpine, Debian -my ($engine) = grep { -e $_ } qw! - /usr/local/lib/engines/pkcs11.so - /usr/lib/engines-3/pkcs11.so - /usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so +my ($provider) = grep { -e $_ } qw! + /usr/local/lib/ossl-modules/pkcs11prov.so + /usr/lib/ossl-modules/pkcs11prov.so + /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11prov.so !; my ($softhsm) = grep { -e $_ } qw! @@ -112,24 +117,29 @@ my ($softhsm) = grep { -e $_ } qw! /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so !; -plan(skip_all => 'no libp11 pkcs11 engine') unless $engine; +plan(skip_all => 'no libp11 pkcs11prov provider') unless $provider; plan(skip_all => 'no softhsm2') unless $softhsm; $t->write_file('openssl.conf', <>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; + + system('pkcs11-tool ' + . "--module=$softhsm " + . '--token-label token0 --pin 1234 --login ' + . "--write-object $d/$name.crt --type cert --label cert-$name " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't store certificate to pkcs11 token: $!\n"; } $t->run()->plan(2); @@ -176,7 +193,7 @@ foreach my $name ('localhost') { ############################################################################### -like(http_get('/proxy'), qr/200 OK/, 'ssl engine keys'); +like(http_get('/proxy'), qr/200 OK/, 'ssl store libp11 pkcs11prov'); like(http_get('/var'), qr/200 OK/, 'ssl_certificate with variable'); ############################################################################### From mdounin at mdounin.ru Tue Oct 14 23:57:53 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 15 Oct 2025 02:57:53 +0300 Subject: [nginx-tests] Tests: loading keys from pkcs11-provider. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/f3f2fec834c4 branches: changeset: 2031:f3f2fec834c4 user: Maxim Dounin date: Wed Oct 15 02:39:23 2025 +0300 description: Tests: loading keys from pkcs11-provider. diffstat: ssl_store_pkcs11.t | 54 ++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 40 insertions(+), 14 deletions(-) diffs (118 lines): diff --git a/ssl_store_libp11.t b/ssl_store_pkcs11.t copy from ssl_store_libp11.t copy to ssl_store_pkcs11.t --- a/ssl_store_libp11.t +++ b/ssl_store_pkcs11.t @@ -5,7 +5,7 @@ # (C) Nginx, Inc. # Tests for http ssl module, loading "store:..." certificates and keys -# from libp11 pkcs11prov OpenSSL provider (https://github.com/OpenSC/libp11). +# from pkcs11-provider (https://github.com/latchset/pkcs11-provider). ############################################################################### @@ -42,6 +42,11 @@ plan(skip_all => 'no store:... keys') daemon off; +# pkcs11-provider tries to reinitialize softhsm after fork(), +# so we need softhsm2 environment variable in worker processes + +env SOFTHSM2_CONF; + events { } @@ -87,14 +92,16 @@ http { EOF # Create a SoftHSM token with a secret key, and configure OpenSSL -# to access it using the libp11 pkcs11prov provider. See detailed example -# posted by Dmitrii Pichulin here: +# to access it using pkcs11-provider. See detailed example posted +# by Dmitrii Pichulin here: # # http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html # -# Adapted to provider usage based on libp11 documentation, see here: +# Adapted to provider usage based on libp11 documentation and +# pkcs11-provider documentation, see here: # # https://github.com/OpenSC/libp11 +# https://github.com/latchset/pkcs11-provider # # Note that library paths are different on different systems. We try # to detect some known ones. @@ -102,22 +109,41 @@ EOF # Still, detected libraries might not match OpenSSL library used when # building nginx, or the "openssl" tool in path, so everything will fail. # As such, this test is marked unsafe. +# +# Note well that pkcs11-provider asks for PIN after fork() via the default +# user interface (not the one explicitly passed to OSSL_STORE_open()) +# if PIN is not explicitly provided in the provider configuration with +# "pkcs11-module-token-pin = ..." and/or PIN caching is not explicitly +# enabled with "pkcs11-module-cache-pins = cache". Even "pin-value=..." in +# PKCS#11 URI is not enough. We use PIN in the configuration explicitly +# set with "pkcs11-module-token-pin = 1234". +# +# Additionally, old versions of pkcs11-provider need various quirks +# to work with SoftHSM. In particular, pkcs11-provider 0.3 as seen +# on Ubuntu 24.04 needs at least: +# +# pkcs11-module-load-behavior = early +# pkcs11-module-quirks = no-operation-state +# +# No quirks are needed with pkcs11-provider 1.0+. -# Libraries on various systems: FreeBSD, Alpine, Debian +# Libraries on various systems: FreeBSD, Alpine, Debian, Fedora my ($provider) = grep { -e $_ } qw! - /usr/local/lib/ossl-modules/pkcs11prov.so - /usr/lib/ossl-modules/pkcs11prov.so - /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11prov.so + /usr/local/lib/ossl-modules/pkcs11.so + /usr/lib/ossl-modules/pkcs11.so + /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so + /usr/lib64/ossl-modules/pkcs11.so !; my ($softhsm) = grep { -e $_ } qw! /usr/local/lib/softhsm/libsofthsm2.so /usr/lib/softhsm/libsofthsm2.so /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so + /usr/lib64/pkcs11/libsofthsm2.so !; -plan(skip_all => 'no libp11 pkcs11prov provider') unless $provider; +plan(skip_all => 'no pkcs11-provider') unless $provider; plan(skip_all => 'no softhsm2') unless $softhsm; $t->write_file('openssl.conf', < Hello! The following patch series fixes (hopefully all) configuration directives which specify file or directory names and do not resolve relative paths properly, that is, either from prefix (typically), or from configuration prefix (when the file is considered to be a part of the configuration, such as "include" or "ssl_certificate"). I was able to identify the following configuration directives which need to be fixed: working_directory google_perftools_profiles geoip_country geoip_city geoip_org xml_entities The patch series fixes them. Notably (and probably the only) remaining exception is unix sockets, where relative paths are not handled and therefore resolved by OS from the process current working directory, whichever it happens to be when the socket is opened. Not sure it worth changing though. In particular, because the socket path exactly as used in bind() is then returned by getsockname() and also shown in various tools such as netstat(), which makes relative paths in unix sockets not equivalent to corresponding absolute paths. Review and comments appreciated. -- Maxim Dounin From mdounin at mdounin.ru Fri Oct 24 01:42:37 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 24 Oct 2025 04:42:37 +0300 Subject: [PATCH 1 of 5] Core: updated "set by ngx_pcalloc()" comment In-Reply-To: References: Message-ID: <150ed2787334acba5701.1761270157@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1761147022 -10800 # Wed Oct 22 18:30:22 2025 +0300 # Node ID 150ed2787334acba57012565d6fc81e314a2f064 # Parent 96e7c245c14d8c30389f4d2ebd4ccfac123f819f Core: updated "set by ngx_pcalloc()" comment. diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -1114,12 +1114,16 @@ ngx_core_module_create_conf(ngx_cycle_t /* * set by ngx_pcalloc() * - * ccf->pid = NULL; - * ccf->oldpid = NULL; + * ccf->working_directory = { 0, NULL }; + * ccf->lock_file = { 0, NULL }; + * ccf->pid = { 0, NULL }; + * ccf->oldpid = { 0, NULL }; * ccf->priority = 0; * ccf->cpu_affinity_auto = 0; * ccf->cpu_affinity_n = 0; * ccf->cpu_affinity = NULL; + * ccf->environment = NULL; + * ccf->transparent = 0; */ ccf->daemon = NGX_CONF_UNSET; From mdounin at mdounin.ru Fri Oct 24 01:42:38 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 24 Oct 2025 04:42:38 +0300 Subject: [PATCH 2 of 5] Core: fixed working_directory to be resolved from prefix In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1761147230 -10800 # Wed Oct 22 18:33:50 2025 +0300 # Node ID df1f91eafba30d5ed1c9ad80d771be043d731e9a # Parent 150ed2787334acba57012565d6fc81e314a2f064 Core: fixed working_directory to be resolved from prefix. Previously, relative values of working_directory set in the configuration were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve working_directory from prefix. diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -1281,6 +1281,13 @@ ngx_core_module_init_conf(ngx_cycle_t *c #endif + + if (ccf->working_directory.len) { + if (ngx_conf_full_name(cycle, &ccf->working_directory, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + return NGX_CONF_OK; } From mdounin at mdounin.ru Fri Oct 24 01:42:39 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 24 Oct 2025 04:42:39 +0300 Subject: [PATCH 3 of 5] Fixed google_perftools_profiles to be resolved from prefix In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1761147233 -10800 # Wed Oct 22 18:33:53 2025 +0300 # Node ID d8642f2ac14d1e5bb73a231c0898e1a1735beb62 # Parent df1f91eafba30d5ed1c9ad80d771be043d731e9a Fixed google_perftools_profiles to be resolved from prefix. Previously, relative values of google_perftools_profiles set in the configuration were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve working_directory from prefix. diff --git a/src/misc/ngx_google_perftools_module.c b/src/misc/ngx_google_perftools_module.c --- a/src/misc/ngx_google_perftools_module.c +++ b/src/misc/ngx_google_perftools_module.c @@ -19,6 +19,7 @@ void ProfilerRegisterThread(void); static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle); +static char *ngx_google_perftools_init_conf(ngx_cycle_t *cycle, void *conf); static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle); @@ -43,7 +44,7 @@ static ngx_command_t ngx_google_perftoo static ngx_core_module_t ngx_google_perftools_module_ctx = { ngx_string("google_perftools"), ngx_google_perftools_create_conf, - NULL + ngx_google_perftools_init_conf }; @@ -83,6 +84,21 @@ ngx_google_perftools_create_conf(ngx_cyc } +static char * +ngx_google_perftools_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_google_perftools_conf_t *gptcf = conf; + + if (gptcf->profiles.len) { + if (ngx_conf_full_name(cycle, &gptcf->profiles, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle) { From mdounin at mdounin.ru Fri Oct 24 01:42:40 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 24 Oct 2025 04:42:40 +0300 Subject: [PATCH 4 of 5] GeoIP: fixed database file names to be resolved from prefix In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1761147436 -10800 # Wed Oct 22 18:37:16 2025 +0300 # Node ID c69974d6becb06d4e90db8a05f18f83886c7a668 # Parent d8642f2ac14d1e5bb73a231c0898e1a1735beb62 GeoIP: fixed database file names to be resolved from prefix. Previously, relative database file names set by the geoip_country, geoip_city, and geoip_org configuration directives were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve relative database file names from prefix. diff --git a/src/http/modules/ngx_http_geoip_module.c b/src/http/modules/ngx_http_geoip_module.c --- a/src/http/modules/ngx_http_geoip_module.c +++ b/src/http/modules/ngx_http_geoip_module.c @@ -686,6 +686,10 @@ ngx_http_geoip_country(ngx_conf_t *cf, n value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->country == NULL) { @@ -741,6 +745,10 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->org == NULL) { @@ -802,6 +810,10 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_ value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->city == NULL) { diff --git a/src/stream/ngx_stream_geoip_module.c b/src/stream/ngx_stream_geoip_module.c --- a/src/stream/ngx_stream_geoip_module.c +++ b/src/stream/ngx_stream_geoip_module.c @@ -635,6 +635,10 @@ ngx_stream_geoip_country(ngx_conf_t *cf, value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->country == NULL) { @@ -690,6 +694,10 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->org == NULL) { @@ -751,6 +759,10 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->city == NULL) { From mdounin at mdounin.ru Fri Oct 24 01:42:41 2025 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 24 Oct 2025 04:42:41 +0300 Subject: [PATCH 5 of 5] Xslt: fixed xml_entities to be resolved from prefix In-Reply-To: References: Message-ID: <700181bd6df59ce05006.1761270161@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1761147641 -10800 # Wed Oct 22 18:40:41 2025 +0300 # Node ID 700181bd6df59ce05006a73f62f0e1438bc7b1fa # Parent c69974d6becb06d4e90db8a05f18f83886c7a668 Xslt: fixed xml_entities to be resolved from prefix. Previously, relative values of xml_entities set in the configuration were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve xml_entities from prefix. Note that xml_entities is handled by libxml2 xmlParseDTD() and therefore might be used with URIs, such as "http://example.com/entities.dtd" or "file:///path/to/entities.dtd". This possibility was never documented though, and highly questionable, especially nowadays (in particular, due to no HTTPS support). Further, support for HTTP URIs was disabled by default in libxml2 2.13.0 and completely removed in libxml2 2.15.0. As such, URIs are not specially handled and therefore effectively disabled by this change. diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c --- a/src/http/modules/ngx_http_xslt_filter_module.c +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -834,6 +834,10 @@ ngx_http_xslt_entities(ngx_conf_t *cf, n value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module); file = xmcf->dtd_files.elts; From logchen2009 at gmail.com Thu Oct 30 06:48:54 2025 From: logchen2009 at gmail.com (solomon) Date: Thu, 30 Oct 2025 14:48:54 +0800 Subject: [PATCH] Upstream: max_fails doesn't respect the next_upstream config Message-ID: # HG changeset patch # User Zhefeng Chen # Date 1761731339 -28800 # Wed Oct 29 17:48:59 2025 +0800 # Node ID b81b918263d445c32cb2cff5e2724e3cac62dab3 # Parent c3be8460587196f376bccf29817c3a6523e18150 Upstream: max_fails doesn't respect the next_upstream config In the description of `max_fails`, it says: What is considered an unsuccessful attempt is defined by the `proxy_next_upstream`, `fastcgi_next_upstreami`, `uwsgi_next_upstream`, `scgi_next_upstream`, `memcached_next_upstream`, and `grpc_next_upstream` directives. But actually 403 and 404 are always considered an successful attempt, while other cases are always considered an unsuccessful attempt. The `ngx_http_upstream_free_round_robin_peer` function depends on this to update the health check state. diff -r c3be84605871 -r b81b918263d4 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Tue Jul 15 22:22:53 2025 +0400 +++ b/src/http/ngx_http_upstream.c Wed Oct 29 17:48:59 2025 +0800 @@ -4553,13 +4553,12 @@ u->state->bytes_sent = u->peer.connection->sent; } - if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403 - || ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) + if ((u->conf->next_upstream & ft_type) == ft_type) { - state = NGX_PEER_NEXT; + state = NGX_PEER_FAILED; } else { - state = NGX_PEER_FAILED; + state = NGX_PEER_NEXT; } u->peer.free(&u->peer, u->peer.data, state); -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Thu Oct 30 16:26:39 2025 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 30 Oct 2025 19:26:39 +0300 Subject: [PATCH] Upstream: max_fails doesn't respect the next_upstream config In-Reply-To: References: Message-ID: Hello! On Thu, Oct 30, 2025 at 02:48:54PM +0800, solomon wrote: > # HG changeset patch > # User Zhefeng Chen > # Date 1761731339 -28800 > # Wed Oct 29 17:48:59 2025 +0800 > # Node ID b81b918263d445c32cb2cff5e2724e3cac62dab3 > # Parent c3be8460587196f376bccf29817c3a6523e18150 > Upstream: max_fails doesn't respect the next_upstream config > > In the description of `max_fails`, it says: > What is considered an unsuccessful attempt is defined by the > `proxy_next_upstream`, `fastcgi_next_upstreami`, > `uwsgi_next_upstream`, `scgi_next_upstream`, > `memcached_next_upstream`, and `grpc_next_upstream` directives. > > But actually 403 and 404 are always considered an successful > attempt, while other cases are always considered an > unsuccessful attempt. > > The `ngx_http_upstream_free_round_robin_peer` function depends > on this to update the health check state. The documentation of the proxy_next_upstream directive (and other directives mentioned) further clarifies the behaviour (http://freenginx.org/r/proxy_next_upstream): : The directive also defines what is considered an unsuccessful : attempt of communication with a server. The cases of error, : timeout and invalid_header are always considered unsuccessful : attempts, even if they are not specified in the directive. The : cases of http_500, http_502, http_503, http_504, and http_429 are : considered unsuccessful attempts only if they are specified in the : directive. The cases of http_403 and http_404 are never considered : unsuccessful attempts. Note that error, timeout, and invalid_header cases are always considered unsuccessful (because they are, indeed, unsuccessful, and there isn't much to be done here). This is what you probably mean by "other cases are always considered an unsuccessful attempt". This doesn't apply to all other cases though - http_500, http_502, http_503, http_504, and http_429 are only considered unsuccessful if they are explicitly specified in the directive. These are valid responses, but at the same time these are error responses, so depending on the particular use case [free]nginx can either accept them as is (and send to the client) or try to fallback to a different server. See ngx_http_upstream_test_next() for details on where these codes are checked. Note well that 403 and 404 are explicitly excluded from being considered unsuccessful attempts. This behaviour is intentional and designed to support some common use cases, notably: 1. When resources are distributed among multiple servers, and "proxy_next_upstream http_404;" is used to iterate over available servers till the requested resource is found. Switching off a server if the resource is not found in such a setup is not desired for obvious reasons. 2. Similarly, but with resources rsynced to all servers, so "proxy_next_upstream http_404 http_403;" makes it possible to fallback to other servers if the resource is not yet synced to a particular server (or the new directory is being synced, see 5231:05c53652e7b4). Could you please elaborate a bit more on why do you suggest to change this behaviour? And how the mentioned use cases are expected to be handled with the new behaviour? Is it intentional that error, timeout, and invalid_header cases won't be considered unsuccessful unless explicitly specified in the proxy_next_upstream directive with the proposed change? And invalid_header won't be considered unsuccessful by default? [...] -- Maxim Dounin http://mdounin.ru/ From logchen2009 at gmail.com Fri Oct 31 06:31:38 2025 From: logchen2009 at gmail.com (solomon) Date: Fri, 31 Oct 2025 14:31:38 +0800 Subject: [PATCH] Upstream: max_fails doesn't respect the next_upstream config In-Reply-To: References: Message-ID: Thank you for your reply. It makes sense to me. I didn't intend to change the behavior. I thought the implementation doesn't match the docs and didn't notice the docs have clarified this behavior. Now I understand it better. For the cases of error, timeout, and invalid_header, ngx_http_upstream_next is called directly. Since they are always considered unsuccessful attempts, there is no need to check the ft_type against u->conf->next_upstream. For other cases, ngx_http_upstream_test_next has already checked the status against u->conf->next_upstream to determine whether to call ngx_http_upstream_next, so there is also no need to check again. I have another question though. When peer.free is called from ngx_http_upstream_next, the state argument is correctly passed. But for the last attempt that doesn't finally enter ngx_http_upstream_next, peer.free is called from ngx_http_upstream_finalize_request with the state being 0, in this case the last attempt is always considered successful. But in fact it may be unsuccessful. So I think the status should also be checked there in order to pass the correct state. I have created a PR for this. Could you have a look? Thanks. https://github.com/nginx/nginx/pull/959 Maxim Dounin ?2025?10?31??? 00:46??? > Hello! > > On Thu, Oct 30, 2025 at 02:48:54PM +0800, solomon wrote: > > > # HG changeset patch > > # User Zhefeng Chen > > # Date 1761731339 -28800 > > # Wed Oct 29 17:48:59 2025 +0800 > > # Node ID b81b918263d445c32cb2cff5e2724e3cac62dab3 > > # Parent c3be8460587196f376bccf29817c3a6523e18150 > > Upstream: max_fails doesn't respect the next_upstream config > > > > In the description of `max_fails`, it says: > > What is considered an unsuccessful attempt is defined by the > > `proxy_next_upstream`, `fastcgi_next_upstreami`, > > `uwsgi_next_upstream`, `scgi_next_upstream`, > > `memcached_next_upstream`, and `grpc_next_upstream` directives. > > > > But actually 403 and 404 are always considered an successful > > attempt, while other cases are always considered an > > unsuccessful attempt. > > > > The `ngx_http_upstream_free_round_robin_peer` function depends > > on this to update the health check state. > > The documentation of the proxy_next_upstream directive (and other > directives mentioned) further clarifies the behaviour > (http://freenginx.org/r/proxy_next_upstream): > > : The directive also defines what is considered an unsuccessful > : attempt of communication with a server. The cases of error, > : timeout and invalid_header are always considered unsuccessful > : attempts, even if they are not specified in the directive. The > : cases of http_500, http_502, http_503, http_504, and http_429 are > : considered unsuccessful attempts only if they are specified in the > : directive. The cases of http_403 and http_404 are never considered > : unsuccessful attempts. > > Note that error, timeout, and invalid_header cases are always > considered unsuccessful (because they are, indeed, unsuccessful, > and there isn't much to be done here). This is what you probably > mean by "other cases are always considered an unsuccessful > attempt". This doesn't apply to all other cases though - > http_500, http_502, http_503, http_504, and http_429 are only > considered unsuccessful if they are explicitly specified in the > directive. These are valid responses, but at the same time these > are error responses, so depending on the particular use case > [free]nginx can either accept them as is (and send to the client) > or try to fallback to a different server. See > ngx_http_upstream_test_next() for details on where these codes are > checked. > > Note well that 403 and 404 are explicitly excluded from being > considered unsuccessful attempts. This behaviour is intentional > and designed to support some common use cases, notably: > > 1. When resources are distributed among multiple servers, and > "proxy_next_upstream http_404;" is used to iterate over available > servers till the requested resource is found. Switching off a > server if the resource is not found in such a setup is not desired > for obvious reasons. > > 2. Similarly, but with resources rsynced to all servers, so > "proxy_next_upstream http_404 http_403;" makes it possible to > fallback to other servers if the resource is not yet synced to a > particular server (or the new directory is being synced, see > 5231:05c53652e7b4). > > Could you please elaborate a bit more on why do you suggest to > change this behaviour? And how the mentioned use cases are > expected to be handled with the new behaviour? Is it intentional > that error, timeout, and invalid_header cases won't be considered > unsuccessful unless explicitly specified in the > proxy_next_upstream directive with the proposed change? > And invalid_header won't be considered unsuccessful by default? > > [...] > > -- > Maxim Dounin > http://mdounin.ru/ > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Fri Oct 31 20:43:14 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Fri, 31 Oct 2025 23:43:14 +0300 Subject: [nginx] Core: updated "set by ngx_pcalloc()" comment. Message-ID: details: http://freenginx.org/hg/nginx/rev/4156274da1ee branches: changeset: 9429:4156274da1ee user: Maxim Dounin date: Fri Oct 31 08:07:51 2025 +0300 description: Core: updated "set by ngx_pcalloc()" comment. diffstat: src/core/nginx.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-) diffs (22 lines): diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -1114,12 +1114,16 @@ ngx_core_module_create_conf(ngx_cycle_t /* * set by ngx_pcalloc() * - * ccf->pid = NULL; - * ccf->oldpid = NULL; + * ccf->working_directory = { 0, NULL }; + * ccf->lock_file = { 0, NULL }; + * ccf->pid = { 0, NULL }; + * ccf->oldpid = { 0, NULL }; * ccf->priority = 0; * ccf->cpu_affinity_auto = 0; * ccf->cpu_affinity_n = 0; * ccf->cpu_affinity = NULL; + * ccf->environment = NULL; + * ccf->transparent = 0; */ ccf->daemon = NGX_CONF_UNSET; From mdounin at mdounin.ru Fri Oct 31 20:43:14 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Fri, 31 Oct 2025 23:43:14 +0300 Subject: [nginx] Core: fixed working_directory to be resolved from prefix. Message-ID: details: http://freenginx.org/hg/nginx/rev/573101bb9ec0 branches: changeset: 9430:573101bb9ec0 user: Maxim Dounin date: Fri Oct 31 08:07:54 2025 +0300 description: Core: fixed working_directory to be resolved from prefix. Previously, relative values of working_directory set in the configuration were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve working_directory from prefix. diffstat: src/core/nginx.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diffs (17 lines): diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -1281,6 +1281,13 @@ ngx_core_module_init_conf(ngx_cycle_t *c #endif + + if (ccf->working_directory.len) { + if (ngx_conf_full_name(cycle, &ccf->working_directory, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + return NGX_CONF_OK; } From mdounin at mdounin.ru Fri Oct 31 20:43:14 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Fri, 31 Oct 2025 23:43:14 +0300 Subject: [nginx] Fixed google_perftools_profiles to be resolved from prefix. Message-ID: details: http://freenginx.org/hg/nginx/rev/33c3d7927c13 branches: changeset: 9431:33c3d7927c13 user: Maxim Dounin date: Fri Oct 31 08:07:57 2025 +0300 description: Fixed google_perftools_profiles to be resolved from prefix. Previously, relative values of google_perftools_profiles set in the configuration were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve working_directory from prefix. diffstat: src/misc/ngx_google_perftools_module.c | 18 +++++++++++++++++- 1 files changed, 17 insertions(+), 1 deletions(-) diffs (42 lines): diff --git a/src/misc/ngx_google_perftools_module.c b/src/misc/ngx_google_perftools_module.c --- a/src/misc/ngx_google_perftools_module.c +++ b/src/misc/ngx_google_perftools_module.c @@ -19,6 +19,7 @@ void ProfilerRegisterThread(void); static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle); +static char *ngx_google_perftools_init_conf(ngx_cycle_t *cycle, void *conf); static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle); @@ -43,7 +44,7 @@ static ngx_command_t ngx_google_perftoo static ngx_core_module_t ngx_google_perftools_module_ctx = { ngx_string("google_perftools"), ngx_google_perftools_create_conf, - NULL + ngx_google_perftools_init_conf }; @@ -83,6 +84,21 @@ ngx_google_perftools_create_conf(ngx_cyc } +static char * +ngx_google_perftools_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_google_perftools_conf_t *gptcf = conf; + + if (gptcf->profiles.len) { + if (ngx_conf_full_name(cycle, &gptcf->profiles, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle) { From mdounin at mdounin.ru Fri Oct 31 20:43:15 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Fri, 31 Oct 2025 23:43:15 +0300 Subject: [nginx] GeoIP: fixed database file names to be resolved from pre... Message-ID: details: http://freenginx.org/hg/nginx/rev/ffc824e2c50f branches: changeset: 9432:ffc824e2c50f user: Maxim Dounin date: Fri Oct 31 08:08:01 2025 +0300 description: GeoIP: fixed database file names to be resolved from prefix. Previously, relative database file names set by the geoip_country, geoip_city, and geoip_org configuration directives were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve relative database file names from prefix. diffstat: src/http/modules/ngx_http_geoip_module.c | 12 ++++++++++++ src/stream/ngx_stream_geoip_module.c | 12 ++++++++++++ 2 files changed, 24 insertions(+), 0 deletions(-) diffs (72 lines): diff --git a/src/http/modules/ngx_http_geoip_module.c b/src/http/modules/ngx_http_geoip_module.c --- a/src/http/modules/ngx_http_geoip_module.c +++ b/src/http/modules/ngx_http_geoip_module.c @@ -686,6 +686,10 @@ ngx_http_geoip_country(ngx_conf_t *cf, n value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->country == NULL) { @@ -741,6 +745,10 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->org == NULL) { @@ -802,6 +810,10 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_ value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->city == NULL) { diff --git a/src/stream/ngx_stream_geoip_module.c b/src/stream/ngx_stream_geoip_module.c --- a/src/stream/ngx_stream_geoip_module.c +++ b/src/stream/ngx_stream_geoip_module.c @@ -635,6 +635,10 @@ ngx_stream_geoip_country(ngx_conf_t *cf, value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->country == NULL) { @@ -690,6 +694,10 @@ ngx_stream_geoip_org(ngx_conf_t *cf, ngx value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->org == NULL) { @@ -751,6 +759,10 @@ ngx_stream_geoip_city(ngx_conf_t *cf, ng value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); if (gcf->city == NULL) { From mdounin at mdounin.ru Fri Oct 31 20:43:15 2025 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Fri, 31 Oct 2025 23:43:15 +0300 Subject: [nginx] Xslt: fixed xml_entities to be resolved from prefix. Message-ID: details: http://freenginx.org/hg/nginx/rev/6c1b100b965a branches: changeset: 9433:6c1b100b965a user: Maxim Dounin date: Fri Oct 31 08:08:06 2025 +0300 description: Xslt: fixed xml_entities to be resolved from prefix. Previously, relative values of xml_entities set in the configuration were not processed with ngx_conf_full_name() and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of configuration directives, where relative paths are expected to be resolved either from prefix or from configuration prefix. Fix is to use ngx_conf_full_name() to resolve xml_entities from prefix. Note that xml_entities is handled by libxml2 xmlParseDTD() and therefore might be used with URIs, such as "http://example.com/entities.dtd" or "file:///path/to/entities.dtd". This possibility was never documented though, and highly questionable, especially nowadays (in particular, due to no HTTPS support). Further, support for HTTP URIs was disabled by default in libxml2 2.13.0 and completely removed in libxml2 2.15.0. As such, URIs are not specially handled and therefore effectively disabled by this change. diffstat: src/http/modules/ngx_http_xslt_filter_module.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diffs (14 lines): diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c --- a/src/http/modules/ngx_http_xslt_filter_module.c +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -834,6 +834,10 @@ ngx_http_xslt_entities(ngx_conf_t *cf, n value = cf->args->elts; + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module); file = xmcf->dtd_files.elts;