From xeioex at nginx.com Wed Mar 1 04:37:08 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 01 Mar 2023 04:37:08 +0000 Subject: [njs] WebCrypto: fixed compilation with --debug=YES. Message-ID: details: https://hg.nginx.org/njs/rev/3e33a686a9fd branches: changeset: 2057:3e33a686a9fd user: Dmitry Volyntsev date: Mon Feb 27 23:55:55 2023 -0800 description: WebCrypto: fixed compilation with --debug=YES. The issue was introduced in 2e3bbe8743af (0.7.10). diffstat: external/njs_webcrypto_module.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 293fe42c5e1c -r 3e33a686a9fd external/njs_webcrypto_module.c --- a/external/njs_webcrypto_module.c Mon Feb 27 22:14:36 2023 -0800 +++ b/external/njs_webcrypto_module.c Mon Feb 27 23:55:55 2023 -0800 @@ -2076,7 +2076,7 @@ njs_export_jwk_oct(njs_vm_t *vm, njs_web static const njs_value_t oct_str = njs_string("oct"); - njs_assert(key->raw.start != NULL) + njs_assert(key->raw.start != NULL); ret = njs_string_base64url(vm, &k, &key->raw); if (njs_slow_path(ret != NJS_OK)) { From xeioex at nginx.com Wed Mar 1 04:37:10 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 01 Mar 2023 04:37:10 +0000 Subject: [njs] Fixed attaching of a stack to an error object. Message-ID: details: https://hg.nginx.org/njs/rev/4911271d5453 branches: changeset: 2058:4911271d5453 user: Dmitry Volyntsev date: Tue Feb 28 00:26:45 2023 -0800 description: Fixed attaching of a stack to an error object. This problem is similar to previous commits. When njs_error_stack_attach() accepted the value as a pointer to vm->retval that value might be changed as a side effert of njs_error_stack_new() evaluation. This may result in a garbage value for njs_object(value) expression. The workaround fix is to make a copy of vm->retval to ensure its intergrity and to preserve it as a retval. The proper fix is to eliminate vm->retval altogether. This fixes #612, #613, #616 issues on Github. diffstat: src/njs_vmcode.c | 7 ++++++- src/test/njs_unit_test.c | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletions(-) diffs (33 lines): diff -r 3e33a686a9fd -r 4911271d5453 src/njs_vmcode.c --- a/src/njs_vmcode.c Mon Feb 27 23:55:55 2023 -0800 +++ b/src/njs_vmcode.c Tue Feb 28 00:26:45 2023 -0800 @@ -1824,7 +1824,12 @@ error: if (njs_is_error(&vm->retval)) { vm->active_frame->native.pc = pc; - (void) njs_error_stack_attach(vm, &vm->retval); + + /* TODO: get rid of copying. */ + + njs_value_assign(&dst, &vm->retval); + (void) njs_error_stack_attach(vm, &dst); + njs_value_assign(&vm->retval, &dst); } for ( ;; ) { diff -r 3e33a686a9fd -r 4911271d5453 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Mon Feb 27 23:55:55 2023 -0800 +++ b/src/test/njs_unit_test.c Tue Feb 28 00:26:45 2023 -0800 @@ -23122,6 +23122,12 @@ static njs_unit_test_t njs_backtraces_t { njs_str("function f(n) { if (n == 0) { throw 'a'; } return f(n-1); }; f(2)"), njs_str("a") }, + { njs_str("Object.defineProperty(Function.__proto__, 'name', {get() { typeof 1;}});" + "(new Uint8Array()).every()"), + njs_str("TypeError: callback argument is not callable\n" + " at TypedArray.prototype.every (native)\n" + " at main (:1)\n") }, + /* line numbers */ { njs_str("/**/(function(){throw Error();})()"), From xeioex at nginx.com Wed Mar 1 04:37:11 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 01 Mar 2023 04:37:11 +0000 Subject: [njs] XML: fixed memory leak when exception happens in node.addChild(). Message-ID: details: https://hg.nginx.org/njs/rev/688c4b1d9626 branches: changeset: 2059:688c4b1d9626 user: Dmitry Volyntsev date: Tue Feb 28 20:34:31 2023 -0800 description: XML: fixed memory leak when exception happens in node.addChild(). The issue was introduced in 3891f338e2c9. Found by Coverity (CID 1521487). diffstat: external/njs_xml_module.c | 25 +++++++++++-------------- 1 files changed, 11 insertions(+), 14 deletions(-) diffs (68 lines): diff -r 4911271d5453 -r 688c4b1d9626 external/njs_xml_module.c --- a/external/njs_xml_module.c Tue Feb 28 00:26:45 2023 -0800 +++ b/external/njs_xml_module.c Tue Feb 28 20:34:31 2023 -0800 @@ -708,7 +708,7 @@ static njs_int_t njs_xml_node_ext_add_child(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - xmlNode *current, *node, *copy; + xmlNode *current, *node, *copy, *rnode; njs_int_t ret; current = njs_vm_external(vm, njs_xml_node_proto_id, njs_argument(args, 0)); @@ -717,26 +717,27 @@ njs_xml_node_ext_add_child(njs_vm_t *vm, return NJS_ERROR; } + node = njs_xml_external_node(vm, njs_arg(args, nargs, 1)); + if (njs_slow_path(node == NULL)) { + njs_vm_error(vm, "node is not a XMLNode object"); + return NJS_ERROR; + } + copy = xmlDocCopyNode(current, current->doc, 1); if (njs_slow_path(copy == NULL)) { njs_vm_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } - node = njs_xml_external_node(vm, njs_arg(args, nargs, 1)); - if (njs_slow_path(node == NULL)) { - njs_vm_error(vm, "node is not a XMLNode object"); - goto error; - } - node = xmlDocCopyNode(node, current->doc, 1); if (njs_slow_path(node == NULL)) { njs_vm_error(vm, "xmlDocCopyNode() failed"); goto error; } - node = xmlAddChild(copy, node); - if (njs_slow_path(node == NULL)) { + rnode = xmlAddChild(copy, node); + if (njs_slow_path(rnode == NULL)) { + xmlFreeNode(node); njs_vm_error(vm, "xmlAddChild() failed"); goto error; } @@ -744,7 +745,7 @@ njs_xml_node_ext_add_child(njs_vm_t *vm, ret = xmlReconciliateNs(current->doc, copy); if (njs_slow_path(ret == -1)) { njs_vm_error(vm, "xmlReconciliateNs() failed"); - return NJS_ERROR; + goto error; } njs_value_undefined_set(njs_vm_retval(vm)); @@ -753,10 +754,6 @@ njs_xml_node_ext_add_child(njs_vm_t *vm, error: - if (node != NULL) { - xmlFreeNode(node); - } - xmlFreeNode(copy); return NJS_ERROR; From xeioex at nginx.com Wed Mar 1 04:37:13 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 01 Mar 2023 04:37:13 +0000 Subject: [njs] XML: fixed memory leaks in node.$tags set handler. Message-ID: details: https://hg.nginx.org/njs/rev/7197f860de2f branches: changeset: 2060:7197f860de2f user: Dmitry Volyntsev date: Tue Feb 28 20:34:38 2023 -0800 description: XML: fixed memory leaks in node.$tags set handler. If xmlAddChild() failed the node pointer leaked, if xmlReconciliateNs() failed the copy pointer leaked. diffstat: external/njs_xml_module.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diffs (32 lines): diff -r 688c4b1d9626 -r 7197f860de2f external/njs_xml_module.c --- a/external/njs_xml_module.c Tue Feb 28 20:34:31 2023 -0800 +++ b/external/njs_xml_module.c Tue Feb 28 20:34:38 2023 -0800 @@ -1211,7 +1211,7 @@ njs_xml_node_tags_handler(njs_vm_t *vm, { size_t size; int64_t i, length; - xmlNode *node, *copy; + xmlNode *node, *rnode, *copy; njs_int_t ret; njs_value_t *push; njs_opaque_value_t *start; @@ -1303,8 +1303,8 @@ njs_xml_node_tags_handler(njs_vm_t *vm, goto error; } - node = xmlAddChild(copy, node); - if (njs_slow_path(node == NULL)) { + rnode = xmlAddChild(copy, node); + if (njs_slow_path(rnode == NULL)) { njs_vm_error(vm, "xmlAddChild() failed"); xmlFreeNode(node); goto error; @@ -1313,7 +1313,7 @@ njs_xml_node_tags_handler(njs_vm_t *vm, ret = xmlReconciliateNs(current->doc, copy); if (njs_slow_path(ret == -1)) { njs_vm_error(vm, "xmlReconciliateNs() failed"); - return NJS_ERROR; + goto error; } } From mdounin at mdounin.ru Wed Mar 1 14:56:01 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 01 Mar 2023 17:56:01 +0300 Subject: [PATCH 0 of 4] logging levels of SSL errors observed with tlsfuzzer Message-ID: Hello! The following patch series adjusts logging levels of various client-related errors observed with tlsfuzzer with different SSL libraries. In particular, tests were done with TLSv1.3 enabled where possible. Note that it doesn't try to quench all the errors observed, but only ones which clearly indicate misbehaving client. Errors which might indicate issues in the library instead are preserved as is. For example, logging level of the ERR_R_INTERNAL_ERROR and ERR_R_EC_LIB errors, which are also reported during tlsfuzzer runs with OpenSSL 3.0.8, are preserved as is. It's up to the library authors to either fix these, or return some better errors instead. Review and testing appreciated. -- Maxim Dounin From mdounin at mdounin.ru Wed Mar 1 14:56:02 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 01 Mar 2023 17:56:02 +0300 Subject: [PATCH 1 of 4] SSL: switched to detect log level based on the last error In-Reply-To: References: Message-ID: <4d0a265c1d20f22f1966.1677682562@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1677682263 -10800 # Wed Mar 01 17:51:03 2023 +0300 # Node ID 4d0a265c1d20f22f196680dfcc9d044f9e711865 # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e SSL: switched to detect log level based on the last error. In some cases there might be multiple errors in the OpenSSL error queue, notably when a libcrypto call fails, and then the SSL layer generates an error itself. For example, the following errors were observed with OpenSSL 3.0.8 with TLSv1.3 enabled: SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) In such cases it seems to be better to determine logging level based on the last error in the error queue (the one added by the SSL layer, SSL_R_BAD_ECPOINT in all of the above example example errors). To do so, the ngx_ssl_connection_error() function was changed to use ERR_peek_last_error(). 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 @@ -3389,7 +3389,7 @@ ngx_ssl_connection_error(ngx_connection_ } else if (sslerr == SSL_ERROR_SSL) { - n = ERR_GET_REASON(ERR_peek_error()); + n = ERR_GET_REASON(ERR_peek_last_error()); /* handshake failures */ if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */ From mdounin at mdounin.ru Wed Mar 1 14:56:04 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 01 Mar 2023 17:56:04 +0300 Subject: [PATCH 3 of 4] SSL: logging levels of errors observed with tlsfuzzer and LibreSSL In-Reply-To: References: Message-ID: <207742991a561c0ed708.1677682564@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1677682426 -10800 # Wed Mar 01 17:53:46 2023 +0300 # Node ID 207742991a561c0ed70834d4ce18e8452689419d # Parent c76e163105f1eac7727ce4e6d955fecb38d93e49 SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. As tested with tlsfuzzer with LibreSSL 2.7.0, the following errors are certainly client-related: SSL_do_handshake() failed (SSL: error:14026073:SSL routines:ACCEPT_SR_CLNT_HELLO:bad packet length) SSL_do_handshake() failed (SSL: error:1402612C:SSL routines:ACCEPT_SR_CLNT_HELLO:ssl3 session id too long) SSL_do_handshake() failed (SSL: error:140380EA:SSL routines:ACCEPT_SR_KEY_EXCH:tls rsa encrypted value length is wrong) Accordingly, the SSL_R_BAD_PACKET_LENGTH ("bad packet length"), SSL_R_SSL3_SESSION_ID_TOO_LONG ("ssl3 session id too long"), SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG ("tls rsa encrypted value length is wrong") errors are now logged at the "info" level. 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 @@ -3406,6 +3406,7 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_MISSING_SIGALGS_EXTENSION || n == SSL_R_MISSING_SIGALGS_EXTENSION /* 112 */ #endif + || n == SSL_R_BAD_PACKET_LENGTH /* 115 */ #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM /* 118 */ #endif @@ -3453,6 +3454,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_CALLBACK_FAILED || n == SSL_R_CALLBACK_FAILED /* 234 */ #endif +#ifdef SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG + || n == SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG /* 234 */ +#endif #ifdef SSL_R_NO_APPLICATION_PROTOCOL || n == SSL_R_NO_APPLICATION_PROTOCOL /* 235 */ #endif @@ -3485,6 +3489,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_RECORD_TOO_SMALL || n == SSL_R_RECORD_TOO_SMALL /* 298 */ #endif +#ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG + || n == SSL_R_SSL3_SESSION_ID_TOO_LONG /* 300 */ +#endif #ifdef SSL_R_BAD_ECPOINT || n == SSL_R_BAD_ECPOINT /* 306 */ #endif From mdounin at mdounin.ru Wed Mar 1 14:56:05 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 01 Mar 2023 17:56:05 +0300 Subject: [PATCH 4 of 4] SSL: logging levels of errors observed with BoringSSL In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1677682467 -10800 # Wed Mar 01 17:54:27 2023 +0300 # Node ID ad67809ab209bd575dac52756ad4aeb5255d430e # Parent 207742991a561c0ed70834d4ce18e8452689419d SSL: logging levels of errors observed with BoringSSL. As tested with tlsfuzzer with BoringSSL, the following errors are certainly client-related: SSL_do_handshake() failed (SSL: error:10000066:SSL routines:OPENSSL_internal:BAD_ALERT) SSL_do_handshake() failed (SSL: error:10000089:SSL routines:OPENSSL_internal:DECODE_ERROR) SSL_do_handshake() failed (SSL: error:100000dc:SSL routines:OPENSSL_internal:TOO_MANY_WARNING_ALERTS) SSL_do_handshake() failed (SSL: error:10000100:SSL routines:OPENSSL_internal:INVALID_COMPRESSION_LIST) SSL_do_handshake() failed (SSL: error:10000102:SSL routines:OPENSSL_internal:MISSING_KEY_SHARE) SSL_do_handshake() failed (SSL: error:1000010e:SSL routines:OPENSSL_internal:TOO_MUCH_SKIPPED_EARLY_DATA) SSL_read() failed (SSL: error:100000b6:SSL routines:OPENSSL_internal:NO_RENEGOTIATION) Accordingly, the SSL_R_BAD_ALERT, SSL_R_DECODE_ERROR, SSL_R_TOO_MANY_WARNING_ALERTS, SSL_R_INVALID_COMPRESSION_LIST, SSL_R_MISSING_KEY_SHARE, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA, and SSL_R_NO_RENEGOTIATION errors are now logged at the "info" level. 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 @@ -3396,6 +3396,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_NO_SUITABLE_KEY_SHARE || n == SSL_R_NO_SUITABLE_KEY_SHARE /* 101 */ #endif +#ifdef SSL_R_BAD_ALERT + || n == SSL_R_BAD_ALERT /* 102 */ +#endif #ifdef SSL_R_BAD_KEY_SHARE || n == SSL_R_BAD_KEY_SHARE /* 108 */ #endif @@ -3415,6 +3418,9 @@ ngx_ssl_connection_error(ngx_connection_ #endif || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ || n == SSL_R_CCS_RECEIVED_EARLY /* 133 */ +#ifdef SSL_R_DECODE_ERROR + || n == SSL_R_DECODE_ERROR /* 137 */ +#endif #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED /* 145 */ #endif @@ -3432,6 +3438,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_LENGTH_TOO_SHORT || n == SSL_R_LENGTH_TOO_SHORT /* 160 */ #endif +#ifdef SSL_R_NO_RENEGOTIATION + || n == SSL_R_NO_RENEGOTIATION /* 182 */ +#endif #ifdef SSL_R_NO_CIPHERS_PASSED || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ #endif @@ -3445,6 +3454,9 @@ ngx_ssl_connection_error(ngx_connection_ || n == SSL_R_PACKET_LENGTH_TOO_LONG /* 198 */ #endif || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ +#ifdef SSL_R_TOO_MANY_WARNING_ALERTS + || n == SSL_R_TOO_MANY_WARNING_ALERTS /* 220 */ +#endif #ifdef SSL_R_CLIENTHELLO_TLSEXT || n == SSL_R_CLIENTHELLO_TLSEXT /* 226 */ #endif @@ -3467,11 +3479,20 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS || n == SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS /* 253 */ #endif +#ifdef SSL_R_INVALID_COMPRESSION_LIST + || n == SSL_R_INVALID_COMPRESSION_LIST /* 256 */ +#endif +#ifdef SSL_R_MISSING_KEY_SHARE + || n == SSL_R_MISSING_KEY_SHARE /* 258 */ +#endif || n == SSL_R_UNSUPPORTED_PROTOCOL /* 258 */ #ifdef SSL_R_NO_SHARED_GROUP || n == SSL_R_NO_SHARED_GROUP /* 266 */ #endif || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ +#ifdef SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA + || n == SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA /* 270 */ +#endif || n == SSL_R_BAD_LENGTH /* 271 */ || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY From mdounin at mdounin.ru Wed Mar 1 14:56:03 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 01 Mar 2023 17:56:03 +0300 Subject: [PATCH 2 of 4] SSL: logging levels of various errors reported with tlsfuzzer In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1677682421 -10800 # Wed Mar 01 17:53:41 2023 +0300 # Node ID c76e163105f1eac7727ce4e6d955fecb38d93e49 # Parent 4d0a265c1d20f22f196680dfcc9d044f9e711865 SSL: logging levels of various errors reported with tlsfuzzer. To further differentiate client-related errors and adjust logging levels of various SSL errors, nginx was tested with tlsfuzzer with multiple OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, 1.0.0s, 0.9.8zh). The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, and are clearly client-related: SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 3.0.8 and with TLSv1.3 enabled: SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) Accordingly, the SSL_R_BAD_ECPOINT ("bad ecpoint"), SSL_R_BAD_DIGEST_LENGTH ("bad digest length"), SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs extension"), SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non handshake data") errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 1.1.1t: SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 1.0.2u: SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now logged at the "info" level. No additional client-related errors were observed while testing with OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, and OpenSSL 0.9.8zh. 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 @@ -3402,16 +3402,35 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_BAD_EXTENSION || n == SSL_R_BAD_EXTENSION /* 110 */ #endif + || n == SSL_R_BAD_DIGEST_LENGTH /* 111 */ +#ifdef SSL_R_MISSING_SIGALGS_EXTENSION + || n == SSL_R_MISSING_SIGALGS_EXTENSION /* 112 */ +#endif #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM /* 118 */ #endif +#ifdef SSL_R_BAD_KEY_UPDATE + || n == SSL_R_BAD_KEY_UPDATE /* 122 */ +#endif || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ + || n == SSL_R_CCS_RECEIVED_EARLY /* 133 */ +#ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED + || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED /* 145 */ +#endif + || n == SSL_R_DATA_LENGTH_TOO_LONG /* 146 */ || n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ + || n == SSL_R_ENCRYPTED_LENGTH_TOO_LONG /* 150 */ || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */ || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */ +#ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS + || n == SSL_R_GOT_A_FIN_BEFORE_A_CCS /* 154 */ +#endif || n == SSL_R_HTTPS_PROXY_REQUEST /* 155 */ || n == SSL_R_HTTP_REQUEST /* 156 */ || n == SSL_R_LENGTH_MISMATCH /* 159 */ +#ifdef SSL_R_LENGTH_TOO_SHORT + || n == SSL_R_LENGTH_TOO_SHORT /* 160 */ +#endif #ifdef SSL_R_NO_CIPHERS_PASSED || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ #endif @@ -3421,6 +3440,9 @@ ngx_ssl_connection_error(ngx_connection_ #endif || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */ || n == SSL_R_NO_SHARED_CIPHER /* 193 */ +#ifdef SSL_R_PACKET_LENGTH_TOO_LONG + || n == SSL_R_PACKET_LENGTH_TOO_LONG /* 198 */ +#endif || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ #ifdef SSL_R_CLIENTHELLO_TLSEXT || n == SSL_R_CLIENTHELLO_TLSEXT /* 226 */ @@ -3446,6 +3468,7 @@ ngx_ssl_connection_error(ngx_connection_ || n == SSL_R_NO_SHARED_GROUP /* 266 */ #endif || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ + || n == SSL_R_BAD_LENGTH /* 271 */ || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY || n == SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY /* 291 */ @@ -3453,6 +3476,15 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_APPLICATION_DATA_ON_SHUTDOWN || n == SSL_R_APPLICATION_DATA_ON_SHUTDOWN /* 291 */ #endif +#ifdef SSL_R_BAD_LEGACY_VERSION + || n == SSL_R_BAD_LEGACY_VERSION /* 292 */ +#endif +#ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA + || n == SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA /* 293 */ +#endif +#ifdef SSL_R_RECORD_TOO_SMALL + || n == SSL_R_RECORD_TOO_SMALL /* 298 */ +#endif #ifdef SSL_R_BAD_ECPOINT || n == SSL_R_BAD_ECPOINT /* 306 */ #endif @@ -3470,12 +3502,21 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_INAPPROPRIATE_FALLBACK || n == SSL_R_INAPPROPRIATE_FALLBACK /* 373 */ #endif +#ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS + || n == SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS /* 376 */ +#endif +#ifdef SSL_R_NO_SHARED_SIGATURE_ALGORITHMS + || n == SSL_R_NO_SHARED_SIGATURE_ALGORITHMS /* 376 */ +#endif #ifdef SSL_R_CERT_CB_ERROR || n == SSL_R_CERT_CB_ERROR /* 377 */ #endif #ifdef SSL_R_VERSION_TOO_LOW || n == SSL_R_VERSION_TOO_LOW /* 396 */ #endif +#ifdef SSL_R_TOO_MANY_WARN_ALERTS + || n == SSL_R_TOO_MANY_WARN_ALERTS /* 409 */ +#endif #ifdef SSL_R_BAD_RECORD_TYPE || n == SSL_R_BAD_RECORD_TYPE /* 443 */ #endif From mdounin at mdounin.ru Wed Mar 1 23:51:11 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 2 Mar 2023 02:51:11 +0300 Subject: [PATCH] Core: return error when the first byte is above 0xf5 in utf-8 In-Reply-To: References: Message-ID: Hello! On Thu, Feb 23, 2023 at 09:24:52AM +0900, u5h wrote: > Thanks reviewing! > > I agree with your early return strategy and I would reconsider that > condition below. > > # HG changeset patch > # User Yugo Horie > # Date 1677107390 -32400 > # Thu Feb 23 08:09:50 2023 +0900 > # Node ID a3ca45d39fcfd32ca92a6bd25ec18b6359b90f1a > # Parent f4653576ffcd286bed7229e18ee30ec3c713b4de > Core: restrict the rule of utf-8 decode. > > The first byte being above 0xf8 which is referred to 5byte > over length older utf-8 becomes invalid. > Even the range of the first byte from 0xf5 to > 0xf7 is valid in the term of the codepoint decoding. > See https://datatracker.ietf.org/doc/html/rfc3629#section-4. > > diff -r f4653576ffcd -r a3ca45d39fcf src/core/ngx_string.c > --- a/src/core/ngx_string.c Thu Feb 23 07:56:44 2023 +0900 > +++ b/src/core/ngx_string.c Thu Feb 23 08:09:50 2023 +0900 > @@ -1363,8 +1363,12 @@ > uint32_t u, i, valid; > > u = **p; > - > - if (u >= 0xf0) { > + if (u >= 0xf8) { > + > + (*p)++; > + return 0xffffffff; > + > + } else if (u >= 0xf0) { > > u &= 0x07; > valid = 0xffff; Slightly adjusted the commit log to better explain the issue (and restored the accidentally removed empty line). Please take a look if it seems good enough: # HG changeset patch # User Yugo Horie # Date 1677107390 -32400 # Thu Feb 23 08:09:50 2023 +0900 # Node ID a10210a45c8b6e6bb75e98b2fd64a80c184ae247 # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e Core: stricter UTF-8 handling in ngx_utf8_decode(). An UTF-8 octet sequence cannot start with a 11111xxx byte (above 0xf8), see https://datatracker.ietf.org/doc/html/rfc3629#section-3. Previously, such bytes were accepted by ngx_utf8_decode() and misinterpreted as 11110xxx bytes (as in a 4-byte sequence). While unlikely, this can potentially cause issues. Fix is to explicitly reject such bytes in ngx_utf8_decode(). diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -1364,7 +1364,12 @@ ngx_utf8_decode(u_char **p, size_t n) u = **p; - if (u >= 0xf0) { + if (u >= 0xf8) { + + (*p)++; + return 0xffffffff; + + } else if (u >= 0xf0) { u &= 0x07; valid = 0xffff; -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Thu Mar 2 05:52:57 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 02 Mar 2023 05:52:57 +0000 Subject: [njs] XML: removed XML_PARSE_DTDVALID during a document parsing. Message-ID: details: https://hg.nginx.org/njs/rev/700f267bd903 branches: changeset: 2061:700f267bd903 user: Dmitry Volyntsev date: Wed Mar 01 21:38:09 2023 -0800 description: XML: removed XML_PARSE_DTDVALID during a document parsing. When XML_PARSE_DTDVALID is enabled libxml2 parses and executes external entities present inside an xml document. This can lead to all the classic XXE exploits, including SSRF and local file disclosure. The issue was introduced in 99b9f83e4d4d (0.7.10). Thanks to @BitK_. diffstat: external/njs_xml_module.c | 3 +-- test/xml/external_entity_ignored.t.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diffs (35 lines): diff -r 7197f860de2f -r 700f267bd903 external/njs_xml_module.c --- a/external/njs_xml_module.c Tue Feb 28 20:34:38 2023 -0800 +++ b/external/njs_xml_module.c Wed Mar 01 21:38:09 2023 -0800 @@ -432,8 +432,7 @@ njs_xml_ext_parse(njs_vm_t *vm, njs_valu } tree->doc = xmlCtxtReadMemory(tree->ctx, (char *) data.start, data.length, - NULL, NULL, XML_PARSE_DTDVALID - | XML_PARSE_NOWARNING + NULL, NULL, XML_PARSE_NOWARNING | XML_PARSE_NOERROR); if (njs_slow_path(tree->doc == NULL)) { njs_xml_error(vm, tree, "failed to parse XML"); diff -r 7197f860de2f -r 700f267bd903 test/xml/external_entity_ignored.t.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/xml/external_entity_ignored.t.js Wed Mar 01 21:38:09 2023 -0800 @@ -0,0 +1,18 @@ +/*--- +includes: [compatXml.js, compatNjs.js] +flags: [] +paths: [] +---*/ + +let data = ` + +]> +&c; +`; + +if (has_njs()) { + const xml = require('xml'); + let doc = xml.parse(data); + assert.sameValue(doc.$root.$text, ""); +} From u5.horie at gmail.com Thu Mar 2 08:17:05 2023 From: u5.horie at gmail.com (u5h) Date: Thu, 2 Mar 2023 17:17:05 +0900 Subject: [PATCH] Core: return error when the first byte is above 0xf5 in utf-8 In-Reply-To: References: Message-ID: Hi, sorry for bothering you. It looks good to me. Thanks! — Yugo Horie On Thu, Mar 2, 2023 at 8:51 Maxim Dounin wrote: > Hello! > > On Thu, Feb 23, 2023 at 09:24:52AM +0900, u5h wrote: > > > Thanks reviewing! > > > > I agree with your early return strategy and I would reconsider that > > condition below. > > > > # HG changeset patch > > # User Yugo Horie > > # Date 1677107390 -32400 > > # Thu Feb 23 08:09:50 2023 +0900 > > # Node ID a3ca45d39fcfd32ca92a6bd25ec18b6359b90f1a > > # Parent f4653576ffcd286bed7229e18ee30ec3c713b4de > > Core: restrict the rule of utf-8 decode. > > > > The first byte being above 0xf8 which is referred to 5byte > > over length older utf-8 becomes invalid. > > Even the range of the first byte from 0xf5 to > > 0xf7 is valid in the term of the codepoint decoding. > > See https://datatracker.ietf.org/doc/html/rfc3629#section-4. > > > > diff -r f4653576ffcd -r a3ca45d39fcf src/core/ngx_string.c > > --- a/src/core/ngx_string.c Thu Feb 23 07:56:44 2023 +0900 > > +++ b/src/core/ngx_string.c Thu Feb 23 08:09:50 2023 +0900 > > @@ -1363,8 +1363,12 @@ > > uint32_t u, i, valid; > > > > u = **p; > > - > > - if (u >= 0xf0) { > > + if (u >= 0xf8) { > > + > > + (*p)++; > > + return 0xffffffff; > > + > > + } else if (u >= 0xf0) { > > > > u &= 0x07; > > valid = 0xffff; > > Slightly adjusted the commit log to better explain the issue (and > restored the accidentally removed empty line). Please take a look > if it seems good enough: > > # HG changeset patch > # User Yugo Horie > # Date 1677107390 -32400 > # Thu Feb 23 08:09:50 2023 +0900 > # Node ID a10210a45c8b6e6bb75e98b2fd64a80c184ae247 > # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e > Core: stricter UTF-8 handling in ngx_utf8_decode(). > > An UTF-8 octet sequence cannot start with a 11111xxx byte (above 0xf8), > see https://datatracker.ietf.org/doc/html/rfc3629#section-3. Previously, > such bytes were accepted by ngx_utf8_decode() and misinterpreted as > 11110xxx > bytes (as in a 4-byte sequence). While unlikely, this can potentially > cause > issues. > > Fix is to explicitly reject such bytes in ngx_utf8_decode(). > > diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c > --- a/src/core/ngx_string.c > +++ b/src/core/ngx_string.c > @@ -1364,7 +1364,12 @@ ngx_utf8_decode(u_char **p, size_t n) > > u = **p; > > - if (u >= 0xf0) { > + if (u >= 0xf8) { > + > + (*p)++; > + return 0xffffffff; > + > + } else if (u >= 0xf0) { > > u &= 0x07; > valid = 0xffff; > > > -- > Maxim Dounin > http://mdounin.ru/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Thu Mar 2 15:49:03 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 2 Mar 2023 18:49:03 +0300 Subject: [PATCH] Core: return error when the first byte is above 0xf5 in utf-8 In-Reply-To: References: Message-ID: Hello! On Thu, Mar 02, 2023 at 05:17:05PM +0900, u5h wrote: > Hi, sorry for bothering you. > It looks good to me. Thanks! Thanks for noticing and fixing this. Pushed to http://mdounin.ru/hg/nginx, it will eventually end up in the nginx.org repo. -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Sat Mar 4 02:50:07 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 04 Mar 2023 02:50:07 +0000 Subject: [njs] Fetch: fixed typo in debug level. Message-ID: details: https://hg.nginx.org/njs/rev/163229f0c552 branches: changeset: 2062:163229f0c552 user: Dmitry Volyntsev date: Fri Mar 03 17:49:10 2023 -0800 description: Fetch: fixed typo in debug level. diffstat: nginx/ngx_js_fetch.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (21 lines): diff -r 700f267bd903 -r 163229f0c552 nginx/ngx_js_fetch.c --- a/nginx/ngx_js_fetch.c Wed Mar 01 21:38:09 2023 -0800 +++ b/nginx/ngx_js_fetch.c Fri Mar 03 17:49:10 2023 -0800 @@ -1351,7 +1351,7 @@ failed: static void ngx_js_http_close_connection(ngx_connection_t *c) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "close js http connection: %d", c->fd); #if (NGX_SSL) @@ -1665,7 +1665,7 @@ ngx_js_http_ssl_name(ngx_js_http_t *http name->data = p; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, http->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js http SSL server name: \"%s\"", name->data); if (SSL_set_tlsext_host_name(http->peer.connection->ssl->connection, From xeioex at nginx.com Sat Mar 4 02:50:10 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 04 Mar 2023 02:50:10 +0000 Subject: [njs] Fixed Array.prototype.sort() when array is changed while sorting. Message-ID: details: https://hg.nginx.org/njs/rev/7ad9dd5da3fb branches: changeset: 2063:7ad9dd5da3fb user: Dmitry Volyntsev date: Fri Mar 03 17:49:11 2023 -0800 description: Fixed Array.prototype.sort() when array is changed while sorting. Previously, the fast-path check did not take into account the fact that the flat array may be resized as a side effect of the array's values evaluation. In addition, the slow_path fix ensures that "this" array is repopulated again even if the array was resized. This fixes #594 issue on Github. diffstat: src/njs_array.c | 13 +++++++------ src/test/njs_unit_test.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diffs (68 lines): diff -r 163229f0c552 -r 7ad9dd5da3fb src/njs_array.c --- a/src/njs_array.c Fri Mar 03 17:49:10 2023 -0800 +++ b/src/njs_array.c Fri Mar 03 17:49:11 2023 -0800 @@ -2663,7 +2663,10 @@ slow_path: goto exception; } - if (njs_fast_path(fast_path && njs_is_fast_array(this))) { + if (njs_fast_path(fast_path + && njs_is_fast_array(this) + && (njs_array(this)->length == length))) + { array = njs_array(this); start = array->start; @@ -2677,11 +2680,9 @@ slow_path: } else { for (i = 0; i < len; i++) { - if (slots[i].pos != i) { - ret = njs_value_property_i64_set(vm, this, i, &slots[i].value); - if (njs_slow_path(ret == NJS_ERROR)) { - goto exception; - } + ret = njs_value_property_i64_set(vm, this, i, &slots[i].value); + if (njs_slow_path(ret == NJS_ERROR)) { + goto exception; } } diff -r 163229f0c552 -r 7ad9dd5da3fb src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Mar 03 17:49:10 2023 -0800 +++ b/src/test/njs_unit_test.c Fri Mar 03 17:49:11 2023 -0800 @@ -7276,6 +7276,34 @@ static njs_unit_test_t njs_test[] = { njs_str("var a = [1,2]; a.sort(() => {a.length = 65535}); a.length"), njs_str("65535") }, + { njs_str("var a = [];" + "var shift = true;" + "for (let i = 0; i < 64; i++) {" + " a[i] = { toString() {" + " if (shift) { a.shift() };" + " return (63 - i).toString().padStart(2, '0');" + " }" + " };" + "}" + "a.sort();" + "shift = false;" + "[a.length, a[0].toString(), a[63].toString()]"), + njs_str("64,00,63") }, + + { njs_str("var a = [];" + "var shift = true;" + "for (let i = 0; i < 64; i++) {" + " a[i] = { toString() {" + " if (shift) { a.shift() };" + " return (i).toString().padStart(2, '0');" + " }" + " };" + "}" + "a.sort();" + "shift = false;" + "[a.length, a[0].toString(), a[63].toString()]"), + njs_str("64,00,63") }, + /* Array.prototype.keys() Array.prototype.values() From xeioex at nginx.com Sat Mar 4 02:50:12 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Sat, 04 Mar 2023 02:50:12 +0000 Subject: [njs] Fixed njs_string_to_number() with additional space symbols. Message-ID: details: https://hg.nginx.org/njs/rev/9367f3dfe0d9 branches: changeset: 2064:9367f3dfe0d9 user: Artem S. Povalyukhin date: Fri Mar 03 22:57:30 2023 +0300 description: Fixed njs_string_to_number() with additional space symbols. This closes #621 issue on Github. diffstat: src/njs_string.c | 2 +- src/test/njs_unit_test.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletions(-) diffs (25 lines): diff -r 7ad9dd5da3fb -r 9367f3dfe0d9 src/njs_string.c --- a/src/njs_string.c Fri Mar 03 17:49:11 2023 -0800 +++ b/src/njs_string.c Fri Mar 03 22:57:30 2023 +0300 @@ -3974,7 +3974,7 @@ njs_string_to_number(const njs_value_t * if (!parse_float) { while (p < end) { - if (*p != ' ' && *p != '\t') { + if (!njs_is_whitespace(*p)) { return NAN; } diff -r 7ad9dd5da3fb -r 9367f3dfe0d9 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Fri Mar 03 17:49:11 2023 -0800 +++ b/src/test/njs_unit_test.c Fri Mar 03 22:57:30 2023 +0300 @@ -13339,6 +13339,9 @@ static njs_unit_test_t njs_test[] = { njs_str("Number('123')"), njs_str("123") }, + { njs_str("['1', ' 1 ', '1\\t', '1\\n', '1\\r\\n'].reduce((a, x) => a + Number(x), 0)"), + njs_str("5") }, + { njs_str("Number('0.'+'1'.repeat(128))"), njs_str("0.1111111111111111") }, From nickrbogdanov at gmail.com Sun Mar 5 00:53:20 2023 From: nickrbogdanov at gmail.com (=?iso-8859-1?q?Nick_Bogdanov?=) Date: Sat, 04 Mar 2023 16:53:20 -0800 Subject: [PATCH] Add ssl_provider directive (ticket #2449) Message-ID: <8cb34ae16de2408cbe91.1677977600@nuc> # HG changeset patch # User Nick Bogdanov # Date 1677975659 28800 # Sat Mar 04 16:20:59 2023 -0800 # Node ID 8cb34ae16de2408cbe91832194baac6ae299f251 # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 Add ssl_provider directive (ticket #2449) This change allows nginx to load modules that use the new OpenSSL Provider interface. My primary use case involves securing the webserver's private TLS key using a TPM2 chip, so it can't be stolen if the server is compromised. The way I tested this is as follows: 1. Install basic TPM2 support. On Ubuntu 22.04 I used apt install tpm2-tools tpm2-abrmd libtss2-tcti-tabrmd0 2. Install https://github.com/tpm2-software/tpm2-openssl . Version 1.2.0-rc0 or higher is required. At the time of this writing, it's likely you'll have to build from source. 3. Generate a parent key on your TPM (one-time setup): tpm2_createprimary -C o -g sha256 -G ecc -c primary_sh.ctx tpm2_evictcontrol -C o -c 0x81000001 || true tpm2_evictcontrol -C o -c primary_sh.ctx 0x81000001 4. Generate a TPM-backed RSA privkey and a corresponding self-signed x509 cert: openssl genpkey -provider tpm2 -algorithm RSA -pkeyopt parent:0x81000001 -out rsakey.pem openssl req -provider tpm2 -provider default -x509 -subj "/C=GB/CN=foo" -key rsakey.pem -out rsacert.pem rsakey.pem will start with "-----BEGIN TSS2 PRIVATE KEY-----" to indicate that the key material is encrypted with a key that is only available inside the TPM chip. 5. At the start of nginx.conf, tell nginx to use the tpm2 provider first, and then fall back to the default provider for unsupported operations: ssl_provider tpm2; ssl_provider default; 6. Inside a "server {" section for an existing TLS server, point nginx to the new TPM-backed cert and key: ssl_certificate /tmp/rsacert.pem; ssl_certificate_key /tmp/rsakey.pem; If the ssl_provider option took effect, it will be able to recognize the new TSS2 rsakey.pem and instruct the TPM chip to handle the signing operation during the TLS handshake. diff -r cffaf3f2eec8 -r 8cb34ae16de2 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Thu Feb 02 23:38:48 2023 +0300 +++ b/contrib/vim/syntax/nginx.vim Sat Mar 04 16:20:59 2023 -0800 @@ -620,6 +620,7 @@ syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread syn keyword ngxDirective contained ssl_protocols +syn keyword ngxDirective contained ssl_provider syn keyword ngxDirective contained ssl_reject_handshake syn keyword ngxDirective contained ssl_session_cache syn keyword ngxDirective contained ssl_session_ticket_key diff -r cffaf3f2eec8 -r 8cb34ae16de2 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Feb 02 23:38:48 2023 +0300 +++ b/src/event/ngx_event_openssl.c Sat Mar 04 16:20:59 2023 -0800 @@ -90,6 +90,7 @@ static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -102,6 +103,13 @@ 0, NULL }, + { ngx_string("ssl_provider"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_openssl_provider, + 0, + 0, + NULL }, + ngx_null_command }; @@ -5939,6 +5947,26 @@ #endif } +static char * +ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#ifdef OPENSSL_PROVIDER_SUPPORT + ngx_str_t *value = cf->args->elts; + + if (OSSL_PROVIDER_load(NULL, (char *)value[1].data) == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "OSSL_PROVIDER_load(\"%V\") failed", &value[1]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + +#else + + return "is not supported"; + +#endif +} static void ngx_openssl_exit(ngx_cycle_t *cycle) diff -r cffaf3f2eec8 -r 8cb34ae16de2 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Thu Feb 02 23:38:48 2023 +0300 +++ b/src/event/ngx_event_openssl.h Sat Mar 04 16:20:59 2023 -0800 @@ -28,6 +28,10 @@ #ifndef OPENSSL_NO_OCSP #include #endif +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +#include +#define OPENSSL_PROVIDER_SUPPORT +#endif #include #include #include From mat999 at gmail.com Sun Mar 5 01:02:23 2023 From: mat999 at gmail.com (Mathew Heard) Date: Sun, 5 Mar 2023 12:02:23 +1100 Subject: [PATCH] Add ssl_provider directive (ticket #2449) In-Reply-To: <8cb34ae16de2408cbe91.1677977600@nuc> References: <8cb34ae16de2408cbe91.1677977600@nuc> Message-ID: But the way have you benchmarked this? On Sun, 5 Mar 2023, 11:55 am Nick Bogdanov, wrote: > # HG changeset patch > # User Nick Bogdanov > # Date 1677975659 28800 > # Sat Mar 04 16:20:59 2023 -0800 > # Node ID 8cb34ae16de2408cbe91832194baac6ae299f251 > # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 > Add ssl_provider directive (ticket #2449) > > This change allows nginx to load modules that use the new OpenSSL > Provider interface. My primary use case involves securing the > webserver's private TLS key using a TPM2 chip, so it can't be stolen > if the server is compromised. The way I tested this is as follows: > > 1. Install basic TPM2 support. On Ubuntu 22.04 I used > > apt install tpm2-tools tpm2-abrmd libtss2-tcti-tabrmd0 > > 2. Install https://github.com/tpm2-software/tpm2-openssl . Version > 1.2.0-rc0 or higher is required. At the time of this writing, it's > likely you'll have to build from source. > > 3. Generate a parent key on your TPM (one-time setup): > > tpm2_createprimary -C o -g sha256 -G ecc -c primary_sh.ctx > > tpm2_evictcontrol -C o -c 0x81000001 || true > > tpm2_evictcontrol -C o -c primary_sh.ctx 0x81000001 > > 4. Generate a TPM-backed RSA privkey and a corresponding self-signed > x509 cert: > > openssl genpkey -provider tpm2 -algorithm RSA > -pkeyopt parent:0x81000001 -out rsakey.pem > > openssl req -provider tpm2 -provider default -x509 > -subj "/C=GB/CN=foo" -key rsakey.pem -out rsacert.pem > > rsakey.pem will start with "-----BEGIN TSS2 PRIVATE KEY-----" to indicate > that the key material is encrypted with a key that is only available inside > the TPM chip. > > 5. At the start of nginx.conf, tell nginx to use the tpm2 provider > first, and then fall back to the default provider for unsupported > operations: > > ssl_provider tpm2; > ssl_provider default; > > 6. Inside a "server {" section for an existing TLS server, point nginx > to the new TPM-backed cert and key: > > ssl_certificate /tmp/rsacert.pem; > ssl_certificate_key /tmp/rsakey.pem; > > If the ssl_provider option took effect, it will be able to recognize > the new TSS2 rsakey.pem and instruct the TPM chip to handle the signing > operation during the TLS handshake. > > diff -r cffaf3f2eec8 -r 8cb34ae16de2 contrib/vim/syntax/nginx.vim > --- a/contrib/vim/syntax/nginx.vim Thu Feb 02 23:38:48 2023 +0300 > +++ b/contrib/vim/syntax/nginx.vim Sat Mar 04 16:20:59 2023 -0800 > @@ -620,6 +620,7 @@ > syn keyword ngxDirective contained ssl_prefer_server_ciphers > syn keyword ngxDirective contained ssl_preread > syn keyword ngxDirective contained ssl_protocols > +syn keyword ngxDirective contained ssl_provider > syn keyword ngxDirective contained ssl_reject_handshake > syn keyword ngxDirective contained ssl_session_cache > syn keyword ngxDirective contained ssl_session_ticket_key > diff -r cffaf3f2eec8 -r 8cb34ae16de2 src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Thu Feb 02 23:38:48 2023 +0300 > +++ b/src/event/ngx_event_openssl.c Sat Mar 04 16:20:59 2023 -0800 > @@ -90,6 +90,7 @@ > > static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); > static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void > *conf); > +static char *ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, > void *conf); > static void ngx_openssl_exit(ngx_cycle_t *cycle); > > > @@ -102,6 +103,13 @@ > 0, > NULL }, > > + { ngx_string("ssl_provider"), > + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, > + ngx_openssl_provider, > + 0, > + 0, > + NULL }, > + > ngx_null_command > }; > > @@ -5939,6 +5947,26 @@ > #endif > } > > +static char * > +ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) > +{ > +#ifdef OPENSSL_PROVIDER_SUPPORT > + ngx_str_t *value = cf->args->elts; > + > + if (OSSL_PROVIDER_load(NULL, (char *)value[1].data) == NULL) { > + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, > + "OSSL_PROVIDER_load(\"%V\") failed", &value[1]); > + return NGX_CONF_ERROR; > + } > + > + return NGX_CONF_OK; > + > +#else > + > + return "is not supported"; > + > +#endif > +} > > static void > ngx_openssl_exit(ngx_cycle_t *cycle) > diff -r cffaf3f2eec8 -r 8cb34ae16de2 src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h Thu Feb 02 23:38:48 2023 +0300 > +++ b/src/event/ngx_event_openssl.h Sat Mar 04 16:20:59 2023 -0800 > @@ -28,6 +28,10 @@ > #ifndef OPENSSL_NO_OCSP > #include > #endif > +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) > +#include > +#define OPENSSL_PROVIDER_SUPPORT > +#endif > #include > #include > #include > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From arut at nginx.com Tue Mar 7 14:40:50 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 7 Mar 2023 18:40:50 +0400 Subject: [PATCH 1 of 4] SSL: switched to detect log level based on the last error In-Reply-To: <4d0a265c1d20f22f1966.1677682562@vm-bsd.mdounin.ru> References: <4d0a265c1d20f22f1966.1677682562@vm-bsd.mdounin.ru> Message-ID: <20230307144050.nwdepiz5xictzncf@N00W24XTQX> Hi, On Wed, Mar 01, 2023 at 05:56:02PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1677682263 -10800 > # Wed Mar 01 17:51:03 2023 +0300 > # Node ID 4d0a265c1d20f22f196680dfcc9d044f9e711865 > # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e > SSL: switched to detect log level based on the last error. > > In some cases there might be multiple errors in the OpenSSL error queue, > notably when a libcrypto call fails, and then the SSL layer generates > an error itself. For example, the following errors were observed > with OpenSSL 3.0.8 with TLSv1.3 enabled: > > SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) > SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) > SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) > > In such cases it seems to be better to determine logging level based on > the last error in the error queue (the one added by the SSL layer, > SSL_R_BAD_ECPOINT in all of the above example example errors). To do so, > the ngx_ssl_connection_error() function was changed to use > ERR_peek_last_error(). > > 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 > @@ -3389,7 +3389,7 @@ ngx_ssl_connection_error(ngx_connection_ > > } else if (sslerr == SSL_ERROR_SSL) { > > - n = ERR_GET_REASON(ERR_peek_error()); > + n = ERR_GET_REASON(ERR_peek_last_error()); > > /* handshake failures */ > if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */ Looks good. Just for the record. BoringSSL, LibreSSL and older versions of OpenSSL sometimes report SSL handshake errors directly from encryption libraries without adding an SSL-layer error. In this case we do not change the log level and report these errors as critical. Luckily, this does not seem to be the case with the newer OpenSSL versions. OpenSSL 1.0.2u: SSL_do_handshake() failed (SSL: error:0406506C:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data greater than mod len) while SSL handshaking SSL_do_handshake() failed (SSL: error:04065084:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data too large for modulus) while SSL handshaking BoringSSL: SSL_do_handshake() failed (SSL: error:04000070:RSA routines:OPENSSL_internal:DATA_LEN_NOT_EQUAL_TO_MOD_LEN) while SSL handshaking SSL_do_handshake() failed (SSL: error:04000073:RSA routines:OPENSSL_internal:DATA_TOO_LARGE_FOR_MODULUS) while SSL handshaking LibreSSL: SSL_do_handshake() failed (SSL: error:10FFF06B:elliptic curve routines:CRYPTO_internal:point is not on curve) while SSL handshaking SSL_do_handshake() failed (SSL: error:06FFF064:digital envelope routines:CRYPTO_internal:bad decrypt) while SSL handshaking -- Roman Arutyunyan From arut at nginx.com Tue Mar 7 14:46:12 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 7 Mar 2023 18:46:12 +0400 Subject: [PATCH 2 of 4] SSL: logging levels of various errors reported with tlsfuzzer In-Reply-To: References: Message-ID: <20230307144612.c6t23xdcyhk3d4xn@N00W24XTQX> Hi, On Wed, Mar 01, 2023 at 05:56:03PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1677682421 -10800 > # Wed Mar 01 17:53:41 2023 +0300 > # Node ID c76e163105f1eac7727ce4e6d955fecb38d93e49 > # Parent 4d0a265c1d20f22f196680dfcc9d044f9e711865 > SSL: logging levels of various errors reported with tlsfuzzer. > > To further differentiate client-related errors and adjust logging levels > of various SSL errors, nginx was tested with tlsfuzzer with multiple > OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, > 1.0.0s, 0.9.8zh). While mentioned here, OpenSSL 3.1.0-beta1 is not referenced anywhere in the text below. It should be added to the list of versions not adding new client errors compared to other versions. > The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, > and are clearly client-related: > > SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) > SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) > SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) > SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) > > Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), > SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION > ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS > ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) > errors are now logged at the "info" level. > > Additionally, the following errors were observed with OpenSSL 3.0.8 and > with TLSv1.3 enabled: > > SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) > SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) > SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) > SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) > SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) > SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) > SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) > SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) > SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) > > Accordingly, the SSL_R_BAD_ECPOINT ("bad ecpoint"), SSL_R_BAD_ECPOINT is already logged at the "info" level, added by you in cac164d0807e. This text is probably a leftover from testing this without the previous patch applied, in which case a crypto error was analyzed and ignored. > SSL_R_BAD_DIGEST_LENGTH > ("bad digest length"), SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs > extension"), SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), > SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), > and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non > handshake data") errors are now logged at the "info" level. > > Additionally, the following errors were observed with OpenSSL 1.1.1t: > > SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) > SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) > SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) > SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) > > Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), > SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), > SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and > SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged > at the "info" level. > > Additionally, the following errors were observed with OpenSSL 1.0.2u: > > SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) > SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) > > Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and > SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now > logged at the "info" level. > > No additional client-related errors were observed while testing with > OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, and OpenSSL 0.9.8zh. > > 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 > @@ -3402,16 +3402,35 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_BAD_EXTENSION > || n == SSL_R_BAD_EXTENSION /* 110 */ > #endif > + || n == SSL_R_BAD_DIGEST_LENGTH /* 111 */ > +#ifdef SSL_R_MISSING_SIGALGS_EXTENSION > + || n == SSL_R_MISSING_SIGALGS_EXTENSION /* 112 */ > +#endif > #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM > || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM /* 118 */ > #endif > +#ifdef SSL_R_BAD_KEY_UPDATE > + || n == SSL_R_BAD_KEY_UPDATE /* 122 */ > +#endif > || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ > + || n == SSL_R_CCS_RECEIVED_EARLY /* 133 */ > +#ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED > + || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED /* 145 */ > +#endif > + || n == SSL_R_DATA_LENGTH_TOO_LONG /* 146 */ > || n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ > + || n == SSL_R_ENCRYPTED_LENGTH_TOO_LONG /* 150 */ > || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */ > || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */ > +#ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS > + || n == SSL_R_GOT_A_FIN_BEFORE_A_CCS /* 154 */ > +#endif > || n == SSL_R_HTTPS_PROXY_REQUEST /* 155 */ > || n == SSL_R_HTTP_REQUEST /* 156 */ > || n == SSL_R_LENGTH_MISMATCH /* 159 */ > +#ifdef SSL_R_LENGTH_TOO_SHORT > + || n == SSL_R_LENGTH_TOO_SHORT /* 160 */ > +#endif > #ifdef SSL_R_NO_CIPHERS_PASSED > || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ > #endif > @@ -3421,6 +3440,9 @@ ngx_ssl_connection_error(ngx_connection_ > #endif > || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */ > || n == SSL_R_NO_SHARED_CIPHER /* 193 */ > +#ifdef SSL_R_PACKET_LENGTH_TOO_LONG > + || n == SSL_R_PACKET_LENGTH_TOO_LONG /* 198 */ > +#endif > || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ > #ifdef SSL_R_CLIENTHELLO_TLSEXT > || n == SSL_R_CLIENTHELLO_TLSEXT /* 226 */ > @@ -3446,6 +3468,7 @@ ngx_ssl_connection_error(ngx_connection_ > || n == SSL_R_NO_SHARED_GROUP /* 266 */ > #endif > || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ > + || n == SSL_R_BAD_LENGTH /* 271 */ > || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ > #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY > || n == SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY /* 291 */ > @@ -3453,6 +3476,15 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_APPLICATION_DATA_ON_SHUTDOWN > || n == SSL_R_APPLICATION_DATA_ON_SHUTDOWN /* 291 */ > #endif > +#ifdef SSL_R_BAD_LEGACY_VERSION > + || n == SSL_R_BAD_LEGACY_VERSION /* 292 */ > +#endif > +#ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA > + || n == SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA /* 293 */ > +#endif > +#ifdef SSL_R_RECORD_TOO_SMALL > + || n == SSL_R_RECORD_TOO_SMALL /* 298 */ > +#endif > #ifdef SSL_R_BAD_ECPOINT > || n == SSL_R_BAD_ECPOINT /* 306 */ > #endif > @@ -3470,12 +3502,21 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_INAPPROPRIATE_FALLBACK > || n == SSL_R_INAPPROPRIATE_FALLBACK /* 373 */ > #endif > +#ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS > + || n == SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS /* 376 */ > +#endif > +#ifdef SSL_R_NO_SHARED_SIGATURE_ALGORITHMS > + || n == SSL_R_NO_SHARED_SIGATURE_ALGORITHMS /* 376 */ > +#endif > #ifdef SSL_R_CERT_CB_ERROR > || n == SSL_R_CERT_CB_ERROR /* 377 */ > #endif > #ifdef SSL_R_VERSION_TOO_LOW > || n == SSL_R_VERSION_TOO_LOW /* 396 */ > #endif > +#ifdef SSL_R_TOO_MANY_WARN_ALERTS > + || n == SSL_R_TOO_MANY_WARN_ALERTS /* 409 */ > +#endif > #ifdef SSL_R_BAD_RECORD_TYPE > || n == SSL_R_BAD_RECORD_TYPE /* 443 */ > #endif > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel Tha patch is ok. -- Roman Arutyunyan From arut at nginx.com Tue Mar 7 14:47:59 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 7 Mar 2023 18:47:59 +0400 Subject: [PATCH 3 of 4] SSL: logging levels of errors observed with tlsfuzzer and LibreSSL In-Reply-To: <207742991a561c0ed708.1677682564@vm-bsd.mdounin.ru> References: <207742991a561c0ed708.1677682564@vm-bsd.mdounin.ru> Message-ID: <20230307144759.os5q4ovmuch32f3b@N00W24XTQX> Hi, On Wed, Mar 01, 2023 at 05:56:04PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1677682426 -10800 > # Wed Mar 01 17:53:46 2023 +0300 > # Node ID 207742991a561c0ed70834d4ce18e8452689419d > # Parent c76e163105f1eac7727ce4e6d955fecb38d93e49 > SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. > > As tested with tlsfuzzer with LibreSSL 2.7.0, the following errors are > certainly client-related: LibreSSL 2.7.0 is ancient - March 21st, 2018. > SSL_do_handshake() failed (SSL: error:14026073:SSL routines:ACCEPT_SR_CLNT_HELLO:bad packet length) > SSL_do_handshake() failed (SSL: error:1402612C:SSL routines:ACCEPT_SR_CLNT_HELLO:ssl3 session id too long) I could not get this one with 2.7.0, but I got it with 3.6.0. > SSL_do_handshake() failed (SSL: error:140380EA:SSL routines:ACCEPT_SR_KEY_EXCH:tls rsa encrypted value length is wrong) With 3.6.0 two more errors are reported: SSL_R_SIGNATURE_ALGORITHMS_ERROR, SSL_R_MISSING_RSA_CERTIFICATE: SSL_do_handshake() failed (SSL: error:1402F0FB:SSL routines:ACCEPT_SW_KEY_EXCH:unknown pkey type error:1402F168:SSL routines:ACCEPT_SW_KEY_EXCH:signature algorithms error) SSL_do_handshake() failed (SSL: error:1402D0FB:SSL routines:ACCEPT_SW_CERT:unknown pkey type error:14FFF0A8:SSL routines:(UNKNOWN)SSL_internal:missing rsa certificate) > Accordingly, the SSL_R_BAD_PACKET_LENGTH ("bad packet length"), > SSL_R_SSL3_SESSION_ID_TOO_LONG ("ssl3 session id too long"), > SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG ("tls rsa encrypted value > length is wrong") errors are now logged at the "info" level. > > 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 > @@ -3406,6 +3406,7 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_MISSING_SIGALGS_EXTENSION > || n == SSL_R_MISSING_SIGALGS_EXTENSION /* 112 */ > #endif > + || n == SSL_R_BAD_PACKET_LENGTH /* 115 */ > #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM > || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM /* 118 */ > #endif > @@ -3453,6 +3454,9 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_CALLBACK_FAILED > || n == SSL_R_CALLBACK_FAILED /* 234 */ > #endif > +#ifdef SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG > + || n == SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG /* 234 */ > +#endif > #ifdef SSL_R_NO_APPLICATION_PROTOCOL > || n == SSL_R_NO_APPLICATION_PROTOCOL /* 235 */ > #endif > @@ -3485,6 +3489,9 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_RECORD_TOO_SMALL > || n == SSL_R_RECORD_TOO_SMALL /* 298 */ > #endif > +#ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG > + || n == SSL_R_SSL3_SESSION_ID_TOO_LONG /* 300 */ > +#endif > #ifdef SSL_R_BAD_ECPOINT > || n == SSL_R_BAD_ECPOINT /* 306 */ > #endif > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From arut at nginx.com Tue Mar 7 14:48:52 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 7 Mar 2023 18:48:52 +0400 Subject: [PATCH 4 of 4] SSL: logging levels of errors observed with BoringSSL In-Reply-To: References: Message-ID: <20230307144852.smb6vinut4oo2zqc@N00W24XTQX> Hi, On Wed, Mar 01, 2023 at 05:56:05PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1677682467 -10800 > # Wed Mar 01 17:54:27 2023 +0300 > # Node ID ad67809ab209bd575dac52756ad4aeb5255d430e > # Parent 207742991a561c0ed70834d4ce18e8452689419d > SSL: logging levels of errors observed with BoringSSL. > > As tested with tlsfuzzer with BoringSSL, the following errors are > certainly client-related: > > SSL_do_handshake() failed (SSL: error:10000066:SSL routines:OPENSSL_internal:BAD_ALERT) > SSL_do_handshake() failed (SSL: error:10000089:SSL routines:OPENSSL_internal:DECODE_ERROR) > SSL_do_handshake() failed (SSL: error:100000dc:SSL routines:OPENSSL_internal:TOO_MANY_WARNING_ALERTS) > SSL_do_handshake() failed (SSL: error:10000100:SSL routines:OPENSSL_internal:INVALID_COMPRESSION_LIST) > SSL_do_handshake() failed (SSL: error:10000102:SSL routines:OPENSSL_internal:MISSING_KEY_SHARE) > SSL_do_handshake() failed (SSL: error:1000010e:SSL routines:OPENSSL_internal:TOO_MUCH_SKIPPED_EARLY_DATA) > SSL_read() failed (SSL: error:100000b6:SSL routines:OPENSSL_internal:NO_RENEGOTIATION) > > Accordingly, the SSL_R_BAD_ALERT, SSL_R_DECODE_ERROR, > SSL_R_TOO_MANY_WARNING_ALERTS, SSL_R_INVALID_COMPRESSION_LIST, > SSL_R_MISSING_KEY_SHARE, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA, > and SSL_R_NO_RENEGOTIATION errors are now logged at the "info" level. > > 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 > @@ -3396,6 +3396,9 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_NO_SUITABLE_KEY_SHARE > || n == SSL_R_NO_SUITABLE_KEY_SHARE /* 101 */ > #endif > +#ifdef SSL_R_BAD_ALERT > + || n == SSL_R_BAD_ALERT /* 102 */ > +#endif > #ifdef SSL_R_BAD_KEY_SHARE > || n == SSL_R_BAD_KEY_SHARE /* 108 */ > #endif > @@ -3415,6 +3418,9 @@ ngx_ssl_connection_error(ngx_connection_ > #endif > || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ > || n == SSL_R_CCS_RECEIVED_EARLY /* 133 */ > +#ifdef SSL_R_DECODE_ERROR > + || n == SSL_R_DECODE_ERROR /* 137 */ > +#endif > #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED > || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED /* 145 */ > #endif > @@ -3432,6 +3438,9 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_LENGTH_TOO_SHORT > || n == SSL_R_LENGTH_TOO_SHORT /* 160 */ > #endif > +#ifdef SSL_R_NO_RENEGOTIATION > + || n == SSL_R_NO_RENEGOTIATION /* 182 */ > +#endif > #ifdef SSL_R_NO_CIPHERS_PASSED > || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ > #endif > @@ -3445,6 +3454,9 @@ ngx_ssl_connection_error(ngx_connection_ > || n == SSL_R_PACKET_LENGTH_TOO_LONG /* 198 */ > #endif > || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ > +#ifdef SSL_R_TOO_MANY_WARNING_ALERTS > + || n == SSL_R_TOO_MANY_WARNING_ALERTS /* 220 */ > +#endif > #ifdef SSL_R_CLIENTHELLO_TLSEXT > || n == SSL_R_CLIENTHELLO_TLSEXT /* 226 */ > #endif > @@ -3467,11 +3479,20 @@ ngx_ssl_connection_error(ngx_connection_ > #ifdef SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS > || n == SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS /* 253 */ > #endif > +#ifdef SSL_R_INVALID_COMPRESSION_LIST > + || n == SSL_R_INVALID_COMPRESSION_LIST /* 256 */ > +#endif > +#ifdef SSL_R_MISSING_KEY_SHARE > + || n == SSL_R_MISSING_KEY_SHARE /* 258 */ > +#endif > || n == SSL_R_UNSUPPORTED_PROTOCOL /* 258 */ > #ifdef SSL_R_NO_SHARED_GROUP > || n == SSL_R_NO_SHARED_GROUP /* 266 */ > #endif > || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ > +#ifdef SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA > + || n == SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA /* 270 */ > +#endif > || n == SSL_R_BAD_LENGTH /* 271 */ > || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ > #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel Looks ok From mdounin at mdounin.ru Tue Mar 7 21:51:41 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 8 Mar 2023 00:51:41 +0300 Subject: [PATCH 1 of 4] SSL: switched to detect log level based on the last error In-Reply-To: <20230307144050.nwdepiz5xictzncf@N00W24XTQX> References: <4d0a265c1d20f22f1966.1677682562@vm-bsd.mdounin.ru> <20230307144050.nwdepiz5xictzncf@N00W24XTQX> Message-ID: Hello! On Tue, Mar 07, 2023 at 06:40:50PM +0400, Roman Arutyunyan wrote: > Hi, > > On Wed, Mar 01, 2023 at 05:56:02PM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1677682263 -10800 > > # Wed Mar 01 17:51:03 2023 +0300 > > # Node ID 4d0a265c1d20f22f196680dfcc9d044f9e711865 > > # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e > > SSL: switched to detect log level based on the last error. > > > > In some cases there might be multiple errors in the OpenSSL error queue, > > notably when a libcrypto call fails, and then the SSL layer generates > > an error itself. For example, the following errors were observed > > with OpenSSL 3.0.8 with TLSv1.3 enabled: > > > > SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) > > SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) > > SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) > > > > In such cases it seems to be better to determine logging level based on > > the last error in the error queue (the one added by the SSL layer, > > SSL_R_BAD_ECPOINT in all of the above example example errors). To do so, > > the ngx_ssl_connection_error() function was changed to use > > ERR_peek_last_error(). > > > > 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 > > @@ -3389,7 +3389,7 @@ ngx_ssl_connection_error(ngx_connection_ > > > > } else if (sslerr == SSL_ERROR_SSL) { > > > > - n = ERR_GET_REASON(ERR_peek_error()); > > + n = ERR_GET_REASON(ERR_peek_last_error()); > > > > /* handshake failures */ > > if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */ > > Looks good. > > Just for the record. BoringSSL, LibreSSL and older versions of OpenSSL > sometimes report SSL handshake errors directly from encryption libraries > without adding an SSL-layer error. In this case we do not change the log > level and report these errors as critical. Luckily, this does not seem to > be the case with the newer OpenSSL versions. > > OpenSSL 1.0.2u: > > SSL_do_handshake() failed (SSL: error:0406506C:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data greater than mod len) while SSL handshaking > SSL_do_handshake() failed (SSL: error:04065084:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data too large for modulus) while SSL handshaking > > BoringSSL: > > SSL_do_handshake() failed (SSL: error:04000070:RSA routines:OPENSSL_internal:DATA_LEN_NOT_EQUAL_TO_MOD_LEN) while SSL handshaking > SSL_do_handshake() failed (SSL: error:04000073:RSA routines:OPENSSL_internal:DATA_TOO_LARGE_FOR_MODULUS) while SSL handshaking > > LibreSSL: > > SSL_do_handshake() failed (SSL: error:10FFF06B:elliptic curve routines:CRYPTO_internal:point is not on curve) while SSL handshaking > SSL_do_handshake() failed (SSL: error:06FFF064:digital envelope routines:CRYPTO_internal:bad decrypt) while SSL handshaking Sure. As mentioned in the introduction to this patch series: : Note that it doesn't try to quench all the errors observed, but only ones : which clearly indicate misbehaving client. Errors which might indicate : issues in the library instead are preserved as is. For example, logging : level of the ERR_R_INTERNAL_ERROR and ERR_R_EC_LIB errors, which are also : reported during tlsfuzzer runs with OpenSSL 3.0.8, are preserved as is. : It's up to the library authors to either fix these, or return some better : errors instead. The RSA errors you've quoted are instead handled as internal errors in the OpenSSL 1.1.1 branch: decrypt_len = (int)RSA_private_decrypt((int)PACKET_remaining(&enc_premaster), PACKET_data(&enc_premaster), rsa_decrypt, rsa, RSA_NO_PADDING); if (decrypt_len < 0) { SSLfatal(s, SSL_AD_DECRYPT_ERROR, SSL_F_TLS_PROCESS_CKE_RSA, ERR_R_INTERNAL_ERROR); goto err; } And in OpenSSL 3.0.x this is fixed to generate proper SSL_R_DECRYPTION_FAILED error: if (EVP_PKEY_decrypt_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_WITH_TLS_PADDING) <= 0) { SSLfatal(s, SSL_AD_DECRYPT_ERROR, SSL_R_DECRYPTION_FAILED); goto err; } Hopefully other libraries will follow. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Mar 7 22:07:45 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 8 Mar 2023 01:07:45 +0300 Subject: [PATCH 2 of 4] SSL: logging levels of various errors reported with tlsfuzzer In-Reply-To: <20230307144612.c6t23xdcyhk3d4xn@N00W24XTQX> References: <20230307144612.c6t23xdcyhk3d4xn@N00W24XTQX> Message-ID: Hello! On Tue, Mar 07, 2023 at 06:46:12PM +0400, Roman Arutyunyan wrote: > Hi, > > On Wed, Mar 01, 2023 at 05:56:03PM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1677682421 -10800 > > # Wed Mar 01 17:53:41 2023 +0300 > > # Node ID c76e163105f1eac7727ce4e6d955fecb38d93e49 > > # Parent 4d0a265c1d20f22f196680dfcc9d044f9e711865 > > SSL: logging levels of various errors reported with tlsfuzzer. > > > > To further differentiate client-related errors and adjust logging levels > > of various SSL errors, nginx was tested with tlsfuzzer with multiple > > OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, > > 1.0.0s, 0.9.8zh). > > While mentioned here, OpenSSL 3.1.0-beta1 is not referenced anywhere in the > text below. It should be added to the list of versions not adding new client > errors compared to other versions. Thanks for noting. I've generally not considered OpenSSL 3.1.0-beta1, since it is mostly identical to OpenSSL 3.0.8 in terms of errors and not really a release, but decided to add it to the list of tested versions for completeness. Updated the last paragraph as well: : No additional client-related errors were observed while testing with : OpenSSL 3.1.0-beta1, OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, : and OpenSSL 0.9.8zh. > > The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, > > and are clearly client-related: > > > > SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) > > SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) > > SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) > > SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) > > > > Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), > > SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION > > ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS > > ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) > > errors are now logged at the "info" level. > > > > Additionally, the following errors were observed with OpenSSL 3.0.8 and > > with TLSv1.3 enabled: > > > > SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) > > SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) > > SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) > > SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) > > SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) > > SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) > > SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) > > SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) > > SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) > > > > Accordingly, the SSL_R_BAD_ECPOINT ("bad ecpoint"), > > SSL_R_BAD_ECPOINT is already logged at the "info" level, added by you in > cac164d0807e. This text is probably a leftover from testing this without the > previous patch applied, in which case a crypto error was analyzed and ignored. Indeed, thanks for catching. Removed this from the commit log (as well as the relevant error messages above). > > SSL_R_BAD_DIGEST_LENGTH > > ("bad digest length"), SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs > > extension"), SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), > > SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), > > and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non > > handshake data") errors are now logged at the "info" level. > > > > Additionally, the following errors were observed with OpenSSL 1.1.1t: > > > > SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) > > SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) > > SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) > > SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) > > > > Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), > > SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), > > SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and > > SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged > > at the "info" level. > > > > Additionally, the following errors were observed with OpenSSL 1.0.2u: > > > > SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) > > SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) > > > > Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and > > SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now > > logged at the "info" level. > > > > No additional client-related errors were observed while testing with > > OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, and OpenSSL 0.9.8zh. Full updated commit log: # HG changeset patch # User Maxim Dounin # Date 1678226771 -10800 # Wed Mar 08 01:06:11 2023 +0300 # Node ID a48cbbec723b1a6d4943e877deb21e719c19d09a # Parent c011fae23865f04518f9279fb98a312ef8f7a29c SSL: logging levels of various errors reported with tlsfuzzer. To further differentiate client-related errors and adjust logging levels of various SSL errors, nginx was tested with tlsfuzzer with multiple OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, 1.0.0s, 0.9.8zh). The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, and are clearly client-related: SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 3.0.8 and with TLSv1.3 enabled: SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) Accordingly, the SSL_R_BAD_DIGEST_LENGTH ("bad digest length"), SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs extension"), SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non handshake data") errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 1.1.1t: SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 1.0.2u: SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now logged at the "info" level. No additional client-related errors were observed while testing with OpenSSL 3.1.0-beta1, OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, and OpenSSL 0.9.8zh. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Mar 7 22:25:23 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 8 Mar 2023 01:25:23 +0300 Subject: [PATCH 3 of 4] SSL: logging levels of errors observed with tlsfuzzer and LibreSSL In-Reply-To: <20230307144759.os5q4ovmuch32f3b@N00W24XTQX> References: <207742991a561c0ed708.1677682564@vm-bsd.mdounin.ru> <20230307144759.os5q4ovmuch32f3b@N00W24XTQX> Message-ID: Hello! On Tue, Mar 07, 2023 at 06:47:59PM +0400, Roman Arutyunyan wrote: > On Wed, Mar 01, 2023 at 05:56:04PM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1677682426 -10800 > > # Wed Mar 01 17:53:46 2023 +0300 > > # Node ID 207742991a561c0ed70834d4ce18e8452689419d > > # Parent c76e163105f1eac7727ce4e6d955fecb38d93e49 > > SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. > > > > As tested with tlsfuzzer with LibreSSL 2.7.0, the following errors are > > certainly client-related: > > LibreSSL 2.7.0 is ancient - March 21st, 2018. Err, typo, should be 3.7.0, the latest development release. Fixed. > > SSL_do_handshake() failed (SSL: error:14026073:SSL routines:ACCEPT_SR_CLNT_HELLO:bad packet length) > > SSL_do_handshake() failed (SSL: error:1402612C:SSL routines:ACCEPT_SR_CLNT_HELLO:ssl3 session id too long) > > I could not get this one with 2.7.0, but I got it with 3.6.0. > > > SSL_do_handshake() failed (SSL: error:140380EA:SSL routines:ACCEPT_SR_KEY_EXCH:tls rsa encrypted value length is wrong) > > With 3.6.0 two more errors are reported: SSL_R_SIGNATURE_ALGORITHMS_ERROR, > SSL_R_MISSING_RSA_CERTIFICATE: > > SSL_do_handshake() failed (SSL: error:1402F0FB:SSL routines:ACCEPT_SW_KEY_EXCH:unknown pkey type error:1402F168:SSL routines:ACCEPT_SW_KEY_EXCH:signature algorithms error) > SSL_do_handshake() failed (SSL: error:1402D0FB:SSL routines:ACCEPT_SW_CERT:unknown pkey type error:14FFF0A8:SSL routines:(UNKNOWN)SSL_internal:missing rsa certificate) Yes, these are also seen with LibreSSL 3.7.0 I've tested. But these looks like library internal errors to me. Given SSL_do_handshake() failed (SSL: error:14FFF3E7:SSL routines:(UNKNOWN)SSL_internal:unknown failure occurred) while SSL handshaking also observed with LibreSSL 3.7.0, I tend to think that error handling in LibreSSL needs some work. [...] -- Maxim Dounin http://mdounin.ru/ From u5.horie at gmail.com Wed Mar 8 00:11:01 2023 From: u5.horie at gmail.com (u5h) Date: Wed, 8 Mar 2023 09:11:01 +0900 Subject: Upstream: Count peer->fails if peers->single is 1 Message-ID: Hello. The upstream peer status is always up when the upstream peer is single. FYI: https://github.com/vozlt/nginx-module-vts/issues/262 The conventional upstream round robin hasn't count peer->fails anywhere when it is peers->single is 1. Thus we’d like to only count it, but not to judge that the peer is down. # HG changeset patch # User Yugo Horie # Date 1678105277 -32400 # Mon Mar 06 21:21:17 2023 +0900 # Node ID bd2cfa9541df90fa45883797cba56a26fc5f39c0 # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e Upstream: Count peer->fails if peers->single is 1 The log based monitoring modules (e.g. nginx-module-vts) can identify the upstream peer is up or down with peer->fails even if it is the single upstream peer. diff -r 2acb00b9b5ff -r bd2cfa9541df src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c Thu Feb 23 20:50:03 2023 +0300 +++ b/src/http/ngx_http_upstream_round_robin.c Mon Mar 06 21:21:17 2023 +0900 @@ -551,7 +551,7 @@ continue; } - if (peer->max_fails + if (!rrp->peers->single && peer->max_fails && peer->fails >= peer->max_fails && now - peer->checked <= peer->fail_timeout) { @@ -618,6 +618,11 @@ if (rrp->peers->single) { peer->conns--; + if (state & NGX_PEER_FAILED) { + peer->fails++; + } else { + peer->fails = 0; + } ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); ngx_http_upstream_rr_peers_unlock(rrp->peers); -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Wed Mar 8 05:09:20 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 08 Mar 2023 05:09:20 +0000 Subject: [njs] Improved njs_string_create_chb(). Message-ID: details: https://hg.nginx.org/njs/rev/e3a609ff9001 branches: changeset: 2065:e3a609ff9001 user: Dmitry Volyntsev date: Fri Mar 03 18:50:23 2023 -0800 description: Improved njs_string_create_chb(). Adding a more informative description when the function cannot build a valid UTF-8 string out of chained buffers. diffstat: src/njs_string.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 9367f3dfe0d9 -r e3a609ff9001 src/njs_string.c --- a/src/njs_string.c Fri Mar 03 22:57:30 2023 +0300 +++ b/src/njs_string.c Fri Mar 03 18:50:23 2023 -0800 @@ -160,7 +160,7 @@ njs_string_create_chb(njs_vm_t *vm, njs_ length = njs_chb_utf8_length(chain); if (njs_slow_path(length < 0)) { - njs_memory_error(vm); + njs_internal_error(vm, "invalid UTF-8 string"); return NJS_ERROR; } From xeioex at nginx.com Wed Mar 8 05:09:22 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 08 Mar 2023 05:09:22 +0000 Subject: [njs] Introduced njs_string_offset() which support any string. Message-ID: details: https://hg.nginx.org/njs/rev/8170f061bbde branches: changeset: 2066:8170f061bbde user: Dmitry Volyntsev date: Tue Mar 07 20:35:00 2023 -0800 description: Introduced njs_string_offset() which support any string. diffstat: src/njs_iterator.c | 2 +- src/njs_json.c | 7 +----- src/njs_parser.c | 5 ++- src/njs_regexp.c | 16 ++++++++------ src/njs_string.c | 57 +++++++++++++++-------------------------------------- src/njs_string.h | 18 +++++++++++++++- 6 files changed, 46 insertions(+), 59 deletions(-) diffs (297 lines): diff -r e3a609ff9001 -r 8170f061bbde src/njs_iterator.c --- a/src/njs_iterator.c Fri Mar 03 18:50:23 2023 -0800 +++ b/src/njs_iterator.c Tue Mar 07 20:35:00 2023 -0800 @@ -558,7 +558,7 @@ njs_object_iterate_reverse(njs_vm_t *vm, i = from + 1; if (i > to) { - p = njs_string_offset(string_prop.start, end, from); + p = njs_string_utf8_offset(string_prop.start, end, from); p = njs_utf8_next(p, end); } diff -r e3a609ff9001 -r 8170f061bbde src/njs_json.c --- a/src/njs_json.c Fri Mar 03 18:50:23 2023 -0800 +++ b/src/njs_json.c Tue Mar 07 20:35:00 2023 -0800 @@ -233,12 +233,7 @@ njs_json_stringify(njs_vm_t *vm, njs_val return NJS_ERROR; } - if (length > 10) { - p = njs_string_offset(prop.start, prop.start + prop.size, 10); - - } else { - p = prop.start + prop.size; - } + p = njs_string_offset(&prop, njs_min(length, 10)); stringify->space.start = prop.start; stringify->space.length = p - prop.start; diff -r e3a609ff9001 -r 8170f061bbde src/njs_parser.c --- a/src/njs_parser.c Fri Mar 03 18:50:23 2023 -0800 +++ b/src/njs_parser.c Tue Mar 07 20:35:00 2023 -0800 @@ -8589,7 +8589,8 @@ njs_parser_string_create(njs_vm_t *vm, n njs_decode_utf8(&dst, &token->text); if (length > NJS_STRING_MAP_STRIDE && dst.length != length) { - njs_string_offset_map_init(value->long_string.data->start, dst.length); + njs_string_utf8_offset_map_init(value->long_string.data->start, + dst.length); } return NJS_OK; @@ -8833,7 +8834,7 @@ next_char: } if (length > NJS_STRING_MAP_STRIDE && length != size) { - njs_string_offset_map_init(start, size); + njs_string_utf8_offset_map_init(start, size); } return NJS_TOKEN_STRING; diff -r e3a609ff9001 -r 8170f061bbde src/njs_regexp.c --- a/src/njs_regexp.c Fri Mar 03 18:50:23 2023 -0800 +++ b/src/njs_regexp.c Tue Mar 07 20:35:00 2023 -0800 @@ -891,9 +891,9 @@ njs_regexp_builtin_exec(njs_vm_t *vm, nj offset = last_index; } else { - /* UTF-8 string. */ - offset = njs_string_offset(string.start, string.start + string.size, - last_index) - string.start; + offset = njs_string_utf8_offset(string.start, + string.start + string.size, last_index) + - string.start; } ret = njs_regexp_match(vm, &pattern->regex[type], string.start, offset, @@ -1360,7 +1360,8 @@ njs_regexp_prototype_symbol_replace(njs_ if ((size_t) length != s.size) { /* UTF-8 string. */ - pos = njs_string_offset(s.start, s.start + s.size, pos) - s.start; + pos = njs_string_utf8_offset(s.start, s.start + s.size, pos) + - s.start; } pos = njs_max(njs_min(pos, (int64_t) s.size), 0); @@ -1643,8 +1644,8 @@ njs_regexp_prototype_symbol_split(njs_vm } if (utf8 == NJS_STRING_UTF8) { - start = njs_string_offset(s.start, s.start + s.size, p); - end = njs_string_offset(s.start, s.start + s.size, q); + start = njs_string_utf8_offset(s.start, s.start + s.size, p); + end = njs_string_utf8_offset(s.start, s.start + s.size, q); } else { start = &s.start[p]; @@ -1691,7 +1692,8 @@ njs_regexp_prototype_symbol_split(njs_vm end = &s.start[s.size]; if (utf8 == NJS_STRING_UTF8) { - start = (p < length) ? njs_string_offset(s.start, s.start + s.size, p) + start = (p < length) ? njs_string_utf8_offset(s.start, s.start + s.size, + p) : end; } else { diff -r e3a609ff9001 -r 8170f061bbde src/njs_string.c --- a/src/njs_string.c Fri Mar 03 18:50:23 2023 -0800 +++ b/src/njs_string.c Tue Mar 07 20:35:00 2023 -0800 @@ -1146,7 +1146,7 @@ njs_string_prototype_to_bytes(njs_vm_t * /* UTF-8 string. */ end = string.start + string.size; - s = njs_string_offset(string.start, end, slice.start); + s = njs_string_utf8_offset(string.start, end, slice.start); length = slice.length; @@ -1503,7 +1503,7 @@ njs_string_slice_string_prop(njs_string_ end = start + string->size; if (slice->start < slice->string_length) { - start = njs_string_offset(start, end, slice->start); + start = njs_string_utf8_offset(start, end, slice->start); /* Evaluate size of the slice in bytes and adjust length. */ p = start; @@ -1584,9 +1584,8 @@ njs_string_prototype_char_code_at(njs_vm } else { njs_utf8_decode_init(&ctx); - /* UTF-8 string. */ end = string.start + string.size; - start = njs_string_offset(string.start, end, index); + start = njs_string_utf8_offset(string.start, end, index); code = njs_utf8_decode(&ctx, &start, end); } @@ -2151,7 +2150,7 @@ njs_string_index_of(njs_string_prop_t *s } else { /* UTF-8 string. */ - p = njs_string_offset(string->start, end, index); + p = njs_string_utf8_offset(string->start, end, index); end -= search->size - 1; while (p < end) { @@ -2296,7 +2295,7 @@ njs_string_prototype_last_index_of(njs_v goto done; } - p = njs_string_offset(string.start, end, index); + p = njs_string_utf8_offset(string.start, end, index); for (; p >= string.start; p = njs_utf8_prev(p)) { if ((p + s.size) <= end && memcmp(p, s.start, s.size) == 0) { @@ -2376,15 +2375,7 @@ njs_string_prototype_includes(njs_vm_t * if (length - index >= search_length) { end = string.start + string.size; - - if (string.size == (size_t) length) { - /* Byte or ASCII string. */ - p = string.start + index; - - } else { - /* UTF-8 string. */ - p = njs_string_offset(string.start, end, index); - } + p = njs_string_offset(&string, index); end -= search.size - 1; @@ -2482,15 +2473,7 @@ njs_string_prototype_starts_or_ends_with } end = string.start + string.size; - - if (string.size == (size_t) length) { - /* Byte or ASCII string. */ - p = string.start + index; - - } else { - /* UTF-8 string. */ - p = njs_string_offset(string.start, end, index); - } + p = njs_string_offset(&string, index); if ((size_t) (end - p) >= search.size && memcmp(p, search.start, search.size) == 0) @@ -2512,11 +2495,11 @@ done: /* - * njs_string_offset() assumes that index is correct. + * njs_string_utf8_offset() assumes that index is correct. */ const u_char * -njs_string_offset(const u_char *start, const u_char *end, size_t index) +njs_string_utf8_offset(const u_char *start, const u_char *end, size_t index) { uint32_t *map; njs_uint_t skip; @@ -2525,7 +2508,7 @@ njs_string_offset(const u_char *start, c map = njs_string_map_start(end); if (map[0] == 0) { - njs_string_offset_map_init(start, end - start); + njs_string_utf8_offset_map_init(start, end - start); } start += map[index / NJS_STRING_MAP_STRIDE - 1]; @@ -2562,7 +2545,7 @@ njs_string_index(njs_string_prop_t *stri map = njs_string_map_start(end); if (map[0] == 0) { - njs_string_offset_map_init(string->start, string->size); + njs_string_utf8_offset_map_init(string->start, string->size); } while (index + NJS_STRING_MAP_STRIDE < string->length @@ -2587,7 +2570,7 @@ njs_string_index(njs_string_prop_t *stri void -njs_string_offset_map_init(const u_char *start, size_t size) +njs_string_utf8_offset_map_init(const u_char *start, size_t size) { size_t offset; uint32_t *map; @@ -3055,7 +3038,7 @@ njs_string_prototype_pad(njs_vm_t *vm, n if (pad_string.size != (size_t) pad_length) { /* UTF-8 string. */ end = pad_string.start + pad_string.size; - end = njs_string_offset(pad_string.start, end, trunc); + end = njs_string_utf8_offset(pad_string.start, end, trunc); trunc = end - pad_string.start; padding = pad_string.size * n + trunc; @@ -3799,14 +3782,7 @@ njs_string_prototype_replace(njs_vm_t *v } } - if (njs_is_byte_or_ascii_string(&string)) { - p = string.start + pos; - - } else { - /* UTF-8 string. */ - p = njs_string_offset(string.start, string.start + string.size, - pos); - } + p = njs_string_offset(&string, pos); (void) njs_string_prop(&ret_string, &retval); @@ -3867,9 +3843,8 @@ njs_string_prototype_replace(njs_vm_t *v p = string.start + pos; } else { - /* UTF-8 string. */ - p = njs_string_offset(string.start, string.start + string.size, - pos); + p = njs_string_utf8_offset(string.start, string.start + string.size, + pos); } (void) njs_string_prop(&ret_string, &retval); diff -r e3a609ff9001 -r 8170f061bbde src/njs_string.h --- a/src/njs_string.h Fri Mar 03 18:50:23 2023 -0800 +++ b/src/njs_string.h Tue Mar 07 20:35:00 2023 -0800 @@ -243,10 +243,10 @@ void njs_string_slice_string_prop(njs_st const njs_string_prop_t *string, const njs_slice_prop_t *slice); njs_int_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst, const njs_string_prop_t *string, const njs_slice_prop_t *slice); -const u_char *njs_string_offset(const u_char *start, const u_char *end, +const u_char *njs_string_utf8_offset(const u_char *start, const u_char *end, size_t index); uint32_t njs_string_index(njs_string_prop_t *string, uint32_t offset); -void njs_string_offset_map_init(const u_char *start, size_t size); +void njs_string_utf8_offset_map_init(const u_char *start, size_t size); double njs_string_to_index(const njs_value_t *value); njs_int_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t component); @@ -266,6 +266,20 @@ njs_int_t njs_string_get_substitution(nj njs_value_t *groups, njs_value_t *replacement, njs_value_t *retval); +njs_inline const u_char * +njs_string_offset(njs_string_prop_t *string, int64_t index) +{ + if (njs_is_byte_or_ascii_string(string)) { + return string->start + index; + } + + /* UTF-8 string. */ + + return njs_string_utf8_offset(string->start, string->start + string->size, + index); +} + + extern const njs_object_init_t njs_string_instance_init; extern const njs_object_type_init_t njs_string_type_init; From xeioex at nginx.com Wed Mar 8 05:09:24 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 08 Mar 2023 05:09:24 +0000 Subject: [njs] Making njs_string_prop() a macro. Message-ID: details: https://hg.nginx.org/njs/rev/107c7098bd6d branches: changeset: 2067:107c7098bd6d user: Dmitry Volyntsev date: Tue Mar 07 20:38:08 2023 -0800 description: Making njs_string_prop() a macro. diffstat: src/njs_string.c | 25 ------------------------- src/njs_string.h | 26 +++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 26 deletions(-) diffs (78 lines): diff -r 8170f061bbde -r 107c7098bd6d src/njs_string.c --- a/src/njs_string.c Tue Mar 07 20:35:00 2023 -0800 +++ b/src/njs_string.c Tue Mar 07 20:38:08 2023 -0800 @@ -566,31 +566,6 @@ njs_string_validate(njs_vm_t *vm, njs_st } -size_t -njs_string_prop(njs_string_prop_t *string, const njs_value_t *value) -{ - size_t size; - uintptr_t length; - - size = value->short_string.size; - - if (size != NJS_STRING_LONG) { - string->start = (u_char *) value->short_string.start; - length = value->short_string.length; - - } else { - string->start = (u_char *) value->long_string.data->start; - size = value->long_string.size; - length = value->long_string.data->length; - } - - string->size = size; - string->length = length; - - return (length == 0) ? size : length; -} - - static njs_int_t njs_string_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) diff -r 8170f061bbde -r 107c7098bd6d src/njs_string.h --- a/src/njs_string.h Tue Mar 07 20:35:00 2023 -0800 +++ b/src/njs_string.h Tue Mar 07 20:38:08 2023 -0800 @@ -237,7 +237,6 @@ uint32_t njs_string_trim(const njs_value void njs_string_copy(njs_value_t *dst, njs_value_t *src); njs_int_t njs_string_validate(njs_vm_t *vm, njs_string_prop_t *string, njs_value_t *value); -size_t njs_string_prop(njs_string_prop_t *string, const njs_value_t *value); njs_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2); void njs_string_slice_string_prop(njs_string_prop_t *dst, const njs_string_prop_t *string, const njs_slice_prop_t *slice); @@ -280,6 +279,31 @@ njs_string_offset(njs_string_prop_t *str } +njs_inline size_t +njs_string_prop(njs_string_prop_t *string, const njs_value_t *value) +{ + size_t size; + uintptr_t length; + + size = value->short_string.size; + + if (size != NJS_STRING_LONG) { + string->start = (u_char *) value->short_string.start; + length = value->short_string.length; + + } else { + string->start = (u_char *) value->long_string.data->start; + size = value->long_string.size; + length = value->long_string.data->length; + } + + string->size = size; + string->length = length; + + return (length == 0) ? size : length; +} + + extern const njs_object_init_t njs_string_instance_init; extern const njs_object_type_init_t njs_string_type_init; From xeioex at nginx.com Wed Mar 8 05:09:25 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 08 Mar 2023 05:09:25 +0000 Subject: [njs] Fixed String.prototype.replace() with replacement containing "$'", "$`". Message-ID: details: https://hg.nginx.org/njs/rev/10127d70e941 branches: changeset: 2068:10127d70e941 user: Dmitry Volyntsev date: Tue Mar 07 20:51:45 2023 -0800 description: Fixed String.prototype.replace() with replacement containing "$'", "$`". Previously, the resulting string was might be broken when the string to replace and the search string were UTF-8. pos is always a character offset, it should not be directly used as a byte size or offset. diffstat: src/njs_string.c | 36 ++++++++++++++++++++---------------- src/test/njs_unit_test.c | 11 +++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diffs (82 lines): diff -r 107c7098bd6d -r 10127d70e941 src/njs_string.c --- a/src/njs_string.c Tue Mar 07 20:38:08 2023 -0800 +++ b/src/njs_string.c Tue Mar 07 20:51:45 2023 -0800 @@ -3476,12 +3476,14 @@ njs_string_get_substitution(njs_vm_t *vm njs_value_t *string, int64_t pos, njs_value_t *captures, int64_t ncaptures, njs_value_t *groups, njs_value_t *replacement, njs_value_t *retval) { - int64_t tail, n; - u_char c, c2, *p, *r, *end; - njs_str_t rep, m, str, cap; - njs_int_t ret; - njs_chb_t chain; - njs_value_t name, value; + u_char c, c2, *p, *r, *end; + size_t length; + int64_t tail, n; + njs_str_t rep, str, cap; + njs_int_t ret; + njs_chb_t chain; + njs_value_t name, value; + njs_string_prop_t s, m; njs_string_get(replacement, &rep); p = rep.start; @@ -3513,24 +3515,26 @@ njs_string_get_substitution(njs_vm_t *vm break; case '&': - njs_string_get(matched, &m); - njs_chb_append_str(&chain, &m); + (void) njs_string_prop(&m, matched); + njs_chb_append(&chain, m.start, m.size); p += 2; break; case '`': - njs_string_get(string, &str); - njs_chb_append(&chain, str.start, pos); + (void) njs_string_prop(&s, string); + n = njs_string_offset(&s, pos) - s.start; + njs_chb_append(&chain, s.start, n); p += 2; break; case '\'': - njs_string_get(matched, &m); - tail = pos + m.length; - - njs_string_get(string, &str); - njs_chb_append(&chain, &str.start[tail], - njs_max((int64_t) str.length - tail, 0)); + length = njs_string_prop(&m, matched); + (void) njs_string_prop(&s, string); + + tail = njs_string_offset(&s, pos + length) - s.start; + + njs_chb_append(&chain, &s.start[tail], + njs_max((int64_t) s.size - tail, 0)); p += 2; break; diff -r 107c7098bd6d -r 10127d70e941 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Tue Mar 07 20:38:08 2023 -0800 +++ b/src/test/njs_unit_test.c Tue Mar 07 20:51:45 2023 -0800 @@ -8887,6 +8887,17 @@ static njs_unit_test_t njs_test[] = { njs_str("'abcdbe'.replaceAll('b', '|$`X$\\'|')"), njs_str("a|aXcdbe|cd|abcdXe|e") }, + { njs_str("var r = 'αβγ'.replace('β', \"$'\"); [r, r.length]"), + njs_str("αγγ,3") }, + + { njs_str("var r = 'αβγαβγ'.replaceAll('β', \"$'\"); [r, r.length]"), + njs_str("αγαβγγαγγ,9") }, + + { njs_str("var r = 'αβγ'.replace('β', \"$`\"); [r, r.length]"), + njs_str("ααγ,3") }, + + { njs_str("var r = 'αβγαβγ'.replaceAll('β', \"$`\"); [r, r.length]"), + njs_str("ααγααβγαγ,9") }, { njs_str("'ABC'.replace('B', '$')"), njs_str("A$C") }, From arut at nginx.com Wed Mar 8 13:31:42 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 8 Mar 2023 17:31:42 +0400 Subject: [PATCH 1 of 2] HTTP/2: socket leak with "return 444" in error_page (ticket #2455) In-Reply-To: <61bd779a868c4021c232.1677070527@vm-bsd.mdounin.ru> References: <61bd779a868c4021c232.1677070527@vm-bsd.mdounin.ru> Message-ID: <20230308133142.dhp4oya4utmzgpkd@N00W24XTQX> Hi, On Wed, Feb 22, 2023 at 03:55:27PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1677070356 -10800 > # Wed Feb 22 15:52:36 2023 +0300 > # Node ID 61bd779a868c4021c232dddfe7abda7e8ad32575 > # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 > HTTP/2: socket leak with "return 444" in error_page (ticket #2455). > > Similarly to ticket #274 (7354:1812f1d79d84), early request finalization > without calling ngx_http_run_posted_requests() resulted in a connection > hang (a socket leak) if the 400 (Bad Request) error was generated in > ngx_http_v2_state_process_header() due to invalid request headers and > "return 444" was used in error_page 400. > > diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -1730,6 +1730,7 @@ ngx_http_v2_state_process_header(ngx_htt > size_t len; > ngx_int_t rc; > ngx_table_elt_t *h; > + ngx_connection_t *fc; > ngx_http_header_t *hh; > ngx_http_request_t *r; > ngx_http_v2_header_t *header; > @@ -1789,6 +1790,7 @@ ngx_http_v2_state_process_header(ngx_htt > } > > r = h2c->state.stream->request; > + fc = r->connection; > > /* TODO Optimization: validate headers while parsing. */ > if (ngx_http_v2_validate_header(r, header) != NGX_OK) { > @@ -1886,6 +1888,8 @@ error: > > h2c->state.stream = NULL; > > + ngx_http_run_posted_requests(fc); > + > return ngx_http_v2_state_header_complete(h2c, pos, end); > } Looks ok From arut at nginx.com Wed Mar 8 14:15:18 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 8 Mar 2023 18:15:18 +0400 Subject: [PATCH 2 of 2] HTTP/2: finalize request as bad if header validation fails In-Reply-To: References: <61bd779a868c4021c232.1677070527@vm-bsd.mdounin.ru> Message-ID: <20230308141518.7kjdcxumawtxiv6b@N00W24XTQX> Hi, On Wed, Feb 22, 2023 at 03:55:28PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1677070454 -10800 > # Wed Feb 22 15:54:14 2023 +0300 > # Node ID ec3819e66f40924efad183c291c850d9ccef16e7 > # Parent 61bd779a868c4021c232dddfe7abda7e8ad32575 > HTTP/2: finalize request as bad if header validation fails. > > Similarly to 7192:d5a535774861, this avoids spurious zero statuses > in access.log, and in line with other header-related errors. This is what RFC 9113, 8.2.1. Field Validity, says about header field validation: A request or response that contains a field that violates any of these conditions MUST be treated as malformed (Section 8.1.1). Also (the previous RFC 7540 did not have this statement): When a request message violates one of these requirements, an implementation SHOULD generate a 400 (Bad Request) status code (see Section 15.5.1 of [HTTP]) Considering this, responding with HTTP 400 is certainly an improvement compared to the current code. Also, it allows to set a custom error page handler. However, not resetting the stream as before is a less obvious change. If we consider nginx an intermediary, the RFC 9113 prescribes us to send stream reset: Intermediaries that process HTTP requests or responses (i.e., any intermediary not acting as a tunnel) MUST NOT forward a malformed request or response. Malformed requests or responses that are detected MUST be treated as a stream error (Section 5.4.2) of type PROTOCOL_ERROR. This restriction probably makes sense for the intermediaries unable to handle the error in a more flexible way. I like the change, but I'd like to hear what you think about the reset issue. > diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -1794,14 +1794,7 @@ ngx_http_v2_state_process_header(ngx_htt > > /* TODO Optimization: validate headers while parsing. */ > if (ngx_http_v2_validate_header(r, header) != NGX_OK) { > - if (ngx_http_v2_terminate_stream(h2c, h2c->state.stream, > - NGX_HTTP_V2_PROTOCOL_ERROR) > - == NGX_ERROR) > - { > - return ngx_http_v2_connection_error(h2c, > - NGX_HTTP_V2_INTERNAL_ERROR); > - } > - > + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > goto error; > } > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From arut at nginx.com Wed Mar 8 14:19:10 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 8 Mar 2023 18:19:10 +0400 Subject: [PATCH 2 of 4] SSL: logging levels of various errors reported with tlsfuzzer In-Reply-To: References: <20230307144612.c6t23xdcyhk3d4xn@N00W24XTQX> Message-ID: <20230308141910.3teidq5q5j4gjpye@N00W24XTQX> On Wed, Mar 08, 2023 at 01:07:45AM +0300, Maxim Dounin wrote: > Hello! > > On Tue, Mar 07, 2023 at 06:46:12PM +0400, Roman Arutyunyan wrote: > > > Hi, > > > > On Wed, Mar 01, 2023 at 05:56:03PM +0300, Maxim Dounin wrote: > > > # HG changeset patch > > > # User Maxim Dounin > > > # Date 1677682421 -10800 > > > # Wed Mar 01 17:53:41 2023 +0300 > > > # Node ID c76e163105f1eac7727ce4e6d955fecb38d93e49 > > > # Parent 4d0a265c1d20f22f196680dfcc9d044f9e711865 > > > SSL: logging levels of various errors reported with tlsfuzzer. > > > > > > To further differentiate client-related errors and adjust logging levels > > > of various SSL errors, nginx was tested with tlsfuzzer with multiple > > > OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, > > > 1.0.0s, 0.9.8zh). > > > > While mentioned here, OpenSSL 3.1.0-beta1 is not referenced anywhere in the > > text below. It should be added to the list of versions not adding new client > > errors compared to other versions. > > Thanks for noting. I've generally not considered OpenSSL > 3.1.0-beta1, since it is mostly identical to OpenSSL 3.0.8 in > terms of errors and not really a release, but decided to add it to > the list of tested versions for completeness. > > Updated the last paragraph as well: > > : No additional client-related errors were observed while testing with > : OpenSSL 3.1.0-beta1, OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, > : and OpenSSL 0.9.8zh. > > > > The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, > > > and are clearly client-related: > > > > > > SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) > > > SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) > > > SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) > > > SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) > > > > > > Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), > > > SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION > > > ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS > > > ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) > > > errors are now logged at the "info" level. > > > > > > Additionally, the following errors were observed with OpenSSL 3.0.8 and > > > with TLSv1.3 enabled: > > > > > > SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) > > > SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) > > > SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) > > > SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) > > > SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) > > > SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) > > > SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) > > > SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) > > > SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) > > > > > > Accordingly, the SSL_R_BAD_ECPOINT ("bad ecpoint"), > > > > SSL_R_BAD_ECPOINT is already logged at the "info" level, added by you in > > cac164d0807e. This text is probably a leftover from testing this without the > > previous patch applied, in which case a crypto error was analyzed and ignored. > > Indeed, thanks for catching. Removed this from the commit log (as > well as the relevant error messages above). > > > > SSL_R_BAD_DIGEST_LENGTH > > > ("bad digest length"), SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs > > > extension"), SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), > > > SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), > > > and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non > > > handshake data") errors are now logged at the "info" level. > > > > > > Additionally, the following errors were observed with OpenSSL 1.1.1t: > > > > > > SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) > > > SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) > > > SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) > > > SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) > > > > > > Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), > > > SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), > > > SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and > > > SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged > > > at the "info" level. > > > > > > Additionally, the following errors were observed with OpenSSL 1.0.2u: > > > > > > SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) > > > SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) > > > > > > Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and > > > SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now > > > logged at the "info" level. > > > > > > No additional client-related errors were observed while testing with > > > OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, and OpenSSL 0.9.8zh. > > Full updated commit log: > > # HG changeset patch > # User Maxim Dounin > # Date 1678226771 -10800 > # Wed Mar 08 01:06:11 2023 +0300 > # Node ID a48cbbec723b1a6d4943e877deb21e719c19d09a > # Parent c011fae23865f04518f9279fb98a312ef8f7a29c > SSL: logging levels of various errors reported with tlsfuzzer. > > To further differentiate client-related errors and adjust logging levels > of various SSL errors, nginx was tested with tlsfuzzer with multiple > OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, > 1.0.0s, 0.9.8zh). > > The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, > and are clearly client-related: > > SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) > SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) > SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) > SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) > > Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), > SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION > ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS > ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) > errors are now logged at the "info" level. > > Additionally, the following errors were observed with OpenSSL 3.0.8 and > with TLSv1.3 enabled: > > SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) > SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) > SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) > SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) > SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) > SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) > > Accordingly, the SSL_R_BAD_DIGEST_LENGTH ("bad digest length"), > SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs extension"), > SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), > SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), > and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non > handshake data") errors are now logged at the "info" level. > > Additionally, the following errors were observed with OpenSSL 1.1.1t: > > SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) > SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) > SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) > SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) > > Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), > SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), > SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and > SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged > at the "info" level. > > Additionally, the following errors were observed with OpenSSL 1.0.2u: > > SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) > SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) > > Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and > SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now > logged at the "info" level. > > No additional client-related errors were observed while testing with > OpenSSL 3.1.0-beta1, OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, > and OpenSSL 0.9.8zh. Looks ok From arut at nginx.com Wed Mar 8 14:22:40 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 8 Mar 2023 18:22:40 +0400 Subject: [PATCH 3 of 4] SSL: logging levels of errors observed with tlsfuzzer and LibreSSL In-Reply-To: References: <207742991a561c0ed708.1677682564@vm-bsd.mdounin.ru> <20230307144759.os5q4ovmuch32f3b@N00W24XTQX> Message-ID: <20230308142240.twcietclkg6pfmsx@N00W24XTQX> Hi, On Wed, Mar 08, 2023 at 01:25:23AM +0300, Maxim Dounin wrote: > Hello! > > On Tue, Mar 07, 2023 at 06:47:59PM +0400, Roman Arutyunyan wrote: > > > On Wed, Mar 01, 2023 at 05:56:04PM +0300, Maxim Dounin wrote: > > > # HG changeset patch > > > # User Maxim Dounin > > > # Date 1677682426 -10800 > > > # Wed Mar 01 17:53:46 2023 +0300 > > > # Node ID 207742991a561c0ed70834d4ce18e8452689419d > > > # Parent c76e163105f1eac7727ce4e6d955fecb38d93e49 > > > SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. > > > > > > As tested with tlsfuzzer with LibreSSL 2.7.0, the following errors are > > > certainly client-related: > > > > LibreSSL 2.7.0 is ancient - March 21st, 2018. > > Err, typo, should be 3.7.0, the latest development release. > Fixed. > > > > SSL_do_handshake() failed (SSL: error:14026073:SSL routines:ACCEPT_SR_CLNT_HELLO:bad packet length) > > > SSL_do_handshake() failed (SSL: error:1402612C:SSL routines:ACCEPT_SR_CLNT_HELLO:ssl3 session id too long) > > > > I could not get this one with 2.7.0, but I got it with 3.6.0. > > > > > SSL_do_handshake() failed (SSL: error:140380EA:SSL routines:ACCEPT_SR_KEY_EXCH:tls rsa encrypted value length is wrong) > > > > With 3.6.0 two more errors are reported: SSL_R_SIGNATURE_ALGORITHMS_ERROR, > > SSL_R_MISSING_RSA_CERTIFICATE: > > > > SSL_do_handshake() failed (SSL: error:1402F0FB:SSL routines:ACCEPT_SW_KEY_EXCH:unknown pkey type error:1402F168:SSL routines:ACCEPT_SW_KEY_EXCH:signature algorithms error) > > SSL_do_handshake() failed (SSL: error:1402D0FB:SSL routines:ACCEPT_SW_CERT:unknown pkey type error:14FFF0A8:SSL routines:(UNKNOWN)SSL_internal:missing rsa certificate) > > Yes, these are also seen with LibreSSL 3.7.0 I've tested. But > these looks like library internal errors to me. > > Given > > SSL_do_handshake() failed (SSL: error:14FFF3E7:SSL routines:(UNKNOWN)SSL_internal:unknown failure occurred) while SSL handshaking > > also observed with LibreSSL 3.7.0, I tend to think that error > handling in LibreSSL needs some work. Absolutely. I caught this one: SSL_do_handshake() failed (SSL: error:14025455:SSL routines:ACCEPT_SR_CLNT_HELLO:reason(1109)) while SSL handshaking OK, no more comments then. Looks good. -- Roman Arutyunyan From mdounin at mdounin.ru Wed Mar 8 19:48:29 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 8 Mar 2023 22:48:29 +0300 Subject: [PATCH 3 of 4] SSL: logging levels of errors observed with tlsfuzzer and LibreSSL In-Reply-To: <20230308142240.twcietclkg6pfmsx@N00W24XTQX> References: <207742991a561c0ed708.1677682564@vm-bsd.mdounin.ru> <20230307144759.os5q4ovmuch32f3b@N00W24XTQX> <20230308142240.twcietclkg6pfmsx@N00W24XTQX> Message-ID: Hello! On Wed, Mar 08, 2023 at 06:22:40PM +0400, Roman Arutyunyan wrote: > Hi, > > On Wed, Mar 08, 2023 at 01:25:23AM +0300, Maxim Dounin wrote: > > Hello! > > > > On Tue, Mar 07, 2023 at 06:47:59PM +0400, Roman Arutyunyan wrote: > > > > > On Wed, Mar 01, 2023 at 05:56:04PM +0300, Maxim Dounin wrote: > > > > # HG changeset patch > > > > # User Maxim Dounin > > > > # Date 1677682426 -10800 > > > > # Wed Mar 01 17:53:46 2023 +0300 > > > > # Node ID 207742991a561c0ed70834d4ce18e8452689419d > > > > # Parent c76e163105f1eac7727ce4e6d955fecb38d93e49 > > > > SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. > > > > > > > > As tested with tlsfuzzer with LibreSSL 2.7.0, the following errors are > > > > certainly client-related: > > > > > > LibreSSL 2.7.0 is ancient - March 21st, 2018. > > > > Err, typo, should be 3.7.0, the latest development release. > > Fixed. > > > > > > SSL_do_handshake() failed (SSL: error:14026073:SSL routines:ACCEPT_SR_CLNT_HELLO:bad packet length) > > > > SSL_do_handshake() failed (SSL: error:1402612C:SSL routines:ACCEPT_SR_CLNT_HELLO:ssl3 session id too long) > > > > > > I could not get this one with 2.7.0, but I got it with 3.6.0. > > > > > > > SSL_do_handshake() failed (SSL: error:140380EA:SSL routines:ACCEPT_SR_KEY_EXCH:tls rsa encrypted value length is wrong) > > > > > > With 3.6.0 two more errors are reported: SSL_R_SIGNATURE_ALGORITHMS_ERROR, > > > SSL_R_MISSING_RSA_CERTIFICATE: > > > > > > SSL_do_handshake() failed (SSL: error:1402F0FB:SSL routines:ACCEPT_SW_KEY_EXCH:unknown pkey type error:1402F168:SSL routines:ACCEPT_SW_KEY_EXCH:signature algorithms error) > > > SSL_do_handshake() failed (SSL: error:1402D0FB:SSL routines:ACCEPT_SW_CERT:unknown pkey type error:14FFF0A8:SSL routines:(UNKNOWN)SSL_internal:missing rsa certificate) > > > > Yes, these are also seen with LibreSSL 3.7.0 I've tested. But > > these looks like library internal errors to me. > > > > Given > > > > SSL_do_handshake() failed (SSL: error:14FFF3E7:SSL routines:(UNKNOWN)SSL_internal:unknown failure occurred) while SSL handshaking > > > > also observed with LibreSSL 3.7.0, I tend to think that error > > handling in LibreSSL needs some work. > > Absolutely. I caught this one: > > SSL_do_handshake() failed (SSL: error:14025455:SSL routines:ACCEPT_SR_CLNT_HELLO:reason(1109)) while SSL handshaking > > OK, no more comments then. Looks good. Thanks for the review, pushed to http://mdounin.ru/hg/nginx. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Wed Mar 8 22:57:04 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 9 Mar 2023 01:57:04 +0300 Subject: Upstream: Count peer->fails if peers->single is 1 In-Reply-To: References: Message-ID: Hello! On Wed, Mar 08, 2023 at 09:11:01AM +0900, u5h wrote: > Hello. > The upstream peer status is always up when the upstream peer is single. > FYI: https://github.com/vozlt/nginx-module-vts/issues/262 > The conventional upstream round robin hasn't count peer->fails anywhere > when it is peers->single is 1. > Thus we’d like to only count it, but not to judge that the peer is down. As per nginx usage of upstreams with a single peer, such peers are never considered unavailable, and to do this failures are never counted (or, which is equivalent, immediately forgotten). If you are trying to show status of a peer as seen by nginx, then "up" is perfectly correct for such peers. If you are instead trying to show some abstract state based on request failures, then looking into the peer's data feels wrong to me: rather, you should look into request results (and/or upstream states), and collect relevant stats yourself. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 9 00:40:26 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 9 Mar 2023 03:40:26 +0300 Subject: [PATCH 2 of 2] HTTP/2: finalize request as bad if header validation fails In-Reply-To: <20230308141518.7kjdcxumawtxiv6b@N00W24XTQX> References: <61bd779a868c4021c232.1677070527@vm-bsd.mdounin.ru> <20230308141518.7kjdcxumawtxiv6b@N00W24XTQX> Message-ID: Hello! On Wed, Mar 08, 2023 at 06:15:18PM +0400, Roman Arutyunyan wrote: > Hi, > > On Wed, Feb 22, 2023 at 03:55:28PM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1677070454 -10800 > > # Wed Feb 22 15:54:14 2023 +0300 > > # Node ID ec3819e66f40924efad183c291c850d9ccef16e7 > > # Parent 61bd779a868c4021c232dddfe7abda7e8ad32575 > > HTTP/2: finalize request as bad if header validation fails. > > > > Similarly to 7192:d5a535774861, this avoids spurious zero statuses > > in access.log, and in line with other header-related errors. > > This is what RFC 9113, 8.2.1. Field Validity, says about header field > validation: > > A request or response that contains a field that violates > any of these conditions MUST be treated as malformed (Section 8.1.1). > > Also (the previous RFC 7540 did not have this statement): > > When a request message violates one of these requirements, an implementation > SHOULD generate a 400 (Bad Request) status code (see Section 15.5.1 of [HTTP]) > > Considering this, responding with HTTP 400 is certainly an improvement compared > to the current code. Also, it allows to set a custom error page handler. > > However, not resetting the stream as before is a less obvious change. If we > consider nginx an intermediary, the RFC 9113 prescribes us to send stream reset: > > Intermediaries that process HTTP requests or responses (i.e., any > intermediary not acting as a tunnel) MUST NOT forward a malformed > request or response. Malformed requests or responses that are detected > MUST be treated as a stream error (Section 5.4.2) of type PROTOCOL_ERROR. > > This restriction probably makes sense for the intermediaries unable to handle > the error in a more flexible way. > > I like the change, but I'd like to hear what you think about the reset issue. We already do exactly the same for quite similar cases, as introduced in 7192:d5a535774861, for unknown and invalid pseudo-headers. This patch simply makes handling of errors detected by ngx_http_v2_validate_header() equivalent. I does not look like returning 400 instead of sending RST_STREAM causes any issues. Further, given that HTTP/2 error handling is mostly unimplemented by clients, I tend to think that returning 400 instead of RST_STREAM is a better option. Note well that nothing in RFC 7540 actually requires to send RST_STREAM: while it says that (detected) malformed requests "MUST be treated as a stream error", handling of stream errors does not require to actually send RST_STREAM, as there is no such normative requirement in section 5.4.2. Further, RFC 9113 explicitly states that (https://www.rfc-editor.org/rfc/rfc9113#section-8.1.1): For malformed requests, a server MAY send an HTTP response prior to closing or resetting the stream. Note "closing or resetting", which implies that RST_STREAM is not mandatory. While we might consider changing the code to generate RST_STREAM after sending the response in such cases, this does not seem to be needed (and might actually make things worse). In either case this doesn't look like something to be addressed in this patch series. -- Maxim Dounin http://mdounin.ru/ From arut at nginx.com Thu Mar 9 10:49:24 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 9 Mar 2023 14:49:24 +0400 Subject: [PATCH 2 of 2] HTTP/2: finalize request as bad if header validation fails In-Reply-To: References: <61bd779a868c4021c232.1677070527@vm-bsd.mdounin.ru> <20230308141518.7kjdcxumawtxiv6b@N00W24XTQX> Message-ID: <20230309104924.kqslt4d4nhe2absh@N00W24XTQX> Hi, On Thu, Mar 09, 2023 at 03:40:26AM +0300, Maxim Dounin wrote: > Hello! > > On Wed, Mar 08, 2023 at 06:15:18PM +0400, Roman Arutyunyan wrote: > > > Hi, > > > > On Wed, Feb 22, 2023 at 03:55:28PM +0300, Maxim Dounin wrote: > > > # HG changeset patch > > > # User Maxim Dounin > > > # Date 1677070454 -10800 > > > # Wed Feb 22 15:54:14 2023 +0300 > > > # Node ID ec3819e66f40924efad183c291c850d9ccef16e7 > > > # Parent 61bd779a868c4021c232dddfe7abda7e8ad32575 > > > HTTP/2: finalize request as bad if header validation fails. > > > > > > Similarly to 7192:d5a535774861, this avoids spurious zero statuses > > > in access.log, and in line with other header-related errors. > > > > This is what RFC 9113, 8.2.1. Field Validity, says about header field > > validation: > > > > A request or response that contains a field that violates > > any of these conditions MUST be treated as malformed (Section 8.1.1). > > > > Also (the previous RFC 7540 did not have this statement): > > > > When a request message violates one of these requirements, an implementation > > SHOULD generate a 400 (Bad Request) status code (see Section 15.5.1 of [HTTP]) > > > > Considering this, responding with HTTP 400 is certainly an improvement compared > > to the current code. Also, it allows to set a custom error page handler. > > > > However, not resetting the stream as before is a less obvious change. If we > > consider nginx an intermediary, the RFC 9113 prescribes us to send stream reset: > > > > Intermediaries that process HTTP requests or responses (i.e., any > > intermediary not acting as a tunnel) MUST NOT forward a malformed > > request or response. Malformed requests or responses that are detected > > MUST be treated as a stream error (Section 5.4.2) of type PROTOCOL_ERROR. > > > > This restriction probably makes sense for the intermediaries unable to handle > > the error in a more flexible way. > > > > I like the change, but I'd like to hear what you think about the reset issue. > > We already do exactly the same for quite similar cases, as > introduced in 7192:d5a535774861, for unknown and invalid > pseudo-headers. This patch simply makes handling of > errors detected by ngx_http_v2_validate_header() equivalent. Yes, I have read that discussion. It's seemed suspicious though that only the pseudo-headers error was treated as HTTP 400 by that change, while ngx_http_v2_validate_header(), which is just a few lines above, wasn't affected. > I does not look like returning 400 instead of sending RST_STREAM > causes any issues. Further, given that HTTP/2 error handling is > mostly unimplemented by clients, I tend to think that returning > 400 instead of RST_STREAM is a better option. I personally like it too. > Note well that nothing in RFC 7540 actually requires to send > RST_STREAM: while it says that (detected) malformed requests "MUST > be treated as a stream error", handling of stream errors does not > require to actually send RST_STREAM, as there is no such normative > requirement in section 5.4.2. Indeed there's no requirement level here, which makes the whole paragraph pointless: An endpoint that detects a stream error sends a RST_STREAM frame (Section 6.4) that contains the stream identifier of the stream where the error occurred. The RST_STREAM frame includes an error code that indicates the type of error. > Further, RFC 9113 explicitly states > that (https://www.rfc-editor.org/rfc/rfc9113#section-8.1.1): > > For malformed requests, a server MAY send an HTTP response prior > to closing or resetting the stream. > > Note "closing or resetting", which implies that RST_STREAM is not > mandatory. If the server is an origin server, and the request is malformed, stream error is indeed not mandatory. But for an intermediary, it is. It's however not clear, whether stream error should trigger RST_STREAM, considering the lack of a requirement level in 5.4.2. > While we might consider changing the code to generate RST_STREAM > after sending the response in such cases, this does not seem to be > needed (and might actually make things worse). In either case > this doesn't look like something to be addressed in this patch > series. The discussion is also relevant to HTTP/3 which may trigger either QUIC or HTTP errors for malformed request streams. As I said before, I like HTTP errors more since they provide more flexibility and are more compatible with the way nginx treats HTTP/1 requests. Please commit the patch. -- Roman Arutyunyan From pluknet at nginx.com Thu Mar 9 16:07:03 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 9 Mar 2023 20:07:03 +0400 Subject: [PATCH 1 of 2] Core: connect() error log message made more verbose In-Reply-To: References: <55553146bd984be7e9e3.1675871646@ssafarly-nb> Message-ID: > On 22 Feb 2023, at 23:55, Maxim Dounin wrote: > > Hello! > > On Wed, Feb 22, 2023 at 03:37:51PM +0400, Sergey Kandaurov wrote: > >>> On 9 Feb 2023, at 12:11, Maxim Dounin wrote: >>> >>> [..] >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1675929813 -10800 >>> # Thu Feb 09 11:03:33 2023 +0300 >>> # Node ID 6b662855bf77c678a3954939aefe3fd4b4af4c70 >>> # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 >>> Syslog: introduced error log handler. >>> >>> This ensures that errors which happen during logging to syslog are logged >>> with proper context, such as "while logging to syslog" and the server name. >>> >>> Prodded by Safar Safarly. >>> >>> diff --git a/src/core/ngx_syslog.c b/src/core/ngx_syslog.c >>> --- a/src/core/ngx_syslog.c >>> +++ b/src/core/ngx_syslog.c >>> @@ -18,6 +18,7 @@ >>> static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer); >>> static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer); >>> static void ngx_syslog_cleanup(void *data); >>> +static u_char *ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len); >>> >>> >>> static char *facilities[] = { >>> @@ -66,6 +67,8 @@ ngx_syslog_process_conf(ngx_conf_t *cf, >>> ngx_str_set(&peer->tag, "nginx"); >>> } >>> >>> + peer->logp = &cf->cycle->new_log; >>> + >> >> You may want to reflect this change in the description. >> That's, this now follows other error logging by using log from >> the configuration that is going to be applied (cycle->new_log): >> >> src/core/ngx_log.c: dummy = &cf->cycle->new_log; >> src/mail/ngx_mail_core_module.c: conf->error_log = &cf->cycle->new_log; >> src/stream/ngx_stream_core_module.c: conf->error_log = &cf->cycle->new_log; >> src/http/ngx_http_core_module.c: conf->error_log = &cf->cycle->new_log; >> src/core/ngx_resolver.c: r->log = &cf->cycle->new_log; >> src/core/ngx_cycle.c: cycle->log = &cycle->new_log; >> >> Previously, before configuration was read, it used init_cycle configuration, >> that's builtin error_log on the NGX_LOG_NOTICE level, which means that its >> own ngx_send debug appeared only after the configuration was read, and other >> syslog error logging was limited to the builtin error log. > > The main goal of introduction of peer->logp is to avoid > re-initializing peer->log on each ngx_syslog_send() call. > Previous code used to initialize peer->conn.log on each call, > though now there are more things to initialize, and doing this on > each call makes the issue obvious. > > But yes, the resulting code is consistent with other uses and > matches how logging is expected to be used: when something is used > in a context of a configuration, it uses logging from the > configuration. > > A side note: it looks like ngx_syslog_add_header() uses > ngx_cycle->hostname. During initial startup this will use > init_cycle->hostname, which is empty. > > Looking at all this again I tend to think it might be a good idea > to ditch ngx_cycle usage in a separate patch (both for > ngx_cycle->log and ngx_cycle->hostname), and implement proper > logging context on top of it. Patches below. > >>> peer->conn.fd = (ngx_socket_t) -1; >>> >>> peer->conn.read = &ngx_syslog_dummy_event; >>> @@ -286,15 +289,19 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, >>> { >>> ssize_t n; >>> >>> + if (peer->log.handler == NULL) { >>> + peer->log = *peer->logp; >>> + peer->log.handler = ngx_syslog_log_error; >>> + peer->log.data = peer; >>> + peer->log.action = "logging to syslog"; >>> + } >>> + >>> if (peer->conn.fd == (ngx_socket_t) -1) { >>> if (ngx_syslog_init_peer(peer) != NGX_OK) { >>> return NGX_ERROR; >>> } >>> } >>> >>> - /* log syslog socket events with valid log */ >>> - peer->conn.log = ngx_cycle->log; >>> - >>> if (ngx_send) { >>> n = ngx_send(&peer->conn, buf, len); >>> >> >> One conversion to &peer->log is missing in the ngx_send error handling: >> >> diff --git a/src/core/ngx_syslog.c b/src/core/ngx_syslog.c >> --- a/src/core/ngx_syslog.c >> +++ b/src/core/ngx_syslog.c >> @@ -313,7 +313,7 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, >> if (n == NGX_ERROR) { >> >> if (ngx_close_socket(peer->conn.fd) == -1) { >> - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, >> + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, >> ngx_close_socket_n " failed"); >> } >> >> >> Other than that it looks good. > > Applied, thanks. > > Updated patches below. > > # HG changeset patch > # User Maxim Dounin > # Date 1677095349 -10800 > # Wed Feb 22 22:49:09 2023 +0300 > # Node ID a964a013031dabbdd05fb0637de496640070c416 > # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 > Syslog: removed usage of ngx_cycle->log and ngx_cycle->hostname. > [..] > > # HG changeset patch > # User Maxim Dounin > # Date 1677095351 -10800 > # Wed Feb 22 22:49:11 2023 +0300 > # Node ID 8f7c464c54e0b18bdb4d505866755cd600fac9fb > # Parent a964a013031dabbdd05fb0637de496640070c416 > Syslog: introduced error log handler. > [..] Both patches are good for me. -- Sergey Kandaurov From xeioex at nginx.com Thu Mar 9 21:22:25 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 09 Mar 2023 21:22:25 +0000 Subject: [njs] Version 0.7.11. Message-ID: details: https://hg.nginx.org/njs/rev/26dd3824b9f3 branches: changeset: 2069:26dd3824b9f3 user: Dmitry Volyntsev date: Thu Mar 09 09:26:03 2023 -0800 description: Version 0.7.11. diffstat: CHANGES | 31 +++++++++++++++++++++++++++++++ 1 files changed, 31 insertions(+), 0 deletions(-) diffs (38 lines): diff -r 10127d70e941 -r 26dd3824b9f3 CHANGES --- a/CHANGES Tue Mar 07 20:51:45 2023 -0800 +++ b/CHANGES Thu Mar 09 09:26:03 2023 -0800 @@ -1,3 +1,34 @@ +Changes with njs 0.7.11 9 Mar 2023 + nginx modules: + + *) Bugfix: added missed linking with libxml2 for the dynamic module. + The bug was introduced in 0.7.10. + + Core: + + *) Feature: added XMLNode API to modify XML documents. + + *) Change: removed XML_PARSE_DTDVALID during parsing of XML document + due to security implications. The issue was introduced + in 0.7.10. When XML_PARSE_DTDVALID is enabled, libxml2 parses and + executes external entities present inside an XML document. + + *) Bugfix: fixed the detection of await in arguments. + + *) Bugfix: fixed Error() instance dumping when "name" prop is not + primitive. + + *) Bugfix: fixed array instance with a getter property dumping. + + *) Bugfix: fixed njs_object_property() with NJS_WHITEOUT properties. + + *) Bugfix: fixed func instance dumping with "name" as getter. + + *) Bugfix: fixed attaching of a stack to an error object. + + *) Bugfix: fixed String.prototype.replace() with replacement containing + "$'", "$`". + Changes with njs 0.7.10 7 Feb 2023 nginx modules: From xeioex at nginx.com Thu Mar 9 21:22:27 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 09 Mar 2023 21:22:27 +0000 Subject: [njs] Added tag 0.7.11 for changeset 26dd3824b9f3 Message-ID: details: https://hg.nginx.org/njs/rev/7072e95495a4 branches: changeset: 2070:7072e95495a4 user: Dmitry Volyntsev date: Thu Mar 09 13:21:48 2023 -0800 description: Added tag 0.7.11 for changeset 26dd3824b9f3 diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff -r 26dd3824b9f3 -r 7072e95495a4 .hgtags --- a/.hgtags Thu Mar 09 09:26:03 2023 -0800 +++ b/.hgtags Thu Mar 09 13:21:48 2023 -0800 @@ -60,3 +60,4 @@ 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0.7.10 0000000000000000000000000000000000000000 0.7.10 3a1b46d51f040f5e7b9b81c3b2b312a2d272f0a3 0.7.10 +26dd3824b9f343e2768609c1b673f788e3a5e154 0.7.11 From mdounin at mdounin.ru Fri Mar 10 04:42:40 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 10 Mar 2023 07:42:40 +0300 Subject: [PATCH 2 of 2] HTTP/2: finalize request as bad if header validation fails In-Reply-To: <20230309104924.kqslt4d4nhe2absh@N00W24XTQX> References: <61bd779a868c4021c232.1677070527@vm-bsd.mdounin.ru> <20230308141518.7kjdcxumawtxiv6b@N00W24XTQX> <20230309104924.kqslt4d4nhe2absh@N00W24XTQX> Message-ID: Hello! On Thu, Mar 09, 2023 at 02:49:24PM +0400, Roman Arutyunyan wrote: > On Thu, Mar 09, 2023 at 03:40:26AM +0300, Maxim Dounin wrote: > > Hello! > > > > On Wed, Mar 08, 2023 at 06:15:18PM +0400, Roman Arutyunyan wrote: > > > > > Hi, > > > > > > On Wed, Feb 22, 2023 at 03:55:28PM +0300, Maxim Dounin wrote: > > > > # HG changeset patch > > > > # User Maxim Dounin > > > > # Date 1677070454 -10800 > > > > # Wed Feb 22 15:54:14 2023 +0300 > > > > # Node ID ec3819e66f40924efad183c291c850d9ccef16e7 > > > > # Parent 61bd779a868c4021c232dddfe7abda7e8ad32575 > > > > HTTP/2: finalize request as bad if header validation fails. > > > > > > > > Similarly to 7192:d5a535774861, this avoids spurious zero statuses > > > > in access.log, and in line with other header-related errors. > > > > > > This is what RFC 9113, 8.2.1. Field Validity, says about header field > > > validation: > > > > > > A request or response that contains a field that violates > > > any of these conditions MUST be treated as malformed (Section 8.1.1). > > > > > > Also (the previous RFC 7540 did not have this statement): > > > > > > When a request message violates one of these requirements, an implementation > > > SHOULD generate a 400 (Bad Request) status code (see Section 15.5.1 of [HTTP]) > > > > > > Considering this, responding with HTTP 400 is certainly an improvement compared > > > to the current code. Also, it allows to set a custom error page handler. > > > > > > However, not resetting the stream as before is a less obvious change. If we > > > consider nginx an intermediary, the RFC 9113 prescribes us to send stream reset: > > > > > > Intermediaries that process HTTP requests or responses (i.e., any > > > intermediary not acting as a tunnel) MUST NOT forward a malformed > > > request or response. Malformed requests or responses that are detected > > > MUST be treated as a stream error (Section 5.4.2) of type PROTOCOL_ERROR. > > > > > > This restriction probably makes sense for the intermediaries unable to handle > > > the error in a more flexible way. > > > > > > I like the change, but I'd like to hear what you think about the reset issue. > > > > We already do exactly the same for quite similar cases, as > > introduced in 7192:d5a535774861, for unknown and invalid > > pseudo-headers. This patch simply makes handling of > > errors detected by ngx_http_v2_validate_header() equivalent. > > Yes, I have read that discussion. It's seemed suspicious though that only the > pseudo-headers error was treated as HTTP 400 by that change, while > ngx_http_v2_validate_header(), which is just a few lines above, wasn't > affected. I think this was mostly because issues with pseudo-headers were seen in practice by Ruslan while working on HTTP/2 push, but he wasn't in position to further audit various other cases which can lead to similar behaviour. > > I does not look like returning 400 instead of sending RST_STREAM > > causes any issues. Further, given that HTTP/2 error handling is > > mostly unimplemented by clients, I tend to think that returning > > 400 instead of RST_STREAM is a better option. > > I personally like it too. > > > Note well that nothing in RFC 7540 actually requires to send > > RST_STREAM: while it says that (detected) malformed requests "MUST > > be treated as a stream error", handling of stream errors does not > > require to actually send RST_STREAM, as there is no such normative > > requirement in section 5.4.2. > > Indeed there's no requirement level here, which makes the whole paragraph > pointless: > > An endpoint that detects a stream error sends a RST_STREAM frame > (Section 6.4) that contains the stream identifier of the stream where > the error occurred. The RST_STREAM frame includes an error code that > indicates the type of error. > > > Further, RFC 9113 explicitly states > > that (https://www.rfc-editor.org/rfc/rfc9113#section-8.1.1): > > > > For malformed requests, a server MAY send an HTTP response prior > > to closing or resetting the stream. > > > > Note "closing or resetting", which implies that RST_STREAM is not > > mandatory. > > If the server is an origin server, and the request is malformed, stream error > is indeed not mandatory. But for an intermediary, it is. It's however not > clear, whether stream error should trigger RST_STREAM, considering the lack > of a requirement level in 5.4.2. Intermediaries "MUST not forward" such requests (and responses). The "MUST be treated as a stream error" requirement applies to all types of servers and clients, IMO. The following paragraph further clarifies how servers and clients are expected to behave (and this includes intermediaries acting as a server or as a client). > > While we might consider changing the code to generate RST_STREAM > > after sending the response in such cases, this does not seem to be > > needed (and might actually make things worse). In either case > > this doesn't look like something to be addressed in this patch > > series. > > The discussion is also relevant to HTTP/3 which may trigger either QUIC or > HTTP errors for malformed request streams. > > As I said before, I like HTTP errors more since they provide more flexibility > and are more compatible with the way nginx treats HTTP/1 requests. Please > commit the patch. Pushed to http://mdounin.ru/hg/nginx, relevant test changes are in http://mdounin.ru/hg/nginx-tests. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Mar 10 04:52:22 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 10 Mar 2023 07:52:22 +0300 Subject: [PATCH 1 of 2] Core: connect() error log message made more verbose In-Reply-To: References: <55553146bd984be7e9e3.1675871646@ssafarly-nb> Message-ID: Hello! On Thu, Mar 09, 2023 at 08:07:03PM +0400, Sergey Kandaurov wrote: > > On 22 Feb 2023, at 23:55, Maxim Dounin wrote: > > > > Hello! > > > > On Wed, Feb 22, 2023 at 03:37:51PM +0400, Sergey Kandaurov wrote: > > > >>> On 9 Feb 2023, at 12:11, Maxim Dounin wrote: > >>> > >>> [..] > >>> # HG changeset patch > >>> # User Maxim Dounin > >>> # Date 1675929813 -10800 > >>> # Thu Feb 09 11:03:33 2023 +0300 > >>> # Node ID 6b662855bf77c678a3954939aefe3fd4b4af4c70 > >>> # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 > >>> Syslog: introduced error log handler. > >>> > >>> This ensures that errors which happen during logging to syslog are logged > >>> with proper context, such as "while logging to syslog" and the server name. > >>> > >>> Prodded by Safar Safarly. > >>> > >>> diff --git a/src/core/ngx_syslog.c b/src/core/ngx_syslog.c > >>> --- a/src/core/ngx_syslog.c > >>> +++ b/src/core/ngx_syslog.c > >>> @@ -18,6 +18,7 @@ > >>> static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer); > >>> static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer); > >>> static void ngx_syslog_cleanup(void *data); > >>> +static u_char *ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len); > >>> > >>> > >>> static char *facilities[] = { > >>> @@ -66,6 +67,8 @@ ngx_syslog_process_conf(ngx_conf_t *cf, > >>> ngx_str_set(&peer->tag, "nginx"); > >>> } > >>> > >>> + peer->logp = &cf->cycle->new_log; > >>> + > >> > >> You may want to reflect this change in the description. > >> That's, this now follows other error logging by using log from > >> the configuration that is going to be applied (cycle->new_log): > >> > >> src/core/ngx_log.c: dummy = &cf->cycle->new_log; > >> src/mail/ngx_mail_core_module.c: conf->error_log = &cf->cycle->new_log; > >> src/stream/ngx_stream_core_module.c: conf->error_log = &cf->cycle->new_log; > >> src/http/ngx_http_core_module.c: conf->error_log = &cf->cycle->new_log; > >> src/core/ngx_resolver.c: r->log = &cf->cycle->new_log; > >> src/core/ngx_cycle.c: cycle->log = &cycle->new_log; > >> > >> Previously, before configuration was read, it used init_cycle configuration, > >> that's builtin error_log on the NGX_LOG_NOTICE level, which means that its > >> own ngx_send debug appeared only after the configuration was read, and other > >> syslog error logging was limited to the builtin error log. > > > > The main goal of introduction of peer->logp is to avoid > > re-initializing peer->log on each ngx_syslog_send() call. > > Previous code used to initialize peer->conn.log on each call, > > though now there are more things to initialize, and doing this on > > each call makes the issue obvious. > > > > But yes, the resulting code is consistent with other uses and > > matches how logging is expected to be used: when something is used > > in a context of a configuration, it uses logging from the > > configuration. > > > > A side note: it looks like ngx_syslog_add_header() uses > > ngx_cycle->hostname. During initial startup this will use > > init_cycle->hostname, which is empty. > > > > Looking at all this again I tend to think it might be a good idea > > to ditch ngx_cycle usage in a separate patch (both for > > ngx_cycle->log and ngx_cycle->hostname), and implement proper > > logging context on top of it. Patches below. > > > >>> peer->conn.fd = (ngx_socket_t) -1; > >>> > >>> peer->conn.read = &ngx_syslog_dummy_event; > >>> @@ -286,15 +289,19 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, > >>> { > >>> ssize_t n; > >>> > >>> + if (peer->log.handler == NULL) { > >>> + peer->log = *peer->logp; > >>> + peer->log.handler = ngx_syslog_log_error; > >>> + peer->log.data = peer; > >>> + peer->log.action = "logging to syslog"; > >>> + } > >>> + > >>> if (peer->conn.fd == (ngx_socket_t) -1) { > >>> if (ngx_syslog_init_peer(peer) != NGX_OK) { > >>> return NGX_ERROR; > >>> } > >>> } > >>> > >>> - /* log syslog socket events with valid log */ > >>> - peer->conn.log = ngx_cycle->log; > >>> - > >>> if (ngx_send) { > >>> n = ngx_send(&peer->conn, buf, len); > >>> > >> > >> One conversion to &peer->log is missing in the ngx_send error handling: > >> > >> diff --git a/src/core/ngx_syslog.c b/src/core/ngx_syslog.c > >> --- a/src/core/ngx_syslog.c > >> +++ b/src/core/ngx_syslog.c > >> @@ -313,7 +313,7 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, > >> if (n == NGX_ERROR) { > >> > >> if (ngx_close_socket(peer->conn.fd) == -1) { > >> - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, > >> + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, > >> ngx_close_socket_n " failed"); > >> } > >> > >> > >> Other than that it looks good. > > > > Applied, thanks. > > > > Updated patches below. > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1677095349 -10800 > > # Wed Feb 22 22:49:09 2023 +0300 > > # Node ID a964a013031dabbdd05fb0637de496640070c416 > > # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 > > Syslog: removed usage of ngx_cycle->log and ngx_cycle->hostname. > > [..] > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1677095351 -10800 > > # Wed Feb 22 22:49:11 2023 +0300 > > # Node ID 8f7c464c54e0b18bdb4d505866755cd600fac9fb > > # Parent a964a013031dabbdd05fb0637de496640070c416 > > Syslog: introduced error log handler. > > [..] > > Both patches are good for me. Thanks, pushed to http://mdounin.ru/hg/nginx. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Mar 10 05:00:04 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 10 Mar 2023 08:00:04 +0300 Subject: [PATCH 1 of 3] Tests: style Message-ID: # HG changeset patch # User Maxim Dounin # Date 1678424069 -10800 # Fri Mar 10 07:54:29 2023 +0300 # Node ID fdebeebd07b160f1d30e18d56e64dfb08570f8b1 # Parent 1e1d0f3874b0c5b1e399ec76b0198b5c9c265a36 Tests: style. diff --git a/lib/Test/Nginx/IMAP.pm b/lib/Test/Nginx/IMAP.pm --- a/lib/Test/Nginx/IMAP.pm +++ b/lib/Test/Nginx/IMAP.pm @@ -66,9 +66,9 @@ sub getline { } while (IO::Select->new($socket)->can_read(8)) { - $socket->blocking(0); + $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); - $socket->blocking(1); + $socket->blocking(1); last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/Stream.pm b/lib/Test/Nginx/Stream.pm --- a/lib/Test/Nginx/Stream.pm +++ b/lib/Test/Nginx/Stream.pm @@ -86,7 +86,7 @@ sub read { $s->blocking(0); if (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { $s->sysread($buf, 1024); - }; + } log_in($buf); return $buf; From mdounin at mdounin.ru Fri Mar 10 05:00:05 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 10 Mar 2023 08:00:05 +0300 Subject: [PATCH 2 of 3] Tests: handling of EAGAIN from sysread() with IO::Socket::SSL In-Reply-To: References: Message-ID: <49d12f8c4cf69e1cbe7f.1678424405@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1678424071 -10800 # Fri Mar 10 07:54:31 2023 +0300 # Node ID 49d12f8c4cf69e1cbe7feccae3b0ea1ac2ca8c2f # Parent fdebeebd07b160f1d30e18d56e64dfb08570f8b1 Tests: handling of EAGAIN from sysread() with IO::Socket::SSL. With IO::Socket::SSL, when select() reports that the socket is readable, reading from it might still fail with EAGAIN, since no application data is available in the socket. In particular, this might happen with TLSv1.3 when a session ticket is received after the handshake. Fix is to explicitly check for EAGAIN errors. diff --git a/lib/Test/Nginx/IMAP.pm b/lib/Test/Nginx/IMAP.pm --- a/lib/Test/Nginx/IMAP.pm +++ b/lib/Test/Nginx/IMAP.pm @@ -68,7 +68,9 @@ sub getline { while (IO::Select->new($socket)->can_read(8)) { $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); + my $again = !defined $n && $!{EAGAIN}; $socket->blocking(1); + next if $again; last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/POP3.pm b/lib/Test/Nginx/POP3.pm --- a/lib/Test/Nginx/POP3.pm +++ b/lib/Test/Nginx/POP3.pm @@ -68,7 +68,9 @@ sub getline { while (IO::Select->new($socket)->can_read(8)) { $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); + my $again = !defined $n && $!{EAGAIN}; $socket->blocking(1); + next if $again; last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/SMTP.pm b/lib/Test/Nginx/SMTP.pm --- a/lib/Test/Nginx/SMTP.pm +++ b/lib/Test/Nginx/SMTP.pm @@ -68,7 +68,9 @@ sub getline { while (IO::Select->new($socket)->can_read(8)) { $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); + my $again = !defined $n && $!{EAGAIN}; $socket->blocking(1); + next if $again; last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/Stream.pm b/lib/Test/Nginx/Stream.pm --- a/lib/Test/Nginx/Stream.pm +++ b/lib/Test/Nginx/Stream.pm @@ -84,8 +84,10 @@ sub read { $s = $self->{_socket}; $s->blocking(0); - if (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { - $s->sysread($buf, 1024); + while (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { + my $n = $s->sysread($buf, 1024); + next if !defined $n && $!{EAGAIN}; + last; } log_in($buf); From mdounin at mdounin.ru Fri Mar 10 05:00:06 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Fri, 10 Mar 2023 08:00:06 +0300 Subject: [PATCH 3 of 3] Tests: adapted session reuse tests to work with TLSv1.3 In-Reply-To: References: Message-ID: <946c3b39d1f9adf3f96f.1678424406@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1678424073 -10800 # Fri Mar 10 07:54:33 2023 +0300 # Node ID 946c3b39d1f9adf3f96f1c04963539ecd1c63a8f # Parent 49d12f8c4cf69e1cbe7feccae3b0ea1ac2ca8c2f Tests: adapted session reuse tests to work with TLSv1.3. In TLSv1.3, session tickets are sent after the handshake, and saving session right after the handshake is not going to work. To properly test session resumption, sessions are now saved after some data exchange. diff --git a/mail_ssl.t b/mail_ssl.t --- a/mail_ssl.t +++ b/mail_ssl.t @@ -182,24 +182,28 @@ my $s = Test::Nginx::IMAP->new(); my ($ssl, $ses); ($s, $ssl) = get_ssl_socket(8145); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(8145, $ses); is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); ($s, $ssl) = get_ssl_socket(8146); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(8146, $ses); is(Net::SSLeay::session_reused($ssl), 0, 'session not reused'); ($s, $ssl) = get_ssl_socket(8147); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(8147, $ses); is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); ($s, $ssl) = get_ssl_socket(8148); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(8148, $ses); diff --git a/stream_ssl.t b/stream_ssl.t --- a/stream_ssl.t +++ b/stream_ssl.t @@ -147,24 +147,32 @@ like(Net::SSLeay::read($ssl), qr/200 OK/ # ssl_session_cache ($s, $ssl) = get_ssl_socket(port(8080)); +Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8080), $ses); is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); ($s, $ssl) = get_ssl_socket(port(8082)); +Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8082), $ses); isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); ($s, $ssl) = get_ssl_socket(port(8083)); +Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8083), $ses); is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); ($s, $ssl) = get_ssl_socket(port(8084)); +Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); +Net::SSLeay::read($ssl); $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8084), $ses); From xeioex at nginx.com Fri Mar 10 22:16:02 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 10 Mar 2023 22:16:02 +0000 Subject: [njs] Version bump. Message-ID: details: https://hg.nginx.org/njs/rev/acacd5d3a276 branches: changeset: 2071:acacd5d3a276 user: Dmitry Volyntsev date: Fri Mar 10 13:28:21 2023 -0800 description: Version bump. diffstat: src/njs.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff -r 7072e95495a4 -r acacd5d3a276 src/njs.h --- a/src/njs.h Thu Mar 09 13:21:48 2023 -0800 +++ b/src/njs.h Fri Mar 10 13:28:21 2023 -0800 @@ -11,8 +11,8 @@ #include -#define NJS_VERSION "0.7.11" -#define NJS_VERSION_NUMBER 0x00070b +#define NJS_VERSION "0.7.12" +#define NJS_VERSION_NUMBER 0x00070c #include /* STDOUT_FILENO, STDERR_FILENO */ From xeioex at nginx.com Fri Mar 10 22:16:04 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 10 Mar 2023 22:16:04 +0000 Subject: [njs] Fetch: fixed Headers() constructor. Message-ID: details: https://hg.nginx.org/njs/rev/95f57822bdaa branches: changeset: 2072:95f57822bdaa user: Dmitry Volyntsev date: Fri Mar 10 14:15:29 2023 -0800 description: Fetch: fixed Headers() constructor. Previously, the 'guard' field of ngx_js_headers_t structure was uninitialized, that caused spurious exception "Error: cannot append to immutable object". This issue was introduced on 0.7.10. diffstat: nginx/ngx_js_fetch.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diffs (12 lines): diff -r acacd5d3a276 -r 95f57822bdaa nginx/ngx_js_fetch.c --- a/nginx/ngx_js_fetch.c Fri Mar 10 13:28:21 2023 -0800 +++ b/nginx/ngx_js_fetch.c Fri Mar 10 14:15:29 2023 -0800 @@ -860,6 +860,8 @@ ngx_js_ext_headers_constructor(njs_vm_t return NJS_ERROR; } + headers->guard = GUARD_NONE; + rc = ngx_list_init(&headers->header_list, pool, 4, sizeof(ngx_js_tb_elt_t)); if (rc != NGX_OK) { njs_vm_memory_error(vm); From mdounin at mdounin.ru Sat Mar 11 09:30:32 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 11 Mar 2023 12:30:32 +0300 Subject: [PATCH 2 of 3] Tests: handling of EAGAIN from sysread() with IO::Socket::SSL In-Reply-To: <49d12f8c4cf69e1cbe7f.1678424405@vm-bsd.mdounin.ru> References: <49d12f8c4cf69e1cbe7f.1678424405@vm-bsd.mdounin.ru> Message-ID: Hello! On Fri, Mar 10, 2023 at 08:00:05AM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1678424071 -10800 > # Fri Mar 10 07:54:31 2023 +0300 > # Node ID 49d12f8c4cf69e1cbe7feccae3b0ea1ac2ca8c2f > # Parent fdebeebd07b160f1d30e18d56e64dfb08570f8b1 > Tests: handling of EAGAIN from sysread() with IO::Socket::SSL. > > With IO::Socket::SSL, when select() reports that the socket is readable, > reading from it might still fail with EAGAIN, since no application data is > available in the socket. In particular, this might happen with TLSv1.3 > when a session ticket is received after the handshake. Fix is to explicitly > check for EAGAIN errors. Err, IO::Socket::SSL actually generates EWOULDBLOCK rather than EAGAIN, and this is important on some systems (notably Windows). s/EAGAIN/EWOULDBLOCK/g; # HG changeset patch # User Maxim Dounin # Date 1678522238 -10800 # Sat Mar 11 11:10:38 2023 +0300 # Node ID 0fefa04c5be1e8095072d176cdf847c7c3766fbf # Parent fdebeebd07b160f1d30e18d56e64dfb08570f8b1 Tests: handling of EWOULDBLOCK from sysread() with IO::Socket::SSL. With IO::Socket::SSL, when select() reports that the socket is readable, reading from it might still fail with EWOULDBLOCK, since no application data is available in the socket. In particular, this might happen with TLSv1.3 when a session ticket is received after the handshake. Fix is to explicitly check for EWOULDBLOCK errors. diff --git a/lib/Test/Nginx/IMAP.pm b/lib/Test/Nginx/IMAP.pm --- a/lib/Test/Nginx/IMAP.pm +++ b/lib/Test/Nginx/IMAP.pm @@ -68,7 +68,9 @@ sub getline { while (IO::Select->new($socket)->can_read(8)) { $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); + my $again = !defined $n && $!{EWOULDBLOCK}; $socket->blocking(1); + next if $again; last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/POP3.pm b/lib/Test/Nginx/POP3.pm --- a/lib/Test/Nginx/POP3.pm +++ b/lib/Test/Nginx/POP3.pm @@ -68,7 +68,9 @@ sub getline { while (IO::Select->new($socket)->can_read(8)) { $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); + my $again = !defined $n && $!{EWOULDBLOCK}; $socket->blocking(1); + next if $again; last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/SMTP.pm b/lib/Test/Nginx/SMTP.pm --- a/lib/Test/Nginx/SMTP.pm +++ b/lib/Test/Nginx/SMTP.pm @@ -68,7 +68,9 @@ sub getline { while (IO::Select->new($socket)->can_read(8)) { $socket->blocking(0); my $n = $socket->sysread(my $buf, 1024); + my $again = !defined $n && $!{EWOULDBLOCK}; $socket->blocking(1); + next if $again; last unless $n; $self->{_read_buffer} .= $buf; diff --git a/lib/Test/Nginx/Stream.pm b/lib/Test/Nginx/Stream.pm --- a/lib/Test/Nginx/Stream.pm +++ b/lib/Test/Nginx/Stream.pm @@ -84,8 +84,10 @@ sub read { $s = $self->{_socket}; $s->blocking(0); - if (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { - $s->sysread($buf, 1024); + while (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { + my $n = $s->sysread($buf, 1024); + next if !defined $n && $!{EWOULDBLOCK}; + last; } log_in($buf); -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Sat Mar 11 11:24:49 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 11 Mar 2023 14:24:49 +0300 Subject: [PATCH] Mail: fixed handling of blocked client read events in proxy Message-ID: # HG changeset patch # User Maxim Dounin # Date 1678533841 -10800 # Sat Mar 11 14:24:01 2023 +0300 # Node ID b97f2b983d1564d29280d03828503edca21a79ee # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 Mail: fixed handling of blocked client read events in proxy. When establishing a connection to the backend, nginx blocks reading from the client with ngx_mail_proxy_block_read(). Previously, such events were lost, and in some cases this resulted in connection hangs. Notably, this affected mail_imap_ssl.t on Windows, since the test closes connections after requesting authentication, but without waiting for any responses (so the connection close events might be lost). Fix is to post an event to read from the client after connecting to the backend if there were blocked events. diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -327,7 +327,9 @@ ngx_mail_proxy_pop3_handler(ngx_event_t c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); - if (s->buffer->pos < s->buffer->last) { + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { ngx_post_event(c->write, &ngx_posted_events); } @@ -486,7 +488,9 @@ ngx_mail_proxy_imap_handler(ngx_event_t c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); - if (s->buffer->pos < s->buffer->last) { + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { ngx_post_event(c->write, &ngx_posted_events); } @@ -821,7 +825,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); - if (s->buffer->pos < s->buffer->last) { + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { ngx_post_event(c->write, &ngx_posted_events); } From arut at nginx.com Mon Mar 13 12:54:16 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 16:54:16 +0400 Subject: [PATCH] Core: return error when the first byte is above 0xf5 in utf-8 In-Reply-To: References: Message-ID: <20230313125416.3w6cgovqtpm64jx2@N00W24XTQX> Hi, On Thu, Mar 02, 2023 at 02:51:11AM +0300, Maxim Dounin wrote: > Hello! > > On Thu, Feb 23, 2023 at 09:24:52AM +0900, u5h wrote: > > > Thanks reviewing! > > > > I agree with your early return strategy and I would reconsider that > > condition below. > > > > # HG changeset patch > > # User Yugo Horie > > # Date 1677107390 -32400 > > # Thu Feb 23 08:09:50 2023 +0900 > > # Node ID a3ca45d39fcfd32ca92a6bd25ec18b6359b90f1a > > # Parent f4653576ffcd286bed7229e18ee30ec3c713b4de > > Core: restrict the rule of utf-8 decode. > > > > The first byte being above 0xf8 which is referred to 5byte > > over length older utf-8 becomes invalid. > > Even the range of the first byte from 0xf5 to > > 0xf7 is valid in the term of the codepoint decoding. > > See https://datatracker.ietf.org/doc/html/rfc3629#section-4. > > > > diff -r f4653576ffcd -r a3ca45d39fcf src/core/ngx_string.c > > --- a/src/core/ngx_string.c Thu Feb 23 07:56:44 2023 +0900 > > +++ b/src/core/ngx_string.c Thu Feb 23 08:09:50 2023 +0900 > > @@ -1363,8 +1363,12 @@ > > uint32_t u, i, valid; > > > > u = **p; > > - > > - if (u >= 0xf0) { > > + if (u >= 0xf8) { > > + > > + (*p)++; > > + return 0xffffffff; > > + > > + } else if (u >= 0xf0) { > > > > u &= 0x07; > > valid = 0xffff; > > Slightly adjusted the commit log to better explain the issue (and > restored the accidentally removed empty line). Please take a look > if it seems good enough: > > # HG changeset patch > # User Yugo Horie > # Date 1677107390 -32400 > # Thu Feb 23 08:09:50 2023 +0900 > # Node ID a10210a45c8b6e6bb75e98b2fd64a80c184ae247 > # Parent 2acb00b9b5fff8a97523b659af4377fc605abe6e > Core: stricter UTF-8 handling in ngx_utf8_decode(). > An UTF-8 octet sequence cannot start with a 11111xxx byte (above 0xf8), It's not just above, 0xf8 is also included. > see https://datatracker.ietf.org/doc/html/rfc3629#section-3. Previously, > such bytes were accepted by ngx_utf8_decode() and misinterpreted as 11110xxx > bytes (as in a 4-byte sequence). While unlikely, this can potentially cause > issues. > > Fix is to explicitly reject such bytes in ngx_utf8_decode(). > > diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c > --- a/src/core/ngx_string.c > +++ b/src/core/ngx_string.c > @@ -1364,7 +1364,12 @@ ngx_utf8_decode(u_char **p, size_t n) > > u = **p; > > - if (u >= 0xf0) { > + if (u >= 0xf8) { > + > + (*p)++; > + return 0xffffffff; > + > + } else if (u >= 0xf0) { > > u &= 0x07; > valid = 0xffff; Looks good to me From arut at nginx.com Mon Mar 13 12:58:58 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:58:58 +0000 Subject: [nginx] Core: stricter UTF-8 handling in ngx_utf8_decode(). Message-ID: details: https://hg.nginx.org/nginx/rev/a10210a45c8b branches: changeset: 8142:a10210a45c8b user: Yugo Horie date: Thu Feb 23 08:09:50 2023 +0900 description: Core: stricter UTF-8 handling in ngx_utf8_decode(). An UTF-8 octet sequence cannot start with a 11111xxx byte (above 0xf8), see https://datatracker.ietf.org/doc/html/rfc3629#section-3. Previously, such bytes were accepted by ngx_utf8_decode() and misinterpreted as 11110xxx bytes (as in a 4-byte sequence). While unlikely, this can potentially cause issues. Fix is to explicitly reject such bytes in ngx_utf8_decode(). diffstat: src/core/ngx_string.c | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diffs (17 lines): diff -r 2acb00b9b5ff -r a10210a45c8b src/core/ngx_string.c --- a/src/core/ngx_string.c Thu Feb 23 20:50:03 2023 +0300 +++ b/src/core/ngx_string.c Thu Feb 23 08:09:50 2023 +0900 @@ -1364,7 +1364,12 @@ ngx_utf8_decode(u_char **p, size_t n) u = **p; - if (u >= 0xf0) { + if (u >= 0xf8) { + + (*p)++; + return 0xffffffff; + + } else if (u >= 0xf0) { u &= 0x07; valid = 0xffff; From arut at nginx.com Mon Mar 13 12:59:01 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:01 +0000 Subject: [nginx] SSL: switched to detect log level based on the last error. Message-ID: details: https://hg.nginx.org/nginx/rev/69c7df4fe5d3 branches: changeset: 8143:69c7df4fe5d3 user: Maxim Dounin date: Wed Mar 08 22:21:53 2023 +0300 description: SSL: switched to detect log level based on the last error. In some cases there might be multiple errors in the OpenSSL error queue, notably when a libcrypto call fails, and then the SSL layer generates an error itself. For example, the following errors were observed with OpenSSL 3.0.8 with TLSv1.3 enabled: SSL_do_handshake() failed (SSL: error:02800066:Diffie-Hellman routines::invalid public key error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:08000066:elliptic curve routines::invalid encoding error:0A000132:SSL routines::bad ecpoint) SSL_do_handshake() failed (SSL: error:0800006B:elliptic curve routines::point is not on curve error:0A000132:SSL routines::bad ecpoint) In such cases it seems to be better to determine logging level based on the last error in the error queue (the one added by the SSL layer, SSL_R_BAD_ECPOINT in all of the above example example errors). To do so, the ngx_ssl_connection_error() function was changed to use ERR_peek_last_error(). diffstat: src/event/ngx_event_openssl.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r a10210a45c8b -r 69c7df4fe5d3 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Feb 23 08:09:50 2023 +0900 +++ b/src/event/ngx_event_openssl.c Wed Mar 08 22:21:53 2023 +0300 @@ -3389,7 +3389,7 @@ ngx_ssl_connection_error(ngx_connection_ } else if (sslerr == SSL_ERROR_SSL) { - n = ERR_GET_REASON(ERR_peek_error()); + n = ERR_GET_REASON(ERR_peek_last_error()); /* handshake failures */ if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */ From arut at nginx.com Mon Mar 13 12:59:04 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:04 +0000 Subject: [nginx] SSL: logging levels of various errors reported with tlsfuzzer. Message-ID: details: https://hg.nginx.org/nginx/rev/6bee5e692579 branches: changeset: 8144:6bee5e692579 user: Maxim Dounin date: Wed Mar 08 22:21:59 2023 +0300 description: SSL: logging levels of various errors reported with tlsfuzzer. To further differentiate client-related errors and adjust logging levels of various SSL errors, nginx was tested with tlsfuzzer with multiple OpenSSL versions (3.1.0-beta1, 3.0.8, 1.1.1t, 1.1.0l, 1.0.2u, 1.0.1u, 1.0.0s, 0.9.8zh). The following errors were observed during tlsfuzzer runs with OpenSSL 3.0.8, and are clearly client-related: SSL_do_handshake() failed (SSL: error:0A000092:SSL routines::data length too long) SSL_do_handshake() failed (SSL: error:0A0000A0:SSL routines::length too short) SSL_do_handshake() failed (SSL: error:0A000124:SSL routines::bad legacy version) SSL_do_handshake() failed (SSL: error:0A000178:SSL routines::no shared signature algorithms) Accordingly, the SSL_R_DATA_LENGTH_TOO_LONG ("data length too long"), SSL_R_LENGTH_TOO_SHORT ("length too short"), SSL_R_BAD_LEGACY_VERSION ("bad legacy version"), and SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS ("no shared signature algorithms", misspelled as "sigature" in OpenSSL 1.0.2) errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 3.0.8 and with TLSv1.3 enabled: SSL_do_handshake() failed (SSL: error:0A00006F:SSL routines::bad digest length) SSL_do_handshake() failed (SSL: error:0A000070:SSL routines::missing sigalgs extension) SSL_do_handshake() failed (SSL: error:0A000096:SSL routines::encrypted length too long) SSL_do_handshake() failed (SSL: error:0A00010F:SSL routines::bad length) SSL_read() failed (SSL: error:0A00007A:SSL routines::bad key update) SSL_read() failed (SSL: error:0A000125:SSL routines::mixed handshake and non handshake data) Accordingly, the SSL_R_BAD_DIGEST_LENGTH ("bad digest length"), SSL_R_MISSING_SIGALGS_EXTENSION ("missing sigalgs extension"), SSL_R_ENCRYPTED_LENGTH_TOO_LONG ("encrypted length too long"), SSL_R_BAD_LENGTH ("bad length"), SSL_R_BAD_KEY_UPDATE ("bad key update"), and SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA ("mixed handshake and non handshake data") errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 1.1.1t: SSL_do_handshake() failed (SSL: error:14094091:SSL routines:ssl3_read_bytes:data between ccs and finished) SSL_do_handshake() failed (SSL: error:14094199:SSL routines:ssl3_read_bytes:too many warn alerts) SSL_read() failed (SSL: error:1408F0C6:SSL routines:ssl3_get_record:packet length too long) SSL_read() failed (SSL: error:14094085:SSL routines:ssl3_read_bytes:ccs received early) Accordingly, the SSL_R_CCS_RECEIVED_EARLY ("ccs received early"), SSL_R_DATA_BETWEEN_CCS_AND_FINISHED ("data between ccs and finished"), SSL_R_PACKET_LENGTH_TOO_LONG ("packet length too long"), and SSL_R_TOO_MANY_WARN_ALERTS ("too many warn alerts") errors are now logged at the "info" level. Additionally, the following errors were observed with OpenSSL 1.0.2u: SSL_do_handshake() failed (SSL: error:1407612A:SSL routines:SSL23_GET_CLIENT_HELLO:record too small) SSL_do_handshake() failed (SSL: error:1408C09A:SSL routines:ssl3_get_finished:got a fin before a ccs) Accordingly, the SSL_R_RECORD_TOO_SMALL ("record too small") and SSL_R_GOT_A_FIN_BEFORE_A_CCS ("got a fin before a ccs") errors are now logged at the "info" level. No additional client-related errors were observed while testing with OpenSSL 3.1.0-beta1, OpenSSL 1.1.0l, OpenSSL 1.0.1u, OpenSSL 1.0.0s, and OpenSSL 0.9.8zh. diffstat: src/event/ngx_event_openssl.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 41 insertions(+), 0 deletions(-) diffs (95 lines): diff -r 69c7df4fe5d3 -r 6bee5e692579 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Mar 08 22:21:53 2023 +0300 +++ b/src/event/ngx_event_openssl.c Wed Mar 08 22:21:59 2023 +0300 @@ -3402,16 +3402,35 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_BAD_EXTENSION || n == SSL_R_BAD_EXTENSION /* 110 */ #endif + || n == SSL_R_BAD_DIGEST_LENGTH /* 111 */ +#ifdef SSL_R_MISSING_SIGALGS_EXTENSION + || n == SSL_R_MISSING_SIGALGS_EXTENSION /* 112 */ +#endif #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM /* 118 */ #endif +#ifdef SSL_R_BAD_KEY_UPDATE + || n == SSL_R_BAD_KEY_UPDATE /* 122 */ +#endif || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ + || n == SSL_R_CCS_RECEIVED_EARLY /* 133 */ +#ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED + || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED /* 145 */ +#endif + || n == SSL_R_DATA_LENGTH_TOO_LONG /* 146 */ || n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ + || n == SSL_R_ENCRYPTED_LENGTH_TOO_LONG /* 150 */ || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */ || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */ +#ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS + || n == SSL_R_GOT_A_FIN_BEFORE_A_CCS /* 154 */ +#endif || n == SSL_R_HTTPS_PROXY_REQUEST /* 155 */ || n == SSL_R_HTTP_REQUEST /* 156 */ || n == SSL_R_LENGTH_MISMATCH /* 159 */ +#ifdef SSL_R_LENGTH_TOO_SHORT + || n == SSL_R_LENGTH_TOO_SHORT /* 160 */ +#endif #ifdef SSL_R_NO_CIPHERS_PASSED || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ #endif @@ -3421,6 +3440,9 @@ ngx_ssl_connection_error(ngx_connection_ #endif || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */ || n == SSL_R_NO_SHARED_CIPHER /* 193 */ +#ifdef SSL_R_PACKET_LENGTH_TOO_LONG + || n == SSL_R_PACKET_LENGTH_TOO_LONG /* 198 */ +#endif || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ #ifdef SSL_R_CLIENTHELLO_TLSEXT || n == SSL_R_CLIENTHELLO_TLSEXT /* 226 */ @@ -3446,6 +3468,7 @@ ngx_ssl_connection_error(ngx_connection_ || n == SSL_R_NO_SHARED_GROUP /* 266 */ #endif || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ + || n == SSL_R_BAD_LENGTH /* 271 */ || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY || n == SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY /* 291 */ @@ -3453,6 +3476,15 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_APPLICATION_DATA_ON_SHUTDOWN || n == SSL_R_APPLICATION_DATA_ON_SHUTDOWN /* 291 */ #endif +#ifdef SSL_R_BAD_LEGACY_VERSION + || n == SSL_R_BAD_LEGACY_VERSION /* 292 */ +#endif +#ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA + || n == SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA /* 293 */ +#endif +#ifdef SSL_R_RECORD_TOO_SMALL + || n == SSL_R_RECORD_TOO_SMALL /* 298 */ +#endif #ifdef SSL_R_BAD_ECPOINT || n == SSL_R_BAD_ECPOINT /* 306 */ #endif @@ -3470,12 +3502,21 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_INAPPROPRIATE_FALLBACK || n == SSL_R_INAPPROPRIATE_FALLBACK /* 373 */ #endif +#ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS + || n == SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS /* 376 */ +#endif +#ifdef SSL_R_NO_SHARED_SIGATURE_ALGORITHMS + || n == SSL_R_NO_SHARED_SIGATURE_ALGORITHMS /* 376 */ +#endif #ifdef SSL_R_CERT_CB_ERROR || n == SSL_R_CERT_CB_ERROR /* 377 */ #endif #ifdef SSL_R_VERSION_TOO_LOW || n == SSL_R_VERSION_TOO_LOW /* 396 */ #endif +#ifdef SSL_R_TOO_MANY_WARN_ALERTS + || n == SSL_R_TOO_MANY_WARN_ALERTS /* 409 */ +#endif #ifdef SSL_R_BAD_RECORD_TYPE || n == SSL_R_BAD_RECORD_TYPE /* 443 */ #endif From arut at nginx.com Mon Mar 13 12:59:06 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:06 +0000 Subject: [nginx] SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. Message-ID: details: https://hg.nginx.org/nginx/rev/64db9e50f6c5 branches: changeset: 8145:64db9e50f6c5 user: Maxim Dounin date: Wed Mar 08 22:22:34 2023 +0300 description: SSL: logging levels of errors observed with tlsfuzzer and LibreSSL. As tested with tlsfuzzer with LibreSSL 3.7.0, the following errors are certainly client-related: SSL_do_handshake() failed (SSL: error:14026073:SSL routines:ACCEPT_SR_CLNT_HELLO:bad packet length) SSL_do_handshake() failed (SSL: error:1402612C:SSL routines:ACCEPT_SR_CLNT_HELLO:ssl3 session id too long) SSL_do_handshake() failed (SSL: error:140380EA:SSL routines:ACCEPT_SR_KEY_EXCH:tls rsa encrypted value length is wrong) Accordingly, the SSL_R_BAD_PACKET_LENGTH ("bad packet length"), SSL_R_SSL3_SESSION_ID_TOO_LONG ("ssl3 session id too long"), SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG ("tls rsa encrypted value length is wrong") errors are now logged at the "info" level. diffstat: src/event/ngx_event_openssl.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diffs (31 lines): diff -r 6bee5e692579 -r 64db9e50f6c5 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Mar 08 22:21:59 2023 +0300 +++ b/src/event/ngx_event_openssl.c Wed Mar 08 22:22:34 2023 +0300 @@ -3406,6 +3406,7 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_MISSING_SIGALGS_EXTENSION || n == SSL_R_MISSING_SIGALGS_EXTENSION /* 112 */ #endif + || n == SSL_R_BAD_PACKET_LENGTH /* 115 */ #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM /* 118 */ #endif @@ -3453,6 +3454,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_CALLBACK_FAILED || n == SSL_R_CALLBACK_FAILED /* 234 */ #endif +#ifdef SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG + || n == SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG /* 234 */ +#endif #ifdef SSL_R_NO_APPLICATION_PROTOCOL || n == SSL_R_NO_APPLICATION_PROTOCOL /* 235 */ #endif @@ -3485,6 +3489,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_RECORD_TOO_SMALL || n == SSL_R_RECORD_TOO_SMALL /* 298 */ #endif +#ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG + || n == SSL_R_SSL3_SESSION_ID_TOO_LONG /* 300 */ +#endif #ifdef SSL_R_BAD_ECPOINT || n == SSL_R_BAD_ECPOINT /* 306 */ #endif From arut at nginx.com Mon Mar 13 12:59:09 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:09 +0000 Subject: [nginx] SSL: logging levels of errors observed with BoringSSL. Message-ID: details: https://hg.nginx.org/nginx/rev/b7d4bfd132d2 branches: changeset: 8146:b7d4bfd132d2 user: Maxim Dounin date: Wed Mar 08 22:22:47 2023 +0300 description: SSL: logging levels of errors observed with BoringSSL. As tested with tlsfuzzer with BoringSSL, the following errors are certainly client-related: SSL_do_handshake() failed (SSL: error:10000066:SSL routines:OPENSSL_internal:BAD_ALERT) SSL_do_handshake() failed (SSL: error:10000089:SSL routines:OPENSSL_internal:DECODE_ERROR) SSL_do_handshake() failed (SSL: error:100000dc:SSL routines:OPENSSL_internal:TOO_MANY_WARNING_ALERTS) SSL_do_handshake() failed (SSL: error:10000100:SSL routines:OPENSSL_internal:INVALID_COMPRESSION_LIST) SSL_do_handshake() failed (SSL: error:10000102:SSL routines:OPENSSL_internal:MISSING_KEY_SHARE) SSL_do_handshake() failed (SSL: error:1000010e:SSL routines:OPENSSL_internal:TOO_MUCH_SKIPPED_EARLY_DATA) SSL_read() failed (SSL: error:100000b6:SSL routines:OPENSSL_internal:NO_RENEGOTIATION) Accordingly, the SSL_R_BAD_ALERT, SSL_R_DECODE_ERROR, SSL_R_TOO_MANY_WARNING_ALERTS, SSL_R_INVALID_COMPRESSION_LIST, SSL_R_MISSING_KEY_SHARE, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA, and SSL_R_NO_RENEGOTIATION errors are now logged at the "info" level. diffstat: src/event/ngx_event_openssl.c | 21 +++++++++++++++++++++ 1 files changed, 21 insertions(+), 0 deletions(-) diffs (64 lines): diff -r 64db9e50f6c5 -r b7d4bfd132d2 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Mar 08 22:22:34 2023 +0300 +++ b/src/event/ngx_event_openssl.c Wed Mar 08 22:22:47 2023 +0300 @@ -3396,6 +3396,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_NO_SUITABLE_KEY_SHARE || n == SSL_R_NO_SUITABLE_KEY_SHARE /* 101 */ #endif +#ifdef SSL_R_BAD_ALERT + || n == SSL_R_BAD_ALERT /* 102 */ +#endif #ifdef SSL_R_BAD_KEY_SHARE || n == SSL_R_BAD_KEY_SHARE /* 108 */ #endif @@ -3415,6 +3418,9 @@ ngx_ssl_connection_error(ngx_connection_ #endif || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ || n == SSL_R_CCS_RECEIVED_EARLY /* 133 */ +#ifdef SSL_R_DECODE_ERROR + || n == SSL_R_DECODE_ERROR /* 137 */ +#endif #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED /* 145 */ #endif @@ -3432,6 +3438,9 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_LENGTH_TOO_SHORT || n == SSL_R_LENGTH_TOO_SHORT /* 160 */ #endif +#ifdef SSL_R_NO_RENEGOTIATION + || n == SSL_R_NO_RENEGOTIATION /* 182 */ +#endif #ifdef SSL_R_NO_CIPHERS_PASSED || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ #endif @@ -3445,6 +3454,9 @@ ngx_ssl_connection_error(ngx_connection_ || n == SSL_R_PACKET_LENGTH_TOO_LONG /* 198 */ #endif || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ +#ifdef SSL_R_TOO_MANY_WARNING_ALERTS + || n == SSL_R_TOO_MANY_WARNING_ALERTS /* 220 */ +#endif #ifdef SSL_R_CLIENTHELLO_TLSEXT || n == SSL_R_CLIENTHELLO_TLSEXT /* 226 */ #endif @@ -3467,11 +3479,20 @@ ngx_ssl_connection_error(ngx_connection_ #ifdef SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS || n == SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS /* 253 */ #endif +#ifdef SSL_R_INVALID_COMPRESSION_LIST + || n == SSL_R_INVALID_COMPRESSION_LIST /* 256 */ +#endif +#ifdef SSL_R_MISSING_KEY_SHARE + || n == SSL_R_MISSING_KEY_SHARE /* 258 */ +#endif || n == SSL_R_UNSUPPORTED_PROTOCOL /* 258 */ #ifdef SSL_R_NO_SHARED_GROUP || n == SSL_R_NO_SHARED_GROUP /* 266 */ #endif || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ +#ifdef SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA + || n == SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA /* 270 */ +#endif || n == SSL_R_BAD_LENGTH /* 271 */ || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY From arut at nginx.com Mon Mar 13 12:59:12 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:12 +0000 Subject: [nginx] HTTP/2: socket leak with "return 444" in error_page (ticket #2455). Message-ID: details: https://hg.nginx.org/nginx/rev/72627f1cf09e branches: changeset: 8147:72627f1cf09e user: Maxim Dounin date: Fri Mar 10 06:47:48 2023 +0300 description: HTTP/2: socket leak with "return 444" in error_page (ticket #2455). Similarly to ticket #274 (7354:1812f1d79d84), early request finalization without calling ngx_http_run_posted_requests() resulted in a connection hang (a socket leak) if the 400 (Bad Request) error was generated in ngx_http_v2_state_process_header() due to invalid request headers and "return 444" was used in error_page 400. diffstat: src/http/v2/ngx_http_v2.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diffs (28 lines): diff -r b7d4bfd132d2 -r 72627f1cf09e src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Wed Mar 08 22:22:47 2023 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Mar 10 06:47:48 2023 +0300 @@ -1730,6 +1730,7 @@ ngx_http_v2_state_process_header(ngx_htt size_t len; ngx_int_t rc; ngx_table_elt_t *h; + ngx_connection_t *fc; ngx_http_header_t *hh; ngx_http_request_t *r; ngx_http_v2_header_t *header; @@ -1789,6 +1790,7 @@ ngx_http_v2_state_process_header(ngx_htt } r = h2c->state.stream->request; + fc = r->connection; /* TODO Optimization: validate headers while parsing. */ if (ngx_http_v2_validate_header(r, header) != NGX_OK) { @@ -1886,6 +1888,8 @@ error: h2c->state.stream = NULL; + ngx_http_run_posted_requests(fc); + return ngx_http_v2_state_header_complete(h2c, pos, end); } From arut at nginx.com Mon Mar 13 12:59:15 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:15 +0000 Subject: [nginx] HTTP/2: finalize request as bad if header validation fails. Message-ID: details: https://hg.nginx.org/nginx/rev/9db24374123b branches: changeset: 8148:9db24374123b user: Maxim Dounin date: Fri Mar 10 06:47:53 2023 +0300 description: HTTP/2: finalize request as bad if header validation fails. Similarly to 7192:d5a535774861, this avoids spurious zero statuses in access.log, and in line with other header-related errors. diffstat: src/http/v2/ngx_http_v2.c | 9 +-------- 1 files changed, 1 insertions(+), 8 deletions(-) diffs (19 lines): diff -r 72627f1cf09e -r 9db24374123b src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Fri Mar 10 06:47:48 2023 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Mar 10 06:47:53 2023 +0300 @@ -1794,14 +1794,7 @@ ngx_http_v2_state_process_header(ngx_htt /* TODO Optimization: validate headers while parsing. */ if (ngx_http_v2_validate_header(r, header) != NGX_OK) { - if (ngx_http_v2_terminate_stream(h2c, h2c->state.stream, - NGX_HTTP_V2_PROTOCOL_ERROR) - == NGX_ERROR) - { - return ngx_http_v2_connection_error(h2c, - NGX_HTTP_V2_INTERNAL_ERROR); - } - + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); goto error; } From arut at nginx.com Mon Mar 13 12:59:18 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:18 +0000 Subject: [nginx] Syslog: removed usage of ngx_cycle->log and ngx_cycle->hostname. Message-ID: details: https://hg.nginx.org/nginx/rev/29adacffdefa branches: changeset: 8149:29adacffdefa user: Maxim Dounin date: Fri Mar 10 07:43:40 2023 +0300 description: Syslog: removed usage of ngx_cycle->log and ngx_cycle->hostname. During initial startup the ngx_cycle->hostname is not available, and previously this resulted in incorrect logging. Instead, hostname from the configuration being parsed is now preserved in the syslog peer structure and then used during logging. Similarly, ngx_cycle->log might not match the configuration where the syslog peer is defined if the configuration is not yet fully applied, and previously this resulted in unexpected logging of syslog errors and debug information. Instead, cf->cycle->new_log is now referenced in the syslog peer structure and used for logging, similarly to how it is done in other modules. diffstat: src/core/ngx_syslog.c | 21 +++++++++++---------- src/core/ngx_syslog.h | 19 ++++++++++++------- 2 files changed, 23 insertions(+), 17 deletions(-) diffs (118 lines): diff -r 9db24374123b -r 29adacffdefa src/core/ngx_syslog.c --- a/src/core/ngx_syslog.c Fri Mar 10 06:47:53 2023 +0300 +++ b/src/core/ngx_syslog.c Fri Mar 10 07:43:40 2023 +0300 @@ -66,6 +66,9 @@ ngx_syslog_process_conf(ngx_conf_t *cf, ngx_str_set(&peer->tag, "nginx"); } + peer->hostname = &cf->cycle->hostname; + peer->log = &cf->cycle->new_log; + peer->conn.fd = (ngx_socket_t) -1; peer->conn.read = &ngx_syslog_dummy_event; @@ -243,7 +246,7 @@ ngx_syslog_add_header(ngx_syslog_peer_t } return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time, - &ngx_cycle->hostname, &peer->tag); + peer->hostname, &peer->tag); } @@ -292,9 +295,6 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, } } - /* log syslog socket events with valid log */ - peer->conn.log = ngx_cycle->log; - if (ngx_send) { n = ngx_send(&peer->conn, buf, len); @@ -306,7 +306,7 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, if (n == NGX_ERROR) { if (ngx_close_socket(peer->conn.fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, ngx_close_socket_n " failed"); } @@ -324,24 +324,25 @@ ngx_syslog_init_peer(ngx_syslog_peer_t * fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0); if (fd == (ngx_socket_t) -1) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } if (ngx_nonblocking(fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, ngx_nonblocking_n " failed"); goto failed; } if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, "connect() failed"); goto failed; } peer->conn.fd = fd; + peer->conn.log = peer->log; /* UDP sockets are always ready to write */ peer->conn.write->ready = 1; @@ -351,7 +352,7 @@ ngx_syslog_init_peer(ngx_syslog_peer_t * failed: if (ngx_close_socket(fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, ngx_close_socket_n " failed"); } @@ -372,7 +373,7 @@ ngx_syslog_cleanup(void *data) } if (ngx_close_socket(peer->conn.fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, ngx_close_socket_n " failed"); } } diff -r 9db24374123b -r 29adacffdefa src/core/ngx_syslog.h --- a/src/core/ngx_syslog.h Fri Mar 10 06:47:53 2023 +0300 +++ b/src/core/ngx_syslog.h Fri Mar 10 07:43:40 2023 +0300 @@ -9,14 +9,19 @@ typedef struct { - ngx_uint_t facility; - ngx_uint_t severity; - ngx_str_t tag; + ngx_uint_t facility; + ngx_uint_t severity; + ngx_str_t tag; + + ngx_str_t *hostname; - ngx_addr_t server; - ngx_connection_t conn; - unsigned busy:1; - unsigned nohostname:1; + ngx_addr_t server; + ngx_connection_t conn; + + ngx_log_t *log; + + unsigned busy:1; + unsigned nohostname:1; } ngx_syslog_peer_t; From arut at nginx.com Mon Mar 13 12:59:21 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 13 Mar 2023 12:59:21 +0000 Subject: [nginx] Syslog: introduced error log handler. Message-ID: details: https://hg.nginx.org/nginx/rev/8771d35d55d0 branches: changeset: 8150:8771d35d55d0 user: Maxim Dounin date: Fri Mar 10 07:43:50 2023 +0300 description: Syslog: introduced error log handler. This ensures that errors which happen during logging to syslog are logged with proper context, such as "while logging to syslog" and the server name. Prodded by Safar Safarly. diffstat: src/core/ngx_syslog.c | 47 +++++++++++++++++++++++++++++++++++++++-------- src/core/ngx_syslog.h | 3 ++- 2 files changed, 41 insertions(+), 9 deletions(-) diffs (127 lines): diff -r 29adacffdefa -r 8771d35d55d0 src/core/ngx_syslog.c --- a/src/core/ngx_syslog.c Fri Mar 10 07:43:40 2023 +0300 +++ b/src/core/ngx_syslog.c Fri Mar 10 07:43:50 2023 +0300 @@ -18,6 +18,7 @@ static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer); static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer); static void ngx_syslog_cleanup(void *data); +static u_char *ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len); static char *facilities[] = { @@ -67,7 +68,7 @@ ngx_syslog_process_conf(ngx_conf_t *cf, } peer->hostname = &cf->cycle->hostname; - peer->log = &cf->cycle->new_log; + peer->logp = &cf->cycle->new_log; peer->conn.fd = (ngx_socket_t) -1; @@ -289,6 +290,13 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, { ssize_t n; + if (peer->log.handler == NULL) { + peer->log = *peer->logp; + peer->log.handler = ngx_syslog_log_error; + peer->log.data = peer; + peer->log.action = "logging to syslog"; + } + if (peer->conn.fd == (ngx_socket_t) -1) { if (ngx_syslog_init_peer(peer) != NGX_OK) { return NGX_ERROR; @@ -306,7 +314,7 @@ ngx_syslog_send(ngx_syslog_peer_t *peer, if (n == NGX_ERROR) { if (ngx_close_socket(peer->conn.fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, ngx_close_socket_n " failed"); } @@ -324,25 +332,25 @@ ngx_syslog_init_peer(ngx_syslog_peer_t * fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0); if (fd == (ngx_socket_t) -1) { - ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } if (ngx_nonblocking(fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, ngx_nonblocking_n " failed"); goto failed; } if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) { - ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, "connect() failed"); goto failed; } peer->conn.fd = fd; - peer->conn.log = peer->log; + peer->conn.log = &peer->log; /* UDP sockets are always ready to write */ peer->conn.write->ready = 1; @@ -352,7 +360,7 @@ ngx_syslog_init_peer(ngx_syslog_peer_t * failed: if (ngx_close_socket(fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, ngx_close_socket_n " failed"); } @@ -373,7 +381,30 @@ ngx_syslog_cleanup(void *data) } if (ngx_close_socket(peer->conn.fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, peer->log, ngx_socket_errno, + ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno, ngx_close_socket_n " failed"); } } + + +static u_char * +ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_syslog_peer_t *peer; + + p = buf; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + } + + peer = log->data; + + if (peer) { + p = ngx_snprintf(p, len, ", server: %V", &peer->server.name); + } + + return p; +} diff -r 29adacffdefa -r 8771d35d55d0 src/core/ngx_syslog.h --- a/src/core/ngx_syslog.h Fri Mar 10 07:43:40 2023 +0300 +++ b/src/core/ngx_syslog.h Fri Mar 10 07:43:50 2023 +0300 @@ -18,7 +18,8 @@ typedef struct { ngx_addr_t server; ngx_connection_t conn; - ngx_log_t *log; + ngx_log_t log; + ngx_log_t *logp; unsigned busy:1; unsigned nohostname:1; From dvershinin at cloudlinux.com Tue Mar 14 11:09:48 2023 From: dvershinin at cloudlinux.com (Danila Vershinin) Date: Tue, 14 Mar 2023 19:09:48 +0800 Subject: [PATCH] HTTP: allow tuning test_types_hash Message-ID: # HG changeset patch # User Danila Vershinin # Date 1678790847 -28800 # Tue Mar 14 18:47:27 2023 +0800 # Node ID ccb905b26f0efcd6eb3814ca9900616bb8095bec # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 HTTP: allow tuning test_types_hash. In edge cases, with admittedly unnecessary long or numerous MIME type entries in config, users are presented with a startup error message asking to adjust test_types_hash_* directives which do not actually exist. This adds those directives. diff -r 8771d35d55d0 -r ccb905b26f0e src/http/ngx_http.c --- a/src/http/ngx_http.c Fri Mar 10 07:43:50 2023 +0300 +++ b/src/http/ngx_http.c Tue Mar 14 18:47:27 2023 +0800 @@ -2083,6 +2083,9 @@ ngx_str_t *default_types) { ngx_hash_init_t hash; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); if (*keys) { @@ -2092,8 +2095,8 @@ hash.hash = types_hash; hash.key = NULL; - hash.max_size = 2048; - hash.bucket_size = 64; + hash.max_size = cmcf->test_types_hash_max_size; + hash.bucket_size = cmcf->test_types_hash_bucket_size; hash.name = "test_types_hash"; hash.pool = cf->pool; hash.temp_pool = NULL; @@ -2122,8 +2125,8 @@ hash.hash = prev_types_hash; hash.key = NULL; - hash.max_size = 2048; - hash.bucket_size = 64; + hash.max_size = cmcf->test_types_hash_max_size; + hash.bucket_size = cmcf->test_types_hash_bucket_size; hash.name = "test_types_hash"; hash.pool = cf->pool; hash.temp_pool = NULL; diff -r 8771d35d55d0 -r ccb905b26f0e src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Fri Mar 10 07:43:50 2023 +0300 +++ b/src/http/ngx_http_core_module.c Tue Mar 14 18:47:27 2023 +0800 @@ -778,6 +778,20 @@ #endif + { ngx_string("test_types_hash_bucket_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_core_main_conf_t, test_types_hash_bucket_size), + NULL }, + + { ngx_string("test_types_hash_max_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_core_main_conf_t, test_types_hash_max_size), + NULL }, + ngx_null_command }; @@ -3401,6 +3415,9 @@ cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT; cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT; + cmcf->test_types_hash_max_size = NGX_CONF_UNSET_UINT; + cmcf->test_types_hash_bucket_size = NGX_CONF_UNSET_UINT; + return cmcf; } @@ -3424,6 +3441,12 @@ cmcf->variables_hash_bucket_size = ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); + ngx_conf_init_uint_value(cmcf->test_types_hash_max_size, 2048); + ngx_conf_init_uint_value(cmcf->test_types_hash_bucket_size, 64); + + cmcf->test_types_hash_bucket_size = + ngx_align(cmcf->test_types_hash_bucket_size, ngx_cacheline_size); + if (cmcf->ncaptures) { cmcf->ncaptures = (cmcf->ncaptures + 1) * 3; } diff -r 8771d35d55d0 -r ccb905b26f0e src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Fri Mar 10 07:43:50 2023 +0300 +++ b/src/http/ngx_http_core_module.h Tue Mar 14 18:47:27 2023 +0800 @@ -168,6 +168,9 @@ ngx_uint_t variables_hash_max_size; ngx_uint_t variables_hash_bucket_size; + ngx_uint_t test_types_hash_bucket_size; + ngx_uint_t test_types_hash_max_size; + ngx_hash_keys_arrays_t *variables_keys; ngx_array_t *ports; From mdounin at mdounin.ru Tue Mar 14 12:09:33 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 14 Mar 2023 15:09:33 +0300 Subject: [PATCH] HTTP: allow tuning test_types_hash In-Reply-To: References: Message-ID: Hello! On Tue, Mar 14, 2023 at 07:09:48PM +0800, Danila Vershinin via nginx-devel wrote: > # HG changeset patch > # User Danila Vershinin > # Date 1678790847 -28800 > # Tue Mar 14 18:47:27 2023 +0800 > # Node ID ccb905b26f0efcd6eb3814ca9900616bb8095bec > # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 > HTTP: allow tuning test_types_hash. > > In edge cases, with admittedly unnecessary long or numerous MIME type > entries in config, users are presented with a startup error message > asking to adjust test_types_hash_* directives which do not actually > exist. This adds those directives. You may want to be more specific about the edge cases. Thanks in advance. In the past, the usually observed "edge cases" were mostly about misconfigurations, and not real edge cases. The rest is probably to be addressed without introduction of additional directives. See the following related tickets: https://trac.nginx.org/nginx/ticket/203 https://trac.nginx.org/nginx/ticket/1537 https://trac.nginx.org/nginx/ticket/1446 > > diff -r 8771d35d55d0 -r ccb905b26f0e src/http/ngx_http.c > --- a/src/http/ngx_http.c Fri Mar 10 07:43:50 2023 +0300 > +++ b/src/http/ngx_http.c Tue Mar 14 18:47:27 2023 +0800 > @@ -2083,6 +2083,9 @@ > ngx_str_t *default_types) > { > ngx_hash_init_t hash; > + ngx_http_core_main_conf_t *cmcf; It looks like there is a style issue here. [...] -- Maxim Dounin http://mdounin.ru/ From nickrbogdanov at gmail.com Tue Mar 14 15:29:10 2023 From: nickrbogdanov at gmail.com (=?iso-8859-1?q?Nick_Bogdanov?=) Date: Tue, 14 Mar 2023 08:29:10 -0700 Subject: [PATCH] Add ssl_provider directive (ticket #2449) Message-ID: # HG changeset patch # User Nick Bogdanov # Date 1677975659 28800 # Sat Mar 04 16:20:59 2023 -0800 # Node ID a00302750c65e122292da5094093e45f2f644600 # Parent cffaf3f2eec8fd33605c2a37814f5ffc30371989 Add ssl_provider directive (ticket #2449). This change allows nginx to load modules that use the new OpenSSL Provider interface. My primary use case involves securing the webserver's private TLS key using a TPM2 chip, so it can't be stolen if the server is compromised. The way I tested this is as follows: 1. Install basic TPM2 support. On Ubuntu 22.04 I used apt install tpm2-tools tpm2-abrmd libtss2-tcti-tabrmd0 2. Install https://github.com/tpm2-software/tpm2-openssl . Version 1.2.0-rc0 or higher is required. At the time of this writing, it's likely you'll have to build from source. 3. Generate a parent key on your TPM (one-time setup): tpm2_createprimary -C o -g sha256 -G ecc -c primary_sh.ctx tpm2_evictcontrol -C o -c 0x81000001 || true tpm2_evictcontrol -C o -c primary_sh.ctx 0x81000001 4. Generate a TPM-backed RSA privkey and a corresponding self-signed x509 cert: openssl genpkey -provider tpm2 -algorithm RSA -pkeyopt parent:0x81000001 -out rsakey.pem openssl req -provider tpm2 -provider default -x509 -subj "/C=GB/CN=foo" -key rsakey.pem -out rsacert.pem rsakey.pem will start with "-----BEGIN TSS2 PRIVATE KEY-----" to indicate that the key material is encrypted with a key that is only available inside the TPM chip. 5. At the start of nginx.conf, tell nginx to use the tpm2 provider first, and then fall back to the default provider for unsupported operations: ssl_provider tpm2; ssl_provider default; 6. Inside a "server {" section for an existing TLS server, point nginx to the new TPM-backed cert and key: ssl_certificate /tmp/rsacert.pem; ssl_certificate_key /tmp/rsakey.pem; If the ssl_provider option took effect, it will be able to recognize the new TSS2 rsakey.pem and instruct the TPM chip to handle the signing operation during the TLS handshake. In my setup, the initial handshake time increased from 5ms->67ms after enabling TPM2. More details here: https://github.com/tpm2-software/tpm2-openssl/issues/58 diff -r cffaf3f2eec8 -r a00302750c65 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Thu Feb 02 23:38:48 2023 +0300 +++ b/contrib/vim/syntax/nginx.vim Sat Mar 04 16:20:59 2023 -0800 @@ -620,6 +620,7 @@ syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread syn keyword ngxDirective contained ssl_protocols +syn keyword ngxDirective contained ssl_provider syn keyword ngxDirective contained ssl_reject_handshake syn keyword ngxDirective contained ssl_session_cache syn keyword ngxDirective contained ssl_session_ticket_key diff -r cffaf3f2eec8 -r a00302750c65 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Feb 02 23:38:48 2023 +0300 +++ b/src/event/ngx_event_openssl.c Sat Mar 04 16:20:59 2023 -0800 @@ -90,6 +90,7 @@ static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -102,6 +103,13 @@ 0, NULL }, + { ngx_string("ssl_provider"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_openssl_provider, + 0, + 0, + NULL }, + ngx_null_command }; @@ -5939,6 +5947,26 @@ #endif } +static char * +ngx_openssl_provider(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#ifdef OPENSSL_PROVIDER_SUPPORT + ngx_str_t *value = cf->args->elts; + + if (OSSL_PROVIDER_load(NULL, (char *)value[1].data) == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "OSSL_PROVIDER_load(\"%V\") failed", &value[1]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + +#else + + return "is not supported"; + +#endif +} static void ngx_openssl_exit(ngx_cycle_t *cycle) diff -r cffaf3f2eec8 -r a00302750c65 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Thu Feb 02 23:38:48 2023 +0300 +++ b/src/event/ngx_event_openssl.h Sat Mar 04 16:20:59 2023 -0800 @@ -28,6 +28,10 @@ #ifndef OPENSSL_NO_OCSP #include #endif +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +#include +#define OPENSSL_PROVIDER_SUPPORT +#endif #include #include #include From sshedi at vmware.com Thu Mar 16 15:26:21 2023 From: sshedi at vmware.com (Shreenidhi Shedi) Date: Thu, 16 Mar 2023 15:26:21 +0000 Subject: Changing ownership of proxy_temp and other temp directories Message-ID: Hi All, I have hosted a nginx server instance and the temp directories are created under /etc/nginx/ $ ls -ld /etc/nginx/*_temp drwx------ 2 nobody root 4096 Mar 16 15:21 /etc/nginx/client_body_temp drwx------ 2 nobody root 4096 Mar 16 15:21 /etc/nginx/fastcgi_temp drwx------ 2 nobody root 4096 Mar 16 15:21 /etc/nginx/proxy_temp drwx------ 2 nobody root 4096 Mar 16 15:21 /etc/nginx/scgi_temp drwx------ 2 nobody root 4096 Mar 16 15:21 /etc/nginx/uwsgi_temp And I updated to a newer version of nginx which runs in "nginx" user context and after that these directory ownership is getting changed to nginx:root but the issue is, it happens only on these top directories and not directories within these temp directories. I did strace on the same to confirm my theory. mkdir("/etc/nginx/client_body_temp", 0700) = -1 EEXIST (File exists) stat("/etc/nginx/client_body_temp", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0 chown("/etc/nginx/client_body_temp", 997, -1) = 0 mkdir("/etc/nginx/proxy_temp", 0700) = -1 EEXIST (File exists) stat("/etc/nginx/proxy_temp", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0 chown("/etc/nginx/proxy_temp", 997, -1) = 0 mkdir("/etc/nginx/fastcgi_temp", 0700) = -1 EEXIST (File exists) stat("/etc/nginx/fastcgi_temp", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0 chown("/etc/nginx/fastcgi_temp", 997, -1) = 0 mkdir("/etc/nginx/uwsgi_temp", 0700) = -1 EEXIST (File exists) stat("/etc/nginx/uwsgi_temp", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0 chown("/etc/nginx/uwsgi_temp", 997, -1) = 0 mkdir("/etc/nginx/scgi_temp", 0700) = -1 EEXIST (File exists) stat("/etc/nginx/scgi_temp", {st_mode=S_IFDIR|0700, st_size=4096, ...}) = 0 Now the issue is, why chown happens only on top directory and not recursively on all files and directories inside them? Is this a bug or is it fixed in latest version of nginx? I'm currently using nginx-1.22.0. Any help would be appreciated. Thanks. -- Shedi -------------- next part -------------- An HTML attachment was scrubbed... URL: From osa at freebsd.org.ru Thu Mar 16 17:00:31 2023 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Thu, 16 Mar 2023 20:00:31 +0300 Subject: Changing ownership of proxy_temp and other temp directories In-Reply-To: References: Message-ID: Hi there, On Thu, Mar 16, 2023 at 03:26:21PM +0000, Shreenidhi Shedi via nginx-devel wrote: > > I have hosted a nginx server instance and the temp directories are created under /etc/nginx/ The nginx-devel mailing list is for nginx development purposes, so I'd suggest to send this request to nginx@ mailing list. Thank you. -- Sergey A. Osokin From mdounin at mdounin.ru Sat Mar 18 14:14:57 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:14:57 +0300 Subject: [PATCH 02 of 20] Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 In-Reply-To: References: Message-ID: <125fb8461d88a81a62cc.1679148897@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679105692 -10800 # Sat Mar 18 05:14:52 2023 +0300 # Node ID 125fb8461d88a81a62ccb40d0e205a01ecc759f5 # Parent 86c394a226d2a7d463da7a1b7e88375c71c0c69b Tests: LibreSSL and BoringSSL session reuse with TLSv1.3. LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL with TLSv1.3 only supports session tickets, but not server-side session cache. diff --git a/ssl.t b/ssl.t --- a/ssl.t +++ b/ssl.t @@ -185,6 +185,8 @@ local $TODO = 'no TLSv1.3 sessions, old if $Net::SSLeay::VERSION < 1.88 && test_tls13(); local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); @@ -211,8 +213,17 @@ like(get('/', 8086, $ctx), qr/^body \.$/ $ctx = get_ssl_context(); like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); +local $TODO = 'no TLSv1.3 sessions ids in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); +} + unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); diff --git a/ssl_session_reuse.t b/ssl_session_reuse.t --- a/ssl_session_reuse.t +++ b/ssl_session_reuse.t @@ -175,14 +175,22 @@ local $TODO = 'no TLSv1.3 sessions, old if $Net::SSLeay::VERSION < 1.88 && test_tls13(); local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); is(test_reuse(8443), 1, 'tickets reused'); is(test_reuse(8444), 1, 'tickets and cache reused'); + +TODO: { +local $TODO = 'no TLSv1.3 session cache in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + is(test_reuse(8445), 1, 'cache shared reused'); is(test_reuse(8446), 1, 'cache builtin reused'); is(test_reuse(8447), 1, 'cache builtin size reused'); } +} is(test_reuse(8448), 0, 'cache none not reused'); is(test_reuse(8449), 0, 'cache off not reused'); From mdounin at mdounin.ru Sat Mar 18 14:14:59 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:14:59 +0300 Subject: [PATCH 04 of 20] Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail In-Reply-To: References: Message-ID: <3ab3b2d1c2e67bc1f05e.1679148899@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679107858 -10800 # Sat Mar 18 05:50:58 2023 +0300 # Node ID 3ab3b2d1c2e67bc1f05e386218ceb08da873a477 # Parent 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail. LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL with TLSv1.3 only supports session tickets, but not server-side session cache. diff --git a/mail_ssl_session_reuse.t b/mail_ssl_session_reuse.t --- a/mail_ssl_session_reuse.t +++ b/mail_ssl_session_reuse.t @@ -144,16 +144,34 @@ my ($ssl, $ses); # - only cache none # - only cache off +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + is(test_reuse(8993), 1, 'tickets reused'); is(test_reuse(8994), 1, 'tickets and cache reused'); + +TODO: { +local $TODO = 'no TLSv1.3 session cache in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + is(test_reuse(8995), 1, 'cache shared reused'); is(test_reuse(8996), 1, 'cache builtin reused'); is(test_reuse(8997), 1, 'cache builtin size reused'); + +} +} + is(test_reuse(8998), 0, 'cache none not reused'); is(test_reuse(8999), 0, 'cache off not reused'); ############################################################################### +sub test_tls13 { + my ($s, $ssl) = get_ssl_socket(8993); + return (Net::SSLeay::version($ssl) > 0x303); +} + sub test_reuse { my ($port) = @_; my ($s, $ssl) = get_ssl_socket($port); From mdounin at mdounin.ru Sat Mar 18 14:14:55 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:14:55 +0300 Subject: [PATCH 00 of 20] tests suite fixes for TLSv1.3 Message-ID: Hello! Here are patch series for the test suite to address test failures observed with TLSv1.3 enabled with BoringSSL and LibreSSL. Short summary of the issues seen: - BoringSSL with TLSv1.3 does not support session reuse via server-side session cache, only with tickets. - BoringSSL with TLSv1.3 does not provide $ssl_session_id. - LibreSSL with TLSv1.3 does not support session reuse. - LibreSSL with TLSv1.3 fails to negotiate certificates based on signature algorithms supported by the client, and fails with "missing rsa certificate" and "unknown pkey type" errors. - LibreSSL with TLSv1.3 does not send CA lists to the client. -- Maxim Dounin From mdounin at mdounin.ru Sat Mar 18 14:14:58 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:14:58 +0300 Subject: [PATCH 03 of 20] Tests: separate SSL session reuse tests in mail In-Reply-To: References: Message-ID: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679107816 -10800 # Sat Mar 18 05:50:16 2023 +0300 # Node ID 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb # Parent 125fb8461d88a81a62ccb40d0e205a01ecc759f5 Tests: separate SSL session reuse tests in mail. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. diff --git a/mail_ssl.t b/mail_ssl.t --- a/mail_ssl.t +++ b/mail_ssl.t @@ -37,7 +37,7 @@ eval { exists &Net::SSLeay::P_alpn_selec plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) - ->has_daemon('openssl')->plan(22); + ->has_daemon('openssl')->plan(18); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -51,44 +51,25 @@ events { mail { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; ssl_password_file password; auth_http http://127.0.0.1:8080; # unused - ssl_session_cache none; - server { listen 127.0.0.1:8143; listen 127.0.0.1:8145 ssl; protocol imap; - - ssl_session_cache builtin; } server { - listen 127.0.0.1:8146 ssl; - protocol imap; - - ssl_session_cache off; - } - - server { - listen 127.0.0.1:8147; + listen 127.0.0.1:8148; protocol imap; # Special case for enabled "ssl" directive. ssl on; - ssl_session_cache builtin:1000; - } - server { - listen 127.0.0.1:8148 ssl; - protocol imap; - - ssl_session_cache shared:SSL:1m; ssl_certificate_key inherits.key; ssl_certificate inherits.crt; } @@ -169,46 +150,16 @@ open STDERR, ">&", \*OLDERR; ############################################################################### +my ($s, $ssl, $ses); + # simple tests to ensure that nothing broke with ssl_password_file directive -my $s = Test::Nginx::IMAP->new(); +$s = Test::Nginx::IMAP->new(); $s->ok('greeting'); $s->send('1 AUTHENTICATE LOGIN'); $s->check(qr/\+ VXNlcm5hbWU6/, 'login'); -# ssl_session_cache - -my ($ssl, $ses); - -($s, $ssl) = get_ssl_socket(8145); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8145, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(8146); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8146, $ses); -is(Net::SSLeay::session_reused($ssl), 0, 'session not reused'); - -($s, $ssl) = get_ssl_socket(8147); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8147, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(8148); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8148, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); - # ssl_certificate inheritance ($s, $ssl) = get_ssl_socket(8145); diff --git a/mail_ssl.t b/mail_ssl_session_reuse.t copy from mail_ssl.t copy to mail_ssl_session_reuse.t --- a/mail_ssl.t +++ b/mail_ssl_session_reuse.t @@ -1,6 +1,7 @@ #!/usr/bin/perl # (C) Andrey Zelenkov +# (C) Maxim Dounin # (C) Nginx, Inc. # Tests for mail ssl module. @@ -33,11 +34,8 @@ eval { }; plan(skip_all => 'Net::SSLeay not installed') if $@; -eval { exists &Net::SSLeay::P_alpn_selected or die; }; -plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; - -my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) - ->has_daemon('openssl')->plan(22); +my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap/) + ->has_daemon('openssl')->plan(7); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -49,90 +47,62 @@ events { } mail { - ssl_certificate_key localhost.key; - ssl_certificate localhost.crt; - ssl_session_tickets off; + auth_http http://127.0.0.1:8080; - ssl_password_file password; - - auth_http http://127.0.0.1:8080; # unused - - ssl_session_cache none; + ssl_certificate localhost.crt; + ssl_certificate_key localhost.key; server { - listen 127.0.0.1:8143; - listen 127.0.0.1:8145 ssl; - protocol imap; - - ssl_session_cache builtin; + listen 127.0.0.1:8993 ssl; + protocol imap; } server { - listen 127.0.0.1:8146 ssl; - protocol imap; + listen 127.0.0.1:8994 ssl; + protocol imap; - ssl_session_cache off; + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; } server { - listen 127.0.0.1:8147; - protocol imap; + listen 127.0.0.1:8995 ssl; + protocol imap; - # Special case for enabled "ssl" directive. - - ssl on; - ssl_session_cache builtin:1000; + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; } server { - listen 127.0.0.1:8148 ssl; - protocol imap; - - ssl_session_cache shared:SSL:1m; - ssl_certificate_key inherits.key; - ssl_certificate inherits.crt; - } + listen 127.0.0.1:8996 ssl; + protocol imap; - server { - listen 127.0.0.1:8149; - protocol imap; - - starttls on; - } - - server { - listen 127.0.0.1:8150; - protocol imap; - - starttls only; + ssl_session_cache builtin; + ssl_session_tickets off; } server { - listen 127.0.0.1:8151; - protocol pop3; + listen 127.0.0.1:8997 ssl; + protocol imap; - starttls on; + ssl_session_cache builtin:1000; + ssl_session_tickets off; } server { - listen 127.0.0.1:8152; - protocol pop3; + listen 127.0.0.1:8998 ssl; + protocol imap; - starttls only; + ssl_session_cache none; + ssl_session_tickets off; } server { - listen 127.0.0.1:8153; - protocol smtp; - - starttls on; - } + listen 127.0.0.1:8999 ssl; + protocol imap; - server { - listen 127.0.0.1:8154; - protocol smtp; - - starttls only; + ssl_session_cache off; + ssl_session_tickets off; } } @@ -148,181 +118,57 @@ EOF my $d = $t->testdir(); -foreach my $name ('localhost', 'inherits') { - system("openssl genrsa -out $d/$name.key -passout pass:localhost " - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 - or die "Can't create private key: $!\n"; +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " - . "-out $d/$name.crt " - . "-key $d/$name.key -passin pass:localhost" + . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; } my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); -$t->write_file('password', 'localhost'); -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -# simple tests to ensure that nothing broke with ssl_password_file directive - -my $s = Test::Nginx::IMAP->new(); -$s->ok('greeting'); - -$s->send('1 AUTHENTICATE LOGIN'); -$s->check(qr/\+ VXNlcm5hbWU6/, 'login'); - -# ssl_session_cache - my ($ssl, $ses); -($s, $ssl) = get_ssl_socket(8145); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8145, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(8146); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8146, $ses); -is(Net::SSLeay::session_reused($ssl), 0, 'session not reused'); - -($s, $ssl) = get_ssl_socket(8147); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8147, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(8148); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8148, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); - -# ssl_certificate inheritance - -($s, $ssl) = get_ssl_socket(8145); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); - -($s, $ssl) = get_ssl_socket(8148); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); - -# alpn - -ok(get_ssl_socket(8148, undef, ['imap']), 'alpn'); - -SKIP: { -$t->{_configure_args} =~ /LibreSSL ([\d\.]+)/; -skip 'LibreSSL too old', 1 if defined $1 and $1 lt '3.4.0'; -$t->{_configure_args} =~ /OpenSSL ([\d\.]+)/; -skip 'OpenSSL too old', 1 if defined $1 and $1 lt '1.1.0'; - -TODO: { -local $TODO = 'not yet' unless $t->has_version('1.21.4'); - -ok(!get_ssl_socket(8148, undef, ['unknown']), 'alpn rejected'); - -} - -} - -# starttls imap - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8149)); -$s->read(); - -$s->send('1 AUTHENTICATE LOGIN'); -$s->check(qr/\+ VXNlcm5hbWU6/, 'imap auth before startls on'); - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8149)); -$s->read(); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -$s->send('1 STARTTLS'); -$s->ok('imap starttls on'); - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8150)); -$s->read(); - -$s->send('1 AUTHENTICATE LOGIN'); -$s->check(qr/^\S+ BAD/, 'imap auth before startls only'); - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8150)); -$s->read(); - -$s->send('1 STARTTLS'); -$s->ok('imap starttls only'); - -# starttls pop3 - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8151)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/\+ VXNlcm5hbWU6/, 'pop3 auth before startls on'); - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8151)); -$s->read(); - -$s->send('STLS'); -$s->ok('pop3 starttls on'); - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8152)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/^-ERR/, 'pop3 auth before startls only'); - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8152)); -$s->read(); - -$s->send('STLS'); -$s->ok('pop3 starttls only'); - -# starttls smtp - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8153)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/^334 VXNlcm5hbWU6/, 'smtp auth before startls on'); - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8153)); -$s->read(); - -$s->send('STARTTLS'); -$s->ok('smtp starttls on'); - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8154)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/^5.. /, 'smtp auth before startls only'); - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8154)); -$s->read(); - -$s->send('STARTTLS'); -$s->ok('smtp starttls only'); +is(test_reuse(8993), 1, 'tickets reused'); +is(test_reuse(8994), 1, 'tickets and cache reused'); +is(test_reuse(8995), 1, 'cache shared reused'); +is(test_reuse(8996), 1, 'cache builtin reused'); +is(test_reuse(8997), 1, 'cache builtin size reused'); +is(test_reuse(8998), 0, 'cache none not reused'); +is(test_reuse(8999), 0, 'cache off not reused'); ############################################################################### +sub test_reuse { + my ($port) = @_; + my ($s, $ssl) = get_ssl_socket($port); + Net::SSLeay::read($ssl); + my $ses = Net::SSLeay::get_session($ssl); + ($s, $ssl) = get_ssl_socket($port, $ses); + return Net::SSLeay::session_reused($ssl); +} + sub get_ssl_socket { - my ($port, $ses, $alpn) = @_; + my ($port, $ses) = @_; my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); Net::SSLeay::set_session($ssl, $ses) if defined $ses; - Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) == 1 or return; return ($s, $ssl); From mdounin at mdounin.ru Sat Mar 18 14:14:56 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:14:56 +0300 Subject: [PATCH 01 of 20] Tests: separate SSL session reuse tests In-Reply-To: References: Message-ID: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679105686 -10800 # Sat Mar 18 05:14:46 2023 +0300 # Node ID 86c394a226d2a7d463da7a1b7e88375c71c0c69b # Parent 3c9aa6c23fc836725b96cf056d218217a5a81603 Tests: separate SSL session reuse tests. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. In the generic SSL tests only basic session reuse is now tested, notably with session tickets enabled and a shared SSL session cache. This should make it possible to reuse sessions in all cases (except when it's not supported, such as with LibreSSL with TLSv1.3). Note that session reuse with tickets implies that $ssl_session_id is selected by the client and therefore is not available on the initial connection. Relevant test is modified to handle this. Further, BoringSSL does not use legacy session ID with TLSv1.3 even if it is sent by the client. In contrast, OpenSSL always generates an unique legacy session id, so it is available with TLSv1.3 even if session resumption does not work (such as with old Net::SSLeay and IO::Socket::SSL modules). diff --git a/ssl.t b/ssl.t --- a/ssl.t +++ b/ssl.t @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( plan(skip_all => 'IO::Socket::SSL too old') if $@; my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) - ->has_daemon('openssl')->plan(28); + ->has_daemon('openssl')->plan(21); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -47,7 +47,6 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; log_format ssl $ssl_protocol; @@ -59,6 +58,7 @@ http { ssl_certificate_key inner.key; ssl_certificate inner.crt; ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; ssl_verify_client optional_no_ca; keepalive_requests 1000; @@ -100,57 +100,11 @@ http { } server { - listen 127.0.0.1:8081; - server_name localhost; - - # Special case for enabled "ssl" directive. - - ssl on; - ssl_session_cache builtin; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8082 ssl; - server_name localhost; - - ssl_session_cache builtin:1000; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8083 ssl; - server_name localhost; - - ssl_session_cache none; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8084 ssl; - server_name localhost; - - ssl_session_cache off; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { listen 127.0.0.1:8086 ssl; server_name localhost; ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; ssl_session_timeout 1; location / { @@ -216,59 +170,34 @@ foreach my $name ('localhost', 'inner') or die "Can't create certificate for $name: $!\n"; } -# suppress deprecation warning - -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -my $ctx; +# ssl session reuse -SKIP: { -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +my $ctx = get_ssl_context(); -$ctx = get_ssl_context(); +like(get('/', 8085, $ctx), qr/^body \.$/m, 'session'); -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); - -$ctx = get_ssl_context(); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); +like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); } -$ctx = get_ssl_context(); - -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); - # ssl certificate inheritance -my $s = get_ssl_socket(8081); +my $s = get_ssl_socket(8086); like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); -$s->close(); - $s = get_ssl_socket(8085); like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); -$s->close(); - # session timeout $ctx = get_ssl_context(); @@ -280,8 +209,12 @@ like(get('/', 8086, $ctx), qr/^body \.$/ # embedded variables -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); +$ctx = get_ssl_context(); +like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); +like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); + unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); + like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); SKIP: { @@ -334,6 +267,10 @@ like(`grep -F '[crit]' ${\($t->testdir() ############################################################################### +sub test_tls13 { + return get('/protocol', 8085) =~ /TLSv1.3/; +} + sub get { my ($uri, $port, $ctx) = @_; my $s = get_ssl_socket($port, $ctx) or return; diff --git a/ssl.t b/ssl_session_reuse.t copy from ssl.t copy to ssl_session_reuse.t --- a/ssl.t +++ b/ssl_session_reuse.t @@ -2,6 +2,7 @@ # (C) Sergey Kandaurov # (C) Andrey Zelenkov +# (C) Maxim Dounin # (C) Nginx, Inc. # Tests for http ssl module. @@ -30,8 +31,8 @@ plan(skip_all => 'IO::Socket::SSL not in eval { IO::Socket::SSL::SSL_VERIFY_NONE(); }; plan(skip_all => 'IO::Socket::SSL too old') if $@; -my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) - ->has_daemon('openssl')->plan(28); +my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/) + ->has_daemon('openssl')->plan(8); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -47,66 +48,37 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; - - log_format ssl $ssl_protocol; server { - listen 127.0.0.1:8085 ssl; - listen 127.0.0.1:8080; + listen 127.0.0.1:8443 ssl; server_name localhost; - ssl_certificate_key inner.key; - ssl_certificate inner.crt; - ssl_session_cache shared:SSL:1m; - ssl_verify_client optional_no_ca; - - keepalive_requests 1000; - location / { return 200 "body $ssl_session_reused"; } - location /id { - return 200 "body $ssl_session_id"; - } - location /cipher { - return 200 "body $ssl_cipher"; - } - location /ciphers { - return 200 "body $ssl_ciphers"; - } - location /client_verify { - return 200 "body $ssl_client_verify"; - } location /protocol { return 200 "body $ssl_protocol"; } - location /issuer { - return 200 "body $ssl_client_i_dn:$ssl_client_i_dn_legacy"; - } - location /subject { - return 200 "body $ssl_client_s_dn:$ssl_client_s_dn_legacy"; - } - location /time { - return 200 "body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain"; - } - - location /body { - add_header X-Body $request_body always; - proxy_pass http://127.0.0.1:8080/; - - access_log %%TESTDIR%%/ssl.log ssl; - } } server { - listen 127.0.0.1:8081; + listen 127.0.0.1:8444 ssl; server_name localhost; - # Special case for enabled "ssl" directive. + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; - ssl on; - ssl_session_cache builtin; + location / { + return 200 "body $ssl_session_reused"; + } + } + + server { + listen 127.0.0.1:8445 ssl; + server_name localhost; + + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -114,10 +86,11 @@ http { } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8446 ssl; server_name localhost; - ssl_session_cache builtin:1000; + ssl_session_cache builtin; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -125,10 +98,11 @@ http { } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8447 ssl; server_name localhost; - ssl_session_cache none; + ssl_session_cache builtin:1000; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -136,10 +110,11 @@ http { } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8448 ssl; server_name localhost; - ssl_session_cache off; + ssl_session_cache none; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -147,11 +122,11 @@ http { } server { - listen 127.0.0.1:8086 ssl; + listen 127.0.0.1:8449 ssl; server_name localhost; - ssl_session_cache shared:SSL:1m; - ssl_session_timeout 1; + ssl_session_cache off; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -171,44 +146,7 @@ EOF my $d = $t->testdir(); -$t->write_file('ca.conf', <write_file('certserial', '1000'); -$t->write_file('certindex', ''); - -system('openssl req -x509 -new ' - . "-config $d/openssl.conf -subj /CN=issuer/ " - . "-out $d/issuer.crt -keyout $d/issuer.key " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't create certificate for issuer: $!\n"; - -system("openssl req -new " - . "-config $d/openssl.conf -subj /CN=subject/ " - . "-out $d/subject.csr -keyout $d/subject.key " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't create certificate for subject: $!\n"; - -system("openssl ca -batch -config $d/ca.conf " - . "-keyfile $d/issuer.key -cert $d/issuer.crt " - . "-subj /CN=subject/ -in $d/subject.csr -out $d/subject.crt " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't sign certificate for subject: $!\n"; - -foreach my $name ('localhost', 'inner') { +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.crt -keyout $d/$name.key " @@ -216,124 +154,56 @@ foreach my $name ('localhost', 'inner') or die "Can't create certificate for $name: $!\n"; } -# suppress deprecation warning - -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### my $ctx; -SKIP: { -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); - -$ctx = get_ssl_context(); - -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache, should work always +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -$ctx = get_ssl_context(); - -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); -$ctx = get_ssl_context(); - -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); +is(test_reuse(8443), 1, 'tickets reused'); +is(test_reuse(8444), 1, 'tickets and cache reused'); +is(test_reuse(8445), 1, 'cache shared reused'); +is(test_reuse(8446), 1, 'cache builtin reused'); +is(test_reuse(8447), 1, 'cache builtin size reused'); } -$ctx = get_ssl_context(); - -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); - -# ssl certificate inheritance - -my $s = get_ssl_socket(8081); -like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); - -$s->close(); - -$s = get_ssl_socket(8085); -like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); - -$s->close(); - -# session timeout - -$ctx = get_ssl_context(); - -get('/', 8086, $ctx); -select undef, undef, undef, 2.1; - -like(get('/', 8086, $ctx), qr/^body \.$/m, 'session timeout'); - -# embedded variables - -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); -unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); -like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); - -SKIP: { -skip 'BoringSSL', 1 if $t->has_module('BoringSSL'); - -like(get('/ciphers', 8085), qr/^body [:\w-]+$/m, 'ciphers'); - -} - -like(get('/client_verify', 8085), qr/^body NONE$/m, 'client verify'); -like(get('/protocol', 8085), qr/^body (TLS|SSL)v(\d|\.)+$/m, 'protocol'); -like(cert('/issuer', 8085), qr!^body CN=issuer:/CN=issuer$!m, 'issuer'); -like(cert('/subject', 8085), qr!^body CN=subject:/CN=subject$!m, 'subject'); -like(cert('/time', 8085), qr/^body [:\s\w]+![:\s\w]+![23]$/m, 'time'); - -# c->read->ready handling bug in ngx_ssl_recv(), triggered with chunked body - -like(get_body('/body', '0123456789', 20, 5), qr/X-Body: (0123456789){100}/, - 'request body chunked'); - -# pipelined requests - -$s = get_ssl_socket(8085); -my $req = < $s) || ""; -$s = undef; -is(() = $r =~ /(200 OK)/g, 1000, 'pipelined requests'); - -# OpenSSL 3.0 error "unexpected eof while reading" seen as a critical error - -ok(get_ssl_socket(8085), 'ssl unexpected eof'); - -# close_notify is sent before lingering close - -is(get_ssl_shutdown(8085), 1, 'ssl shutdown on lingering close'); +is(test_reuse(8448), 0, 'cache none not reused'); +is(test_reuse(8449), 0, 'cache off not reused'); $t->stop(); -like($t->read_file('ssl.log'), qr/^(TLS|SSL)v(\d|\.)+$/m, - 'log ssl variable on lingering close'); - like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); ############################################################################### +sub test_tls13 { + return get('/protocol', 8443) =~ /TLSv1.3/; +} + +sub test_reuse { + my ($port) = @_; + $ctx = get_ssl_context(); + get('/', $port, $ctx); + return (get('/', $port, $ctx) =~ qr/^body r$/m) ? 1 : 0; +} + sub get { my ($uri, $port, $ctx) = @_; my $s = get_ssl_socket($port, $ctx) or return; @@ -342,30 +212,6 @@ sub get { return $r; } -sub get_body { - my ($uri, $body, $len, $n) = @_; - my $s = get_ssl_socket(8085) or return; - http("GET /body HTTP/1.1" . CRLF - . "Host: localhost" . CRLF - . "Connection: close" . CRLF - . "Transfer-Encoding: chunked" . CRLF . CRLF, - socket => $s, start => 1); - my $chs = unpack("H*", pack("C", length($body) * $len)); - http($chs . CRLF . $body x $len . CRLF, socket => $s, start => 1) - for 1 .. $n; - my $r = http("0" . CRLF . CRLF, socket => $s); - $s->close(); - return $r; -} - -sub cert { - my ($uri, $port) = @_; - my $s = get_ssl_socket($port, undef, - SSL_cert_file => "$d/subject.crt", - SSL_key_file => "$d/subject.key") or return; - http_get($uri, socket => $s); -} - sub get_ssl_context { return IO::Socket::SSL::SSL_Context->new( SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), @@ -402,18 +248,4 @@ sub get_ssl_socket { return $s; } -sub get_ssl_shutdown { - my ($port) = @_; - - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - Net::SSLeay::write($ssl, 'GET /' . CRLF . 'extra'); - Net::SSLeay::read($ssl); - Net::SSLeay::set_shutdown($ssl, 1); - Net::SSLeay::shutdown($ssl); -} - ############################################################################### From mdounin at mdounin.ru Sat Mar 18 14:15:03 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:03 +0300 Subject: [PATCH 08 of 20] Tests: enabled session reuse via TLS session tickets In-Reply-To: References: Message-ID: <01721a51ba94e022d1f5.1679148903@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679140615 -10800 # Sat Mar 18 14:56:55 2023 +0300 # Node ID 01721a51ba94e022d1f59485b9da490683ac0c2f # Parent 36b78c9dc2db19bb92becb5cac0090c34538313e Tests: enabled session reuse via TLS session tickets. This fixes tests with TLSv1.3 enabled when using BoringSSL, since for TLSv1.3 it only supports session reuse via TLS session tickets, and not server-side session cache. diff --git a/ssl_certificate.t b/ssl_certificate.t --- a/ssl_certificate.t +++ b/ssl_certificate.t @@ -71,7 +71,7 @@ http { add_header X-SSL $ssl_server_name:$ssl_session_reused; ssl_session_cache shared:SSL:1m; - ssl_session_tickets off; + ssl_session_tickets on; server { listen 127.0.0.1:8080 ssl; diff --git a/stream_ssl_certificate.t b/stream_ssl_certificate.t --- a/stream_ssl_certificate.t +++ b/stream_ssl_certificate.t @@ -68,7 +68,7 @@ stream { } ssl_session_cache shared:SSL:1m; - ssl_session_tickets off; + ssl_session_tickets on; server { listen 127.0.0.1:8080 ssl; From mdounin at mdounin.ru Sat Mar 18 14:15:02 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:02 +0300 Subject: [PATCH 07 of 20] Tests: BoringSSL does not provide session ids with TLSv1.3 In-Reply-To: References: Message-ID: <36b78c9dc2db19bb92be.1679148902@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679140613 -10800 # Sat Mar 18 14:56:53 2023 +0300 # Node ID 36b78c9dc2db19bb92becb5cac0090c34538313e # Parent d90fe31a80d5e85b59e525e874d24f409716b64c Tests: BoringSSL does not provide session ids with TLSv1.3. diff --git a/stream_ssl_variables.t b/stream_ssl_variables.t --- a/stream_ssl_variables.t +++ b/stream_ssl_variables.t @@ -103,7 +103,7 @@ like(Net::SSLeay::read($ssl), qr/^\.:(\w my $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8081), $ses); -like(Net::SSLeay::read($ssl), qr/^r:\w{64}:[\w-]+:(TLS|SSL)v(\d|\.)+$/, +like(Net::SSLeay::read($ssl), qr/^r:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables - session reused'); SKIP: { From mdounin at mdounin.ru Sat Mar 18 14:15:04 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:04 +0300 Subject: [PATCH 09 of 20] Tests: restored proper port numbers in ssl_sni_sessions.t In-Reply-To: References: Message-ID: <8d33ee433f58be6100fb.1679148904@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679140616 -10800 # Sat Mar 18 14:56:56 2023 +0300 # Node ID 8d33ee433f58be6100fb084a12ae8c569259666a # Parent 01721a51ba94e022d1f59485b9da490683ac0c2f Tests: restored proper port numbers in ssl_sni_sessions.t. While using port 8080 for SSL connection works, it's usually a bad idea, since 8080 implies plain HTTP, much like 80. For HTTPS we generally use port 8443 instead. Unfortunately, proper port numbers were lost in 952:e9064d691790 and 974:882267679006, while converting tests to run in parallel. diff --git a/ssl_sni_sessions.t b/ssl_sni_sessions.t --- a/ssl_sni_sessions.t +++ b/ssl_sni_sessions.t @@ -39,7 +39,7 @@ http { ssl_certificate localhost.crt; server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8443 ssl; server_name default; ssl_session_tickets off; @@ -51,7 +51,7 @@ http { } server { - listen 127.0.0.1:8080; + listen 127.0.0.1:8443; server_name nocache; ssl_session_tickets off; @@ -63,7 +63,7 @@ http { } server { - listen 127.0.0.1:8081 ssl; + listen 127.0.0.1:8444 ssl; server_name default; ssl_session_ticket_key ticket1.key; @@ -74,7 +74,7 @@ http { } server { - listen 127.0.0.1:8081; + listen 127.0.0.1:8444; server_name tickets; ssl_session_ticket_key ticket2.key; @@ -128,7 +128,7 @@ foreach my $name ('localhost') { $t->run(); plan(skip_all => 'no TLS 1.3 sessions') - if get('default', port(8080), get_ssl_context()) =~ /TLSv1.3/ + if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); $t->plan(6); @@ -139,8 +139,8 @@ plan(skip_all => 'no TLS 1.3 sessions') my $ctx = get_ssl_context(); -like(get('default', port(8080), $ctx), qr!default:\.!, 'default server'); -like(get('default', port(8080), $ctx), qr!default:r!, 'default server reused'); +like(get('default', port(8443), $ctx), qr!default:\.!, 'default server'); +like(get('default', port(8443), $ctx), qr!default:r!, 'default server reused'); # check that sessions are still properly saved and restored # when using an SNI-based virtual server with different session cache; @@ -154,16 +154,16 @@ like(get('default', port(8080), $ctx), q $ctx = get_ssl_context(); -like(get('nocache', port(8080), $ctx), qr!nocache:\.!, 'without cache'); -like(get('nocache', port(8080), $ctx), qr!nocache:r!, 'without cache reused'); +like(get('nocache', port(8443), $ctx), qr!nocache:\.!, 'without cache'); +like(get('nocache', port(8443), $ctx), qr!nocache:r!, 'without cache reused'); # make sure tickets can be used if an SNI-based virtual server # uses a different set of session ticket keys explicitly set $ctx = get_ssl_context(); -like(get('tickets', port(8081), $ctx), qr!tickets:\.!, 'tickets'); -like(get('tickets', port(8081), $ctx), qr!tickets:r!, 'tickets reused'); +like(get('tickets', port(8444), $ctx), qr!tickets:\.!, 'tickets'); +like(get('tickets', port(8444), $ctx), qr!tickets:r!, 'tickets reused'); ############################################################################### From mdounin at mdounin.ru Sat Mar 18 14:15:05 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:05 +0300 Subject: [PATCH 10 of 20] Tests: disabled ssl_sni_sessions.t with LibreSSL and BoringSSL In-Reply-To: References: Message-ID: <79177a6c535839517137.1679148905@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679140619 -10800 # Sat Mar 18 14:56:59 2023 +0300 # Node ID 79177a6c535839517137dd7e3c7e7d57a5aa35dd # Parent 8d33ee433f58be6100fb084a12ae8c569259666a Tests: disabled ssl_sni_sessions.t with LibreSSL and BoringSSL. With TLSv1.3, LibreSSL does not provide session reuse at all, and BoringSSL only supports session tickets, and not server-side session cache. Since the test is focused on session cache usage, it is now skipped. diff --git a/ssl_sni_sessions.t b/ssl_sni_sessions.t --- a/ssl_sni_sessions.t +++ b/ssl_sni_sessions.t @@ -130,6 +130,12 @@ foreach my $name ('localhost') { plan(skip_all => 'no TLS 1.3 sessions') if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +plan(skip_all => 'no TLS 1.3 sessions in LibreSSL') + if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ + && $t->has_module('LibreSSL'); +plan(skip_all => 'no TLS 1.3 session cache in BoringSSL') + if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ + && $t->has_module('BoringSSL'); $t->plan(6); From mdounin at mdounin.ru Sat Mar 18 14:15:00 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:00 +0300 Subject: [PATCH 05 of 20] Tests: separate SSL session reuse tests in stream In-Reply-To: References: Message-ID: <530336cb449dcb028a55.1679148900@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679140351 -10800 # Sat Mar 18 14:52:31 2023 +0300 # Node ID 530336cb449dcb028a55a5a401a122d07521e3a4 # Parent 3ab3b2d1c2e67bc1f05e386218ceb08da873a477 Tests: separate SSL session reuse tests in stream. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. diff --git a/stream_ssl.t b/stream_ssl.t --- a/stream_ssl.t +++ b/stream_ssl.t @@ -37,7 +37,7 @@ plan(skip_all => 'win32') if $^O eq 'MSW my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); -$t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); +$t->plan(5)->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -51,40 +51,35 @@ stream { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; # inherited by server "inherits" ssl_password_file password_stream; server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8443 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache builtin; ssl_password_file password; } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8444 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache off; ssl_password_file password_many; } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8445 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache builtin:1000; ssl_password_file password_fifo; } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8446 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache shared:SSL:1m; ssl_certificate_key inherits.key; ssl_certificate inherits.crt; } @@ -138,52 +133,26 @@ kill 'INT', $p if $@; ############################################################################### -my ($s, $ssl, $ses); +my ($s, $ssl); -($s, $ssl) = get_ssl_socket(port(8080)); +($s, $ssl) = get_ssl_socket(8443); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); -# ssl_session_cache - -($s, $ssl) = get_ssl_socket(port(8080)); +($s, $ssl) = get_ssl_socket(8444); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8080), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(port(8082)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); -($s, $ssl) = get_ssl_socket(port(8082), $ses); -isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); - -($s, $ssl) = get_ssl_socket(port(8083)); +($s, $ssl) = get_ssl_socket(8444); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8083), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(port(8084)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8084), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); # ssl_certificate inheritance -($s, $ssl) = get_ssl_socket(port(8080)); +($s, $ssl) = get_ssl_socket(8443); like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); -($s, $ssl) = get_ssl_socket(port(8084)); +($s, $ssl) = get_ssl_socket(8446); like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); ############################################################################### @@ -191,7 +160,7 @@ like(Net::SSLeay::dump_peer_certificate( sub get_ssl_socket { my ($port, $ses) = @_; - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); + my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_fd($ssl, fileno($s)); diff --git a/stream_ssl.t b/stream_ssl_session_reuse.t copy from stream_ssl.t copy to stream_ssl_session_reuse.t --- a/stream_ssl.t +++ b/stream_ssl_session_reuse.t @@ -1,6 +1,7 @@ #!/usr/bin/perl # (C) Sergey Kandaurov +# (C) Maxim Dounin # (C) Nginx, Inc. # Tests for stream ssl module. @@ -12,7 +13,6 @@ use strict; use Test::More; -use POSIX qw/ mkfifo /; use Socket qw/ $CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } @@ -49,44 +49,60 @@ events { stream { %%TEST_GLOBALS_STREAM%% + ssl_certificate localhost.crt; ssl_certificate_key localhost.key; - ssl_certificate localhost.crt; - ssl_session_tickets off; - # inherited by server "inherits" - ssl_password_file password_stream; + server { + listen 127.0.0.1:8443 ssl; + proxy_pass 127.0.0.1:8081; + } server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8444 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache builtin; - ssl_password_file password; + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; + } + + server { + listen 127.0.0.1:8445 ssl; + proxy_pass 127.0.0.1:8081; + + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8446 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache off; - ssl_password_file password_many; + ssl_session_cache builtin; + ssl_session_tickets off; } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8447 ssl; proxy_pass 127.0.0.1:8081; ssl_session_cache builtin:1000; - ssl_password_file password_fifo; + ssl_session_tickets off; } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8448 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache shared:SSL:1m; - ssl_certificate_key inherits.key; - ssl_certificate inherits.crt; + ssl_session_cache none; + ssl_session_tickets off; + } + + server { + listen 127.0.0.1:8449 ssl; + proxy_pass 127.0.0.1:8081; + + ssl_session_cache off; + ssl_session_tickets off; } } @@ -101,16 +117,11 @@ distinguished_name = req_distinguished_n EOF my $d = $t->testdir(); -mkfifo("$d/password_fifo", 0700); -foreach my $name ('localhost', 'inherits') { - system("openssl genrsa -out $d/$name.key -passout pass:$name " - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 - or die "Can't create private key: $!\n"; +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " - . "-out $d/$name.crt " - . "-key $d/$name.key -passin pass:$name" + . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; } @@ -118,80 +129,48 @@ foreach my $name ('localhost', 'inherits my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); -$t->write_file('password', 'localhost'); -$t->write_file('password_many', "wrong$CRLF" . "localhost$CRLF"); -$t->write_file('password_stream', 'inherits'); - -my $p = fork(); -exec("echo localhost > $d/password_fifo") if $p == 0; - $t->run_daemon(\&http_daemon); -eval { - open OLDERR, ">&", \*STDERR; close STDERR; - $t->run(); - open STDERR, ">&", \*OLDERR; -}; -kill 'INT', $p if $@; +$t->run(); $t->waitforsocket('127.0.0.1:' . port(8081)); ############################################################################### -my ($s, $ssl, $ses); - -($s, $ssl) = get_ssl_socket(port(8080)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); - -# ssl_session_cache - -($s, $ssl) = get_ssl_socket(port(8080)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8080), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(port(8082)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8082), $ses); -isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache, should work always +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -($s, $ssl) = get_ssl_socket(port(8083)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8083), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(port(8084)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8084), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); - -# ssl_certificate inheritance - -($s, $ssl) = get_ssl_socket(port(8080)); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); - -($s, $ssl) = get_ssl_socket(port(8084)); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); +is(test_reuse(8443), 1, 'tickets reused'); +is(test_reuse(8444), 1, 'tickets and cache reused'); +is(test_reuse(8445), 1, 'cache shared reused'); +is(test_reuse(8446), 1, 'cache builtin reused'); +is(test_reuse(8447), 1, 'cache builtin size reused'); +is(test_reuse(8448), 0, 'cache none not reused'); +is(test_reuse(8449), 0, 'cache off not reused'); ############################################################################### +sub test_reuse { + my ($port) = @_; + my ($s, $ssl) = get_ssl_socket($port); + Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); + Net::SSLeay::read($ssl); + my $ses = Net::SSLeay::get_session($ssl); + ($s, $ssl) = get_ssl_socket($port, $ses); + return Net::SSLeay::session_reused($ssl); +} + sub get_ssl_socket { my ($port, $ses) = @_; - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); + my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_fd($ssl, fileno($s)); From mdounin at mdounin.ru Sat Mar 18 14:15:01 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:01 +0300 Subject: [PATCH 06 of 20] Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1679140402 -10800 # Sat Mar 18 14:53:22 2023 +0300 # Node ID d90fe31a80d5e85b59e525e874d24f409716b64c # Parent 530336cb449dcb028a55a5a401a122d07521e3a4 Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail. LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL with TLSv1.3 only supports session tickets, but not server-side session cache. diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t --- a/stream_ssl_session_reuse.t +++ b/stream_ssl_session_reuse.t @@ -147,16 +147,35 @@ my $ctx = Net::SSLeay::CTX_new() or die( # - only cache none # - only cache off +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + is(test_reuse(8443), 1, 'tickets reused'); is(test_reuse(8444), 1, 'tickets and cache reused'); + +TODO: { +local $TODO = 'no TLSv1.3 session cache in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + is(test_reuse(8445), 1, 'cache shared reused'); is(test_reuse(8446), 1, 'cache builtin reused'); is(test_reuse(8447), 1, 'cache builtin size reused'); + +} +} + is(test_reuse(8448), 0, 'cache none not reused'); is(test_reuse(8449), 0, 'cache off not reused'); + ############################################################################### +sub test_tls13 { + my ($s, $ssl) = get_ssl_socket(8443); + return (Net::SSLeay::version($ssl) > 0x303); +} + sub test_reuse { my ($port) = @_; my ($s, $ssl) = get_ssl_socket($port); From mdounin at mdounin.ru Sat Mar 18 14:15:07 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:07 +0300 Subject: [PATCH 12 of 20] Tests: fixed ssl_session_ticket_key.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: <25c625a731909efe2978.1679148907@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679148620 -10800 # Sat Mar 18 17:10:20 2023 +0300 # Node ID 25c625a731909efe2978b1fd497e9bdda9217a35 # Parent 7dec25ecd3db606258647bd2ce2bca15e793accb Tests: fixed ssl_session_ticket_key.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/ssl_session_ticket_key.t b/ssl_session_ticket_key.t --- a/ssl_session_ticket_key.t +++ b/ssl_session_ticket_key.t @@ -96,6 +96,10 @@ select undef, undef, undef, 0.5; is(get_ticket_key_name(), $key, 'ticket key match'); select undef, undef, undef, 2.5; + +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + cmp_ok(get_ticket_key_name(), 'ne', $key, 'ticket key next'); ############################################################################### @@ -107,7 +111,7 @@ sub get_ticket_key_name { next: # tag(10) | len{2} | OCTETSTRING(4) | len{2} | ticket(key_name|..) $asn =~ /\xaa\x81($any)\x04\x81($any)($any{16})/g; - return if !defined $3; + return '' if !defined $3; goto next if unpack("C", $1) - unpack("C", $2) != 3; my $key = unpack "H*", $3; Test::Nginx::log_core('||', "ticket key: $key"); @@ -126,6 +130,11 @@ EOF Net::SSLeay::get_session($ssl); } +sub test_tls13 { + my ($s, $ssl) = get_ssl_socket(); + return (Net::SSLeay::version($ssl) > 0x303); +} + sub get_ssl_socket { my $s = IO::Socket::INET->new('127.0.0.1:' . port(8080)); my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); From mdounin at mdounin.ru Sat Mar 18 14:15:08 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:08 +0300 Subject: [PATCH 13 of 20] Tests: fixed ssl_sni.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1679148654 -10800 # Sat Mar 18 17:10:54 2023 +0300 # Node ID c6a91e9fa97e2c99ddaa26471364f2345837e698 # Parent 25c625a731909efe2978b1fd497e9bdda9217a35 Tests: fixed ssl_sni.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/ssl_sni.t b/ssl_sni.t --- a/ssl_sni.t +++ b/ssl_sni.t @@ -148,9 +148,13 @@ my $ctx = new IO::Socket::SSL::SSL_Conte like(get('/', 'localhost', 8081, $ctx), qr/^\.:localhost$/m, 'ssl server name'); -SKIP: { -skip 'no TLS 1.3 sessions', 1 if get('/protocol', 'localhost') =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); like(get('/', 'localhost', 8081, $ctx), qr/^r:localhost$/m, 'ssl server name - reused'); @@ -159,6 +163,10 @@ like(get('/', 'localhost', 8081, $ctx), ############################################################################### +sub test_tls13 { + get('/protocol', 'localhost') =~ /TLSv1.3/; +} + sub get_ssl_socket { my ($host, $port, $ctx) = @_; my $s; From mdounin at mdounin.ru Sat Mar 18 14:15:06 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:06 +0300 Subject: [PATCH 11 of 20] Tests: fixed proxy_ssl.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: <7dec25ecd3db60625864.1679148906@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679148613 -10800 # Sat Mar 18 17:10:13 2023 +0300 # Node ID 7dec25ecd3db606258647bd2ce2bca15e793accb # Parent 79177a6c535839517137dd7e3c7e7d57a5aa35dd Tests: fixed proxy_ssl.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/proxy_ssl.t b/proxy_ssl.t --- a/proxy_ssl.t +++ b/proxy_ssl.t @@ -46,6 +46,7 @@ http { location / { add_header X-Session $ssl_session_reused; + add_header X-Protocol $ssl_protocol; } } @@ -109,9 +110,16 @@ foreach my $name ('localhost') { like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl'); like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl 2'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: \./s, 'ssl session new'); + +TODO: { +local $TODO = 'no TLS 1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && http_get('/ssl') =~ /TLSv1.3/; + like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused 2'); +} + SKIP: { skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE}; From mdounin at mdounin.ru Sat Mar 18 14:15:09 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:09 +0300 Subject: [PATCH 14 of 20] Tests: LibreSSL certificate negotiation with TLSv1.3 In-Reply-To: References: Message-ID: <230b9cadce9b57213bf5.1679148909@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679148657 -10800 # Sat Mar 18 17:10:57 2023 +0300 # Node ID 230b9cadce9b57213bf529940ca04224f9f121eb # Parent c6a91e9fa97e2c99ddaa26471364f2345837e698 Tests: LibreSSL certificate negotiation with TLSv1.3. LibreSSL fails to negotiate certificates based on signature algorithms when using TLSv1.3, and fails with "missing rsa certificate" and "unknown pkey type" errors. diff --git a/ssl_stapling.t b/ssl_stapling.t --- a/ssl_stapling.t +++ b/ssl_stapling.t @@ -38,7 +38,7 @@ my $t = Test::Nginx->new()->has(qw/http plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL'); -$t->plan(9)->write_file_expand('nginx.conf', <<'EOF'); +$t->plan(10)->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -259,11 +259,25 @@ staple(8449, 'ECDSA'); sleep 1; ok(!staple(8443, 'RSA'), 'staple revoked'); + +TODO: { +local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' + if $t->has_module('LibreSSL') && $version > 0x303; + ok(staple(8443, 'ECDSA'), 'staple success'); +} + ok(!staple(8444, 'RSA'), 'responder revoked'); + +TODO: { +local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' + if $t->has_module('LibreSSL') && $version > 0x303; + ok(staple(8444, 'ECDSA'), 'responder success'); +} + ok(!staple(8445, 'ECDSA'), 'verify - root not trusted'); ok(staple(8446, 'ECDSA', "$d/int.crt"), 'cert store'); @@ -273,6 +287,14 @@ is(staple(8448, 'ECDSA'), '1 0', 'file s ok(!staple(8449, 'ECDSA'), 'ocsp error'); +TODO: { +local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' + if $t->has_module('LibreSSL') && $version > 0x303; + +like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); + +} + ############################################################################### sub staple { From mdounin at mdounin.ru Sat Mar 18 14:15:10 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:10 +0300 Subject: [PATCH 15 of 20] Tests: LibreSSL does not send CA lists with TLSv1.3 In-Reply-To: References: Message-ID: <6d5bede76a77ca86483f.1679148910@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679148737 -10800 # Sat Mar 18 17:12:17 2023 +0300 # Node ID 6d5bede76a77ca86483f63088587913a61b8b18d # Parent 230b9cadce9b57213bf529940ca04224f9f121eb Tests: LibreSSL does not send CA lists with TLSv1.3. diff --git a/ssl_verify_client.t b/ssl_verify_client.t --- a/ssl_verify_client.t +++ b/ssl_verify_client.t @@ -55,6 +55,7 @@ http { %%TEST_GLOBALS_HTTP%% add_header X-Verify x$ssl_client_verify:${ssl_client_cert}x; + add_header X-Protocol $ssl_protocol; ssl_session_cache shared:SSL:1m; ssl_session_tickets off; @@ -169,15 +170,24 @@ like(get('optional', '3.example.com'), q SKIP: { skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; +TODO: { +local $TODO = 'broken TLSv1.3 CA list in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + my $ca = join ' ', get('optional', '3.example.com'); is($ca, '/CN=2.example.com', 'no trusted sent'); } +} like(get('optional', undef, 'localhost'), qr/421 Misdirected/, 'misdirected'); ############################################################################### +sub test_tls13 { + get('optional') =~ /TLSv1.3/; +} + sub get { my ($sni, $cert, $host) = @_; diff --git a/stream_ssl_verify_client.t b/stream_ssl_verify_client.t --- a/stream_ssl_verify_client.t +++ b/stream_ssl_verify_client.t @@ -86,6 +86,11 @@ stream { ssl_verify_client optional_no_ca; ssl_client_certificate 2.example.com.crt; } + + server { + listen 127.0.0.1:8084 ssl; + return $ssl_protocol; + } } EOF @@ -126,10 +131,15 @@ like(get(8082, '3.example.com'), qr/SUCC SKIP: { skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; +TODO: { +local $TODO = 'broken TLSv1.3 CA list in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + my $ca = join ' ', get(8082, '3.example.com'); is($ca, '/CN=2.example.com', 'no trusted sent'); } +} $t->stop(); @@ -137,6 +147,10 @@ is($t->read_file('status.log'), "500\n20 ############################################################################### +sub test_tls13 { + get(8084) =~ /TLSv1.3/; +} + sub get { my ($port, $cert) = @_; From mdounin at mdounin.ru Sat Mar 18 14:15:11 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:11 +0300 Subject: [PATCH 16 of 20] Tests: fixed stream_proxy_ssl.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: <778dcba0f619808fef92.1679148911@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679148836 -10800 # Sat Mar 18 17:13:56 2023 +0300 # Node ID 778dcba0f619808fef92999b174c86cf9668ffeb # Parent 6d5bede76a77ca86483f63088587913a61b8b18d Tests: fixed stream_proxy_ssl.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/stream_proxy_ssl.t b/stream_proxy_ssl.t --- a/stream_proxy_ssl.t +++ b/stream_proxy_ssl.t @@ -78,6 +78,8 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; + + add_header X-Protocol $ssl_protocol; } } @@ -111,9 +113,16 @@ is(stream('127.0.0.1:' . port(8081))->re is(stream('127.0.0.1:' . port(8081))->read(), '.', 'ssl 2'); is(stream('127.0.0.1:' . port(8082))->read(), '.', 'ssl session new'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + is(stream('127.0.0.1:' . port(8082))->read(), 'r', 'ssl session reused'); is(stream('127.0.0.1:' . port(8082))->read(), 'r', 'ssl session reused 2'); +} + my $s = http('', start => 1); sleep 3; @@ -121,3 +130,9 @@ sleep 3; like(http_get('/', socket => $s), qr/200 OK/, 'proxy connect timeout'); ############################################################################### + +sub test_tls13 { + http_get('/') =~ m/TLSv1.3/; +} + +############################################################################### From mdounin at mdounin.ru Sat Mar 18 14:15:13 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:13 +0300 Subject: [PATCH 18 of 20] Tests: cleaned up ssl_ocsp.t In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1679148851 -10800 # Sat Mar 18 17:14:11 2023 +0300 # Node ID c140f78fbc8f62c9694d3b969d1309570a96f2e7 # Parent fc68109603c10abff643444b39cf52daa240a3e2 Tests: cleaned up ssl_ocsp.t. Fixed verbose logging, added $SIG{PIPE} handling to avoid hangs if the server closes connection, fixed SKIP message for BoringSSL. diff --git a/ssl_ocsp.t b/ssl_ocsp.t --- a/ssl_ocsp.t +++ b/ssl_ocsp.t @@ -43,7 +43,8 @@ plan(skip_all => 'Net::SSLeay with OpenS my $t = Test::Nginx->new()->has(qw/http http_ssl sni/)->has_daemon('openssl'); -plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL'); +plan(skip_all => 'no OCSP support in BoringSSL') + if $t->has_module('BoringSSL'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -416,9 +417,11 @@ sub get { my $cipher = Net::SSLeay::get_cipher($ssl); Test::Nginx::log_core('||', "cipher: $cipher"); my $host = $extra{sni} ? $extra{sni} : 'localhost'; + local $SIG{PIPE} = 'IGNORE'; + log_out("GET /serial HTTP/1.0\nHost: $host\n\n"); Net::SSLeay::write($ssl, "GET /serial HTTP/1.0\nHost: $host\n\n"); my $r = Net::SSLeay::read($ssl); - Test::Nginx::log_core($r); + log_in($r); $s->close(); return $r unless wantarray(); return ($s, $ssl); @@ -496,6 +499,7 @@ sub http_daemon { my $resp; while (<$client>) { + Test::Nginx::log_core('||', $_); $headers .= $_; last if (/^\x0d?\x0a?$/); } From mdounin at mdounin.ru Sat Mar 18 14:15:15 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:15 +0300 Subject: [PATCH 20 of 20] Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1679148869 -10800 # Sat Mar 18 17:14:29 2023 +0300 # Node ID f6f6a21b1c2a0d88cb2a4993f4c0113a3fb1e019 # Parent 782531c3cd79dcf700276e10bef00e524de009d1 Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/ssl_ocsp.t b/ssl_ocsp.t --- a/ssl_ocsp.t +++ b/ssl_ocsp.t @@ -371,9 +371,15 @@ like(get('ec-end'), qr/200 OK.*SUCCESS/s my ($s, $ssl) = get('ec-end'); my $ses = Net::SSLeay::get_session($ssl); +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and $version > 0x303; + like(get('ec-end', ses => $ses), qr/200 OK.*SUCCESS:r/s, 'session reused'); +} + # revoke with saved session system("openssl ca -config $d/ca.conf -revoke $d/ec-end.crt " @@ -393,9 +399,15 @@ system("openssl ocsp -index $d/certindex # reusing session with revoked certificate +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and $version > 0x303; + like(get('ec-end', ses => $ses), qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); +} + # regression test for self-signed like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); From mdounin at mdounin.ru Sat Mar 18 14:15:14 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:14 +0300 Subject: [PATCH 19 of 20] Tests: removed multiple server certificates from ssl_ocsp.t In-Reply-To: References: Message-ID: <782531c3cd79dcf70027.1679148914@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1679148855 -10800 # Sat Mar 18 17:14:15 2023 +0300 # Node ID 782531c3cd79dcf700276e10bef00e524de009d1 # Parent c140f78fbc8f62c9694d3b969d1309570a96f2e7 Tests: removed multiple server certificates from ssl_ocsp.t. Multiple server certificates are not needed to test OCSP verification of client certificates (in contrast to OCSP stapling, where server certificates are verified, and different staples should be correctly returned with different server certificates). And using multiple server certificates causes issues when testing with LibreSSL due to broken sigalgs-based server certificate selection in LibreSSL with TLSv1.3. Accordingly, the test is simplified to do not use multiple server certificates. diff --git a/ssl_ocsp.t b/ssl_ocsp.t --- a/ssl_ocsp.t +++ b/ssl_ocsp.t @@ -63,10 +63,7 @@ http { ssl_verify_depth 2; ssl_client_certificate trusted.crt; - ssl_ciphers DEFAULT:ECCdraft; - - ssl_certificate_key ec.key; - ssl_certificate ec.crt; +# ssl_ciphers DEFAULT:ECCdraft; ssl_certificate_key rsa.key; ssl_certificate rsa.crt; @@ -273,13 +270,8 @@ system("openssl ocsp -index $d/certindex # server cert/key -system("openssl ecparam -genkey -out $d/ec.key -name prime256v1 " - . ">>$d/openssl.out 2>&1") == 0 or die "Can't create EC pem: $!\n"; -system("openssl genrsa -out $d/rsa.key 2048 >>$d/openssl.out 2>&1") == 0 - or die "Can't create RSA pem: $!\n"; - -foreach my $name ('ec', 'rsa') { - system("openssl req -x509 -new -key $d/$name.key " +foreach my $name ('rsa') { + system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 @@ -288,7 +280,7 @@ foreach my $name ('ec', 'rsa') { $t->run_daemon(\&http_daemon, $t, port(8081)); $t->run_daemon(\&http_daemon, $t, port(8082)); -$t->run()->plan(14); +$t->run()->plan(15); $t->waitforsocket("127.0.0.1:" . port(8081)); $t->waitforsocket("127.0.0.1:" . port(8082)); @@ -297,17 +289,17 @@ my $version = get_version(); ############################################################################### -like(get('RSA', 'end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); +like(get('end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); # demonstrate that ocsp int request is failed due to missing resolver -like(get('RSA', 'end', sni => 'resolver'), +like(get('end', sni => 'resolver'), qr/400 Bad.*FAILED:certificate status request failed/s, 'ocsp many failed request'); # demonstrate that ocsp int request is actually made by failing ocsp response -like(get('RSA', 'end', port => 8444), +like(get('end', port => 8444), qr/400 Bad.*FAILED:certificate status request failed/s, 'ocsp many failed'); @@ -323,11 +315,11 @@ system("openssl ocsp -index $d/certindex . ">>$d/openssl.out 2>&1") == 0 or die "Can't create OCSP response: $!\n"; -like(get('RSA', 'end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); +like(get('end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); # store into ssl_ocsp_cache -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); # revoke @@ -346,23 +338,23 @@ system("openssl ocsp -index $d/certindex . ">>$d/openssl.out 2>&1") == 0 or die "Can't create OCSP response: $!\n"; -like(get('RSA', 'end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); +like(get('end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); # with different responder where it's still valid -like(get('RSA', 'end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); +like(get('end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); # with different context to responder where it's still valid -like(get('RSA', 'end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); +like(get('end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); # with cached ocsp response it's still valid -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); # ocsp end response signed with invalid (root) cert, expect HTTP 400 -like(get('ECDSA', 'ec-end'), +like(get('ec-end'), qr/400 Bad.*FAILED:certificate status request failed/s, 'root ca not trusted'); @@ -374,12 +366,12 @@ system("openssl ocsp -index $d/certindex . ">>$d/openssl.out 2>&1") == 0 or die "Can't create EC OCSP response: $!\n"; -like(get('ECDSA', 'ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); +like(get('ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); -my ($s, $ssl) = get('ECDSA', 'ec-end'); +my ($s, $ssl) = get('ec-end'); my $ses = Net::SSLeay::get_session($ssl); -like(get('ECDSA', 'ec-end', ses => $ses), +like(get('ec-end', ses => $ses), qr/200 OK.*SUCCESS:r/s, 'session reused'); # revoke with saved session @@ -401,19 +393,22 @@ system("openssl ocsp -index $d/certindex # reusing session with revoked certificate -like(get('ECDSA', 'ec-end', ses => $ses), +like(get('ec-end', ses => $ses), qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); # regression test for self-signed -like(get('RSA', 'root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); +like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); + +# check for errors + +like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); ############################################################################### sub get { - my ($type, $cert, %extra) = @_; - $type = 'PSS' if $type eq 'RSA' && $version > 0x0303; - my ($s, $ssl) = get_ssl_socket($type, $cert, %extra); + my ($cert, %extra) = @_; + my ($s, $ssl) = get_ssl_socket($cert, %extra); my $cipher = Net::SSLeay::get_cipher($ssl); Test::Nginx::log_core('||', "cipher: $cipher"); my $host = $extra{sni} ? $extra{sni} : 'localhost'; @@ -428,7 +423,7 @@ sub get { } sub get_ssl_socket { - my ($type, $cert, %extra) = @_; + my ($cert, %extra) = @_; my $ses = $extra{ses}; my $sni = $extra{sni}; my $port = $extra{port} || 8443; @@ -450,18 +445,6 @@ sub get_ssl_socket { my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - if (defined $type) { - my $ssleay = Net::SSLeay::SSLeay(); - if ($ssleay < 0x1000200f || $ssleay == 0x20000000) { - Net::SSLeay::CTX_set_cipher_list($ctx, $type) - or die("Failed to set cipher list"); - } else { - # SSL_CTRL_SET_SIGALGS_LIST - Net::SSLeay::CTX_ctrl($ctx, 98, 0, $type . '+SHA256') - or die("Failed to set sigalgs"); - } - } - Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") or die if $cert; my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); From mdounin at mdounin.ru Sat Mar 18 14:15:12 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sat, 18 Mar 2023 17:15:12 +0300 Subject: [PATCH 17 of 20] Tests: fixed stream_ssl_variables.t.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1679148849 -10800 # Sat Mar 18 17:14:09 2023 +0300 # Node ID fc68109603c10abff643444b39cf52daa240a3e2 # Parent 778dcba0f619808fef92999b174c86cf9668ffeb Tests: fixed stream_ssl_variables.t.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/stream_ssl_variables.t b/stream_ssl_variables.t --- a/stream_ssl_variables.t +++ b/stream_ssl_variables.t @@ -101,11 +101,17 @@ is(stream('127.0.0.1:' . port(8080))->re like(Net::SSLeay::read($ssl), qr/^\.:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables'); +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + my $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8081), $ses); like(Net::SSLeay::read($ssl), qr/^r:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables - session reused'); +} + SKIP: { skip 'no sni', 3 unless $t->has_module('sni'); @@ -123,6 +129,11 @@ is(Net::SSLeay::ssl_read_all($ssl), '', ############################################################################### +sub test_tls13 { + ($s, $ssl) = get_ssl_socket(port(8081)); + Net::SSLeay::read($ssl) =~ /TLSv1.3/; +} + sub get_ssl_socket { my ($port, $ses, $name) = @_; From pluknet at nginx.com Mon Mar 20 14:13:34 2023 From: pluknet at nginx.com (=?iso-8859-1?q?Sergey_Kandaurov?=) Date: Mon, 20 Mar 2023 18:13:34 +0400 Subject: [PATCH] Tests: run syslog and error_log tests on win32 Message-ID: # HG changeset patch # User Sergey Kandaurov # Date 1679321601 -14400 # Mon Mar 20 18:13:21 2023 +0400 # Node ID f3225ad9300ee2c11c0dec54b9605e67060b7347 # Parent 1e1d0f3874b0c5b1e399ec76b0198b5c9c265a36 Tests: run syslog and error_log tests on win32. They are supposed to work well now, no reason to skip them. An exception is logging to stderr. diff --git a/error_log.t b/error_log.t --- a/error_log.t +++ b/error_log.t @@ -22,8 +22,6 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/http limit_req/) ->plan(25)->write_file_expand('nginx.conf', <<'EOF'); @@ -110,7 +108,7 @@ EOF open OLDERR, ">&", \*STDERR; open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; open my $stderr, '<', $t->testdir() . '/stderr' - or die "Can't open stderr file: $!"; + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; $t->run(); @@ -123,40 +121,69 @@ open STDERR, ">&", \*OLDERR; http_get('/'); SKIP: { - skip "no --with-debug", 3 unless $t->has_module('--with-debug'); http_get('/debug'); isnt(lines($t, 'e_debug_debug.log', '[debug]'), 0, 'file debug debug'); is(lines($t, 'e_debug_info.log', '[debug]'), 0, 'file debug info'); + +SKIP: { +skip 'win32', 1 if $^O eq 'MSWin32'; + isnt(lines($t, 'stderr', '[debug]'), 0, 'stderr debug'); } +} + http_get('/info'); is(lines($t, 'e_info_debug.log', '[info]'), 1, 'file info debug'); is(lines($t, 'e_info_info.log', '[info]'), 1, 'file info info'); is(lines($t, 'e_info_notice.log', '[info]'), 0, 'file info notice'); + +SKIP: { +skip 'win32', 1 if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[info]'), 1, 'stderr info'); +} + http_get('/notice'); is(lines($t, 'e_notice_info.log', '[notice]'), 1, 'file notice info'); is(lines($t, 'e_notice_notice.log', '[notice]'), 1, 'file notice notice'); is(lines($t, 'e_notice_warn.log', '[notice]'), 0, 'file notice warn'); + +SKIP: { +skip 'win32', 1 if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[notice]'), 1, 'stderr notice'); +} + http_get('/warn'); is(lines($t, 'e_warn_notice.log', '[warn]'), 1, 'file warn notice'); is(lines($t, 'e_warn_warn.log', '[warn]'), 1, 'file warn warn'); is(lines($t, 'e_warn_error.log', '[warn]'), 0, 'file warn error'); + +SKIP: { +skip 'win32', 1 if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[warn]'), 1, 'stderr warn'); +} + http_get('/error'); is(lines($t, 'e_error_warn.log', '[error]'), 1, 'file error warn'); is(lines($t, 'e_error_error.log', '[error]'), 1, 'file error error'); is(lines($t, 'e_error_alert.log', '[error]'), 0, 'file error alert'); + +SKIP: { +skip 'win32', 1 if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[error]'), 1, 'stderr error'); +} + # count log messages emitted with various error_log levels http_get('/file_low'); @@ -168,6 +195,9 @@ is(lines($t, 'e_multi.log', '[error]'), http_get('/file_high'); is(lines($t, 'e_multi_high.log', '[error]'), 1, 'file high'); +SKIP: { +skip 'win32', 3 if $^O eq 'MSWin32'; + http_get('/stderr_low'); is(lines($t, 'stderr', '[error]'), 2, 'stderr low'); @@ -177,6 +207,8 @@ is(lines($t, 'stderr', '[error]'), 2, 's http_get('/stderr_high'); is(lines($t, 'stderr', '[error]'), 1, 'stderr high'); +} + ############################################################################### sub lines { diff --git a/mail_error_log.t b/mail_error_log.t --- a/mail_error_log.t +++ b/mail_error_log.t @@ -26,8 +26,6 @@ use Test::Nginx::IMAP; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/mail imap http rewrite/); $t->plan(30)->write_file_expand('nginx.conf', <<'EOF'); @@ -87,7 +85,7 @@ EOF open OLDERR, ">&", \*STDERR; open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; open my $stderr, '<', $t->testdir() . '/stderr' - or die "Can't open stderr file: $!"; + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; $t->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon); $t->run_daemon(\&syslog_daemon, port(8981), $t, 's_glob.log'); @@ -117,9 +115,15 @@ isnt(lines($t, 'e_debug.log', '[debug]') isnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info'); is(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info'); + +SKIP: { +skip 'win32', 2 if $^O eq 'MSWin32'; + isnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info'); is(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info'); +} + # multiple error_log like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); @@ -255,13 +259,16 @@ sub syslog_daemon { LocalAddr => "127.0.0.1:$port" ); - open my $fh, '>', $t->testdir() . '/' . $file; - select $fh; $| = 1; + my $path = $t->testdir() . '/' . $file; + open my $fh, '>', $path; + close $fh; while (1) { my $buffer; $s->recv($buffer, 4096); + open $fh, '>>', $path; print $fh $buffer . "\n"; + close $fh; } } diff --git a/stream_error_log.t b/stream_error_log.t --- a/stream_error_log.t +++ b/stream_error_log.t @@ -26,8 +26,6 @@ use Test::Nginx::Stream qw/ stream /; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/stream/)->plan(34); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -75,7 +73,7 @@ EOF open OLDERR, ">&", \*STDERR; open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; open my $stderr, '<', $t->testdir() . '/stderr' - or die "Can't open stderr file: $!"; + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; $t->run_daemon(\&stream_daemon); $t->run_daemon(\&syslog_daemon, port(8983), $t, 's_glob.log'); @@ -104,9 +102,15 @@ isnt(lines($t, 'e_debug.log', '[debug]') isnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info'); is(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info'); + +SKIP: { +skip 'win32', 2 if $^O eq 'MSWin32'; + isnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info'); is(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info'); +} + # multiple error_log like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); @@ -135,8 +139,14 @@ my $msg = 'no live upstreams while conne unlike($t->read_file('e_glob.log'), qr/$msg/ms, 'stream error in global'); like($t->read_file('e_info.log'), qr/$msg/ms, 'stream error in info'); +unlike($t->read_file('e_emerg.log'), qr/$msg/ms, 'stream error in emerg'); + +SKIP: { +skip 'win32', 1 if $^O eq 'MSWin32'; + like($t->read_file('stderr'), qr/$msg/ms, 'stream error in info stderr'); -unlike($t->read_file('e_emerg.log'), qr/$msg/ms, 'stream error in emerg'); + +} $msg = "bytes from/to client:5/4, bytes from/to upstream:4/5"; @@ -264,13 +274,16 @@ sub syslog_daemon { LocalAddr => "127.0.0.1:$port" ); - open my $fh, '>', $t->testdir() . '/' . $file; - select $fh; $| = 1; + my $path = $t->testdir() . '/' . $file; + open my $fh, '>', $path; + close $fh; while (1) { my $buffer; $s->recv($buffer, 4096); + open $fh, '>>', $path; print $fh $buffer . "\n"; + close $fh; } } diff --git a/syslog.t b/syslog.t --- a/syslog.t +++ b/syslog.t @@ -25,8 +25,6 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(62); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -186,7 +184,6 @@ is($lines[1], $lines[2], 'error_log many # error_log log levels SKIP: { - skip "no --with-debug", 1 unless $t->has_module('--with-debug'); isnt(syslog_lines('/debug', '[debug]'), 0, 'debug'); @@ -340,13 +337,16 @@ sub syslog_daemon { LocalAddr => "127.0.0.1:$port" ); - open my $fh, '>', $t->testdir() . '/' . $file; - select $fh; $| = 1; + my $path = $t->testdir() . '/' . $file; + open my $fh, '>', $path; + close $fh; while (1) { my $buffer; $s->recv($buffer, 4096); + open $fh, '>>', $path; print $fh $buffer . "\n"; + close $fh; } } From pluknet at nginx.com Mon Mar 20 14:58:32 2023 From: pluknet at nginx.com (=?iso-8859-1?q?Sergey_Kandaurov?=) Date: Mon, 20 Mar 2023 18:58:32 +0400 Subject: [PATCH] Win32: PCRE2 Unicode support with MSVC Message-ID: # HG changeset patch # User Sergey Kandaurov # Date 1679324252 -14400 # Mon Mar 20 18:57:32 2023 +0400 # Node ID d0b013a7050e00613804b399ae2ca74551b2a071 # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 Win32: PCRE2 Unicode support with MSVC. Unicode support in PCRE2 is enabled by default on configure/cmake side by defining SUPPORT_UNICODE. Previously, this macro was not defined when compiling directly PCRE2 sources for Windows with MSVC. In particular, this change allows to specify Unicode properties, such as \P, \p, or \X, as caught by http_server_name.t adjusted to run on Windows: nginx: [emerg] pcre2_compile() failed: this version of PCRE2 does not have support for \P, \p, or \X diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make --- a/auto/lib/pcre/make +++ b/auto/lib/pcre/make @@ -61,7 +61,7 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then PCRE_CFLAGS = -O2 -Ob1 -Oi -Gs $LIBC $CPU_OPT PCRE_FLAGS = -DHAVE_CONFIG_H -DPCRE2_STATIC -DPCRE2_CODE_UNIT_WIDTH=8 \\ - -DHAVE_MEMMOVE + -DHAVE_MEMMOVE -DSUPPORT_UNICODE PCRE_SRCS = $ngx_pcre_srcs PCRE_OBJS = $ngx_pcre_objs From pluknet at nginx.com Mon Mar 20 15:34:06 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 20 Mar 2023 19:34:06 +0400 Subject: [PATCH 2 of 3] Tests: handling of EAGAIN from sysread() with IO::Socket::SSL In-Reply-To: References: <49d12f8c4cf69e1cbe7f.1678424405@vm-bsd.mdounin.ru> Message-ID: <8F956F83-BEB6-4F66-BB5C-511ECC16CA86@nginx.com> > On 11 Mar 2023, at 13:30, Maxim Dounin wrote: > > Hello! > > On Fri, Mar 10, 2023 at 08:00:05AM +0300, Maxim Dounin wrote: > >> # HG changeset patch >> # User Maxim Dounin >> # Date 1678424071 -10800 >> # Fri Mar 10 07:54:31 2023 +0300 >> # Node ID 49d12f8c4cf69e1cbe7feccae3b0ea1ac2ca8c2f >> # Parent fdebeebd07b160f1d30e18d56e64dfb08570f8b1 >> Tests: handling of EAGAIN from sysread() with IO::Socket::SSL. >> >> With IO::Socket::SSL, when select() reports that the socket is readable, >> reading from it might still fail with EAGAIN, since no application data is >> available in the socket. In particular, this might happen with TLSv1.3 >> when a session ticket is received after the handshake. Fix is to explicitly >> check for EAGAIN errors. > > Err, IO::Socket::SSL actually generates EWOULDBLOCK rather than > EAGAIN, and this is important on some systems (notably Windows). > > s/EAGAIN/EWOULDBLOCK/g; > > # HG changeset patch > # User Maxim Dounin > # Date 1678522238 -10800 > # Sat Mar 11 11:10:38 2023 +0300 > # Node ID 0fefa04c5be1e8095072d176cdf847c7c3766fbf > # Parent fdebeebd07b160f1d30e18d56e64dfb08570f8b1 > Tests: handling of EWOULDBLOCK from sysread() with IO::Socket::SSL. > > With IO::Socket::SSL, when select() reports that the socket is readable, > reading from it might still fail with EWOULDBLOCK, since no application > data is available in the socket. In particular, this might happen with > TLSv1.3 when a session ticket is received after the handshake. Fix is > to explicitly check for EWOULDBLOCK errors. > > diff --git a/lib/Test/Nginx/IMAP.pm b/lib/Test/Nginx/IMAP.pm > --- a/lib/Test/Nginx/IMAP.pm > +++ b/lib/Test/Nginx/IMAP.pm > @@ -68,7 +68,9 @@ sub getline { > while (IO::Select->new($socket)->can_read(8)) { > $socket->blocking(0); > my $n = $socket->sysread(my $buf, 1024); > + my $again = !defined $n && $!{EWOULDBLOCK}; > $socket->blocking(1); > + next if $again; > last unless $n; > > $self->{_read_buffer} .= $buf; > diff --git a/lib/Test/Nginx/POP3.pm b/lib/Test/Nginx/POP3.pm > --- a/lib/Test/Nginx/POP3.pm > +++ b/lib/Test/Nginx/POP3.pm > @@ -68,7 +68,9 @@ sub getline { > while (IO::Select->new($socket)->can_read(8)) { > $socket->blocking(0); > my $n = $socket->sysread(my $buf, 1024); > + my $again = !defined $n && $!{EWOULDBLOCK}; > $socket->blocking(1); > + next if $again; > last unless $n; > > $self->{_read_buffer} .= $buf; > diff --git a/lib/Test/Nginx/SMTP.pm b/lib/Test/Nginx/SMTP.pm > --- a/lib/Test/Nginx/SMTP.pm > +++ b/lib/Test/Nginx/SMTP.pm > @@ -68,7 +68,9 @@ sub getline { > while (IO::Select->new($socket)->can_read(8)) { > $socket->blocking(0); > my $n = $socket->sysread(my $buf, 1024); > + my $again = !defined $n && $!{EWOULDBLOCK}; > $socket->blocking(1); > + next if $again; > last unless $n; > > $self->{_read_buffer} .= $buf; > diff --git a/lib/Test/Nginx/Stream.pm b/lib/Test/Nginx/Stream.pm > --- a/lib/Test/Nginx/Stream.pm > +++ b/lib/Test/Nginx/Stream.pm > @@ -84,8 +84,10 @@ sub read { > $s = $self->{_socket}; > > $s->blocking(0); > - if (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { > - $s->sysread($buf, 1024); > + while (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { > + my $n = $s->sysread($buf, 1024); > + next if !defined $n && $!{EWOULDBLOCK}; > + last; > } > > log_in($buf); > Looks good. Note that this occurs with non-blocking sockets, as seen with diff. From POD: Using Non-Blocking Sockets If you have a non-blocking socket, the expected behavior on read, write, accept or connect is to set $! to EWOULDBLOCK if the operation cannot be completed immediately. Note that EWOULDBLOCK is the same as EAGAIN on UNIX systems, but is different on Windows. This reminds me how OpenSSL has changed the SSL_MODE_AUTO_RETRY default, then IO::Socket::SSL disabled it again on non-blocking sockets. -- Sergey Kandaurov From pluknet at nginx.com Mon Mar 20 15:35:50 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 20 Mar 2023 19:35:50 +0400 Subject: [PATCH 1 of 3] Tests: style In-Reply-To: References: Message-ID: <2909E0E4-C6F8-4650-81BA-EF1E4CE45E49@nginx.com> > On 10 Mar 2023, at 09:00, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1678424069 -10800 > # Fri Mar 10 07:54:29 2023 +0300 > # Node ID fdebeebd07b160f1d30e18d56e64dfb08570f8b1 > # Parent 1e1d0f3874b0c5b1e399ec76b0198b5c9c265a36 > Tests: style. > > diff --git a/lib/Test/Nginx/IMAP.pm b/lib/Test/Nginx/IMAP.pm > --- a/lib/Test/Nginx/IMAP.pm > +++ b/lib/Test/Nginx/IMAP.pm > @@ -66,9 +66,9 @@ sub getline { > } > > while (IO::Select->new($socket)->can_read(8)) { > - $socket->blocking(0); > + $socket->blocking(0); > my $n = $socket->sysread(my $buf, 1024); > - $socket->blocking(1); > + $socket->blocking(1); > last unless $n; > > $self->{_read_buffer} .= $buf; > diff --git a/lib/Test/Nginx/Stream.pm b/lib/Test/Nginx/Stream.pm > --- a/lib/Test/Nginx/Stream.pm > +++ b/lib/Test/Nginx/Stream.pm > @@ -86,7 +86,7 @@ sub read { > $s->blocking(0); > if (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) { > $s->sysread($buf, 1024); > - }; > + } > > log_in($buf); > return $buf; Looks good. For the record, there are similar (unrelated) style bugs, conveniently found with grep -n '\t ' *.t auth_basic.t:56: } js_fetch.t:689: print $client "0" . CRLF . CRLF; slice.t:117: } -- Sergey Kandaurov From pluknet at nginx.com Mon Mar 20 15:48:13 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 20 Mar 2023 19:48:13 +0400 Subject: [PATCH 3 of 3] Tests: adapted session reuse tests to work with TLSv1.3 In-Reply-To: <946c3b39d1f9adf3f96f.1678424406@vm-bsd.mdounin.ru> References: <946c3b39d1f9adf3f96f.1678424406@vm-bsd.mdounin.ru> Message-ID: <976BD0E8-0B6B-4C96-9255-54B6F915B7F1@nginx.com> > On 10 Mar 2023, at 09:00, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1678424073 -10800 > # Fri Mar 10 07:54:33 2023 +0300 > # Node ID 946c3b39d1f9adf3f96f1c04963539ecd1c63a8f > # Parent 49d12f8c4cf69e1cbe7feccae3b0ea1ac2ca8c2f > Tests: adapted session reuse tests to work with TLSv1.3. > > In TLSv1.3, session tickets are sent after the handshake, and saving session > right after the handshake is not going to work. To properly test session > resumption, sessions are now saved after some data exchange. > > diff --git a/mail_ssl.t b/mail_ssl.t > --- a/mail_ssl.t > +++ b/mail_ssl.t > @@ -182,24 +182,28 @@ my $s = Test::Nginx::IMAP->new(); > my ($ssl, $ses); > > ($s, $ssl) = get_ssl_socket(8145); > +Net::SSLeay::read($ssl); > $ses = Net::SSLeay::get_session($ssl); > > [..] Good for me. -- Sergey Kandaurov From mdounin at mdounin.ru Mon Mar 20 13:19:54 2023 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 20 Mar 2023 16:19:54 +0300 Subject: [PATCH] SSL: enabled TLSv1.3 by default Message-ID: <3496c963b43636104fa7.1679318394@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1678891161 -10800 # Wed Mar 15 17:39:21 2023 +0300 # Node ID 3496c963b43636104fa794e84969eb077abec6b0 # Parent b97f2b983d1564d29280d03828503edca21a79ee SSL: enabled TLSv1.3 by default. diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -4473,8 +4473,9 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t prev->upstream.ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -3734,8 +3734,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t prev->upstream.ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -632,8 +632,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, NGX_SSL_BUFSIZE); diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1875,8 +1875,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t prev->upstream.ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -360,8 +360,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, prev->prefer_server_ciphers, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -2163,8 +2163,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf prev->ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -703,8 +703,9 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf prev->prefer_server_ciphers, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); From thresh at nginx.com Mon Mar 20 18:38:19 2023 From: thresh at nginx.com (=?iso-8859-1?q?Konstantin_Pavlov?=) Date: Mon, 20 Mar 2023 11:38:19 -0700 Subject: [PATCH] Linux packages: added Amazon Linux 2023 Message-ID: <23d3cabaab95fb09ea40.1679337499@QGCD7XG9R9> # HG changeset patch # User Konstantin Pavlov # Date 1679336457 25200 # Mon Mar 20 11:20:57 2023 -0700 # Node ID 23d3cabaab95fb09ea40f113759f4eaed99ec9d7 # Parent 0f468b4e01d67cab96a44e0886dda3180104ae1a Linux packages: added Amazon Linux 2023. diff -r 0f468b4e01d6 -r 23d3cabaab95 xml/en/linux_packages.xml --- a/xml/en/linux_packages.xml Thu Mar 09 22:20:24 2023 +0000 +++ b/xml/en/linux_packages.xml Mon Mar 20 11:20:57 2023 -0700 @@ -7,7 +7,7 @@
+ rev="83">
@@ -166,6 +166,11 @@ versions: x86_64, aarch64/arm64 + +2023 +x86_64, aarch64/arm64 + + @@ -531,7 +536,7 @@ Install the prerequisites: sudo yum install yum-utils -To set up the yum repository, create the file named +To set up the yum repository for Amazon Linux 2, create the file named /etc/yum.repos.d/nginx.repo with the following contents: @@ -553,6 +558,28 @@ gpgkey=https://nginx.org/keys/nginx_sign module_hotfixes=true +To set up the yum repository for Amazon Linux 2023, create the file named +/etc/yum.repos.d/nginx.repo +with the following contents: + + +[nginx-stable] +name=nginx stable repo +baseurl=http://nginx.org/packages/amzn/2023/$basearch/ +gpgcheck=1 +enabled=1 +gpgkey=https://nginx.org/keys/nginx_signing.key +module_hotfixes=true + +[nginx-mainline] +name=nginx mainline repo +baseurl=http://nginx.org/packages/mainline/amzn/2023/$basearch/ +gpgcheck=1 +enabled=0 +gpgkey=https://nginx.org/keys/nginx_signing.key +module_hotfixes=true + + By default, the repository for stable nginx packages is used. If you would like to use mainline nginx packages, run the following command: diff -r 0f468b4e01d6 -r 23d3cabaab95 xml/ru/linux_packages.xml --- a/xml/ru/linux_packages.xml Thu Mar 09 22:20:24 2023 +0000 +++ b/xml/ru/linux_packages.xml Mon Mar 20 11:20:57 2023 -0700 @@ -7,7 +7,7 @@
+ rev="83">
@@ -166,6 +166,11 @@ x86_64, aarch64/arm64 + +2023 +x86_64, aarch64/arm64 + + @@ -528,7 +533,7 @@ sudo apk add nginx-module-image-filter at n sudo yum install yum-utils -Для подключения yum-репозитория создайте файл с именем +Для подключения yum-репозитория для Amazon Linux 2 создайте файл с именем /etc/yum.repos.d/nginx.repo со следующим содержимым: @@ -550,6 +555,28 @@ gpgkey=https://nginx.org/keys/nginx_sign module_hotfixes=true +Для подключения yum-репозитория для Amazon Linux 2023 создайте файл с именем +/etc/yum.repos.d/nginx.repo +со следующим содержимым: + + +[nginx-stable] +name=nginx stable repo +baseurl=http://nginx.org/packages/amzn/2023/$basearch/ +gpgcheck=1 +enabled=1 +gpgkey=https://nginx.org/keys/nginx_signing.key +module_hotfixes=true + +[nginx-mainline] +name=nginx mainline repo +baseurl=http://nginx.org/packages/mainline/amzn/2023/$basearch/ +gpgcheck=1 +enabled=0 +gpgkey=https://nginx.org/keys/nginx_signing.key +module_hotfixes=true + + По умолчанию используется репозиторий для стабильной версии nginx. Если предпочтительно использовать пакеты для основной версии nginx, выполните следующую команду: From mdounin at mdounin.ru Mon Mar 20 23:55:38 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 21 Mar 2023 02:55:38 +0300 Subject: [PATCH] Win32: PCRE2 Unicode support with MSVC In-Reply-To: References: Message-ID: Hello! On Mon, Mar 20, 2023 at 06:58:32PM +0400, Sergey Kandaurov wrote: > # HG changeset patch > # User Sergey Kandaurov > # Date 1679324252 -14400 > # Mon Mar 20 18:57:32 2023 +0400 > # Node ID d0b013a7050e00613804b399ae2ca74551b2a071 > # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 > Win32: PCRE2 Unicode support with MSVC. > > Unicode support in PCRE2 is enabled by default on configure/cmake side > by defining SUPPORT_UNICODE. Previously, this macro was not defined > when compiling directly PCRE2 sources for Windows with MSVC. > > In particular, this change allows to specify Unicode properties, such as > \P, \p, or \X, as caught by http_server_name.t adjusted to run on Windows: > > nginx: [emerg] pcre2_compile() failed: this version of PCRE2 does not have > support for \P, \p, or \X > > diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make > --- a/auto/lib/pcre/make > +++ b/auto/lib/pcre/make > @@ -61,7 +61,7 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then > > PCRE_CFLAGS = -O2 -Ob1 -Oi -Gs $LIBC $CPU_OPT > PCRE_FLAGS = -DHAVE_CONFIG_H -DPCRE2_STATIC -DPCRE2_CODE_UNIT_WIDTH=8 \\ > - -DHAVE_MEMMOVE > + -DHAVE_MEMMOVE -DSUPPORT_UNICODE > > PCRE_SRCS = $ngx_pcre_srcs > PCRE_OBJS = $ngx_pcre_objs The PCRE2 compilation in auto/lib/pcre/make mostly matches PCRE compilation in auto/lib/pcre/makefile.msvc, and it never tried to enable Unicode / UTF-8 support. This in turn matches PCRE configure behaviour: UTF-8 support is disabled by default and needs to be explicitly enabled. While we might consider enabling Unicode support for PCRE2, since it is now enabled by default in PCRE2 (or for both PCRE and PCRE2, since it is something usually expected to work nowadays), for tests a better solution might be to don't rely on this. Unicode / UTF-8 support might not be available on various other platforms as well, so it's generally might be a good idea to adjust tests to tolerate PCRE/PCRE2 compiled without Unicode / UTF-8 support. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon Mar 20 23:59:29 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 21 Mar 2023 02:59:29 +0300 Subject: [PATCH 3 of 3] Tests: adapted session reuse tests to work with TLSv1.3 In-Reply-To: <976BD0E8-0B6B-4C96-9255-54B6F915B7F1@nginx.com> References: <946c3b39d1f9adf3f96f.1678424406@vm-bsd.mdounin.ru> <976BD0E8-0B6B-4C96-9255-54B6F915B7F1@nginx.com> Message-ID: Hello! On Mon, Mar 20, 2023 at 07:48:13PM +0400, Sergey Kandaurov wrote: > > On 10 Mar 2023, at 09:00, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1678424073 -10800 > > # Fri Mar 10 07:54:33 2023 +0300 > > # Node ID 946c3b39d1f9adf3f96f1c04963539ecd1c63a8f > > # Parent 49d12f8c4cf69e1cbe7feccae3b0ea1ac2ca8c2f > > Tests: adapted session reuse tests to work with TLSv1.3. > > > > In TLSv1.3, session tickets are sent after the handshake, and saving session > > right after the handshake is not going to work. To properly test session > > resumption, sessions are now saved after some data exchange. > > > > diff --git a/mail_ssl.t b/mail_ssl.t > > --- a/mail_ssl.t > > +++ b/mail_ssl.t > > @@ -182,24 +182,28 @@ my $s = Test::Nginx::IMAP->new(); > > my ($ssl, $ses); > > > > ($s, $ssl) = get_ssl_socket(8145); > > +Net::SSLeay::read($ssl); > > $ses = Net::SSLeay::get_session($ssl); > > > > [..] > > Good for me. Thanks, pushed to http://mdounin.ru/hg/nginx-tests. -- Maxim Dounin http://mdounin.ru/ From v.zhestikov at f5.com Tue Mar 21 04:18:03 2023 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Tue, 21 Mar 2023 04:18:03 +0000 Subject: [njs] Added support for export {name as default} statement. Message-ID: details: https://hg.nginx.org/njs/rev/fbd36220ea17 branches: changeset: 2073:fbd36220ea17 user: Vadim Zhestikov date: Mon Mar 20 21:09:58 2023 -0700 description: Added support for export {name as default} statement. This fixes #624 issue on Github. diffstat: src/njs_parser.c | 78 +++++++++++++++++++++++++++++++++- test/js/import_as_default.t.js | 9 +++ test/js/import_as_default_compare.t.js | 14 ++++++ test/js/import_as_default_double.t.js | 9 +++ test/js/module/lib4.js | 2 + test/js/module/lib5.js | 6 ++ test/js/module/lib6-1.js | 6 ++ test/js/module/lib6.js | 6 ++ 8 files changed, 128 insertions(+), 2 deletions(-) diffs (175 lines): diff -r 95f57822bdaa -r fbd36220ea17 src/njs_parser.c --- a/src/njs_parser.c Fri Mar 10 14:15:29 2023 -0800 +++ b/src/njs_parser.c Mon Mar 20 21:09:58 2023 -0700 @@ -7973,6 +7973,9 @@ njs_parser_export(njs_parser_t *parser, njs_queue_link_t *current) { njs_parser_node_t *node; + njs_lexer_token_t *peek; + + static const njs_str_t as_string = njs_str("as"); if (!parser->module) { njs_parser_syntax_error(parser, "Illegal export statement"); @@ -7980,8 +7983,79 @@ njs_parser_export(njs_parser_t *parser, } if (token->type != NJS_TOKEN_DEFAULT) { - njs_parser_syntax_error(parser, "Non-default export is not supported"); - return NJS_DONE; + + if (token->type != NJS_TOKEN_OPEN_BRACE) { + njs_parser_syntax_error(parser, + "Non-default export is not supported"); + return NJS_DONE; + } + + /* + * 'export {' + * supported only: export {identifier as default}; + */ + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (njs_slow_path(token == NULL)) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_NAME) { + njs_parser_syntax_error(parser, "Identifier expected"); + return NJS_DONE; + } + + peek = njs_lexer_peek_token(parser->lexer, token, 0); + if (njs_slow_path(peek == NULL)) { + return NJS_ERROR; + } + + if (peek->type != NJS_TOKEN_NAME || + !njs_strstr_eq(&peek->text, &as_string)) + { + njs_parser_syntax_error(parser, "'as' expected"); + return NJS_DONE; + } + + peek = njs_lexer_peek_token(parser->lexer, peek, 0); + if (njs_slow_path(peek == NULL)) { + return NJS_ERROR; + } + + if (peek->type != NJS_TOKEN_DEFAULT) { + njs_parser_syntax_error(parser, + "Non-default export is not supported"); + return NJS_DONE; + } + + peek = njs_lexer_peek_token(parser->lexer, peek, 1); + if (njs_slow_path(token == NULL)) { + return NJS_ERROR; + } + + if (peek->type != NJS_TOKEN_CLOSE_BRACE) { + njs_parser_syntax_error(parser, "Close brace is expected"); + return NJS_DONE; + } + + node = njs_parser_node_new(parser, NJS_TOKEN_EXPORT); + if (node == NULL) { + return NJS_ERROR; + } + + node->token_line = parser->line; + node->right = njs_parser_reference(parser, token); + if (node->right == NULL) { + return NJS_ERROR; + } + + parser->node = node; + + njs_lexer_consume_token(parser->lexer, 4); + + return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); diff -r 95f57822bdaa -r fbd36220ea17 test/js/import_as_default.t.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/import_as_default.t.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,9 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +---*/ + +import imp from 'lib4.js'; + +assert.sameValue(imp, 10); diff -r 95f57822bdaa -r fbd36220ea17 test/js/import_as_default_compare.t.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/import_as_default_compare.t.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,14 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module] +---*/ + +import a from 'lib6.js'; +import b from 'lib6-1.js'; + +assert.sameValue(a.a, 1); +assert.sameValue(a.b, 2); + +assert.sameValue(a.a, b.a); +assert.sameValue(a.b, b.b); diff -r 95f57822bdaa -r fbd36220ea17 test/js/import_as_default_double.t.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/import_as_default_double.t.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,9 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module] +negative: + phase: runtime +---*/ + +import m from 'lib5.js'; diff -r 95f57822bdaa -r fbd36220ea17 test/js/module/lib4.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/module/lib4.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,2 @@ +var var10 = 10; +export { var10 as default }; diff -r 95f57822bdaa -r fbd36220ea17 test/js/module/lib5.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/module/lib5.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,6 @@ +var uu = 10; +var uu1 = 11; + +export { uu as default }; + +export { uu1 as default }; diff -r 95f57822bdaa -r fbd36220ea17 test/js/module/lib6-1.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/module/lib6-1.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,6 @@ +var obj = { + a:1, + b:2 +} + +export default obj; diff -r 95f57822bdaa -r fbd36220ea17 test/js/module/lib6.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/js/module/lib6.js Mon Mar 20 21:09:58 2023 -0700 @@ -0,0 +1,6 @@ +var obj = { + a:1, + b:2 +} + +export {obj as default}; From xeioex at nginx.com Tue Mar 21 04:26:39 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 21 Mar 2023 04:26:39 +0000 Subject: [njs] Crypto: added Hash.copy() method. Message-ID: details: https://hg.nginx.org/njs/rev/edf1c2aef957 branches: changeset: 2074:edf1c2aef957 user: Dmitry Volyntsev date: Mon Mar 20 21:25:47 2023 -0700 description: Crypto: added Hash.copy() method. This closes #625 issue on Github. diffstat: external/njs_crypto_module.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/test/njs_unit_test.c | 20 ++++++++++++++++++++ 2 files changed, 62 insertions(+), 0 deletions(-) diffs (96 lines): diff -r fbd36220ea17 -r edf1c2aef957 external/njs_crypto_module.c --- a/external/njs_crypto_module.c Mon Mar 20 21:09:58 2023 -0700 +++ b/external/njs_crypto_module.c Mon Mar 20 21:25:47 2023 -0700 @@ -67,6 +67,8 @@ static njs_int_t njs_hash_prototype_upda njs_uint_t nargs, njs_index_t hmac); static njs_int_t njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t hmac); +static njs_int_t njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t hmac); static njs_int_t njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); @@ -173,6 +175,16 @@ static njs_external_t njs_ext_crypto_ha { .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("copy"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_hash_prototype_copy, + } + }, + + { + .flags = NJS_EXTERN_METHOD, .name.string = njs_str("constructor"), .writable = 1, .configurable = 1, @@ -471,6 +483,36 @@ exception: static njs_int_t +njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_digest_t *dgst, *copy; + + dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, njs_argument(args, 0)); + if (njs_slow_path(dgst == NULL)) { + njs_type_error(vm, "\"this\" is not a hash object"); + return NJS_ERROR; + } + + if (njs_slow_path(dgst->alg == NULL)) { + njs_error(vm, "Digest already called"); + return NJS_ERROR; + } + + copy = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_digest_t)); + if (njs_slow_path(copy == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + memcpy(copy, dgst, sizeof(njs_digest_t)); + + return njs_vm_external_create(vm, njs_vm_retval(vm), + njs_crypto_hash_proto_id, copy, 0); +} + + +static njs_int_t njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { diff -r fbd36220ea17 -r edf1c2aef957 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Mon Mar 20 21:09:58 2023 -0700 +++ b/src/test/njs_unit_test.c Mon Mar 20 21:25:47 2023 -0700 @@ -20360,6 +20360,26 @@ static njs_unit_test_t njs_crypto_modul "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=," "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM") }, + { njs_str("const crypto = require('crypto');" + "let hash = crypto.createHash('sha256');" + "let digests = [];" + "hash.update('one');" + "digests.push(hash.copy().digest('hex'));" + "hash.update('two');" + "digests.push(hash.copy().digest('hex'));" + "hash.update('three');" + "digests.push(hash.copy().digest('hex'));" + "digests"), + njs_str("7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed," + "25b6746d5172ed6352966a013d93ac846e1110d5a25e8f183b5931f4688842a1," + "4592092e1061c7ea85af2aed194621cc17a2762bae33a79bf8ce33fd0168b801") }, + + { njs_str("const crypto = require('crypto');" + "let hash = crypto.createHash('sha256');" + "hash.update('one').digest();" + "hash.copy()"), + njs_str("Error: Digest already called") }, + { njs_str("var hash = require('crypto').createHash;" "njs.dump(['', 'abc'.repeat(100)].map(v => {" " return ['md5', 'sha1', 'sha256'].map(h => {" From yar at nginx.com Tue Mar 21 07:19:09 2023 From: yar at nginx.com (=?utf-8?q?Yaroslav_Zhuravlev?=) Date: Tue, 21 Mar 2023 08:19:09 +0100 Subject: [PATCH] Added TLSv1.3 to the default value of ssl_protocols and friends Message-ID: <477d0fe1e6cb95533ffb.1679383149@ORK-ML-00007151> xml/en/docs/http/configuring_https_servers.xml | 13 +++++++++---- xml/en/docs/http/ngx_http_grpc_module.xml | 4 ++-- xml/en/docs/http/ngx_http_proxy_module.xml | 4 ++-- xml/en/docs/http/ngx_http_ssl_module.xml | 6 +++--- xml/en/docs/http/ngx_http_uwsgi_module.xml | 4 ++-- xml/en/docs/mail/ngx_mail_ssl_module.xml | 6 +++--- xml/en/docs/stream/ngx_stream_proxy_module.xml | 4 ++-- xml/en/docs/stream/ngx_stream_ssl_module.xml | 6 +++--- xml/ru/docs/http/configuring_https_servers.xml | 13 +++++++++---- xml/ru/docs/http/ngx_http_grpc_module.xml | 4 ++-- xml/ru/docs/http/ngx_http_proxy_module.xml | 4 ++-- xml/ru/docs/http/ngx_http_ssl_module.xml | 6 +++--- xml/ru/docs/http/ngx_http_uwsgi_module.xml | 4 ++-- xml/ru/docs/mail/ngx_mail_ssl_module.xml | 6 +++--- xml/ru/docs/stream/ngx_stream_proxy_module.xml | 4 ++-- xml/ru/docs/stream/ngx_stream_ssl_module.xml | 6 +++--- 16 files changed, 52 insertions(+), 42 deletions(-) -------------- next part -------------- A non-text attachment was scrubbed... Name: nginx.org.patch Type: text/x-patch Size: 16654 bytes Desc: not available URL: From pluknet at nginx.com Tue Mar 21 09:50:58 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 21 Mar 2023 13:50:58 +0400 Subject: [PATCH] Win32: PCRE2 Unicode support with MSVC In-Reply-To: References: Message-ID: <066209D7-B63D-4A51-98C1-2CD0CFA33BFB@nginx.com> > On 21 Mar 2023, at 03:55, Maxim Dounin wrote: > > Hello! > > On Mon, Mar 20, 2023 at 06:58:32PM +0400, Sergey Kandaurov wrote: > >> # HG changeset patch >> # User Sergey Kandaurov >> # Date 1679324252 -14400 >> # Mon Mar 20 18:57:32 2023 +0400 >> # Node ID d0b013a7050e00613804b399ae2ca74551b2a071 >> # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 >> Win32: PCRE2 Unicode support with MSVC. >> >> Unicode support in PCRE2 is enabled by default on configure/cmake side >> by defining SUPPORT_UNICODE. Previously, this macro was not defined >> when compiling directly PCRE2 sources for Windows with MSVC. >> >> In particular, this change allows to specify Unicode properties, such as >> \P, \p, or \X, as caught by http_server_name.t adjusted to run on Windows: >> >> nginx: [emerg] pcre2_compile() failed: this version of PCRE2 does not have >> support for \P, \p, or \X >> >> diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make >> --- a/auto/lib/pcre/make >> +++ b/auto/lib/pcre/make >> @@ -61,7 +61,7 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then >> >> PCRE_CFLAGS = -O2 -Ob1 -Oi -Gs $LIBC $CPU_OPT >> PCRE_FLAGS = -DHAVE_CONFIG_H -DPCRE2_STATIC -DPCRE2_CODE_UNIT_WIDTH=8 \\ >> - -DHAVE_MEMMOVE >> + -DHAVE_MEMMOVE -DSUPPORT_UNICODE >> >> PCRE_SRCS = $ngx_pcre_srcs >> PCRE_OBJS = $ngx_pcre_objs > > The PCRE2 compilation in auto/lib/pcre/make mostly matches PCRE > compilation in auto/lib/pcre/makefile.msvc, and it never tried to > enable Unicode / UTF-8 support. This in turn matches PCRE > configure behaviour: UTF-8 support is disabled by default and > needs to be explicitly enabled. This looks unrelated, because PCRE(1) is a different library. > > While we might consider enabling Unicode support for PCRE2, since > it is now enabled by default in PCRE2 (or for both PCRE and PCRE2, > since it is something usually expected to work nowadays), for > tests a better solution might be to don't rely on this. Unicode / > UTF-8 support might not be available on various other platforms as > well, so it's generally might be a good idea to adjust tests to > tolerate PCRE/PCRE2 compiled without Unicode / UTF-8 support. > As explained in the commit log, PCRE2 has Unicode support by default, as configured by configure or cmake, which makes it depend on how nginx for Windows was built: - PCRE2 Unicode support is present if built with GCC MinGW - it is not if built manually (missing configuration) with MSVC Such inconsistency doesn't look related to "various other platforms". -- Sergey Kandaurov From pluknet at nginx.com Tue Mar 21 11:24:47 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 21 Mar 2023 15:24:47 +0400 Subject: [PATCH 11 of 12] Win32: fixed ngx_fs_bsize() for symlinks In-Reply-To: <5EC16E2F-2F2D-4049-A734-6C734B7AE541@nginx.com> References: <5F74999F-228E-43AB-B059-879964530E55@nginx.com> <5EC16E2F-2F2D-4049-A734-6C734B7AE541@nginx.com> Message-ID: <5495B4DB-F94F-49B9-A3B5-721ABBF442DF@nginx.com> > On 24 Feb 2023, at 14:40, Sergey Kandaurov wrote: > >> >> On 23 Feb 2023, at 22:46, Maxim Dounin wrote: >> >> Hello! >> >> On Wed, Feb 22, 2023 at 08:01:15PM +0400, Sergey Kandaurov wrote: >> >>>> On 19 Feb 2023, at 21:23, Maxim Dounin wrote: >>>> >>>> Hello! >>>> >>>> On Fri, Feb 17, 2023 at 07:17:02PM +0400, Sergey Kandaurov wrote: >>>> >>>>>> On 13 Jan 2023, at 01:35, Maxim Dounin wrote: >>>>>> >>>>>> # HG changeset patch >>>>>> # User Maxim Dounin >>>>>> # Date 1673549010 -10800 >>>>>> # Thu Jan 12 21:43:30 2023 +0300 >>>>>> # Node ID be7eb9ec28dcbfdfd2e850befc8d051c0e4d46fd >>>>>> # Parent e62c8e9724ba68a698a2c3613edca73fe4e1c4ae >>>>>> Win32: fixed ngx_fs_bsize() for symlinks. >>>>>> >>>>>> Just a drive letter might not correctly represent file system being used, >>>>>> notably when using symlinks (as created by "mklink /d"). As such, instead >>>>>> of calling GetDiskFreeSpace() with just a drive letter, we now always >>>>>> use GetDiskFreeSpace() with full path. >>>>>> >>>>>> diff -r e62c8e9724ba -r be7eb9ec28dc src/os/win32/ngx_files.c >>>>>> --- a/src/os/win32/ngx_files.c Thu Jan 12 21:43:14 2023 +0300 >>>>>> +++ b/src/os/win32/ngx_files.c Thu Jan 12 21:43:30 2023 +0300 >>>>>> @@ -955,14 +955,8 @@ ngx_directio_off(ngx_fd_t fd) >>>>>> size_t >>>>>> ngx_fs_bsize(u_char *name) >>>>>> { >>>>>> - u_char root[4]; >>>>>> u_long sc, bs, nfree, ncl; >>>>>> >>>>>> - if (name[2] == ':') { >>>>>> - ngx_cpystrn(root, name, 4); >>>>>> - name = root; >>>>>> - } >>>>>> - >>>>> >>>>> BTW, I wonder how this condition could be true. >>>>> Specifically, what name should represent in order to match. >>>>> I'm happy that it's leaving though. >>>> >>>> I tend to think that this actually never worked, and the original >>>> intention was to test name[1] instead. >>>> >>>> Updated commit log: >>>> >>>> : Win32: removed attempt to use a drive letter in ngx_fs_bsize(). >>>> : >>>> : Just a drive letter might not correctly represent file system being used, >>>> : notably when using symlinks (as created by "mklink /d"). As such, instead >>>> : of trying to call GetDiskFreeSpace() with just a drive letter, we now always >>>> : use GetDiskFreeSpace() with full path. >>>> : >>>> : Further, it looks like the code to use just a drive letter never worked, >>>> : since it tried to test name[2] instead of name[1] to be ':'. >>>> >>>> [...] >>>> >>> >>> Looks fine, thanks. >> >> Thanks for the review, pushed to http://mdounin.ru/hg/nginx. > > Tests for autoindex and dav modules for your consideration. Pushed, with some cleanup. -- Sergey Kandaurov From arut at nginx.com Tue Mar 21 13:13:45 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 21 Mar 2023 17:13:45 +0400 Subject: [PATCH] SSL: enabled TLSv1.3 by default In-Reply-To: <3496c963b43636104fa7.1679318394@vm-bsd.mdounin.ru> References: <3496c963b43636104fa7.1679318394@vm-bsd.mdounin.ru> Message-ID: <20230321131345.cd3hmxihqls46ovw@N00W24XTQX> Hi, On Mon, Mar 20, 2023 at 04:19:54PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1678891161 -10800 > # Wed Mar 15 17:39:21 2023 +0300 > # Node ID 3496c963b43636104fa794e84969eb077abec6b0 > # Parent b97f2b983d1564d29280d03828503edca21a79ee > SSL: enabled TLSv1.3 by default. > > diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c > --- a/src/http/modules/ngx_http_grpc_module.c > +++ b/src/http/modules/ngx_http_grpc_module.c > @@ -4473,8 +4473,9 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t > prev->upstream.ssl_session_reuse, 1); > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, > "DEFAULT"); > diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c > --- a/src/http/modules/ngx_http_proxy_module.c > +++ b/src/http/modules/ngx_http_proxy_module.c > @@ -3734,8 +3734,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t > prev->upstream.ssl_session_reuse, 1); > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, > "DEFAULT"); > diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c > +++ b/src/http/modules/ngx_http_ssl_module.c > @@ -632,8 +632,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * > ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); > > ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, > NGX_SSL_BUFSIZE); > diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c > --- a/src/http/modules/ngx_http_uwsgi_module.c > +++ b/src/http/modules/ngx_http_uwsgi_module.c > @@ -1875,8 +1875,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t > prev->upstream.ssl_session_reuse, 1); > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, > "DEFAULT"); > diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c > --- a/src/mail/ngx_mail_ssl_module.c > +++ b/src/mail/ngx_mail_ssl_module.c > @@ -360,8 +360,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, > prev->prefer_server_ciphers, 0); > > ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); > ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); > diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c > --- a/src/stream/ngx_stream_proxy_module.c > +++ b/src/stream/ngx_stream_proxy_module.c > @@ -2163,8 +2163,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf > prev->ssl_session_reuse, 1); > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); > > diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c > --- a/src/stream/ngx_stream_ssl_module.c > +++ b/src/stream/ngx_stream_ssl_module.c > @@ -703,8 +703,9 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf > prev->prefer_server_ciphers, 0); > > ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > + (NGX_CONF_BITMASK_SET > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); > ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel Looks fine From maxim at nginx.com Tue Mar 21 15:12:35 2023 From: maxim at nginx.com (Maxim Konovalov) Date: Tue, 21 Mar 2023 08:12:35 -0700 Subject: [PATCH] Linux packages: added Amazon Linux 2023 In-Reply-To: <23d3cabaab95fb09ea40.1679337499@QGCD7XG9R9> References: <23d3cabaab95fb09ea40.1679337499@QGCD7XG9R9> Message-ID: <07b0ed91-1a95-2b19-9121-9128b7d734d3@nginx.com> On 20.03.2023 11:38, Konstantin Pavlov wrote: > # HG changeset patch > # User Konstantin Pavlov > # Date 1679336457 25200 > # Mon Mar 20 11:20:57 2023 -0700 > # Node ID 23d3cabaab95fb09ea40f113759f4eaed99ec9d7 > # Parent 0f468b4e01d67cab96a44e0886dda3180104ae1a > Linux packages: added Amazon Linux 2023. > [...] Looks good. -- Maxim Konovalov From mdounin at mdounin.ru Tue Mar 21 19:49:53 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 21 Mar 2023 22:49:53 +0300 Subject: [PATCH] Win32: PCRE2 Unicode support with MSVC In-Reply-To: <066209D7-B63D-4A51-98C1-2CD0CFA33BFB@nginx.com> References: <066209D7-B63D-4A51-98C1-2CD0CFA33BFB@nginx.com> Message-ID: Hello! On Tue, Mar 21, 2023 at 01:50:58PM +0400, Sergey Kandaurov wrote: > > > On 21 Mar 2023, at 03:55, Maxim Dounin wrote: > > > > Hello! > > > > On Mon, Mar 20, 2023 at 06:58:32PM +0400, Sergey Kandaurov wrote: > > > >> # HG changeset patch > >> # User Sergey Kandaurov > >> # Date 1679324252 -14400 > >> # Mon Mar 20 18:57:32 2023 +0400 > >> # Node ID d0b013a7050e00613804b399ae2ca74551b2a071 > >> # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 > >> Win32: PCRE2 Unicode support with MSVC. > >> > >> Unicode support in PCRE2 is enabled by default on configure/cmake side > >> by defining SUPPORT_UNICODE. Previously, this macro was not defined > >> when compiling directly PCRE2 sources for Windows with MSVC. > >> > >> In particular, this change allows to specify Unicode properties, such as > >> \P, \p, or \X, as caught by http_server_name.t adjusted to run on Windows: > >> > >> nginx: [emerg] pcre2_compile() failed: this version of PCRE2 does not have > >> support for \P, \p, or \X > >> > >> diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make > >> --- a/auto/lib/pcre/make > >> +++ b/auto/lib/pcre/make > >> @@ -61,7 +61,7 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then > >> > >> PCRE_CFLAGS = -O2 -Ob1 -Oi -Gs $LIBC $CPU_OPT > >> PCRE_FLAGS = -DHAVE_CONFIG_H -DPCRE2_STATIC -DPCRE2_CODE_UNIT_WIDTH=8 \\ > >> - -DHAVE_MEMMOVE > >> + -DHAVE_MEMMOVE -DSUPPORT_UNICODE > >> > >> PCRE_SRCS = $ngx_pcre_srcs > >> PCRE_OBJS = $ngx_pcre_objs > > > > The PCRE2 compilation in auto/lib/pcre/make mostly matches PCRE > > compilation in auto/lib/pcre/makefile.msvc, and it never tried to > > enable Unicode / UTF-8 support. This in turn matches PCRE > > configure behaviour: UTF-8 support is disabled by default and > > needs to be explicitly enabled. > > This looks unrelated, because PCRE(1) is a different library. Well, not really. Even if we consider these to be different libraries, both PCRE and PCRE2 are libraries to implement regular expression matching as used by nginx, and it looks logical that nginx compiles them with similar / identical feature sets when asked to compile. > > While we might consider enabling Unicode support for PCRE2, since > > it is now enabled by default in PCRE2 (or for both PCRE and PCRE2, > > since it is something usually expected to work nowadays), for > > tests a better solution might be to don't rely on this. Unicode / > > UTF-8 support might not be available on various other platforms as > > well, so it's generally might be a good idea to adjust tests to > > tolerate PCRE/PCRE2 compiled without Unicode / UTF-8 support. > > As explained in the commit log, PCRE2 has Unicode support by default, > as configured by configure or cmake, which makes it depend on how > nginx for Windows was built: > - PCRE2 Unicode support is present if built with GCC MinGW > - it is not if built manually (missing configuration) with MSVC > > Such inconsistency doesn't look related to "various other platforms". Similarly, when nginx on Windows is built with PCRE instead of PCRE2, Unicode support won't be available. Further, the same applies to PCRE on Unix as long as it is built by nginx (and UTF-8 support is not explicitly enabled with "--with-pcre-opt=..."). Further, there can be other platforms where PCRE (or even PCRE2) is built without Unicode support. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Wed Mar 22 01:02:01 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 22 Mar 2023 04:02:01 +0300 Subject: [PATCH] Tests: run syslog and error_log tests on win32 In-Reply-To: References: Message-ID: Hello! On Mon, Mar 20, 2023 at 06:13:34PM +0400, Sergey Kandaurov wrote: > # HG changeset patch > # User Sergey Kandaurov > # Date 1679321601 -14400 > # Mon Mar 20 18:13:21 2023 +0400 > # Node ID f3225ad9300ee2c11c0dec54b9605e67060b7347 > # Parent 1e1d0f3874b0c5b1e399ec76b0198b5c9c265a36 > Tests: run syslog and error_log tests on win32. > > They are supposed to work well now, no reason to skip them. It might be a good idea to explicitly mention why it wasn't supposed to work. > An exception is logging to stderr. And why stderr is not supposed to. (Just in case, the culprit is CreateProcess(CREATE_NO_WINDOW) used when starting worker processes, which creates a process without a console. This probably can be improved to preserve console if logging to stderr is used.) > > diff --git a/error_log.t b/error_log.t > --- a/error_log.t > +++ b/error_log.t > @@ -22,8 +22,6 @@ use Test::Nginx; > select STDERR; $| = 1; > select STDOUT; $| = 1; > > -plan(skip_all => 'win32') if $^O eq 'MSWin32'; > - > my $t = Test::Nginx->new()->has(qw/http limit_req/) > ->plan(25)->write_file_expand('nginx.conf', <<'EOF'); > > @@ -110,7 +108,7 @@ EOF > open OLDERR, ">&", \*STDERR; > open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; > open my $stderr, '<', $t->testdir() . '/stderr' > - or die "Can't open stderr file: $!"; > + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; It shouldn't be a problem to open the file on Windows. Though will require explicitly closing it at the end of the test, so the test suite will be able to remove the test directory in destructor. (In other tests this will also require reordering of daemon startup, to avoid leaking the descriptor to daemons.) > > $t->run(); > > @@ -123,40 +121,69 @@ open STDERR, ">&", \*OLDERR; > http_get('/'); > > SKIP: { > - > skip "no --with-debug", 3 unless $t->has_module('--with-debug'); > > http_get('/debug'); > isnt(lines($t, 'e_debug_debug.log', '[debug]'), 0, 'file debug debug'); > is(lines($t, 'e_debug_info.log', '[debug]'), 0, 'file debug info'); > + > +SKIP: { > +skip 'win32', 1 if $^O eq 'MSWin32'; > + > isnt(lines($t, 'stderr', '[debug]'), 0, 'stderr debug'); So this won't be a SKIP, but rather TODO. Further, it will actually work with "master_process off;". > > } > > +} > + > http_get('/info'); > is(lines($t, 'e_info_debug.log', '[info]'), 1, 'file info debug'); > is(lines($t, 'e_info_info.log', '[info]'), 1, 'file info info'); > is(lines($t, 'e_info_notice.log', '[info]'), 0, 'file info notice'); > + > +SKIP: { > +skip 'win32', 1 if $^O eq 'MSWin32'; > + > is(lines($t, 'stderr', '[info]'), 1, 'stderr info'); > > +} > + > http_get('/notice'); > is(lines($t, 'e_notice_info.log', '[notice]'), 1, 'file notice info'); > is(lines($t, 'e_notice_notice.log', '[notice]'), 1, 'file notice notice'); > is(lines($t, 'e_notice_warn.log', '[notice]'), 0, 'file notice warn'); > + > +SKIP: { > +skip 'win32', 1 if $^O eq 'MSWin32'; > + > is(lines($t, 'stderr', '[notice]'), 1, 'stderr notice'); > > +} > + > http_get('/warn'); > is(lines($t, 'e_warn_notice.log', '[warn]'), 1, 'file warn notice'); > is(lines($t, 'e_warn_warn.log', '[warn]'), 1, 'file warn warn'); > is(lines($t, 'e_warn_error.log', '[warn]'), 0, 'file warn error'); > + > +SKIP: { > +skip 'win32', 1 if $^O eq 'MSWin32'; > + > is(lines($t, 'stderr', '[warn]'), 1, 'stderr warn'); > > +} > + > http_get('/error'); > is(lines($t, 'e_error_warn.log', '[error]'), 1, 'file error warn'); > is(lines($t, 'e_error_error.log', '[error]'), 1, 'file error error'); > is(lines($t, 'e_error_alert.log', '[error]'), 0, 'file error alert'); > + > +SKIP: { > +skip 'win32', 1 if $^O eq 'MSWin32'; > + > is(lines($t, 'stderr', '[error]'), 1, 'stderr error'); > > +} > + > # count log messages emitted with various error_log levels > > http_get('/file_low'); > @@ -168,6 +195,9 @@ is(lines($t, 'e_multi.log', '[error]'), > http_get('/file_high'); > is(lines($t, 'e_multi_high.log', '[error]'), 1, 'file high'); > > +SKIP: { > +skip 'win32', 3 if $^O eq 'MSWin32'; > + > http_get('/stderr_low'); > is(lines($t, 'stderr', '[error]'), 2, 'stderr low'); > > @@ -177,6 +207,8 @@ is(lines($t, 'stderr', '[error]'), 2, 's > http_get('/stderr_high'); > is(lines($t, 'stderr', '[error]'), 1, 'stderr high'); > > +} > + > ############################################################################### > > sub lines { > diff --git a/mail_error_log.t b/mail_error_log.t > --- a/mail_error_log.t > +++ b/mail_error_log.t > @@ -26,8 +26,6 @@ use Test::Nginx::IMAP; > select STDERR; $| = 1; > select STDOUT; $| = 1; > > -plan(skip_all => 'win32') if $^O eq 'MSWin32'; > - > my $t = Test::Nginx->new()->has(qw/mail imap http rewrite/); > > $t->plan(30)->write_file_expand('nginx.conf', <<'EOF'); > @@ -87,7 +85,7 @@ EOF > open OLDERR, ">&", \*STDERR; > open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; > open my $stderr, '<', $t->testdir() . '/stderr' > - or die "Can't open stderr file: $!"; > + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; > > $t->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon); > $t->run_daemon(\&syslog_daemon, port(8981), $t, 's_glob.log'); > @@ -117,9 +115,15 @@ isnt(lines($t, 'e_debug.log', '[debug]') > > isnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info'); > is(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info'); > + > +SKIP: { > +skip 'win32', 2 if $^O eq 'MSWin32'; > + > isnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info'); > is(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info'); > > +} > + > # multiple error_log > > like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); > @@ -255,13 +259,16 @@ sub syslog_daemon { > LocalAddr => "127.0.0.1:$port" > ); > > - open my $fh, '>', $t->testdir() . '/' . $file; > - select $fh; $| = 1; > + my $path = $t->testdir() . '/' . $file; > + open my $fh, '>', $path; > + close $fh; > > while (1) { > my $buffer; > $s->recv($buffer, 4096); > + open $fh, '>>', $path; > print $fh $buffer . "\n"; > + close $fh; > } It might be a good idea to clarify in the commit log why this is needed. (Notably, that pseudo-processes created by Perl's fork() emulation on win32 cannot be gracefully stopped when they are blocked in a system call, and using kill('KILL') as nginx does seems to leave the files open.) > } > > diff --git a/stream_error_log.t b/stream_error_log.t > --- a/stream_error_log.t > +++ b/stream_error_log.t > @@ -26,8 +26,6 @@ use Test::Nginx::Stream qw/ stream /; > select STDERR; $| = 1; > select STDOUT; $| = 1; > > -plan(skip_all => 'win32') if $^O eq 'MSWin32'; > - > my $t = Test::Nginx->new()->has(qw/stream/)->plan(34); > > $t->write_file_expand('nginx.conf', <<'EOF'); > @@ -75,7 +73,7 @@ EOF > open OLDERR, ">&", \*STDERR; > open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; > open my $stderr, '<', $t->testdir() . '/stderr' > - or die "Can't open stderr file: $!"; > + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; > > $t->run_daemon(\&stream_daemon); > $t->run_daemon(\&syslog_daemon, port(8983), $t, 's_glob.log'); > @@ -104,9 +102,15 @@ isnt(lines($t, 'e_debug.log', '[debug]') > > isnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info'); > is(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info'); > + > +SKIP: { > +skip 'win32', 2 if $^O eq 'MSWin32'; > + > isnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info'); > is(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info'); > > +} > + > # multiple error_log > > like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); > @@ -135,8 +139,14 @@ my $msg = 'no live upstreams while conne > > unlike($t->read_file('e_glob.log'), qr/$msg/ms, 'stream error in global'); > like($t->read_file('e_info.log'), qr/$msg/ms, 'stream error in info'); > +unlike($t->read_file('e_emerg.log'), qr/$msg/ms, 'stream error in emerg'); > + > +SKIP: { > +skip 'win32', 1 if $^O eq 'MSWin32'; > + > like($t->read_file('stderr'), qr/$msg/ms, 'stream error in info stderr'); > -unlike($t->read_file('e_emerg.log'), qr/$msg/ms, 'stream error in emerg'); > + > +} > > $msg = "bytes from/to client:5/4, bytes from/to upstream:4/5"; > > @@ -264,13 +274,16 @@ sub syslog_daemon { > LocalAddr => "127.0.0.1:$port" > ); > > - open my $fh, '>', $t->testdir() . '/' . $file; > - select $fh; $| = 1; > + my $path = $t->testdir() . '/' . $file; > + open my $fh, '>', $path; > + close $fh; > > while (1) { > my $buffer; > $s->recv($buffer, 4096); > + open $fh, '>>', $path; > print $fh $buffer . "\n"; > + close $fh; > } > } > > diff --git a/syslog.t b/syslog.t > --- a/syslog.t > +++ b/syslog.t > @@ -25,8 +25,6 @@ use Test::Nginx; > select STDERR; $| = 1; > select STDOUT; $| = 1; > > -plan(skip_all => 'win32') if $^O eq 'MSWin32'; > - > my $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(62); > > $t->write_file_expand('nginx.conf', <<'EOF'); > @@ -186,7 +184,6 @@ is($lines[1], $lines[2], 'error_log many > # error_log log levels > > SKIP: { > - > skip "no --with-debug", 1 unless $t->has_module('--with-debug'); > > isnt(syslog_lines('/debug', '[debug]'), 0, 'debug'); > @@ -340,13 +337,16 @@ sub syslog_daemon { > LocalAddr => "127.0.0.1:$port" > ); > > - open my $fh, '>', $t->testdir() . '/' . $file; > - select $fh; $| = 1; > + my $path = $t->testdir() . '/' . $file; > + open my $fh, '>', $path; > + close $fh; > > while (1) { > my $buffer; > $s->recv($buffer, 4096); > + open $fh, '>>', $path; > print $fh $buffer . "\n"; > + close $fh; > } > } > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel -- Maxim Dounin http://mdounin.ru/ From pluknet at nginx.com Wed Mar 22 08:57:56 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 12:57:56 +0400 Subject: [PATCH 01 of 20] Tests: separate SSL session reuse tests In-Reply-To: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> References: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> Message-ID: <6B831977-21E8-402B-A254-ACA0A59E7387@nginx.com> > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679105686 -10800 > # Sat Mar 18 05:14:46 2023 +0300 > # Node ID 86c394a226d2a7d463da7a1b7e88375c71c0c69b > # Parent 3c9aa6c23fc836725b96cf056d218217a5a81603 > Tests: separate SSL session reuse tests. > > Instead of being mixed with generic SSL tests, session reuse variants > are now tested in a separate file. > > In the generic SSL tests only basic session reuse is now tested, > notably with session tickets enabled and a shared SSL session cache. > This should make it possible to reuse sessions in all cases (except > when it's not supported, such as with LibreSSL with TLSv1.3). > > Note that session reuse with tickets implies that $ssl_session_id > is selected by the client and therefore is not available on the > initial connection. Relevant test is modified to handle this. > > Further, BoringSSL does not use legacy session ID with TLSv1.3 even > if it is sent by the client. In contrast, OpenSSL always generates > an unique legacy session id, so it is available with TLSv1.3 even if > session resumption does not work (such as with old Net::SSLeay and > IO::Socket::SSL modules). Note that TLSv1.3 has only ticket based session resumption. BoringSSL has a different notion on using legacy session IDs in TLSv1.3, see tls13_create_session_with_ticket() in sources: // Historically, OpenSSL filled in fake session IDs for ticket-based sessions. // Envoy's tests depend on this, although perhaps they shouldn't. SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id); Later, SSL_SESSION_get_id() was additionally annotated in ssl.h: // As a workaround for some broken applications, BoringSSL sometimes synthesizes // arbitrary session IDs for non-ID-based sessions. This behavior may be // removed in the future. As for TLSv1.3 server context, BoringSSL doesn't seem to use "session ID" besides echoing the client's legacy_session_id field content in the legacy_session_id_echo field of ServerHello/HRR during handshake, as mandated in RFC 8446, 4.1.3. So it doesn't settle in the session. > > diff --git a/ssl.t b/ssl.t > --- a/ssl.t > +++ b/ssl.t > @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( > plan(skip_all => 'IO::Socket::SSL too old') if $@; > > my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) > - ->has_daemon('openssl')->plan(28); > + ->has_daemon('openssl')->plan(21); > > $t->write_file_expand('nginx.conf', <<'EOF'); > > @@ -47,7 +47,6 @@ http { > > ssl_certificate_key localhost.key; > ssl_certificate localhost.crt; > - ssl_session_tickets off; > > log_format ssl $ssl_protocol; > > @@ -59,6 +58,7 @@ http { > ssl_certificate_key inner.key; > ssl_certificate inner.crt; > ssl_session_cache shared:SSL:1m; > + ssl_session_tickets on; > ssl_verify_client optional_no_ca; > > keepalive_requests 1000; > @@ -100,57 +100,11 @@ http { > } > > server { > - listen 127.0.0.1:8081; > - server_name localhost; > - > - # Special case for enabled "ssl" directive. > - > - ssl on; Removing tests for the "ssl" legacy directive doesn't feel right now and is out of scope of this change. Please put this back. Not being able to test it is fragile and can be left silently broken, especially with the upcoming quic merge. On the other hand, I'm fine with finally removing this directive. It was made obsolete in version 1.15.0, released on 05 Jun 2018, with the "listen .. ssl" upgrade path available back to 0.7.14, released on 01 Sep 2008. > - ssl_session_cache builtin; > - > - location / { > - return 200 "body $ssl_session_reused"; > - } > - } > - > - server { > - listen 127.0.0.1:8082 ssl; > - server_name localhost; > - > - ssl_session_cache builtin:1000; > - > - location / { > - return 200 "body $ssl_session_reused"; > - } > - } > - > - server { > - listen 127.0.0.1:8083 ssl; > - server_name localhost; > - > - ssl_session_cache none; > - > - location / { > - return 200 "body $ssl_session_reused"; > - } > - } > - > - server { > - listen 127.0.0.1:8084 ssl; > - server_name localhost; > - > - ssl_session_cache off; > - > - location / { > - return 200 "body $ssl_session_reused"; > - } > - } > - > - server { > listen 127.0.0.1:8086 ssl; Since you noticeably touched this file, you could renumber these ports as well. > server_name localhost; > > ssl_session_cache shared:SSL:1m; > + ssl_session_tickets on; > ssl_session_timeout 1; > > location / { > @@ -216,59 +170,34 @@ foreach my $name ('localhost', 'inner') > or die "Can't create certificate for $name: $!\n"; > } > > -# suppress deprecation warning > - > -open OLDERR, ">&", \*STDERR; close STDERR; > $t->run(); > -open STDERR, ">&", \*OLDERR; > > ############################################################################### > > -my $ctx; > +# ssl session reuse > > -SKIP: { > -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ > - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); > +my $ctx = get_ssl_context(); > > -$ctx = get_ssl_context(); > +like(get('/', 8085, $ctx), qr/^body \.$/m, 'session'); > > -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); > -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); > - > -$ctx = get_ssl_context(); > +TODO: { > +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' > + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); > +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' > + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); Not sure why do you convert this to TODO. TODO blocks are used for something we control or able to fix. SSL libraries are not something like that, so using SKIP there is more appropriate and follows other platform-specific tests. Besides that, TODOs plagues diagnostic output for no purpose. See also Test::More documentation: When do I use SKIP vs. TODO? If it's something the user might not be able to do, use SKIP. This includes optional modules that aren't installed, running under an OS that doesn't have some feature (like "fork()" or symlinks), or maybe you need an Internet connection and one isn't available. If it's something the programmer hasn't done yet, use TODO. This is for any code you haven't written yet, or bugs you have yet to fix, but want to put tests in your testing script (always a good idea). > > -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); > -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); > - > -$ctx = get_ssl_context(); > - > -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); > -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); > +like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); > > } > > -$ctx = get_ssl_context(); > - > -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); > -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); > - > -$ctx = get_ssl_context(); > - > -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); > -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); > - > # ssl certificate inheritance > > -my $s = get_ssl_socket(8081); > +my $s = get_ssl_socket(8086); > like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); > > -$s->close(); > - > $s = get_ssl_socket(8085); > like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); > > -$s->close(); > - > # session timeout > > $ctx = get_ssl_context(); > @@ -280,8 +209,12 @@ like(get('/', 8086, $ctx), qr/^body \.$/ > > # embedded variables > > -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); > +$ctx = get_ssl_context(); > +like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); > +like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); > + > unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); > + > like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); > > SKIP: { > @@ -334,6 +267,10 @@ like(`grep -F '[crit]' ${\($t->testdir() > > ############################################################################### > > +sub test_tls13 { > + return get('/protocol', 8085) =~ /TLSv1.3/; > +} > + > sub get { > my ($uri, $port, $ctx) = @_; > my $s = get_ssl_socket($port, $ctx) or return; > diff --git a/ssl.t b/ssl_session_reuse.t > copy from ssl.t > copy to ssl_session_reuse.t > --- a/ssl.t > +++ b/ssl_session_reuse.t > @@ -2,6 +2,7 @@ > > # (C) Sergey Kandaurov Initial session resumption tests in ssl.t is Andrey's work. You can drop this line, doesn't belong there. > # (C) Andrey Zelenkov > +# (C) Maxim Dounin > # (C) Nginx, Inc. > > # Tests for http ssl module. The description needs update. You can drop the line "use Socket qw/ CRLF /;" below, now unused. > @@ -30,8 +31,8 @@ plan(skip_all => 'IO::Socket::SSL not in > eval { IO::Socket::SSL::SSL_VERIFY_NONE(); }; > plan(skip_all => 'IO::Socket::SSL too old') if $@; > > -my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) > - ->has_daemon('openssl')->plan(28); > +my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/) > + ->has_daemon('openssl')->plan(8); > > $t->write_file_expand('nginx.conf', <<'EOF'); > > @@ -47,66 +48,37 @@ http { > > ssl_certificate_key localhost.key; > ssl_certificate localhost.crt; > - ssl_session_tickets off; > - > - log_format ssl $ssl_protocol; > > server { > - listen 127.0.0.1:8085 ssl; > - listen 127.0.0.1:8080; > + listen 127.0.0.1:8443 ssl; > server_name localhost; > > - ssl_certificate_key inner.key; > - ssl_certificate inner.crt; > - ssl_session_cache shared:SSL:1m; > - ssl_verify_client optional_no_ca; > - > - keepalive_requests 1000; > - > location / { > return 200 "body $ssl_session_reused"; > } > - location /id { > - return 200 "body $ssl_session_id"; > - } > - location /cipher { > - return 200 "body $ssl_cipher"; > - } > - location /ciphers { > - return 200 "body $ssl_ciphers"; > - } > - location /client_verify { > - return 200 "body $ssl_client_verify"; > - } > location /protocol { > return 200 "body $ssl_protocol"; > } > - location /issuer { > - return 200 "body $ssl_client_i_dn:$ssl_client_i_dn_legacy"; > - } > - location /subject { > - return 200 "body $ssl_client_s_dn:$ssl_client_s_dn_legacy"; > - } > - location /time { > - return 200 "body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain"; > - } > - > - location /body { > - add_header X-Body $request_body always; > - proxy_pass http://127.0.0.1:8080/; > - > - access_log %%TESTDIR%%/ssl.log ssl; > - } > } > > server { > - listen 127.0.0.1:8081; > + listen 127.0.0.1:8444 ssl; > server_name localhost; > > - # Special case for enabled "ssl" directive. > + ssl_session_cache shared:SSL:1m; > + ssl_session_tickets on; > > - ssl on; > - ssl_session_cache builtin; > + location / { > + return 200 "body $ssl_session_reused"; > + } > + } > + > + server { > + listen 127.0.0.1:8445 ssl; > + server_name localhost; > + > + ssl_session_cache shared:SSL:1m; > + ssl_session_tickets off; > > location / { > return 200 "body $ssl_session_reused"; > @@ -114,10 +86,11 @@ http { > } > > server { > - listen 127.0.0.1:8082 ssl; > + listen 127.0.0.1:8446 ssl; > server_name localhost; > > - ssl_session_cache builtin:1000; > + ssl_session_cache builtin; > + ssl_session_tickets off; > > location / { > return 200 "body $ssl_session_reused"; > @@ -125,10 +98,11 @@ http { > } > > server { > - listen 127.0.0.1:8083 ssl; > + listen 127.0.0.1:8447 ssl; > server_name localhost; > > - ssl_session_cache none; > + ssl_session_cache builtin:1000; > + ssl_session_tickets off; > > location / { > return 200 "body $ssl_session_reused"; > @@ -136,10 +110,11 @@ http { > } > > server { > - listen 127.0.0.1:8084 ssl; > + listen 127.0.0.1:8448 ssl; > server_name localhost; > > - ssl_session_cache off; > + ssl_session_cache none; > + ssl_session_tickets off; > > location / { > return 200 "body $ssl_session_reused"; > @@ -147,11 +122,11 @@ http { > } > > server { > - listen 127.0.0.1:8086 ssl; > + listen 127.0.0.1:8449 ssl; > server_name localhost; > > - ssl_session_cache shared:SSL:1m; > - ssl_session_timeout 1; > + ssl_session_cache off; > + ssl_session_tickets off; > > location / { > return 200 "body $ssl_session_reused"; > @@ -171,44 +146,7 @@ EOF > > my $d = $t->testdir(); > > -$t->write_file('ca.conf', < -[ ca ] > -default_ca = myca > - > -[ myca ] > -new_certs_dir = $d > -database = $d/certindex > -default_md = sha256 > -policy = myca_policy > -serial = $d/certserial > -default_days = 3 > - > -[ myca_policy ] > -commonName = supplied > -EOF > - > -$t->write_file('certserial', '1000'); > -$t->write_file('certindex', ''); > - > -system('openssl req -x509 -new ' > - . "-config $d/openssl.conf -subj /CN=issuer/ " > - . "-out $d/issuer.crt -keyout $d/issuer.key " > - . ">>$d/openssl.out 2>&1") == 0 > - or die "Can't create certificate for issuer: $!\n"; > - > -system("openssl req -new " > - . "-config $d/openssl.conf -subj /CN=subject/ " > - . "-out $d/subject.csr -keyout $d/subject.key " > - . ">>$d/openssl.out 2>&1") == 0 > - or die "Can't create certificate for subject: $!\n"; > - > -system("openssl ca -batch -config $d/ca.conf " > - . "-keyfile $d/issuer.key -cert $d/issuer.crt " > - . "-subj /CN=subject/ -in $d/subject.csr -out $d/subject.crt " > - . ">>$d/openssl.out 2>&1") == 0 > - or die "Can't sign certificate for subject: $!\n"; > - > -foreach my $name ('localhost', 'inner') { > +foreach my $name ('localhost') { > system('openssl req -x509 -new ' > . "-config $d/openssl.conf -subj /CN=$name/ " > . "-out $d/$name.crt -keyout $d/$name.key " > @@ -216,124 +154,56 @@ foreach my $name ('localhost', 'inner') > or die "Can't create certificate for $name: $!\n"; > } > > -# suppress deprecation warning > - > -open OLDERR, ">&", \*STDERR; close STDERR; > $t->run(); > -open STDERR, ">&", \*OLDERR; > > ############################################################################### > > my $ctx; You can remove this declaration to the only sub test_reuse user. > > -SKIP: { > -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ > - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); > - > -$ctx = get_ssl_context(); > - > -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); > -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); > +# session reuse: > +# > +# - only tickets, the default > +# - tickets and shared cache, should work always > +# - only shared cache > +# - only builtin cache > +# - only builtin cache with explicitly configured size > +# - only cache none > +# - only cache off > > -$ctx = get_ssl_context(); > - > -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); > -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); > +TODO: { > +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' > + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); > +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' > + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); > > -$ctx = get_ssl_context(); > - > -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); > -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); > +is(test_reuse(8443), 1, 'tickets reused'); > +is(test_reuse(8444), 1, 'tickets and cache reused'); > +is(test_reuse(8445), 1, 'cache shared reused'); > +is(test_reuse(8446), 1, 'cache builtin reused'); > +is(test_reuse(8447), 1, 'cache builtin size reused'); > > } > > -$ctx = get_ssl_context(); > - > -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); > -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); > - > -$ctx = get_ssl_context(); > - > -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); > -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); > - > -# ssl certificate inheritance > - > -my $s = get_ssl_socket(8081); > -like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); > - > -$s->close(); > - > -$s = get_ssl_socket(8085); > -like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); > - > -$s->close(); > - > -# session timeout > - > -$ctx = get_ssl_context(); > - > -get('/', 8086, $ctx); > -select undef, undef, undef, 2.1; > - > -like(get('/', 8086, $ctx), qr/^body \.$/m, 'session timeout'); > - > -# embedded variables > - > -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); > -unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); > -like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); > - > -SKIP: { > -skip 'BoringSSL', 1 if $t->has_module('BoringSSL'); > - > -like(get('/ciphers', 8085), qr/^body [:\w-]+$/m, 'ciphers'); > - > -} > - > -like(get('/client_verify', 8085), qr/^body NONE$/m, 'client verify'); > -like(get('/protocol', 8085), qr/^body (TLS|SSL)v(\d|\.)+$/m, 'protocol'); > -like(cert('/issuer', 8085), qr!^body CN=issuer:/CN=issuer$!m, 'issuer'); > -like(cert('/subject', 8085), qr!^body CN=subject:/CN=subject$!m, 'subject'); > -like(cert('/time', 8085), qr/^body [:\s\w]+![:\s\w]+![23]$/m, 'time'); > - > -# c->read->ready handling bug in ngx_ssl_recv(), triggered with chunked body > - > -like(get_body('/body', '0123456789', 20, 5), qr/X-Body: (0123456789){100}/, > - 'request body chunked'); > - > -# pipelined requests > - > -$s = get_ssl_socket(8085); > -my $req = < -GET / HTTP/1.1 > -Host: localhost > - > -EOF > - > -$req x= 1000; > - > -my $r = http($req, socket => $s) || ""; > -$s = undef; > -is(() = $r =~ /(200 OK)/g, 1000, 'pipelined requests'); > - > -# OpenSSL 3.0 error "unexpected eof while reading" seen as a critical error > - > -ok(get_ssl_socket(8085), 'ssl unexpected eof'); > - > -# close_notify is sent before lingering close > - > -is(get_ssl_shutdown(8085), 1, 'ssl shutdown on lingering close'); > +is(test_reuse(8448), 0, 'cache none not reused'); > +is(test_reuse(8449), 0, 'cache off not reused'); > > $t->stop(); > > -like($t->read_file('ssl.log'), qr/^(TLS|SSL)v(\d|\.)+$/m, > - 'log ssl variable on lingering close'); > - > like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); > > ############################################################################### > > +sub test_tls13 { > + return get('/protocol', 8443) =~ /TLSv1.3/; > +} > + > +sub test_reuse { > + my ($port) = @_; > + $ctx = get_ssl_context(); > + get('/', $port, $ctx); > + return (get('/', $port, $ctx) =~ qr/^body r$/m) ? 1 : 0; > +} > + > sub get { > my ($uri, $port, $ctx) = @_; > my $s = get_ssl_socket($port, $ctx) or return; > @@ -342,30 +212,6 @@ sub get { > return $r; > } > > -sub get_body { > - my ($uri, $body, $len, $n) = @_; > - my $s = get_ssl_socket(8085) or return; > - http("GET /body HTTP/1.1" . CRLF > - . "Host: localhost" . CRLF > - . "Connection: close" . CRLF > - . "Transfer-Encoding: chunked" . CRLF . CRLF, > - socket => $s, start => 1); > - my $chs = unpack("H*", pack("C", length($body) * $len)); > - http($chs . CRLF . $body x $len . CRLF, socket => $s, start => 1) > - for 1 .. $n; > - my $r = http("0" . CRLF . CRLF, socket => $s); > - $s->close(); > - return $r; > -} > - > -sub cert { > - my ($uri, $port) = @_; > - my $s = get_ssl_socket($port, undef, > - SSL_cert_file => "$d/subject.crt", > - SSL_key_file => "$d/subject.key") or return; > - http_get($uri, socket => $s); > -} > - > sub get_ssl_context { > return IO::Socket::SSL::SSL_Context->new( > SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), > @@ -402,18 +248,4 @@ sub get_ssl_socket { > return $s; > } > > -sub get_ssl_shutdown { > - my ($port) = @_; > - > - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > - Net::SSLeay::set_fd($ssl, fileno($s)); > - Net::SSLeay::connect($ssl) or die("ssl connect"); > - Net::SSLeay::write($ssl, 'GET /' . CRLF . 'extra'); > - Net::SSLeay::read($ssl); > - Net::SSLeay::set_shutdown($ssl, 1); > - Net::SSLeay::shutdown($ssl); > -} > - > ############################################################################### -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 09:20:25 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 13:20:25 +0400 Subject: [PATCH 03 of 20] Tests: separate SSL session reuse tests in mail In-Reply-To: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> References: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> Message-ID: > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679107816 -10800 > # Sat Mar 18 05:50:16 2023 +0300 > # Node ID 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb > # Parent 125fb8461d88a81a62ccb40d0e205a01ecc759f5 > Tests: separate SSL session reuse tests in mail. > > Instead of being mixed with generic SSL tests, session reuse variants > are now tested in a separate file. > > diff --git a/mail_ssl.t b/mail_ssl.t > --- a/mail_ssl.t > +++ b/mail_ssl.t > @@ -37,7 +37,7 @@ eval { exists &Net::SSLeay::P_alpn_selec > plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; > > my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) > - ->has_daemon('openssl')->plan(22); > + ->has_daemon('openssl')->plan(18); > > $t->write_file_expand('nginx.conf', <<'EOF'); > > @@ -51,44 +51,25 @@ events { > mail { > ssl_certificate_key localhost.key; > ssl_certificate localhost.crt; > - ssl_session_tickets off; > > ssl_password_file password; > > auth_http http://127.0.0.1:8080; # unused > > - ssl_session_cache none; > - > server { > listen 127.0.0.1:8143; > listen 127.0.0.1:8145 ssl; > protocol imap; > - > - ssl_session_cache builtin; > } > > server { > - listen 127.0.0.1:8146 ssl; > - protocol imap; > - > - ssl_session_cache off; > - } > - > - server { > - listen 127.0.0.1:8147; > + listen 127.0.0.1:8148; > protocol imap; > > # Special case for enabled "ssl" directive. > > ssl on; > - ssl_session_cache builtin:1000; > - } > > - server { > - listen 127.0.0.1:8148 ssl; > - protocol imap; > - > - ssl_session_cache shared:SSL:1m; > ssl_certificate_key inherits.key; > ssl_certificate inherits.crt; > } > @@ -169,46 +150,16 @@ open STDERR, ">&", \*OLDERR; > > ############################################################################### > > +my ($s, $ssl, $ses); > + $ses is no longer used there, and other cleanup on top off this change: diff --git a/mail_ssl.t b/mail_ssl.t --- a/mail_ssl.t +++ b/mail_ssl.t @@ -150,7 +150,7 @@ open STDERR, ">&", \*OLDERR; ############################################################################### -my ($s, $ssl, $ses); +my ($s, $ssl); # simple tests to ensure that nothing broke with ssl_password_file directive @@ -170,7 +170,7 @@ like(Net::SSLeay::dump_peer_certificate( # alpn -ok(get_ssl_socket(8148, undef, ['imap']), 'alpn'); +ok(get_ssl_socket(8148, ['imap']), 'alpn'); SKIP: { $t->{_configure_args} =~ /LibreSSL ([\d\.]+)/; @@ -181,7 +181,7 @@ skip 'OpenSSL too old', 1 if defined $1 TODO: { local $TODO = 'not yet' unless $t->has_version('1.21.4'); -ok(!get_ssl_socket(8148, undef, ['unknown']), 'alpn rejected'); +ok(!get_ssl_socket(8148, ['unknown']), 'alpn rejected'); } @@ -268,11 +268,10 @@ ok(!get_ssl_socket(8148, undef, ['unknow ############################################################################### sub get_ssl_socket { - my ($port, $ses, $alpn) = @_; + my ($port, $alpn) = @_; my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) == 1 or return; diff --git a/mail_ssl_session_reuse.t b/mail_ssl_session_reuse.t --- a/mail_ssl_session_reuse.t +++ b/mail_ssl_session_reuse.t @@ -132,8 +132,6 @@ my $ctx = Net::SSLeay::CTX_new() or die( ############################################################################### -my ($ssl, $ses); - # session reuse: # # - only tickets, the default -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 09:46:33 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 13:46:33 +0400 Subject: [PATCH 03 of 20] Tests: separate SSL session reuse tests in mail In-Reply-To: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> References: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> Message-ID: > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679107816 -10800 > # Sat Mar 18 05:50:16 2023 +0300 > # Node ID 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb > # Parent 125fb8461d88a81a62ccb40d0e205a01ecc759f5 > Tests: separate SSL session reuse tests in mail. > > Instead of being mixed with generic SSL tests, session reuse variants > are now tested in a separate file. > > [..] > diff --git a/mail_ssl.t b/mail_ssl_session_reuse.t > copy from mail_ssl.t > copy to mail_ssl_session_reuse.t > --- a/mail_ssl.t > +++ b/mail_ssl_session_reuse.t > @@ -1,6 +1,7 @@ > #!/usr/bin/perl > > # (C) Andrey Zelenkov > +# (C) Maxim Dounin > # (C) Nginx, Inc. > > # Tests for mail ssl module. > @@ -33,11 +34,8 @@ eval { > }; > plan(skip_all => 'Net::SSLeay not installed') if $@; > > -eval { exists &Net::SSLeay::P_alpn_selected or die; }; > -plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; > - > -my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) > - ->has_daemon('openssl')->plan(22); > +my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap/) > + ->has_daemon('openssl')->plan(7); > > $t->write_file_expand('nginx.conf', <<'EOF'); > > @@ -49,90 +47,62 @@ events { > } > > mail { > - ssl_certificate_key localhost.key; > - ssl_certificate localhost.crt; > - ssl_session_tickets off; > + auth_http http://127.0.0.1:8080; > > - ssl_password_file password; > - > - auth_http http://127.0.0.1:8080; # unused > - > - ssl_session_cache none; > + ssl_certificate localhost.crt; > + ssl_certificate_key localhost.key; > > server { > - listen 127.0.0.1:8143; > - listen 127.0.0.1:8145 ssl; > - protocol imap; > - > - ssl_session_cache builtin; > + listen 127.0.0.1:8993 ssl; > + protocol imap; > } > > server { > - listen 127.0.0.1:8146 ssl; > - protocol imap; > + listen 127.0.0.1:8994 ssl; > + protocol imap; > > - ssl_session_cache off; > + ssl_session_cache shared:SSL:1m; > + ssl_session_tickets on; > } > > server { > - listen 127.0.0.1:8147; > - protocol imap; > + listen 127.0.0.1:8995 ssl; > + protocol imap; > > - # Special case for enabled "ssl" directive. > - > - ssl on; > - ssl_session_cache builtin:1000; > + ssl_session_cache shared:SSL:1m; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8148 ssl; > - protocol imap; > - > - ssl_session_cache shared:SSL:1m; > - ssl_certificate_key inherits.key; > - ssl_certificate inherits.crt; > - } > + listen 127.0.0.1:8996 ssl; > + protocol imap; > > - server { > - listen 127.0.0.1:8149; > - protocol imap; > - > - starttls on; > - } > - > - server { > - listen 127.0.0.1:8150; > - protocol imap; > - > - starttls only; > + ssl_session_cache builtin; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8151; > - protocol pop3; > + listen 127.0.0.1:8997 ssl; > + protocol imap; > > - starttls on; > + ssl_session_cache builtin:1000; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8152; > - protocol pop3; > + listen 127.0.0.1:8998 ssl; > + protocol imap; > > - starttls only; > + ssl_session_cache none; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8153; > - protocol smtp; > - > - starttls on; > - } > + listen 127.0.0.1:8999 ssl; > + protocol imap; > > - server { > - listen 127.0.0.1:8154; > - protocol smtp; > - > - starttls only; > + ssl_session_cache off; > + ssl_session_tickets off; > } > } > > @@ -148,181 +118,57 @@ EOF > > my $d = $t->testdir(); > > -foreach my $name ('localhost', 'inherits') { > - system("openssl genrsa -out $d/$name.key -passout pass:localhost " > - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 > - or die "Can't create private key: $!\n"; > +foreach my $name ('localhost') { > system('openssl req -x509 -new ' > . "-config $d/openssl.conf -subj /CN=$name/ " > - . "-out $d/$name.crt " > - . "-key $d/$name.key -passin pass:localhost" > + . "-out $d/$name.crt -keyout $d/$name.key " > . ">>$d/openssl.out 2>&1") == 0 > or die "Can't create certificate for $name: $!\n"; > } > > my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > -$t->write_file('password', 'localhost'); > > -open OLDERR, ">&", \*STDERR; close STDERR; > $t->run(); > -open STDERR, ">&", \*OLDERR; > > ############################################################################### > > [..] > +# session reuse: > +# > +# - only tickets, the default > +# - tickets and shared cache "should work always", like in http and stream? > +# - only shared cache > +# - only builtin cache > +# - only builtin cache with explicitly configured size > +# - only cache none > +# - only cache off > > [..] > +is(test_reuse(8993), 1, 'tickets reused'); > +is(test_reuse(8994), 1, 'tickets and cache reused'); > +is(test_reuse(8995), 1, 'cache shared reused'); > +is(test_reuse(8996), 1, 'cache builtin reused'); > +is(test_reuse(8997), 1, 'cache builtin size reused'); > +is(test_reuse(8998), 0, 'cache none not reused'); > +is(test_reuse(8999), 0, 'cache off not reused'); > > ############################################################################### > > +sub test_reuse { > + my ($port) = @_; > + my ($s, $ssl) = get_ssl_socket($port); > + Net::SSLeay::read($ssl); > + my $ses = Net::SSLeay::get_session($ssl); > + ($s, $ssl) = get_ssl_socket($port, $ses); > + return Net::SSLeay::session_reused($ssl); > +} > + > sub get_ssl_socket { > - my ($port, $ses, $alpn) = @_; > + my ($port, $ses) = @_; > > my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > Net::SSLeay::set_session($ssl, $ses) if defined $ses; > - Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; > Net::SSLeay::set_fd($ssl, fileno($s)); > Net::SSLeay::connect($ssl) == 1 or return; Since this won't fail anymore on ALPN checks, you can convert this back to die. > return ($s, $ssl); -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 09:55:48 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 13:55:48 +0400 Subject: [PATCH 05 of 20] Tests: separate SSL session reuse tests in stream In-Reply-To: <530336cb449dcb028a55.1679148900@vm-bsd.mdounin.ru> References: <530336cb449dcb028a55.1679148900@vm-bsd.mdounin.ru> Message-ID: <2640439F-CFF2-4ABB-92F6-7D71AA1DF4A6@nginx.com> > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679140351 -10800 > # Sat Mar 18 14:52:31 2023 +0300 > # Node ID 530336cb449dcb028a55a5a401a122d07521e3a4 > # Parent 3ab3b2d1c2e67bc1f05e386218ceb08da873a477 > Tests: separate SSL session reuse tests in stream. > > Instead of being mixed with generic SSL tests, session reuse variants > are now tested in a separate file. > > diff --git a/stream_ssl.t b/stream_ssl.t > --- a/stream_ssl.t > +++ b/stream_ssl.t > @@ -37,7 +37,7 @@ plan(skip_all => 'win32') if $^O eq 'MSW > > my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); > > -$t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); > +$t->plan(5)->write_file_expand('nginx.conf', <<'EOF'); > > %%TEST_GLOBALS%% > > @@ -51,40 +51,35 @@ stream { > > ssl_certificate_key localhost.key; > ssl_certificate localhost.crt; > - ssl_session_tickets off; > > # inherited by server "inherits" > ssl_password_file password_stream; > > server { > - listen 127.0.0.1:8080 ssl; > + listen 127.0.0.1:8443 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache builtin; > ssl_password_file password; > } > > server { > - listen 127.0.0.1:8082 ssl; > + listen 127.0.0.1:8444 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache off; > ssl_password_file password_many; > } > > server { > - listen 127.0.0.1:8083 ssl; > + listen 127.0.0.1:8445 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache builtin:1000; > ssl_password_file password_fifo; > } > > server { > - listen 127.0.0.1:8084 ssl; > + listen 127.0.0.1:8446 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache shared:SSL:1m; > ssl_certificate_key inherits.key; > ssl_certificate inherits.crt; > } > @@ -138,52 +133,26 @@ kill 'INT', $p if $@; > > ############################################################################### > > -my ($s, $ssl, $ses); > +my ($s, $ssl); > > -($s, $ssl) = get_ssl_socket(port(8080)); > +($s, $ssl) = get_ssl_socket(8443); > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); > > -# ssl_session_cache > - > -($s, $ssl) = get_ssl_socket(port(8080)); > +($s, $ssl) = get_ssl_socket(8444); > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > -Net::SSLeay::read($ssl); > -$ses = Net::SSLeay::get_session($ssl); > - > -($s, $ssl) = get_ssl_socket(port(8080), $ses); > -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); > - > -($s, $ssl) = get_ssl_socket(port(8082)); > -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > -Net::SSLeay::read($ssl); > -$ses = Net::SSLeay::get_session($ssl); > +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); > > -($s, $ssl) = get_ssl_socket(port(8082), $ses); > -isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); > - > -($s, $ssl) = get_ssl_socket(port(8083)); > +($s, $ssl) = get_ssl_socket(8444); should be 8445 > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > -Net::SSLeay::read($ssl); > -$ses = Net::SSLeay::get_session($ssl); > - > -($s, $ssl) = get_ssl_socket(port(8083), $ses); > -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); > - > -($s, $ssl) = get_ssl_socket(port(8084)); > -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > -Net::SSLeay::read($ssl); > -$ses = Net::SSLeay::get_session($ssl); > - > -($s, $ssl) = get_ssl_socket(port(8084), $ses); > -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); > +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); > > # ssl_certificate inheritance > > -($s, $ssl) = get_ssl_socket(port(8080)); > +($s, $ssl) = get_ssl_socket(8443); > like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); > > -($s, $ssl) = get_ssl_socket(port(8084)); > +($s, $ssl) = get_ssl_socket(8446); > like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); > > ############################################################################### > @@ -191,7 +160,7 @@ like(Net::SSLeay::dump_peer_certificate( > sub get_ssl_socket { > my ($port, $ses) = @_; > > - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); > + my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > Net::SSLeay::set_session($ssl, $ses) if defined $ses; Session test remnants can be cleaned up. > Net::SSLeay::set_fd($ssl, fileno($s)); > diff --git a/stream_ssl.t b/stream_ssl_session_reuse.t > copy from stream_ssl.t > copy to stream_ssl_session_reuse.t > --- a/stream_ssl.t > +++ b/stream_ssl_session_reuse.t > @@ -1,6 +1,7 @@ > #!/usr/bin/perl > > # (C) Sergey Kandaurov > +# (C) Maxim Dounin > # (C) Nginx, Inc. > > # Tests for stream ssl module. > @@ -12,7 +13,6 @@ use strict; > > use Test::More; > > -use POSIX qw/ mkfifo /; This file can be run now on win32. > use Socket qw/ $CRLF /; > > BEGIN { use FindBin; chdir($FindBin::Bin); } > @@ -49,44 +49,60 @@ events { > stream { > %%TEST_GLOBALS_STREAM%% > > + ssl_certificate localhost.crt; > ssl_certificate_key localhost.key; > - ssl_certificate localhost.crt; > - ssl_session_tickets off; > > - # inherited by server "inherits" > - ssl_password_file password_stream; > + server { > + listen 127.0.0.1:8443 ssl; > + proxy_pass 127.0.0.1:8081; > + } > > server { > - listen 127.0.0.1:8080 ssl; > + listen 127.0.0.1:8444 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache builtin; > - ssl_password_file password; > + ssl_session_cache shared:SSL:1m; > + ssl_session_tickets on; > + } > + > + server { > + listen 127.0.0.1:8445 ssl; > + proxy_pass 127.0.0.1:8081; > + > + ssl_session_cache shared:SSL:1m; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8082 ssl; > + listen 127.0.0.1:8446 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache off; > - ssl_password_file password_many; > + ssl_session_cache builtin; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8083 ssl; > + listen 127.0.0.1:8447 ssl; > proxy_pass 127.0.0.1:8081; > > ssl_session_cache builtin:1000; > - ssl_password_file password_fifo; > + ssl_session_tickets off; > } > > server { > - listen 127.0.0.1:8084 ssl; > + listen 127.0.0.1:8448 ssl; > proxy_pass 127.0.0.1:8081; > > - ssl_session_cache shared:SSL:1m; > - ssl_certificate_key inherits.key; > - ssl_certificate inherits.crt; > + ssl_session_cache none; > + ssl_session_tickets off; > + } > + > + server { > + listen 127.0.0.1:8449 ssl; > + proxy_pass 127.0.0.1:8081; > + > + ssl_session_cache off; > + ssl_session_tickets off; > } > } > > @@ -101,16 +117,11 @@ distinguished_name = req_distinguished_n > EOF > > my $d = $t->testdir(); > -mkfifo("$d/password_fifo", 0700); > > -foreach my $name ('localhost', 'inherits') { > - system("openssl genrsa -out $d/$name.key -passout pass:$name " > - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 > - or die "Can't create private key: $!\n"; > +foreach my $name ('localhost') { > system('openssl req -x509 -new ' > . "-config $d/openssl.conf -subj /CN=$name/ " > - . "-out $d/$name.crt " > - . "-key $d/$name.key -passin pass:$name" > + . "-out $d/$name.crt -keyout $d/$name.key " > . ">>$d/openssl.out 2>&1") == 0 > or die "Can't create certificate for $name: $!\n"; > } > @@ -118,80 +129,48 @@ foreach my $name ('localhost', 'inherits > This introduces one more occurrence of extra blank line on copy/paste, I'd rather fix it in both files. [..] diff --git a/stream_ssl.t b/stream_ssl.t --- a/stream_ssl.t +++ b/stream_ssl.t @@ -110,7 +110,6 @@ foreach my $name ('localhost', 'inherits or die "Can't create certificate for $name: $!\n"; } - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); $t->write_file('password', 'localhost'); @@ -143,7 +142,7 @@ like(Net::SSLeay::read($ssl), qr/200 OK/ Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); -($s, $ssl) = get_ssl_socket(8444); +($s, $ssl) = get_ssl_socket(8445); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); @@ -158,11 +157,10 @@ like(Net::SSLeay::dump_peer_certificate( ############################################################################### sub get_ssl_socket { - my ($port, $ses) = @_; + my ($port) = @_; my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) or die("ssl connect"); return ($s, $ssl); diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t --- a/stream_ssl_session_reuse.t +++ b/stream_ssl_session_reuse.t @@ -33,8 +33,6 @@ eval { }; plan(skip_all => 'Net::SSLeay not installed') if $@; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); $t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); @@ -126,7 +124,6 @@ foreach my $name ('localhost') { or die "Can't create certificate for $name: $!\n"; } - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); $t->run_daemon(\&http_daemon); -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 09:59:27 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 13:59:27 +0400 Subject: [PATCH 06 of 20] Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail In-Reply-To: References: Message-ID: <10CCED02-0019-4520-AD41-CFCE12E38417@nginx.com> > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679140402 -10800 > # Sat Mar 18 14:53:22 2023 +0300 > # Node ID d90fe31a80d5e85b59e525e874d24f409716b64c > # Parent 530336cb449dcb028a55a5a401a122d07521e3a4 > Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail. in stream > > LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL > with TLSv1.3 only supports session tickets, but not server-side session > cache. > By the way, why introduce three separate changesets (p02, p04, p06) with identical description and similar changes. I'd combine them. > diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t > --- a/stream_ssl_session_reuse.t > +++ b/stream_ssl_session_reuse.t > @@ -147,16 +147,35 @@ my $ctx = Net::SSLeay::CTX_new() or die( > # - only cache none > # - only cache off > > +TODO: { > +local $TODO = 'no TLSv1.3 sessions in LibreSSL' > + if $t->has_module('LibreSSL') && test_tls13(); > + > is(test_reuse(8443), 1, 'tickets reused'); > is(test_reuse(8444), 1, 'tickets and cache reused'); > + > +TODO: { > +local $TODO = 'no TLSv1.3 session cache in BoringSSL' > + if $t->has_module('BoringSSL') && test_tls13(); > + > is(test_reuse(8445), 1, 'cache shared reused'); > is(test_reuse(8446), 1, 'cache builtin reused'); > is(test_reuse(8447), 1, 'cache builtin size reused'); > + > +} > +} > + > is(test_reuse(8448), 0, 'cache none not reused'); > is(test_reuse(8449), 0, 'cache off not reused'); > > + extra blank line > ############################################################################### > > +sub test_tls13 { > + my ($s, $ssl) = get_ssl_socket(8443); > + return (Net::SSLeay::version($ssl) > 0x303); > +} > + > sub test_reuse { > my ($port) = @_; > my ($s, $ssl) = get_ssl_socket($port); -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 10:39:12 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 14:39:12 +0400 Subject: [PATCH 15 of 20] Tests: LibreSSL does not send CA lists with TLSv1.3 In-Reply-To: <6d5bede76a77ca86483f.1679148910@vm-bsd.mdounin.ru> References: <6d5bede76a77ca86483f.1679148910@vm-bsd.mdounin.ru> Message-ID: > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679148737 -10800 > # Sat Mar 18 17:12:17 2023 +0300 > # Node ID 6d5bede76a77ca86483f63088587913a61b8b18d > # Parent 230b9cadce9b57213bf529940ca04224f9f121eb > Tests: LibreSSL does not send CA lists with TLSv1.3. Specifically, it doesn't implement the "certificate_authorities" extension used in TLSv1.3 to carry CA lists in the CertificateRequest message. > > diff --git a/ssl_verify_client.t b/ssl_verify_client.t > --- a/ssl_verify_client.t > +++ b/ssl_verify_client.t > @@ -55,6 +55,7 @@ http { > %%TEST_GLOBALS_HTTP%% > > add_header X-Verify x$ssl_client_verify:${ssl_client_cert}x; > + add_header X-Protocol $ssl_protocol; > > ssl_session_cache shared:SSL:1m; > ssl_session_tickets off; > @@ -169,15 +170,24 @@ like(get('optional', '3.example.com'), q > SKIP: { > skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; > > +TODO: { > +local $TODO = 'broken TLSv1.3 CA list in LibreSSL' > + if $t->has_module('LibreSSL') && test_tls13(); > + > my $ca = join ' ', get('optional', '3.example.com'); > is($ca, '/CN=2.example.com', 'no trusted sent'); > > } > +} > > like(get('optional', undef, 'localhost'), qr/421 Misdirected/, 'misdirected'); > > ############################################################################### > > +sub test_tls13 { > + get('optional') =~ /TLSv1.3/; > +} > + > sub get { > my ($sni, $cert, $host) = @_; > > diff --git a/stream_ssl_verify_client.t b/stream_ssl_verify_client.t > --- a/stream_ssl_verify_client.t > +++ b/stream_ssl_verify_client.t > @@ -86,6 +86,11 @@ stream { > ssl_verify_client optional_no_ca; > ssl_client_certificate 2.example.com.crt; > } > + > + server { > + listen 127.0.0.1:8084 ssl; > + return $ssl_protocol; > + } > } > > EOF > @@ -126,10 +131,15 @@ like(get(8082, '3.example.com'), qr/SUCC > SKIP: { > skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; > > +TODO: { > +local $TODO = 'broken TLSv1.3 CA list in LibreSSL' > + if $t->has_module('LibreSSL') && test_tls13(); > + > my $ca = join ' ', get(8082, '3.example.com'); > is($ca, '/CN=2.example.com', 'no trusted sent'); > > } > +} > > $t->stop(); > > @@ -137,6 +147,10 @@ is($t->read_file('status.log'), "500\n20 > > ############################################################################### > > +sub test_tls13 { > + get(8084) =~ /TLSv1.3/; > +} > + > sub get { > my ($port, $cert) = @_; > -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 11:05:16 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 15:05:16 +0400 Subject: [PATCH 19 of 20] Tests: removed multiple server certificates from ssl_ocsp.t In-Reply-To: <782531c3cd79dcf70027.1679148914@vm-bsd.mdounin.ru> References: <782531c3cd79dcf70027.1679148914@vm-bsd.mdounin.ru> Message-ID: <5AD7ACF5-169D-4578-8D3C-DA691B2F935F@nginx.com> > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679148855 -10800 > # Sat Mar 18 17:14:15 2023 +0300 > # Node ID 782531c3cd79dcf700276e10bef00e524de009d1 > # Parent c140f78fbc8f62c9694d3b969d1309570a96f2e7 > Tests: removed multiple server certificates from ssl_ocsp.t. > > Multiple server certificates are not needed to test OCSP verification of > client certificates (in contrast to OCSP stapling, where server certificates > are verified, and different staples should be correctly returned with > different server certificates). And using multiple server certificates > causes issues when testing with LibreSSL due to broken sigalgs-based > server certificate selection in LibreSSL with TLSv1.3. > > Accordingly, the test is simplified to do not use multiple server > certificates. > > diff --git a/ssl_ocsp.t b/ssl_ocsp.t > --- a/ssl_ocsp.t > +++ b/ssl_ocsp.t > @@ -63,10 +63,7 @@ http { > ssl_verify_depth 2; > ssl_client_certificate trusted.crt; > > - ssl_ciphers DEFAULT:ECCdraft; > - > - ssl_certificate_key ec.key; > - ssl_certificate ec.crt; > +# ssl_ciphers DEFAULT:ECCdraft; This doesn't serve its purpose now and can be removed, now that you've removed multiple (ECC) certificates. It was used to run tests with ECC certificates/ciphers, as otherwise it would result in "no shared cipher" error. ECCdraft is an old alias used to enable ECC ciphersuites and run tests with ECC certificate on OpenSSL 0.9.8, before they became official in RFC 4492. - ECC ciphersuites were disabled by default in 0.9.8c, and ECCdraft alias was used to turn them back. - ECC ciphersuites were re-enabled in 0.9.9 (1.0.0) > > ssl_certificate_key rsa.key; > ssl_certificate rsa.crt; > @@ -273,13 +270,8 @@ system("openssl ocsp -index $d/certindex > > # server cert/key > > -system("openssl ecparam -genkey -out $d/ec.key -name prime256v1 " > - . ">>$d/openssl.out 2>&1") == 0 or die "Can't create EC pem: $!\n"; > -system("openssl genrsa -out $d/rsa.key 2048 >>$d/openssl.out 2>&1") == 0 > - or die "Can't create RSA pem: $!\n"; > - > -foreach my $name ('ec', 'rsa') { > - system("openssl req -x509 -new -key $d/$name.key " > +foreach my $name ('rsa') { > + system('openssl req -x509 -new ' > . "-config $d/openssl.conf -subj /CN=$name/ " > . "-out $d/$name.crt -keyout $d/$name.key " > . ">>$d/openssl.out 2>&1") == 0 > @@ -288,7 +280,7 @@ foreach my $name ('ec', 'rsa') { > > $t->run_daemon(\&http_daemon, $t, port(8081)); > $t->run_daemon(\&http_daemon, $t, port(8082)); > -$t->run()->plan(14); > +$t->run()->plan(15); > > $t->waitforsocket("127.0.0.1:" . port(8081)); > $t->waitforsocket("127.0.0.1:" . port(8082)); > @@ -297,17 +289,17 @@ my $version = get_version(); > > ############################################################################### > > -like(get('RSA', 'end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); > +like(get('end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); > > # demonstrate that ocsp int request is failed due to missing resolver > > -like(get('RSA', 'end', sni => 'resolver'), > +like(get('end', sni => 'resolver'), > qr/400 Bad.*FAILED:certificate status request failed/s, > 'ocsp many failed request'); > > # demonstrate that ocsp int request is actually made by failing ocsp response > > -like(get('RSA', 'end', port => 8444), > +like(get('end', port => 8444), > qr/400 Bad.*FAILED:certificate status request failed/s, > 'ocsp many failed'); > > @@ -323,11 +315,11 @@ system("openssl ocsp -index $d/certindex > . ">>$d/openssl.out 2>&1") == 0 > or die "Can't create OCSP response: $!\n"; > > -like(get('RSA', 'end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); > +like(get('end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); > > # store into ssl_ocsp_cache > > -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); > +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); > > # revoke > > @@ -346,23 +338,23 @@ system("openssl ocsp -index $d/certindex > . ">>$d/openssl.out 2>&1") == 0 > or die "Can't create OCSP response: $!\n"; > > -like(get('RSA', 'end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); > +like(get('end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); > > # with different responder where it's still valid > > -like(get('RSA', 'end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); > +like(get('end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); > > # with different context to responder where it's still valid > > -like(get('RSA', 'end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); > +like(get('end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); > > # with cached ocsp response it's still valid > > -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); > +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); > > # ocsp end response signed with invalid (root) cert, expect HTTP 400 > > -like(get('ECDSA', 'ec-end'), > +like(get('ec-end'), > qr/400 Bad.*FAILED:certificate status request failed/s, > 'root ca not trusted'); > > @@ -374,12 +366,12 @@ system("openssl ocsp -index $d/certindex > . ">>$d/openssl.out 2>&1") == 0 > or die "Can't create EC OCSP response: $!\n"; > > -like(get('ECDSA', 'ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); > +like(get('ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); > > -my ($s, $ssl) = get('ECDSA', 'ec-end'); > +my ($s, $ssl) = get('ec-end'); > my $ses = Net::SSLeay::get_session($ssl); > > -like(get('ECDSA', 'ec-end', ses => $ses), > +like(get('ec-end', ses => $ses), > qr/200 OK.*SUCCESS:r/s, 'session reused'); > > # revoke with saved session > @@ -401,19 +393,22 @@ system("openssl ocsp -index $d/certindex > > # reusing session with revoked certificate > > -like(get('ECDSA', 'ec-end', ses => $ses), > +like(get('ec-end', ses => $ses), > qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); > > # regression test for self-signed > > -like(get('RSA', 'root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); > +like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); > + > +# check for errors > + > +like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); > > ############################################################################### > > sub get { > - my ($type, $cert, %extra) = @_; > - $type = 'PSS' if $type eq 'RSA' && $version > 0x0303; > - my ($s, $ssl) = get_ssl_socket($type, $cert, %extra); > + my ($cert, %extra) = @_; > + my ($s, $ssl) = get_ssl_socket($cert, %extra); > my $cipher = Net::SSLeay::get_cipher($ssl); > Test::Nginx::log_core('||', "cipher: $cipher"); > my $host = $extra{sni} ? $extra{sni} : 'localhost'; > @@ -428,7 +423,7 @@ sub get { > } > > sub get_ssl_socket { > - my ($type, $cert, %extra) = @_; > + my ($cert, %extra) = @_; > my $ses = $extra{ses}; > my $sni = $extra{sni}; > my $port = $extra{port} || 8443; > @@ -450,18 +445,6 @@ sub get_ssl_socket { > > my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > > - if (defined $type) { > - my $ssleay = Net::SSLeay::SSLeay(); > - if ($ssleay < 0x1000200f || $ssleay == 0x20000000) { > - Net::SSLeay::CTX_set_cipher_list($ctx, $type) > - or die("Failed to set cipher list"); > - } else { > - # SSL_CTRL_SET_SIGALGS_LIST > - Net::SSLeay::CTX_ctrl($ctx, 98, 0, $type . '+SHA256') > - or die("Failed to set sigalgs"); > - } > - } > - > Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") > or die if $cert; > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 11:11:44 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 15:11:44 +0400 Subject: [PATCH 20 of 20] Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > # HG changeset patch > # User Maxim Dounin > # Date 1679148869 -10800 > # Sat Mar 18 17:14:29 2023 +0300 > # Node ID f6f6a21b1c2a0d88cb2a4993f4c0113a3fb1e019 > # Parent 782531c3cd79dcf700276e10bef00e524de009d1 > Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3. > > LibreSSL does not support session reuse with TLSv1.3. Since LibreSSL is so broken wrt TLSv1.3, I don't think it deserves annotating every test in separate changed. The LibreSSL changes could be easily combined: this is at least p11, p12, p13, p16, p17, p20. Other broken or missing functionality such as signature algorithms and certificate authorities can be skipped in separate commits, though. > > diff --git a/ssl_ocsp.t b/ssl_ocsp.t > --- a/ssl_ocsp.t > +++ b/ssl_ocsp.t > @@ -371,9 +371,15 @@ like(get('ec-end'), qr/200 OK.*SUCCESS/s > my ($s, $ssl) = get('ec-end'); > my $ses = Net::SSLeay::get_session($ssl); > > +TODO: { > +local $TODO = 'no TLSv1.3 sessions in LibreSSL' > + if $t->has_module('LibreSSL') and $version > 0x303; > + > like(get('ec-end', ses => $ses), > qr/200 OK.*SUCCESS:r/s, 'session reused'); > > +} > + > # revoke with saved session > > system("openssl ca -config $d/ca.conf -revoke $d/ec-end.crt " > @@ -393,9 +399,15 @@ system("openssl ocsp -index $d/certindex > > # reusing session with revoked certificate > > +TODO: { > +local $TODO = 'no TLSv1.3 sessions in LibreSSL' > + if $t->has_module('LibreSSL') and $version > 0x303; > + > like(get('ec-end', ses => $ses), > qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); > > +} > + > # regression test for self-signed > > like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 22 11:43:12 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 22 Mar 2023 15:43:12 +0400 Subject: [PATCH 00 of 20] tests suite fixes for TLSv1.3 In-Reply-To: References: Message-ID: > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > Hello! > > Here are patch series for the test suite to address test failures > observed with TLSv1.3 enabled with BoringSSL and LibreSSL. > > Short summary of the issues seen: > > - BoringSSL with TLSv1.3 does not support session reuse via server-side > session cache, only with tickets. > > - BoringSSL with TLSv1.3 does not provide $ssl_session_id. > > - LibreSSL with TLSv1.3 does not support session reuse. > > - LibreSSL with TLSv1.3 fails to negotiate certificates based on > signature algorithms supported by the client, and fails with > "missing rsa certificate" and "unknown pkey type" errors. > > - LibreSSL with TLSv1.3 does not send CA lists to the client. > Missing peaces that allow me to run with LibreSSL: # HG changeset patch # User Sergey Kandaurov # Date 1679485246 -14400 # Wed Mar 22 15:40:46 2023 +0400 # Node ID dfe434f295d3da7e3b67bbbafeab245bb591f397 # Parent 826e00e7c037d617781239963e8b868b6b0de225 Tests: fixed upstream zone tests with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/stream_upstream_zone_ssl.t b/stream_upstream_zone_ssl.t --- a/stream_upstream_zone_ssl.t +++ b/stream_upstream_zone_ssl.t @@ -82,6 +82,19 @@ stream { ssl_certificate localhost.crt; ssl_session_cache builtin; } + + server { + listen 127.0.0.1:8085; + proxy_pass 127.0.0.1:8086; + } + + server { + listen 127.0.0.1:8086 ssl; + return $ssl_protocol; + + ssl_certificate_key localhost.key; + ssl_certificate localhost.crt; + } } EOF @@ -112,13 +125,33 @@ is(stream('127.0.0.1:' . port(8080))->re is(stream('127.0.0.1:' . port(8080))->read(), '.', 'ssl 2'); is(stream('127.0.0.1:' . port(8081))->read(), '.', 'ssl session new'); + +SKIP: { +skip 'no TLSv1.3 sessions in LibreSSL', 2 + if $t->has_module('LibreSSL') && test_tls13(); + is(stream('127.0.0.1:' . port(8081))->read(), 'r', 'ssl session reused'); is(stream('127.0.0.1:' . port(8081))->read(), 'r', 'ssl session reused 2'); +} + is(stream('127.0.0.1:' . port(8082))->read(), '.', 'backup ssl'); is(stream('127.0.0.1:' . port(8082))->read(), '.', 'backup ssl 2'); is(stream('127.0.0.1:' . port(8083))->read(), '.', 'backup ssl session new'); + +SKIP: { +skip 'no TLSv1.3 sessions in LibreSSL', 1 + if $t->has_module('LibreSSL') && test_tls13(); + is(stream('127.0.0.1:' . port(8083))->read(), 'r', 'backup ssl session reused'); +} + ############################################################################### + +sub test_tls13 { + stream('127.0.0.1:' . port(8085))->read() eq 'TLSv1.3'; +} + +############################################################################### diff --git a/upstream_zone_ssl.t b/upstream_zone_ssl.t --- a/upstream_zone_ssl.t +++ b/upstream_zone_ssl.t @@ -56,6 +56,7 @@ http { location / { add_header X-Session $ssl_session_reused; + add_header X-Protocol $ssl_protocol; } } @@ -114,12 +115,32 @@ foreach my $name ('localhost') { like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl'); like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl 2'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: \./s, 'ssl session new'); + +SKIP: { +skip 'no TLSv1.3 sessions in LibreSSL', 2 + if $t->has_module('LibreSSL') && test_tls13(); + like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused 2'); +} + like(http_get('/backup'), qr/200 OK.*X-Session: \./s, 'backup'); like(http_get('/backup'), qr/200 OK.*X-Session: \./s, 'backup 2'); like(http_get('/backup_reuse'), qr/200 OK.*X-Session: \./s, 'backup new'); + +SKIP: { +skip 'no TLSv1.3 sessions in LibreSSL', 1 + if $t->has_module('LibreSSL') && test_tls13(); + like(http_get('/backup_reuse'), qr/200 OK.*X-Session: r/s, 'backup reused'); +} + ############################################################################### + +sub test_tls13 { + return http_get('/ssl') =~ /TLSv1.3/; +} + +############################################################################### -- Sergey Kandaurov From v.zhestikov at f5.com Wed Mar 22 16:38:15 2023 From: v.zhestikov at f5.com (Vadim Zhestikov) Date: Wed, 22 Mar 2023 16:38:15 +0000 Subject: [njs] Fixed invalid NULL pointer check. Message-ID: details: https://hg.nginx.org/njs/rev/8e6a2cb0c23d branches: changeset: 2075:8e6a2cb0c23d user: Vadim Zhestikov date: Wed Mar 22 09:36:58 2023 -0700 description: Fixed invalid NULL pointer check. This fixes Coverity issues CID 1524495 and CID 1524494. diffstat: src/njs_parser.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff -r edf1c2aef957 -r 8e6a2cb0c23d src/njs_parser.c --- a/src/njs_parser.c Mon Mar 20 21:25:47 2023 -0700 +++ b/src/njs_parser.c Wed Mar 22 09:36:58 2023 -0700 @@ -8030,8 +8030,8 @@ njs_parser_export(njs_parser_t *parser, return NJS_DONE; } - peek = njs_lexer_peek_token(parser->lexer, peek, 1); - if (njs_slow_path(token == NULL)) { + peek = njs_lexer_peek_token(parser->lexer, peek, 0); + if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } From arut at nginx.com Wed Mar 22 16:38:52 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Wed, 22 Mar 2023 20:38:52 +0400 Subject: [PATCH] Mail: fixed handling of blocked client read events in proxy In-Reply-To: References: Message-ID: <20230322163852.yoqkx7aimwkvzrnf@N00W24XTQX> Hi, On Sat, Mar 11, 2023 at 02:24:49PM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1678533841 -10800 > # Sat Mar 11 14:24:01 2023 +0300 > # Node ID b97f2b983d1564d29280d03828503edca21a79ee > # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 > Mail: fixed handling of blocked client read events in proxy. > > When establishing a connection to the backend, nginx blocks reading > from the client with ngx_mail_proxy_block_read(). Previously, such > events were lost, and in some cases this resulted in connection hangs. > > Notably, this affected mail_imap_ssl.t on Windows, since the test > closes connections after requesting authentication, but without > waiting for any responses (so the connection close events might be > lost). The following patch alowed me to trigger the issue on my laptop. It slows down the auth http backend and switches event processing to select. diff --git a/mail_imap_ssl.t b/mail_imap_ssl.t --- a/mail_imap_ssl.t +++ b/mail_imap_ssl.t @@ -42,6 +42,7 @@ my $t = Test::Nginx->new()->has(qw/mail daemon off; events { + use select; } mail { @@ -104,6 +105,7 @@ http { server { listen 127.0.0.1:8080; server_name localhost; + limit_rate 100; location = /mail/auth { access_log auth.log test; > Fix is to post an event to read from the client after connecting to > the backend if there were blocked events. > > diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c > --- a/src/mail/ngx_mail_proxy_module.c > +++ b/src/mail/ngx_mail_proxy_module.c > @@ -327,7 +327,9 @@ ngx_mail_proxy_pop3_handler(ngx_event_t > c->log->action = NULL; > ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); > > - if (s->buffer->pos < s->buffer->last) { > + if (s->buffer->pos < s->buffer->last > + || s->connection->read->ready) > + { > ngx_post_event(c->write, &ngx_posted_events); > } > > @@ -486,7 +488,9 @@ ngx_mail_proxy_imap_handler(ngx_event_t > c->log->action = NULL; > ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); > > - if (s->buffer->pos < s->buffer->last) { > + if (s->buffer->pos < s->buffer->last > + || s->connection->read->ready) > + { > ngx_post_event(c->write, &ngx_posted_events); > } > > @@ -821,7 +825,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t > c->log->action = NULL; > ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); > > - if (s->buffer->pos < s->buffer->last) { > + if (s->buffer->pos < s->buffer->last > + || s->connection->read->ready) > + { > ngx_post_event(c->write, &ngx_posted_events); > } Looks ok From xeioex at nginx.com Thu Mar 23 05:43:15 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 23 Mar 2023 05:43:15 +0000 Subject: [njs] Fix compiler detection when CC has a wrapper. Message-ID: details: https://hg.nginx.org/njs/rev/ec007866a53b branches: changeset: 2076:ec007866a53b user: Orgad Shaneh date: Wed Mar 22 15:22:37 2023 +0200 description: Fix compiler detection when CC has a wrapper. For example CC='ccache gcc'. diffstat: auto/cc | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 8e6a2cb0c23d -r ec007866a53b auto/cc --- a/auto/cc Wed Mar 22 09:36:58 2023 -0700 +++ b/auto/cc Wed Mar 22 15:22:37 2023 +0200 @@ -13,7 +13,7 @@ END # Allow error exit status. set +e -if [ -z `which $CC` ]; then +if [ -z "$(which $CC)" ]; then echo echo $0: error: $CC not found. echo From roy at teeuwen.be Thu Mar 23 08:26:48 2023 From: roy at teeuwen.be (Roy Teeuwen) Date: Thu, 23 Mar 2023 09:26:48 +0100 Subject: Configurable sleep period for connections In-Reply-To: References: Message-ID: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> Hey, We are using NGINX as a proxy / caching layer for a backend application. Our backend has a relatively slow response time, ranging between the 100 to 300ms. We want the NGINX proxy to be as speedy as possible, to do this we have implemented the following logic: - Cache all responses for 5 mins (based on cache control headers) - Use stale cache for error's on the backend - Do a background update for stale cache The last part has an issue, namely if a first request reaches nginx, it will trigger a background request, but other requests for the same resource will be locked until this background request is finished instead of still returning the stale cache that is available. This is caused by the fact that there is a keepalive on the connection, which locks all subsequent requests until the background request is finished. The issue that we are facing in this situation is that the locking is very long, namely 500ms hardcoded. I think it is caused by this: https://github.com/nginx/nginx/blob/master/src/core/ngx_connection.c#L703 This means that our relatively slow backend of 100-200ms actually gets worse than better. Is it an option to make this 500ms a configurable setting instead of 500ms? Are there any downsides to making this 500ms lower? I'd be willing to see if we can contribute this. Another option that I'd tried is to set the keepalive to 0, so that every request is a new connection. In small amounts of requests this actually seemed to solve the issue, but the moment that we went to a real life situation, this degraded the performance massively, so we had to revert this Greets, Roy -------------- next part -------------- An HTML attachment was scrubbed... URL: From pluknet at nginx.com Thu Mar 23 13:18:00 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 23 Mar 2023 17:18:00 +0400 Subject: [PATCH] QUIC: OpenSSL compatibility layer In-Reply-To: <20230208144138.u7pozeds34nqr6mi@N00W24XTQX> References: <64a365dcb52503e91d91.1671606452@arut-laptop> <20230109111316.yfqj24unoubibihz@N00W24XTQX> <20230202183522.o4w5xbjvegbchts4@Y9MQ9X2QVV> <20230206142701.pxzaq3irckqads7y@N00W24XTQX> <20230208122810.tsxynyhlqgt2ubsm@Y9MQ9X2QVV> <20230208144138.u7pozeds34nqr6mi@N00W24XTQX> Message-ID: <20230323131800.3tvdw4i336x36dsv@Y9MQ9X2QVV> On Wed, Feb 08, 2023 at 06:41:38PM +0400, Roman Arutyunyan wrote: > # HG changeset patch > # User Roman Arutyunyan > # Date 1675867049 -14400 > # Wed Feb 08 18:37:29 2023 +0400 > # Branch quic > # Node ID a3142c8833f5bf1186599e7938141f5062fac4a2 > # Parent 3c33d39a51d334d99fcc7d2b45e8d8190c431492 > QUIC: OpenSSL compatibility layer. > > The change allows to compile QUIC with OpenSSL which lacks BoringSSL QUIC API. > > This implementation does not support 0-RTT. [..] > diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c > +++ b/src/http/modules/ngx_http_ssl_module.c > @@ -9,6 +9,10 @@ > #include > #include > > +#if (NGX_QUIC_OPENSSL_COMPAT) > +#include > +#endif > + > > typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, > ngx_pool_t *pool, ngx_str_t *s); > @@ -1317,16 +1321,22 @@ ngx_http_ssl_init(ngx_conf_t *cf) > continue; > } > > + cscf = addr[a].default_server; > + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > + > if (addr[a].opt.http3) { > name = "http3"; > > +#if (NGX_QUIC_OPENSSL_COMPAT) > + if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { > + return NGX_ERROR; > + } > +#endif > + > } else { > name = "ssl"; > } > > - cscf = addr[a].default_server; > - sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > - > if (sscf->certificates) { > > if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { > diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c > --- a/src/stream/ngx_stream_ssl_module.c > +++ b/src/stream/ngx_stream_ssl_module.c > @@ -9,6 +9,10 @@ > #include > #include > > +#if (NGX_QUIC_OPENSSL_COMPAT) > +#include > +#endif > + > > typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, > ngx_pool_t *pool, ngx_str_t *s); > @@ -1218,6 +1222,12 @@ ngx_stream_ssl_init(ngx_conf_t *cf) > > scf = listen[i].ctx->srv_conf[ngx_stream_ssl_module.ctx_index]; > > +#if (NGX_QUIC_OPENSSL_COMPAT) > + if (ngx_quic_compat_init(cf, scf->ssl.ctx) != NGX_OK) { > + return NGX_ERROR; > + } > +#endif > + > if (scf->certificates && !(scf->protocols & NGX_SSL_TLSv1_3)) { > ngx_log_error(NGX_LOG_EMERG, cf->log, 0, > "\"ssl_protocols\" must enable TLSv1.3 for " Following internal discussion with Roman, below is a fix for compat layer initialization. This is a fallout after moving initialization from ngx_ssl_create(). Although that change was justified, it has left a couple of issues: - SSL context can be NULL (normally caught later in function) - only the default server was initialized, breaks with SNI. The latter was caught by h3_ssl_reject_handshake.t The fix is to initialize all servers that have SSL context. The postconfiguration function is quite large, so this is moved to the separate function. While here, moved back assignments closer to the place of their use. # HG changeset patch # User Sergey Kandaurov # Date 1679577205 -14400 # Thu Mar 23 17:13:25 2023 +0400 # Branch quic # Node ID dcef88bdea77cf84b97580f21fbe05cc05ab631d # Parent f4279edda9fdbbd836a86926d86cdc727492e94c HTTP/3: fixed OpenSSL compatibility layer initialization. SSL context is not present if the default server has neither certificates nor ssl_reject_handshake enabled. Previously, this led to null pointer dereference before it would be caught with configuration checks. Additionally, virtual servers with distinct SSL contexts need to initialize compatibility layer in order to complete a QUIC handshake. diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -56,6 +56,10 @@ static char *ngx_http_ssl_conf_command_c void *data); static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf); +#if (NGX_QUIC_OPENSSL_COMPAT) +static ngx_int_t ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, + ngx_http_conf_addr_t *addr); +#endif static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { @@ -1328,14 +1332,11 @@ ngx_http_ssl_init(ngx_conf_t *cf) continue; } - cscf = addr[a].default_server; - sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; - if (addr[a].opt.quic) { name = "quic"; #if (NGX_QUIC_OPENSSL_COMPAT) - if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { + if (ngx_http_ssl_quic_compat_init(cf, &addr[a]) != NGX_OK) { return NGX_ERROR; } #endif @@ -1344,6 +1345,9 @@ ngx_http_ssl_init(ngx_conf_t *cf) name = "ssl"; } + cscf = addr[a].default_server; + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + if (sscf->certificates) { if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { @@ -1391,3 +1395,27 @@ ngx_http_ssl_init(ngx_conf_t *cf) return NGX_OK; } + + +static ngx_int_t +ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) +{ + ngx_uint_t s; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_srv_conf_t **cscfp, *cscf; + + cscfp = addr->servers.elts; + for (s = 0; s < addr->servers.nelts; s++) { + + cscf = cscfp[s]; + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + + if (sscf->certificates || sscf->reject_handshake) { + if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { + return NGX_ERROR; + } + } + } + + return NGX_OK; +} From arut at nginx.com Thu Mar 23 13:53:57 2023 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 23 Mar 2023 17:53:57 +0400 Subject: [PATCH] QUIC: OpenSSL compatibility layer In-Reply-To: <20230323131800.3tvdw4i336x36dsv@Y9MQ9X2QVV> References: <64a365dcb52503e91d91.1671606452@arut-laptop> <20230109111316.yfqj24unoubibihz@N00W24XTQX> <20230202183522.o4w5xbjvegbchts4@Y9MQ9X2QVV> <20230206142701.pxzaq3irckqads7y@N00W24XTQX> <20230208122810.tsxynyhlqgt2ubsm@Y9MQ9X2QVV> <20230208144138.u7pozeds34nqr6mi@N00W24XTQX> <20230323131800.3tvdw4i336x36dsv@Y9MQ9X2QVV> Message-ID: <20230323135357.3s3wf75r4no6snbk@N00W24XTQX> Hi, On Thu, Mar 23, 2023 at 05:18:00PM +0400, Sergey Kandaurov wrote: > On Wed, Feb 08, 2023 at 06:41:38PM +0400, Roman Arutyunyan wrote: > > # HG changeset patch > > # User Roman Arutyunyan > > # Date 1675867049 -14400 > > # Wed Feb 08 18:37:29 2023 +0400 > > # Branch quic > > # Node ID a3142c8833f5bf1186599e7938141f5062fac4a2 > > # Parent 3c33d39a51d334d99fcc7d2b45e8d8190c431492 > > QUIC: OpenSSL compatibility layer. > > > > The change allows to compile QUIC with OpenSSL which lacks BoringSSL QUIC API. > > > > This implementation does not support 0-RTT. > > [..] > > > diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c > > --- a/src/http/modules/ngx_http_ssl_module.c > > +++ b/src/http/modules/ngx_http_ssl_module.c > > @@ -9,6 +9,10 @@ > > #include > > #include > > > > +#if (NGX_QUIC_OPENSSL_COMPAT) > > +#include > > +#endif > > + > > > > typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, > > ngx_pool_t *pool, ngx_str_t *s); > > @@ -1317,16 +1321,22 @@ ngx_http_ssl_init(ngx_conf_t *cf) > > continue; > > } > > > > + cscf = addr[a].default_server; > > + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > > + > > if (addr[a].opt.http3) { > > name = "http3"; > > > > +#if (NGX_QUIC_OPENSSL_COMPAT) > > + if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { > > + return NGX_ERROR; > > + } > > +#endif > > + > > } else { > > name = "ssl"; > > } > > > > - cscf = addr[a].default_server; > > - sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > > - > > if (sscf->certificates) { > > > > if (addr[a].opt.http3 && !(sscf->protocols & NGX_SSL_TLSv1_3)) { > > diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c > > --- a/src/stream/ngx_stream_ssl_module.c > > +++ b/src/stream/ngx_stream_ssl_module.c > > @@ -9,6 +9,10 @@ > > #include > > #include > > > > +#if (NGX_QUIC_OPENSSL_COMPAT) > > +#include > > +#endif > > + > > > > typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, > > ngx_pool_t *pool, ngx_str_t *s); > > @@ -1218,6 +1222,12 @@ ngx_stream_ssl_init(ngx_conf_t *cf) > > > > scf = listen[i].ctx->srv_conf[ngx_stream_ssl_module.ctx_index]; > > > > +#if (NGX_QUIC_OPENSSL_COMPAT) > > + if (ngx_quic_compat_init(cf, scf->ssl.ctx) != NGX_OK) { > > + return NGX_ERROR; > > + } > > +#endif > > + > > if (scf->certificates && !(scf->protocols & NGX_SSL_TLSv1_3)) { > > ngx_log_error(NGX_LOG_EMERG, cf->log, 0, > > "\"ssl_protocols\" must enable TLSv1.3 for " > > Following internal discussion with Roman, below is a fix for compat > layer initialization. This is a fallout after moving initialization > from ngx_ssl_create(). Although that change was justified, it has > left a couple of issues: > - SSL context can be NULL (normally caught later in function) > - only the default server was initialized, breaks with SNI. > The latter was caught by h3_ssl_reject_handshake.t > > The fix is to initialize all servers that have SSL context. > The postconfiguration function is quite large, so this is moved > to the separate function. While here, moved back assignments > closer to the place of their use. > > # HG changeset patch > # User Sergey Kandaurov > # Date 1679577205 -14400 > # Thu Mar 23 17:13:25 2023 +0400 > # Branch quic > # Node ID dcef88bdea77cf84b97580f21fbe05cc05ab631d > # Parent f4279edda9fdbbd836a86926d86cdc727492e94c > HTTP/3: fixed OpenSSL compatibility layer initialization. > > SSL context is not present if the default server has neither certificates nor > ssl_reject_handshake enabled. Previously, this led to null pointer dereference > before it would be caught with configuration checks. > > Additionally, virtual servers with distinct SSL contexts need to initialize > compatibility layer in order to complete a QUIC handshake. > > diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c > +++ b/src/http/modules/ngx_http_ssl_module.c > @@ -56,6 +56,10 @@ static char *ngx_http_ssl_conf_command_c > void *data); > > static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf); > +#if (NGX_QUIC_OPENSSL_COMPAT) > +static ngx_int_t ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, > + ngx_http_conf_addr_t *addr); > +#endif > > > static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { > @@ -1328,14 +1332,11 @@ ngx_http_ssl_init(ngx_conf_t *cf) > continue; > } > > - cscf = addr[a].default_server; > - sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > - > if (addr[a].opt.quic) { > name = "quic"; > > #if (NGX_QUIC_OPENSSL_COMPAT) > - if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { > + if (ngx_http_ssl_quic_compat_init(cf, &addr[a]) != NGX_OK) { > return NGX_ERROR; > } > #endif > @@ -1344,6 +1345,9 @@ ngx_http_ssl_init(ngx_conf_t *cf) > name = "ssl"; > } > > + cscf = addr[a].default_server; > + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > + > if (sscf->certificates) { > > if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) { > @@ -1391,3 +1395,27 @@ ngx_http_ssl_init(ngx_conf_t *cf) > > return NGX_OK; > } > + > + > +static ngx_int_t > +ngx_http_ssl_quic_compat_init(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) > +{ > + ngx_uint_t s; > + ngx_http_ssl_srv_conf_t *sscf; > + ngx_http_core_srv_conf_t **cscfp, *cscf; > + > + cscfp = addr->servers.elts; > + for (s = 0; s < addr->servers.nelts; s++) { > + > + cscf = cscfp[s]; > + sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; > + > + if (sscf->certificates || sscf->reject_handshake) { > + if (ngx_quic_compat_init(cf, sscf->ssl.ctx) != NGX_OK) { > + return NGX_ERROR; > + } > + } > + } > + > + return NGX_OK; > +} Looks good From mdounin at mdounin.ru Thu Mar 23 14:10:12 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:10:12 +0300 Subject: [PATCH 01 of 20] Tests: separate SSL session reuse tests In-Reply-To: <6B831977-21E8-402B-A254-ACA0A59E7387@nginx.com> References: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> <6B831977-21E8-402B-A254-ACA0A59E7387@nginx.com> Message-ID: Hello! On Wed, Mar 22, 2023 at 12:57:56PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679105686 -10800 > > # Sat Mar 18 05:14:46 2023 +0300 > > # Node ID 86c394a226d2a7d463da7a1b7e88375c71c0c69b > > # Parent 3c9aa6c23fc836725b96cf056d218217a5a81603 > > Tests: separate SSL session reuse tests. > > > > Instead of being mixed with generic SSL tests, session reuse variants > > are now tested in a separate file. > > > > In the generic SSL tests only basic session reuse is now tested, > > notably with session tickets enabled and a shared SSL session cache. > > This should make it possible to reuse sessions in all cases (except > > when it's not supported, such as with LibreSSL with TLSv1.3). > > > > Note that session reuse with tickets implies that $ssl_session_id > > is selected by the client and therefore is not available on the > > initial connection. Relevant test is modified to handle this. > > > > Further, BoringSSL does not use legacy session ID with TLSv1.3 even > > if it is sent by the client. In contrast, OpenSSL always generates > > an unique legacy session id, so it is available with TLSv1.3 even if > > session resumption does not work (such as with old Net::SSLeay and > > IO::Socket::SSL modules). > > Note that TLSv1.3 has only ticket based session resumption. > BoringSSL has a different notion on using legacy session IDs > in TLSv1.3, see tls13_create_session_with_ticket() in sources: > > // Historically, OpenSSL filled in fake session IDs for ticket-based sessions. > // Envoy's tests depend on this, although perhaps they shouldn't. > SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id); > > Later, SSL_SESSION_get_id() was additionally annotated in ssl.h: > > // As a workaround for some broken applications, BoringSSL sometimes synthesizes > // arbitrary session IDs for non-ID-based sessions. This behavior may be > // removed in the future. > > As for TLSv1.3 server context, BoringSSL doesn't seem to use "session ID" > besides echoing the client's legacy_session_id field content in the > legacy_session_id_echo field of ServerHello/HRR during handshake, > as mandated in RFC 8446, 4.1.3. So it doesn't settle in the session. Yes, that's basically what "BoringSSL does not use legacy session ID with TLSv1.3..." sentence describes. > > > > diff --git a/ssl.t b/ssl.t > > --- a/ssl.t > > +++ b/ssl.t > > @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( > > plan(skip_all => 'IO::Socket::SSL too old') if $@; > > > > my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) > > - ->has_daemon('openssl')->plan(28); > > + ->has_daemon('openssl')->plan(21); > > > > $t->write_file_expand('nginx.conf', <<'EOF'); > > > > @@ -47,7 +47,6 @@ http { > > > > ssl_certificate_key localhost.key; > > ssl_certificate localhost.crt; > > - ssl_session_tickets off; > > > > log_format ssl $ssl_protocol; > > > > @@ -59,6 +58,7 @@ http { > > ssl_certificate_key inner.key; > > ssl_certificate inner.crt; > > ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets on; > > ssl_verify_client optional_no_ca; > > > > keepalive_requests 1000; > > @@ -100,57 +100,11 @@ http { > > } > > > > server { > > - listen 127.0.0.1:8081; > > - server_name localhost; > > - > > - # Special case for enabled "ssl" directive. > > - > > - ssl on; > > Removing tests for the "ssl" legacy directive doesn't feel right > now and is out of scope of this change. Please put this back. > Not being able to test it is fragile and can be left silently > broken, especially with the upcoming quic merge. It used to be tested as a side effect of session reuse tests, and hence it is now gone. It is also tested at least in ssl_reject_handshake.t, so I tend to think it is enough. > On the other hand, I'm fine with finally removing this directive. > It was made obsolete in version 1.15.0, released on 05 Jun 2018, > with the "listen .. ssl" upgrade path available back to 0.7.14, > released on 01 Sep 2008. I think we'll consider this in 1.25.x branch. > > - ssl_session_cache builtin; > > - > > - location / { > > - return 200 "body $ssl_session_reused"; > > - } > > - } > > - > > - server { > > - listen 127.0.0.1:8082 ssl; > > - server_name localhost; > > - > > - ssl_session_cache builtin:1000; > > - > > - location / { > > - return 200 "body $ssl_session_reused"; > > - } > > - } > > - > > - server { > > - listen 127.0.0.1:8083 ssl; > > - server_name localhost; > > - > > - ssl_session_cache none; > > - > > - location / { > > - return 200 "body $ssl_session_reused"; > > - } > > - } > > - > > - server { > > - listen 127.0.0.1:8084 ssl; > > - server_name localhost; > > - > > - ssl_session_cache off; > > - > > - location / { > > - return 200 "body $ssl_session_reused"; > > - } > > - } > > - > > - server { > > listen 127.0.0.1:8086 ssl; > > Since you noticeably touched this file, > you could renumber these ports as well. I've tried my best to keep refrain from unrelated changes, the patch is already rather big. > > server_name localhost; > > > > ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets on; > > ssl_session_timeout 1; > > > > location / { > > @@ -216,59 +170,34 @@ foreach my $name ('localhost', 'inner') > > or die "Can't create certificate for $name: $!\n"; > > } > > > > -# suppress deprecation warning > > - > > -open OLDERR, ">&", \*STDERR; close STDERR; > > $t->run(); > > -open STDERR, ">&", \*OLDERR; > > > > ############################################################################### > > > > -my $ctx; > > +# ssl session reuse > > > > -SKIP: { > > -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ > > - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); > > +my $ctx = get_ssl_context(); > > > > -$ctx = get_ssl_context(); > > +like(get('/', 8085, $ctx), qr/^body \.$/m, 'session'); > > > > -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); > > -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); > > - > > -$ctx = get_ssl_context(); > > +TODO: { > > +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' > > + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); > > +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' > > + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); > > Not sure why do you convert this to TODO. > > TODO blocks are used for something we control or able to fix. > SSL libraries are not something like that, so using SKIP there > is more appropriate and follows other platform-specific tests. > Besides that, TODOs plagues diagnostic output for no purpose. > > See also Test::More documentation: > > When do I use SKIP vs. TODO? > If it's something the user might not be able to do, use SKIP. This > includes optional modules that aren't installed, running under an OS > that doesn't have some feature (like "fork()" or symlinks), or maybe > you need an Internet connection and one isn't available. > > If it's something the programmer hasn't done yet, use TODO. This is > for any code you haven't written yet, or bugs you have yet to fix, > but want to put tests in your testing script (always a good idea). As you can see from the Test::More documentation, the main distinction is: SKIP is to be used when something cannot be tested, and TODO is to use when something doesn't work (but we can test it). In this particular case, things can be tested - but tests will fail due to lack of support in IO::Socket::SSL / Net::SSLeay. Immediate benefits of using TODO here is that a) test output provides good indication of what actually happens with these module versions (that is, it does not unexpectedly die or segfault, but session reuse does not work), b) tests are actually run and it can be easily spotted if they unexpectedly succeed, and c) it combines nicely with LibreSSL / BoringSSL TODOs (which are TODO for similar reasons, see the following patches). As the code in question is not under our control, it might be good enough to use SKIP instead of TODO (and the patch series does this at least once). But I tend to think that at least in case of LibreSSL it would be much better to use TODOs, so we'll be able to see when things are finally fixed in LibreSSL - I would expect this to happen in a foreseeable future. > > > > -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); > > -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); > > - > > -$ctx = get_ssl_context(); > > - > > -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); > > -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); > > +like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); > > > > } > > > > -$ctx = get_ssl_context(); > > - > > -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); > > -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); > > - > > -$ctx = get_ssl_context(); > > - > > -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); > > -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); > > - > > # ssl certificate inheritance > > > > -my $s = get_ssl_socket(8081); > > +my $s = get_ssl_socket(8086); > > like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); > > > > -$s->close(); > > - > > $s = get_ssl_socket(8085); > > like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); > > > > -$s->close(); > > - > > # session timeout > > > > $ctx = get_ssl_context(); > > @@ -280,8 +209,12 @@ like(get('/', 8086, $ctx), qr/^body \.$/ > > > > # embedded variables > > > > -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); > > +$ctx = get_ssl_context(); > > +like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); > > +like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); > > + > > unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); > > + > > like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); > > > > SKIP: { > > @@ -334,6 +267,10 @@ like(`grep -F '[crit]' ${\($t->testdir() > > > > ############################################################################### > > > > +sub test_tls13 { > > + return get('/protocol', 8085) =~ /TLSv1.3/; > > +} > > + > > sub get { > > my ($uri, $port, $ctx) = @_; > > my $s = get_ssl_socket($port, $ctx) or return; > > diff --git a/ssl.t b/ssl_session_reuse.t > > copy from ssl.t > > copy to ssl_session_reuse.t > > --- a/ssl.t > > +++ b/ssl_session_reuse.t > > @@ -2,6 +2,7 @@ > > > > # (C) Sergey Kandaurov > > Initial session resumption tests in ssl.t is Andrey's work. > You can drop this line, doesn't belong there. Ok, removed. > > # (C) Andrey Zelenkov > > +# (C) Maxim Dounin > > # (C) Nginx, Inc. > > > > # Tests for http ssl module. > > The description needs update. Thanks, fixed. -# Tests for http ssl module. +# Tests for http ssl module, session reuse. The same in the other *_ssl_session_reuse.t tests. > You can drop the line "use Socket qw/ CRLF /;" below, now unused. Thanks, removed. > > @@ -30,8 +31,8 @@ plan(skip_all => 'IO::Socket::SSL not in > > eval { IO::Socket::SSL::SSL_VERIFY_NONE(); }; > > plan(skip_all => 'IO::Socket::SSL too old') if $@; > > > > -my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) > > - ->has_daemon('openssl')->plan(28); > > +my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/) > > + ->has_daemon('openssl')->plan(8); > > > > $t->write_file_expand('nginx.conf', <<'EOF'); > > > > @@ -47,66 +48,37 @@ http { > > > > ssl_certificate_key localhost.key; > > ssl_certificate localhost.crt; > > - ssl_session_tickets off; > > - > > - log_format ssl $ssl_protocol; > > > > server { > > - listen 127.0.0.1:8085 ssl; > > - listen 127.0.0.1:8080; > > + listen 127.0.0.1:8443 ssl; > > server_name localhost; > > > > - ssl_certificate_key inner.key; > > - ssl_certificate inner.crt; > > - ssl_session_cache shared:SSL:1m; > > - ssl_verify_client optional_no_ca; > > - > > - keepalive_requests 1000; > > - > > location / { > > return 200 "body $ssl_session_reused"; > > } > > - location /id { > > - return 200 "body $ssl_session_id"; > > - } > > - location /cipher { > > - return 200 "body $ssl_cipher"; > > - } > > - location /ciphers { > > - return 200 "body $ssl_ciphers"; > > - } > > - location /client_verify { > > - return 200 "body $ssl_client_verify"; > > - } > > location /protocol { > > return 200 "body $ssl_protocol"; > > } > > - location /issuer { > > - return 200 "body $ssl_client_i_dn:$ssl_client_i_dn_legacy"; > > - } > > - location /subject { > > - return 200 "body $ssl_client_s_dn:$ssl_client_s_dn_legacy"; > > - } > > - location /time { > > - return 200 "body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain"; > > - } > > - > > - location /body { > > - add_header X-Body $request_body always; > > - proxy_pass http://127.0.0.1:8080/; > > - > > - access_log %%TESTDIR%%/ssl.log ssl; > > - } > > } > > > > server { > > - listen 127.0.0.1:8081; > > + listen 127.0.0.1:8444 ssl; > > server_name localhost; > > > > - # Special case for enabled "ssl" directive. > > + ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets on; > > > > - ssl on; > > - ssl_session_cache builtin; > > + location / { > > + return 200 "body $ssl_session_reused"; > > + } > > + } > > + > > + server { > > + listen 127.0.0.1:8445 ssl; > > + server_name localhost; > > + > > + ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets off; > > > > location / { > > return 200 "body $ssl_session_reused"; > > @@ -114,10 +86,11 @@ http { > > } > > > > server { > > - listen 127.0.0.1:8082 ssl; > > + listen 127.0.0.1:8446 ssl; > > server_name localhost; > > > > - ssl_session_cache builtin:1000; > > + ssl_session_cache builtin; > > + ssl_session_tickets off; > > > > location / { > > return 200 "body $ssl_session_reused"; > > @@ -125,10 +98,11 @@ http { > > } > > > > server { > > - listen 127.0.0.1:8083 ssl; > > + listen 127.0.0.1:8447 ssl; > > server_name localhost; > > > > - ssl_session_cache none; > > + ssl_session_cache builtin:1000; > > + ssl_session_tickets off; > > > > location / { > > return 200 "body $ssl_session_reused"; > > @@ -136,10 +110,11 @@ http { > > } > > > > server { > > - listen 127.0.0.1:8084 ssl; > > + listen 127.0.0.1:8448 ssl; > > server_name localhost; > > > > - ssl_session_cache off; > > + ssl_session_cache none; > > + ssl_session_tickets off; > > > > location / { > > return 200 "body $ssl_session_reused"; > > @@ -147,11 +122,11 @@ http { > > } > > > > server { > > - listen 127.0.0.1:8086 ssl; > > + listen 127.0.0.1:8449 ssl; > > server_name localhost; > > > > - ssl_session_cache shared:SSL:1m; > > - ssl_session_timeout 1; > > + ssl_session_cache off; > > + ssl_session_tickets off; > > > > location / { > > return 200 "body $ssl_session_reused"; > > @@ -171,44 +146,7 @@ EOF > > > > my $d = $t->testdir(); > > > > -$t->write_file('ca.conf', < > -[ ca ] > > -default_ca = myca > > - > > -[ myca ] > > -new_certs_dir = $d > > -database = $d/certindex > > -default_md = sha256 > > -policy = myca_policy > > -serial = $d/certserial > > -default_days = 3 > > - > > -[ myca_policy ] > > -commonName = supplied > > -EOF > > - > > -$t->write_file('certserial', '1000'); > > -$t->write_file('certindex', ''); > > - > > -system('openssl req -x509 -new ' > > - . "-config $d/openssl.conf -subj /CN=issuer/ " > > - . "-out $d/issuer.crt -keyout $d/issuer.key " > > - . ">>$d/openssl.out 2>&1") == 0 > > - or die "Can't create certificate for issuer: $!\n"; > > - > > -system("openssl req -new " > > - . "-config $d/openssl.conf -subj /CN=subject/ " > > - . "-out $d/subject.csr -keyout $d/subject.key " > > - . ">>$d/openssl.out 2>&1") == 0 > > - or die "Can't create certificate for subject: $!\n"; > > - > > -system("openssl ca -batch -config $d/ca.conf " > > - . "-keyfile $d/issuer.key -cert $d/issuer.crt " > > - . "-subj /CN=subject/ -in $d/subject.csr -out $d/subject.crt " > > - . ">>$d/openssl.out 2>&1") == 0 > > - or die "Can't sign certificate for subject: $!\n"; > > - > > -foreach my $name ('localhost', 'inner') { > > +foreach my $name ('localhost') { > > system('openssl req -x509 -new ' > > . "-config $d/openssl.conf -subj /CN=$name/ " > > . "-out $d/$name.crt -keyout $d/$name.key " > > @@ -216,124 +154,56 @@ foreach my $name ('localhost', 'inner') > > or die "Can't create certificate for $name: $!\n"; > > } > > > > -# suppress deprecation warning > > - > > -open OLDERR, ">&", \*STDERR; close STDERR; > > $t->run(); > > -open STDERR, ">&", \*OLDERR; > > > > ############################################################################### > > > > my $ctx; > > You can remove this declaration to the only sub test_reuse user. Thanks, fixed (as well as in mail). [...] Updated patch: # HG changeset patch # User Maxim Dounin # Date 1679498636 -10800 # Wed Mar 22 18:23:56 2023 +0300 # Node ID 8fb46a1a0f3e6ca94e2c59ce50e3043a67cde674 # Parent 1f125771f1a1014dcceb8b765f3dd3153d4552f9 Tests: separate SSL session reuse tests. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. In the generic SSL tests only basic session reuse is now tested, notably with session tickets enabled and a shared SSL session cache. This should make it possible to reuse sessions in all cases (except when it's not supported, such as with LibreSSL with TLSv1.3). Note that session reuse with tickets implies that $ssl_session_id is selected by the client and therefore is not available on the initial connection. Relevant test is modified to handle this. Further, BoringSSL does not use legacy session ID with TLSv1.3 even if it is sent by the client. In contrast, OpenSSL always generates an unique legacy session id, so it is available with TLSv1.3 even if session resumption does not work (such as with old Net::SSLeay and IO::Socket::SSL modules). diff --git a/ssl.t b/ssl.t --- a/ssl.t +++ b/ssl.t @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( plan(skip_all => 'IO::Socket::SSL too old') if $@; my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) - ->has_daemon('openssl')->plan(28); + ->has_daemon('openssl')->plan(21); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -47,7 +47,6 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; log_format ssl $ssl_protocol; @@ -59,6 +58,7 @@ http { ssl_certificate_key inner.key; ssl_certificate inner.crt; ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; ssl_verify_client optional_no_ca; keepalive_requests 1000; @@ -100,57 +100,11 @@ http { } server { - listen 127.0.0.1:8081; - server_name localhost; - - # Special case for enabled "ssl" directive. - - ssl on; - ssl_session_cache builtin; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8082 ssl; - server_name localhost; - - ssl_session_cache builtin:1000; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8083 ssl; - server_name localhost; - - ssl_session_cache none; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8084 ssl; - server_name localhost; - - ssl_session_cache off; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { listen 127.0.0.1:8086 ssl; server_name localhost; ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; ssl_session_timeout 1; location / { @@ -216,59 +170,34 @@ foreach my $name ('localhost', 'inner') or die "Can't create certificate for $name: $!\n"; } -# suppress deprecation warning - -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -my $ctx; +# ssl session reuse -SKIP: { -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +my $ctx = get_ssl_context(); -$ctx = get_ssl_context(); +like(get('/', 8085, $ctx), qr/^body \.$/m, 'session'); -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); - -$ctx = get_ssl_context(); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); +like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); } -$ctx = get_ssl_context(); - -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); - # ssl certificate inheritance -my $s = get_ssl_socket(8081); +my $s = get_ssl_socket(8086); like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); -$s->close(); - $s = get_ssl_socket(8085); like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); -$s->close(); - # session timeout $ctx = get_ssl_context(); @@ -280,8 +209,12 @@ like(get('/', 8086, $ctx), qr/^body \.$/ # embedded variables -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); +$ctx = get_ssl_context(); +like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); +like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); + unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); + like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); SKIP: { @@ -334,6 +267,10 @@ like(`grep -F '[crit]' ${\($t->testdir() ############################################################################### +sub test_tls13 { + return get('/protocol', 8085) =~ /TLSv1.3/; +} + sub get { my ($uri, $port, $ctx) = @_; my $s = get_ssl_socket($port, $ctx) or return; diff --git a/ssl.t b/ssl_session_reuse.t copy from ssl.t copy to ssl_session_reuse.t --- a/ssl.t +++ b/ssl_session_reuse.t @@ -1,10 +1,10 @@ #!/usr/bin/perl -# (C) Sergey Kandaurov # (C) Andrey Zelenkov +# (C) Maxim Dounin # (C) Nginx, Inc. -# Tests for http ssl module. +# Tests for http ssl module, session reuse. ############################################################################### @@ -13,8 +13,6 @@ use strict; use Test::More; -use Socket qw/ CRLF /; - BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; @@ -30,8 +28,8 @@ plan(skip_all => 'IO::Socket::SSL not in eval { IO::Socket::SSL::SSL_VERIFY_NONE(); }; plan(skip_all => 'IO::Socket::SSL too old') if $@; -my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) - ->has_daemon('openssl')->plan(28); +my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/) + ->has_daemon('openssl')->plan(8); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -47,66 +45,37 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; - - log_format ssl $ssl_protocol; server { - listen 127.0.0.1:8085 ssl; - listen 127.0.0.1:8080; + listen 127.0.0.1:8443 ssl; server_name localhost; - ssl_certificate_key inner.key; - ssl_certificate inner.crt; - ssl_session_cache shared:SSL:1m; - ssl_verify_client optional_no_ca; - - keepalive_requests 1000; - location / { return 200 "body $ssl_session_reused"; } - location /id { - return 200 "body $ssl_session_id"; - } - location /cipher { - return 200 "body $ssl_cipher"; - } - location /ciphers { - return 200 "body $ssl_ciphers"; - } - location /client_verify { - return 200 "body $ssl_client_verify"; - } location /protocol { return 200 "body $ssl_protocol"; } - location /issuer { - return 200 "body $ssl_client_i_dn:$ssl_client_i_dn_legacy"; - } - location /subject { - return 200 "body $ssl_client_s_dn:$ssl_client_s_dn_legacy"; - } - location /time { - return 200 "body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain"; - } - - location /body { - add_header X-Body $request_body always; - proxy_pass http://127.0.0.1:8080/; - - access_log %%TESTDIR%%/ssl.log ssl; - } } server { - listen 127.0.0.1:8081; + listen 127.0.0.1:8444 ssl; server_name localhost; - # Special case for enabled "ssl" directive. + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; - ssl on; - ssl_session_cache builtin; + location / { + return 200 "body $ssl_session_reused"; + } + } + + server { + listen 127.0.0.1:8445 ssl; + server_name localhost; + + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -114,10 +83,11 @@ http { } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8446 ssl; server_name localhost; - ssl_session_cache builtin:1000; + ssl_session_cache builtin; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -125,10 +95,11 @@ http { } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8447 ssl; server_name localhost; - ssl_session_cache none; + ssl_session_cache builtin:1000; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -136,10 +107,11 @@ http { } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8448 ssl; server_name localhost; - ssl_session_cache off; + ssl_session_cache none; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -147,11 +119,11 @@ http { } server { - listen 127.0.0.1:8086 ssl; + listen 127.0.0.1:8449 ssl; server_name localhost; - ssl_session_cache shared:SSL:1m; - ssl_session_timeout 1; + ssl_session_cache off; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -171,44 +143,7 @@ EOF my $d = $t->testdir(); -$t->write_file('ca.conf', <write_file('certserial', '1000'); -$t->write_file('certindex', ''); - -system('openssl req -x509 -new ' - . "-config $d/openssl.conf -subj /CN=issuer/ " - . "-out $d/issuer.crt -keyout $d/issuer.key " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't create certificate for issuer: $!\n"; - -system("openssl req -new " - . "-config $d/openssl.conf -subj /CN=subject/ " - . "-out $d/subject.csr -keyout $d/subject.key " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't create certificate for subject: $!\n"; - -system("openssl ca -batch -config $d/ca.conf " - . "-keyfile $d/issuer.key -cert $d/issuer.crt " - . "-subj /CN=subject/ -in $d/subject.csr -out $d/subject.crt " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't sign certificate for subject: $!\n"; - -foreach my $name ('localhost', 'inner') { +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.crt -keyout $d/$name.key " @@ -216,124 +151,54 @@ foreach my $name ('localhost', 'inner') or die "Can't create certificate for $name: $!\n"; } -# suppress deprecation warning - -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -my $ctx; - -SKIP: { -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); - -$ctx = get_ssl_context(); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache, should work always +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); - -$ctx = get_ssl_context(); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); +is(test_reuse(8443), 1, 'tickets reused'); +is(test_reuse(8444), 1, 'tickets and cache reused'); +is(test_reuse(8445), 1, 'cache shared reused'); +is(test_reuse(8446), 1, 'cache builtin reused'); +is(test_reuse(8447), 1, 'cache builtin size reused'); } -$ctx = get_ssl_context(); - -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); - -# ssl certificate inheritance - -my $s = get_ssl_socket(8081); -like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); - -$s->close(); - -$s = get_ssl_socket(8085); -like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); - -$s->close(); - -# session timeout - -$ctx = get_ssl_context(); - -get('/', 8086, $ctx); -select undef, undef, undef, 2.1; - -like(get('/', 8086, $ctx), qr/^body \.$/m, 'session timeout'); - -# embedded variables - -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); -unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); -like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); - -SKIP: { -skip 'BoringSSL', 1 if $t->has_module('BoringSSL'); - -like(get('/ciphers', 8085), qr/^body [:\w-]+$/m, 'ciphers'); - -} - -like(get('/client_verify', 8085), qr/^body NONE$/m, 'client verify'); -like(get('/protocol', 8085), qr/^body (TLS|SSL)v(\d|\.)+$/m, 'protocol'); -like(cert('/issuer', 8085), qr!^body CN=issuer:/CN=issuer$!m, 'issuer'); -like(cert('/subject', 8085), qr!^body CN=subject:/CN=subject$!m, 'subject'); -like(cert('/time', 8085), qr/^body [:\s\w]+![:\s\w]+![23]$/m, 'time'); - -# c->read->ready handling bug in ngx_ssl_recv(), triggered with chunked body - -like(get_body('/body', '0123456789', 20, 5), qr/X-Body: (0123456789){100}/, - 'request body chunked'); - -# pipelined requests - -$s = get_ssl_socket(8085); -my $req = < $s) || ""; -$s = undef; -is(() = $r =~ /(200 OK)/g, 1000, 'pipelined requests'); - -# OpenSSL 3.0 error "unexpected eof while reading" seen as a critical error - -ok(get_ssl_socket(8085), 'ssl unexpected eof'); - -# close_notify is sent before lingering close - -is(get_ssl_shutdown(8085), 1, 'ssl shutdown on lingering close'); +is(test_reuse(8448), 0, 'cache none not reused'); +is(test_reuse(8449), 0, 'cache off not reused'); $t->stop(); -like($t->read_file('ssl.log'), qr/^(TLS|SSL)v(\d|\.)+$/m, - 'log ssl variable on lingering close'); - like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); ############################################################################### +sub test_tls13 { + return get('/protocol', 8443) =~ /TLSv1.3/; +} + +sub test_reuse { + my ($port) = @_; + my $ctx = get_ssl_context(); + get('/', $port, $ctx); + return (get('/', $port, $ctx) =~ qr/^body r$/m) ? 1 : 0; +} + sub get { my ($uri, $port, $ctx) = @_; my $s = get_ssl_socket($port, $ctx) or return; @@ -342,30 +207,6 @@ sub get { return $r; } -sub get_body { - my ($uri, $body, $len, $n) = @_; - my $s = get_ssl_socket(8085) or return; - http("GET /body HTTP/1.1" . CRLF - . "Host: localhost" . CRLF - . "Connection: close" . CRLF - . "Transfer-Encoding: chunked" . CRLF . CRLF, - socket => $s, start => 1); - my $chs = unpack("H*", pack("C", length($body) * $len)); - http($chs . CRLF . $body x $len . CRLF, socket => $s, start => 1) - for 1 .. $n; - my $r = http("0" . CRLF . CRLF, socket => $s); - $s->close(); - return $r; -} - -sub cert { - my ($uri, $port) = @_; - my $s = get_ssl_socket($port, undef, - SSL_cert_file => "$d/subject.crt", - SSL_key_file => "$d/subject.key") or return; - http_get($uri, socket => $s); -} - sub get_ssl_context { return IO::Socket::SSL::SSL_Context->new( SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), @@ -402,18 +243,4 @@ sub get_ssl_socket { return $s; } -sub get_ssl_shutdown { - my ($port) = @_; - - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - Net::SSLeay::write($ssl, 'GET /' . CRLF . 'extra'); - Net::SSLeay::read($ssl); - Net::SSLeay::set_shutdown($ssl, 1); - Net::SSLeay::shutdown($ssl); -} - ############################################################################### -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:15:47 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:15:47 +0300 Subject: [PATCH 03 of 20] Tests: separate SSL session reuse tests in mail In-Reply-To: References: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> Message-ID: Hello! On Wed, Mar 22, 2023 at 01:46:33PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679107816 -10800 > > # Sat Mar 18 05:50:16 2023 +0300 > > # Node ID 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb > > # Parent 125fb8461d88a81a62ccb40d0e205a01ecc759f5 > > Tests: separate SSL session reuse tests in mail. > > > > Instead of being mixed with generic SSL tests, session reuse variants > > are now tested in a separate file. > > > > [..] > > diff --git a/mail_ssl.t b/mail_ssl_session_reuse.t > > copy from mail_ssl.t > > copy to mail_ssl_session_reuse.t > > --- a/mail_ssl.t > > +++ b/mail_ssl_session_reuse.t > > @@ -1,6 +1,7 @@ > > #!/usr/bin/perl > > > > # (C) Andrey Zelenkov > > +# (C) Maxim Dounin > > # (C) Nginx, Inc. > > > > # Tests for mail ssl module. > > @@ -33,11 +34,8 @@ eval { > > }; > > plan(skip_all => 'Net::SSLeay not installed') if $@; > > > > -eval { exists &Net::SSLeay::P_alpn_selected or die; }; > > -plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; > > - > > -my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) > > - ->has_daemon('openssl')->plan(22); > > +my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap/) > > + ->has_daemon('openssl')->plan(7); > > > > $t->write_file_expand('nginx.conf', <<'EOF'); > > > > @@ -49,90 +47,62 @@ events { > > } > > > > mail { > > - ssl_certificate_key localhost.key; > > - ssl_certificate localhost.crt; > > - ssl_session_tickets off; > > + auth_http http://127.0.0.1:8080; > > > > - ssl_password_file password; > > - > > - auth_http http://127.0.0.1:8080; # unused > > - > > - ssl_session_cache none; > > + ssl_certificate localhost.crt; > > + ssl_certificate_key localhost.key; > > > > server { > > - listen 127.0.0.1:8143; > > - listen 127.0.0.1:8145 ssl; > > - protocol imap; > > - > > - ssl_session_cache builtin; > > + listen 127.0.0.1:8993 ssl; > > + protocol imap; > > } > > > > server { > > - listen 127.0.0.1:8146 ssl; > > - protocol imap; > > + listen 127.0.0.1:8994 ssl; > > + protocol imap; > > > > - ssl_session_cache off; > > + ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets on; > > } > > > > server { > > - listen 127.0.0.1:8147; > > - protocol imap; > > + listen 127.0.0.1:8995 ssl; > > + protocol imap; > > > > - # Special case for enabled "ssl" directive. > > - > > - ssl on; > > - ssl_session_cache builtin:1000; > > + ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8148 ssl; > > - protocol imap; > > - > > - ssl_session_cache shared:SSL:1m; > > - ssl_certificate_key inherits.key; > > - ssl_certificate inherits.crt; > > - } > > + listen 127.0.0.1:8996 ssl; > > + protocol imap; > > > > - server { > > - listen 127.0.0.1:8149; > > - protocol imap; > > - > > - starttls on; > > - } > > - > > - server { > > - listen 127.0.0.1:8150; > > - protocol imap; > > - > > - starttls only; > > + ssl_session_cache builtin; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8151; > > - protocol pop3; > > + listen 127.0.0.1:8997 ssl; > > + protocol imap; > > > > - starttls on; > > + ssl_session_cache builtin:1000; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8152; > > - protocol pop3; > > + listen 127.0.0.1:8998 ssl; > > + protocol imap; > > > > - starttls only; > > + ssl_session_cache none; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8153; > > - protocol smtp; > > - > > - starttls on; > > - } > > + listen 127.0.0.1:8999 ssl; > > + protocol imap; > > > > - server { > > - listen 127.0.0.1:8154; > > - protocol smtp; > > - > > - starttls only; > > + ssl_session_cache off; > > + ssl_session_tickets off; > > } > > } > > > > @@ -148,181 +118,57 @@ EOF > > > > my $d = $t->testdir(); > > > > -foreach my $name ('localhost', 'inherits') { > > - system("openssl genrsa -out $d/$name.key -passout pass:localhost " > > - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 > > - or die "Can't create private key: $!\n"; > > +foreach my $name ('localhost') { > > system('openssl req -x509 -new ' > > . "-config $d/openssl.conf -subj /CN=$name/ " > > - . "-out $d/$name.crt " > > - . "-key $d/$name.key -passin pass:localhost" > > + . "-out $d/$name.crt -keyout $d/$name.key " > > . ">>$d/openssl.out 2>&1") == 0 > > or die "Can't create certificate for $name: $!\n"; > > } > > > > my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > > -$t->write_file('password', 'localhost'); > > > > -open OLDERR, ">&", \*STDERR; close STDERR; > > $t->run(); > > -open STDERR, ">&", \*OLDERR; > > > > ############################################################################### > > > > [..] > > +# session reuse: > > +# > > +# - only tickets, the default > > +# - tickets and shared cache > > "should work always", like in http and stream? Added, thanks. > > +# - only shared cache > > +# - only builtin cache > > +# - only builtin cache with explicitly configured size > > +# - only cache none > > +# - only cache off > > > > [..] > > +is(test_reuse(8993), 1, 'tickets reused'); > > +is(test_reuse(8994), 1, 'tickets and cache reused'); > > +is(test_reuse(8995), 1, 'cache shared reused'); > > +is(test_reuse(8996), 1, 'cache builtin reused'); > > +is(test_reuse(8997), 1, 'cache builtin size reused'); > > +is(test_reuse(8998), 0, 'cache none not reused'); > > +is(test_reuse(8999), 0, 'cache off not reused'); > > > > ############################################################################### > > > > +sub test_reuse { > > + my ($port) = @_; > > + my ($s, $ssl) = get_ssl_socket($port); > > + Net::SSLeay::read($ssl); > > + my $ses = Net::SSLeay::get_session($ssl); > > + ($s, $ssl) = get_ssl_socket($port, $ses); > > + return Net::SSLeay::session_reused($ssl); > > +} > > + > > sub get_ssl_socket { > > - my ($port, $ses, $alpn) = @_; > > + my ($port, $ses) = @_; > > > > my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > > Net::SSLeay::set_session($ssl, $ses) if defined $ses; > > - Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; > > Net::SSLeay::set_fd($ssl, fileno($s)); > > Net::SSLeay::connect($ssl) == 1 or return; > > Since this won't fail anymore on ALPN checks, > you can convert this back to die. Strictly speaking, it shouldn't die regardless of whether connect() can legitimately fail during tests or not. If connect() fails (for example, due to a bug in some of the session reuse variants), it still should run other tests. Fixing this (and other issues, such as missing timeout and SIGPIPE handling) is out of the scope of this patch series though. > > return ($s, $ssl); -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:16:19 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:16:19 +0300 Subject: [PATCH 05 of 20] Tests: separate SSL session reuse tests in stream In-Reply-To: <2640439F-CFF2-4ABB-92F6-7D71AA1DF4A6@nginx.com> References: <530336cb449dcb028a55.1679148900@vm-bsd.mdounin.ru> <2640439F-CFF2-4ABB-92F6-7D71AA1DF4A6@nginx.com> Message-ID: Hello! On Wed, Mar 22, 2023 at 01:55:48PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679140351 -10800 > > # Sat Mar 18 14:52:31 2023 +0300 > > # Node ID 530336cb449dcb028a55a5a401a122d07521e3a4 > > # Parent 3ab3b2d1c2e67bc1f05e386218ceb08da873a477 > > Tests: separate SSL session reuse tests in stream. > > > > Instead of being mixed with generic SSL tests, session reuse variants > > are now tested in a separate file. > > > > diff --git a/stream_ssl.t b/stream_ssl.t > > --- a/stream_ssl.t > > +++ b/stream_ssl.t > > @@ -37,7 +37,7 @@ plan(skip_all => 'win32') if $^O eq 'MSW > > > > my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); > > > > -$t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); > > +$t->plan(5)->write_file_expand('nginx.conf', <<'EOF'); > > > > %%TEST_GLOBALS%% > > > > @@ -51,40 +51,35 @@ stream { > > > > ssl_certificate_key localhost.key; > > ssl_certificate localhost.crt; > > - ssl_session_tickets off; > > > > # inherited by server "inherits" > > ssl_password_file password_stream; > > > > server { > > - listen 127.0.0.1:8080 ssl; > > + listen 127.0.0.1:8443 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache builtin; > > ssl_password_file password; > > } > > > > server { > > - listen 127.0.0.1:8082 ssl; > > + listen 127.0.0.1:8444 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache off; > > ssl_password_file password_many; > > } > > > > server { > > - listen 127.0.0.1:8083 ssl; > > + listen 127.0.0.1:8445 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache builtin:1000; > > ssl_password_file password_fifo; > > } > > > > server { > > - listen 127.0.0.1:8084 ssl; > > + listen 127.0.0.1:8446 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache shared:SSL:1m; > > ssl_certificate_key inherits.key; > > ssl_certificate inherits.crt; > > } > > @@ -138,52 +133,26 @@ kill 'INT', $p if $@; > > > > ############################################################################### > > > > -my ($s, $ssl, $ses); > > +my ($s, $ssl); > > > > -($s, $ssl) = get_ssl_socket(port(8080)); > > +($s, $ssl) = get_ssl_socket(8443); > > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > > like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); > > > > -# ssl_session_cache > > - > > -($s, $ssl) = get_ssl_socket(port(8080)); > > +($s, $ssl) = get_ssl_socket(8444); > > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > > -Net::SSLeay::read($ssl); > > -$ses = Net::SSLeay::get_session($ssl); > > - > > -($s, $ssl) = get_ssl_socket(port(8080), $ses); > > -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); > > - > > -($s, $ssl) = get_ssl_socket(port(8082)); > > -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > > -Net::SSLeay::read($ssl); > > -$ses = Net::SSLeay::get_session($ssl); > > +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); > > > > -($s, $ssl) = get_ssl_socket(port(8082), $ses); > > -isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); > > - > > -($s, $ssl) = get_ssl_socket(port(8083)); > > +($s, $ssl) = get_ssl_socket(8444); > > should be 8445 Fixed, thanks for catching. > > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > > -Net::SSLeay::read($ssl); > > -$ses = Net::SSLeay::get_session($ssl); > > - > > -($s, $ssl) = get_ssl_socket(port(8083), $ses); > > -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); > > - > > -($s, $ssl) = get_ssl_socket(port(8084)); > > -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > > -Net::SSLeay::read($ssl); > > -$ses = Net::SSLeay::get_session($ssl); > > - > > -($s, $ssl) = get_ssl_socket(port(8084), $ses); > > -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); > > +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); > > > > # ssl_certificate inheritance > > > > -($s, $ssl) = get_ssl_socket(port(8080)); > > +($s, $ssl) = get_ssl_socket(8443); > > like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); > > > > -($s, $ssl) = get_ssl_socket(port(8084)); > > +($s, $ssl) = get_ssl_socket(8446); > > like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); > > > > ############################################################################### > > @@ -191,7 +160,7 @@ like(Net::SSLeay::dump_peer_certificate( > > sub get_ssl_socket { > > my ($port, $ses) = @_; > > > > - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); > > + my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > > Net::SSLeay::set_session($ssl, $ses) if defined $ses; > > Session test remnants can be cleaned up. Removed, thanks. > > Net::SSLeay::set_fd($ssl, fileno($s)); > > diff --git a/stream_ssl.t b/stream_ssl_session_reuse.t > > copy from stream_ssl.t > > copy to stream_ssl_session_reuse.t > > --- a/stream_ssl.t > > +++ b/stream_ssl_session_reuse.t > > @@ -1,6 +1,7 @@ > > #!/usr/bin/perl > > > > # (C) Sergey Kandaurov > > +# (C) Maxim Dounin > > # (C) Nginx, Inc. > > > > # Tests for stream ssl module. > > @@ -12,7 +13,6 @@ use strict; > > > > use Test::More; > > > > -use POSIX qw/ mkfifo /; > > This file can be run now on win32. Yes, thanks, removed the win32 check. > > > use Socket qw/ $CRLF /; > > > > BEGIN { use FindBin; chdir($FindBin::Bin); } > > @@ -49,44 +49,60 @@ events { > > stream { > > %%TEST_GLOBALS_STREAM%% > > > > + ssl_certificate localhost.crt; > > ssl_certificate_key localhost.key; > > - ssl_certificate localhost.crt; > > - ssl_session_tickets off; > > > > - # inherited by server "inherits" > > - ssl_password_file password_stream; > > + server { > > + listen 127.0.0.1:8443 ssl; > > + proxy_pass 127.0.0.1:8081; > > + } > > > > server { > > - listen 127.0.0.1:8080 ssl; > > + listen 127.0.0.1:8444 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache builtin; > > - ssl_password_file password; > > + ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets on; > > + } > > + > > + server { > > + listen 127.0.0.1:8445 ssl; > > + proxy_pass 127.0.0.1:8081; > > + > > + ssl_session_cache shared:SSL:1m; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8082 ssl; > > + listen 127.0.0.1:8446 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache off; > > - ssl_password_file password_many; > > + ssl_session_cache builtin; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8083 ssl; > > + listen 127.0.0.1:8447 ssl; > > proxy_pass 127.0.0.1:8081; > > > > ssl_session_cache builtin:1000; > > - ssl_password_file password_fifo; > > + ssl_session_tickets off; > > } > > > > server { > > - listen 127.0.0.1:8084 ssl; > > + listen 127.0.0.1:8448 ssl; > > proxy_pass 127.0.0.1:8081; > > > > - ssl_session_cache shared:SSL:1m; > > - ssl_certificate_key inherits.key; > > - ssl_certificate inherits.crt; > > + ssl_session_cache none; > > + ssl_session_tickets off; > > + } > > + > > + server { > > + listen 127.0.0.1:8449 ssl; > > + proxy_pass 127.0.0.1:8081; > > + > > + ssl_session_cache off; > > + ssl_session_tickets off; > > } > > } > > > > @@ -101,16 +117,11 @@ distinguished_name = req_distinguished_n > > EOF > > > > my $d = $t->testdir(); > > -mkfifo("$d/password_fifo", 0700); > > > > -foreach my $name ('localhost', 'inherits') { > > - system("openssl genrsa -out $d/$name.key -passout pass:$name " > > - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 > > - or die "Can't create private key: $!\n"; > > +foreach my $name ('localhost') { > > system('openssl req -x509 -new ' > > . "-config $d/openssl.conf -subj /CN=$name/ " > > - . "-out $d/$name.crt " > > - . "-key $d/$name.key -passin pass:$name" > > + . "-out $d/$name.crt -keyout $d/$name.key " > > . ">>$d/openssl.out 2>&1") == 0 > > or die "Can't create certificate for $name: $!\n"; > > } > > @@ -118,80 +129,48 @@ foreach my $name ('localhost', 'inherits > > > > This introduces one more occurrence of extra blank line on copy/paste, > I'd rather fix it in both files. Multiple blank lines not exactly violate style, but I agree that it's probably better to remove these. Removed. > > [..] > > diff --git a/stream_ssl.t b/stream_ssl.t > --- a/stream_ssl.t > +++ b/stream_ssl.t > @@ -110,7 +110,6 @@ foreach my $name ('localhost', 'inherits > or die "Can't create certificate for $name: $!\n"; > } > > - > my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > > $t->write_file('password', 'localhost'); > @@ -143,7 +142,7 @@ like(Net::SSLeay::read($ssl), qr/200 OK/ > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); > > -($s, $ssl) = get_ssl_socket(8444); > +($s, $ssl) = get_ssl_socket(8445); > Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); > like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); > > @@ -158,11 +157,10 @@ like(Net::SSLeay::dump_peer_certificate( > ############################################################################### > > sub get_ssl_socket { > - my ($port, $ses) = @_; > + my ($port) = @_; > > my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > - Net::SSLeay::set_session($ssl, $ses) if defined $ses; > Net::SSLeay::set_fd($ssl, fileno($s)); > Net::SSLeay::connect($ssl) or die("ssl connect"); > return ($s, $ssl); > diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t > --- a/stream_ssl_session_reuse.t > +++ b/stream_ssl_session_reuse.t > @@ -33,8 +33,6 @@ eval { > }; > plan(skip_all => 'Net::SSLeay not installed') if $@; > > -plan(skip_all => 'win32') if $^O eq 'MSWin32'; > - > my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); > > $t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); > @@ -126,7 +124,6 @@ foreach my $name ('localhost') { > or die "Can't create certificate for $name: $!\n"; > } > > - > my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > > $t->run_daemon(\&http_daemon); My diff matches, thanks. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:16:47 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:16:47 +0300 Subject: [PATCH 06 of 20] Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail In-Reply-To: <10CCED02-0019-4520-AD41-CFCE12E38417@nginx.com> References: <10CCED02-0019-4520-AD41-CFCE12E38417@nginx.com> Message-ID: Hello! On Wed, Mar 22, 2023 at 01:59:27PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679140402 -10800 > > # Sat Mar 18 14:53:22 2023 +0300 > > # Node ID d90fe31a80d5e85b59e525e874d24f409716b64c > > # Parent 530336cb449dcb028a55a5a401a122d07521e3a4 > > Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail. > > in stream Fixed, thnx. > > > > LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL > > with TLSv1.3 only supports session tickets, but not server-side session > > cache. > > > > By the way, why introduce three separate changesets (p02, p04, p06) > with identical description and similar changes. I'd combine them. These patches complement corresponding changes to introduce separate session reuse tests. And these in turn are mostly preparation to make it possible to easily add TODOs for LibreSSL and BoringSSL. While combining some or even all of these patches is certainly possible, I believe it is much easier to understand and review them separately. In general, the patch series follows logic "prepare a test file for changes if needed, add appropriate TODOs". This ensures that preparation changes can be easily seen as such, and also ensures that each individual patch is simple enough. > > diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t > > --- a/stream_ssl_session_reuse.t > > +++ b/stream_ssl_session_reuse.t > > @@ -147,16 +147,35 @@ my $ctx = Net::SSLeay::CTX_new() or die( > > # - only cache none > > # - only cache off > > > > +TODO: { > > +local $TODO = 'no TLSv1.3 sessions in LibreSSL' > > + if $t->has_module('LibreSSL') && test_tls13(); > > + > > is(test_reuse(8443), 1, 'tickets reused'); > > is(test_reuse(8444), 1, 'tickets and cache reused'); > > + > > +TODO: { > > +local $TODO = 'no TLSv1.3 session cache in BoringSSL' > > + if $t->has_module('BoringSSL') && test_tls13(); > > + > > is(test_reuse(8445), 1, 'cache shared reused'); > > is(test_reuse(8446), 1, 'cache builtin reused'); > > is(test_reuse(8447), 1, 'cache builtin size reused'); > > + > > +} > > +} > > + > > is(test_reuse(8448), 0, 'cache none not reused'); > > is(test_reuse(8449), 0, 'cache off not reused'); > > > > + > > extra blank line Fixed, thanks. > > ############################################################################### > > > > +sub test_tls13 { > > + my ($s, $ssl) = get_ssl_socket(8443); > > + return (Net::SSLeay::version($ssl) > 0x303); > > +} > > + > > sub test_reuse { > > my ($port) = @_; > > my ($s, $ssl) = get_ssl_socket($port); -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:17:29 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:17:29 +0300 Subject: [PATCH 19 of 20] Tests: removed multiple server certificates from ssl_ocsp.t In-Reply-To: <5AD7ACF5-169D-4578-8D3C-DA691B2F935F@nginx.com> References: <782531c3cd79dcf70027.1679148914@vm-bsd.mdounin.ru> <5AD7ACF5-169D-4578-8D3C-DA691B2F935F@nginx.com> Message-ID: Hello! On Wed, Mar 22, 2023 at 03:05:16PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679148855 -10800 > > # Sat Mar 18 17:14:15 2023 +0300 > > # Node ID 782531c3cd79dcf700276e10bef00e524de009d1 > > # Parent c140f78fbc8f62c9694d3b969d1309570a96f2e7 > > Tests: removed multiple server certificates from ssl_ocsp.t. > > > > Multiple server certificates are not needed to test OCSP verification of > > client certificates (in contrast to OCSP stapling, where server certificates > > are verified, and different staples should be correctly returned with > > different server certificates). And using multiple server certificates > > causes issues when testing with LibreSSL due to broken sigalgs-based > > server certificate selection in LibreSSL with TLSv1.3. > > > > Accordingly, the test is simplified to do not use multiple server > > certificates. > > > > diff --git a/ssl_ocsp.t b/ssl_ocsp.t > > --- a/ssl_ocsp.t > > +++ b/ssl_ocsp.t > > @@ -63,10 +63,7 @@ http { > > ssl_verify_depth 2; > > ssl_client_certificate trusted.crt; > > > > - ssl_ciphers DEFAULT:ECCdraft; > > - > > - ssl_certificate_key ec.key; > > - ssl_certificate ec.crt; > > +# ssl_ciphers DEFAULT:ECCdraft; > > This doesn't serve its purpose now and can be removed, > now that you've removed multiple (ECC) certificates. > It was used to run tests with ECC certificates/ciphers, > as otherwise it would result in "no shared cipher" error. > > ECCdraft is an old alias used to enable ECC ciphersuites > and run tests with ECC certificate on OpenSSL 0.9.8, > before they became official in RFC 4492. > > - ECC ciphersuites were disabled by default in 0.9.8c, > and ECCdraft alias was used to turn them back. > - ECC ciphersuites were re-enabled in 0.9.9 (1.0.0) Err, thanks for catching. I actually commented it out to make sure it's safe to remove it now, but forgot to actually remove. Removed. > > > > ssl_certificate_key rsa.key; > > ssl_certificate rsa.crt; > > @@ -273,13 +270,8 @@ system("openssl ocsp -index $d/certindex > > > > # server cert/key > > > > -system("openssl ecparam -genkey -out $d/ec.key -name prime256v1 " > > - . ">>$d/openssl.out 2>&1") == 0 or die "Can't create EC pem: $!\n"; > > -system("openssl genrsa -out $d/rsa.key 2048 >>$d/openssl.out 2>&1") == 0 > > - or die "Can't create RSA pem: $!\n"; > > - > > -foreach my $name ('ec', 'rsa') { > > - system("openssl req -x509 -new -key $d/$name.key " > > +foreach my $name ('rsa') { > > + system('openssl req -x509 -new ' > > . "-config $d/openssl.conf -subj /CN=$name/ " > > . "-out $d/$name.crt -keyout $d/$name.key " > > . ">>$d/openssl.out 2>&1") == 0 > > @@ -288,7 +280,7 @@ foreach my $name ('ec', 'rsa') { > > > > $t->run_daemon(\&http_daemon, $t, port(8081)); > > $t->run_daemon(\&http_daemon, $t, port(8082)); > > -$t->run()->plan(14); > > +$t->run()->plan(15); > > > > $t->waitforsocket("127.0.0.1:" . port(8081)); > > $t->waitforsocket("127.0.0.1:" . port(8082)); > > @@ -297,17 +289,17 @@ my $version = get_version(); > > > > ############################################################################### > > > > -like(get('RSA', 'end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); > > +like(get('end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); > > > > # demonstrate that ocsp int request is failed due to missing resolver > > > > -like(get('RSA', 'end', sni => 'resolver'), > > +like(get('end', sni => 'resolver'), > > qr/400 Bad.*FAILED:certificate status request failed/s, > > 'ocsp many failed request'); > > > > # demonstrate that ocsp int request is actually made by failing ocsp response > > > > -like(get('RSA', 'end', port => 8444), > > +like(get('end', port => 8444), > > qr/400 Bad.*FAILED:certificate status request failed/s, > > 'ocsp many failed'); > > > > @@ -323,11 +315,11 @@ system("openssl ocsp -index $d/certindex > > . ">>$d/openssl.out 2>&1") == 0 > > or die "Can't create OCSP response: $!\n"; > > > > -like(get('RSA', 'end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); > > +like(get('end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); > > > > # store into ssl_ocsp_cache > > > > -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); > > +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); > > > > # revoke > > > > @@ -346,23 +338,23 @@ system("openssl ocsp -index $d/certindex > > . ">>$d/openssl.out 2>&1") == 0 > > or die "Can't create OCSP response: $!\n"; > > > > -like(get('RSA', 'end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); > > +like(get('end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); > > > > # with different responder where it's still valid > > > > -like(get('RSA', 'end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); > > +like(get('end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); > > > > # with different context to responder where it's still valid > > > > -like(get('RSA', 'end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); > > +like(get('end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); > > > > # with cached ocsp response it's still valid > > > > -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); > > +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); > > > > # ocsp end response signed with invalid (root) cert, expect HTTP 400 > > > > -like(get('ECDSA', 'ec-end'), > > +like(get('ec-end'), > > qr/400 Bad.*FAILED:certificate status request failed/s, > > 'root ca not trusted'); > > > > @@ -374,12 +366,12 @@ system("openssl ocsp -index $d/certindex > > . ">>$d/openssl.out 2>&1") == 0 > > or die "Can't create EC OCSP response: $!\n"; > > > > -like(get('ECDSA', 'ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); > > +like(get('ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); > > > > -my ($s, $ssl) = get('ECDSA', 'ec-end'); > > +my ($s, $ssl) = get('ec-end'); > > my $ses = Net::SSLeay::get_session($ssl); > > > > -like(get('ECDSA', 'ec-end', ses => $ses), > > +like(get('ec-end', ses => $ses), > > qr/200 OK.*SUCCESS:r/s, 'session reused'); > > > > # revoke with saved session > > @@ -401,19 +393,22 @@ system("openssl ocsp -index $d/certindex > > > > # reusing session with revoked certificate > > > > -like(get('ECDSA', 'ec-end', ses => $ses), > > +like(get('ec-end', ses => $ses), > > qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); > > > > # regression test for self-signed > > > > -like(get('RSA', 'root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); > > +like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); > > + > > +# check for errors > > + > > +like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); > > > > ############################################################################### > > > > sub get { > > - my ($type, $cert, %extra) = @_; > > - $type = 'PSS' if $type eq 'RSA' && $version > 0x0303; > > - my ($s, $ssl) = get_ssl_socket($type, $cert, %extra); > > + my ($cert, %extra) = @_; > > + my ($s, $ssl) = get_ssl_socket($cert, %extra); > > my $cipher = Net::SSLeay::get_cipher($ssl); > > Test::Nginx::log_core('||', "cipher: $cipher"); > > my $host = $extra{sni} ? $extra{sni} : 'localhost'; > > @@ -428,7 +423,7 @@ sub get { > > } > > > > sub get_ssl_socket { > > - my ($type, $cert, %extra) = @_; > > + my ($cert, %extra) = @_; > > my $ses = $extra{ses}; > > my $sni = $extra{sni}; > > my $port = $extra{port} || 8443; > > @@ -450,18 +445,6 @@ sub get_ssl_socket { > > > > my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); > > > > - if (defined $type) { > > - my $ssleay = Net::SSLeay::SSLeay(); > > - if ($ssleay < 0x1000200f || $ssleay == 0x20000000) { > > - Net::SSLeay::CTX_set_cipher_list($ctx, $type) > > - or die("Failed to set cipher list"); > > - } else { > > - # SSL_CTRL_SET_SIGALGS_LIST > > - Net::SSLeay::CTX_ctrl($ctx, 98, 0, $type . '+SHA256') > > - or die("Failed to set sigalgs"); > > - } > > - } > > - > > Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") > > or die if $cert; > > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:17:43 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:17:43 +0300 Subject: [PATCH 03 of 20] Tests: separate SSL session reuse tests in mail In-Reply-To: References: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> Message-ID: Hello! On Wed, Mar 22, 2023 at 01:20:25PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679107816 -10800 > > # Sat Mar 18 05:50:16 2023 +0300 > > # Node ID 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb > > # Parent 125fb8461d88a81a62ccb40d0e205a01ecc759f5 > > Tests: separate SSL session reuse tests in mail. > > > > Instead of being mixed with generic SSL tests, session reuse variants > > are now tested in a separate file. > > > > diff --git a/mail_ssl.t b/mail_ssl.t > > --- a/mail_ssl.t > > +++ b/mail_ssl.t > > @@ -37,7 +37,7 @@ eval { exists &Net::SSLeay::P_alpn_selec > > plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; > > > > my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) > > - ->has_daemon('openssl')->plan(22); > > + ->has_daemon('openssl')->plan(18); > > > > $t->write_file_expand('nginx.conf', <<'EOF'); > > > > @@ -51,44 +51,25 @@ events { > > mail { > > ssl_certificate_key localhost.key; > > ssl_certificate localhost.crt; > > - ssl_session_tickets off; > > > > ssl_password_file password; > > > > auth_http http://127.0.0.1:8080; # unused > > > > - ssl_session_cache none; > > - > > server { > > listen 127.0.0.1:8143; > > listen 127.0.0.1:8145 ssl; > > protocol imap; > > - > > - ssl_session_cache builtin; > > } > > > > server { > > - listen 127.0.0.1:8146 ssl; > > - protocol imap; > > - > > - ssl_session_cache off; > > - } > > - > > - server { > > - listen 127.0.0.1:8147; > > + listen 127.0.0.1:8148; > > protocol imap; > > > > # Special case for enabled "ssl" directive. > > > > ssl on; > > - ssl_session_cache builtin:1000; > > - } > > > > - server { > > - listen 127.0.0.1:8148 ssl; > > - protocol imap; > > - > > - ssl_session_cache shared:SSL:1m; > > ssl_certificate_key inherits.key; > > ssl_certificate inherits.crt; > > } > > @@ -169,46 +150,16 @@ open STDERR, ">&", \*OLDERR; > > > > ############################################################################### > > > > +my ($s, $ssl, $ses); > > + > > $ses is no longer used there, and other cleanup on top off this change: > > diff --git a/mail_ssl.t b/mail_ssl.t > --- a/mail_ssl.t > +++ b/mail_ssl.t > @@ -150,7 +150,7 @@ open STDERR, ">&", \*OLDERR; > > ############################################################################### > > -my ($s, $ssl, $ses); > +my ($s, $ssl); > > # simple tests to ensure that nothing broke with ssl_password_file directive > > @@ -170,7 +170,7 @@ like(Net::SSLeay::dump_peer_certificate( > > # alpn > > -ok(get_ssl_socket(8148, undef, ['imap']), 'alpn'); > +ok(get_ssl_socket(8148, ['imap']), 'alpn'); > > SKIP: { > $t->{_configure_args} =~ /LibreSSL ([\d\.]+)/; > @@ -181,7 +181,7 @@ skip 'OpenSSL too old', 1 if defined $1 > TODO: { > local $TODO = 'not yet' unless $t->has_version('1.21.4'); > > -ok(!get_ssl_socket(8148, undef, ['unknown']), 'alpn rejected'); > +ok(!get_ssl_socket(8148, ['unknown']), 'alpn rejected'); > > } > > @@ -268,11 +268,10 @@ ok(!get_ssl_socket(8148, undef, ['unknow > ############################################################################### > > sub get_ssl_socket { > - my ($port, $ses, $alpn) = @_; > + my ($port, $alpn) = @_; > > my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); > my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); > - Net::SSLeay::set_session($ssl, $ses) if defined $ses; > Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; > Net::SSLeay::set_fd($ssl, fileno($s)); > Net::SSLeay::connect($ssl) == 1 or return; > diff --git a/mail_ssl_session_reuse.t b/mail_ssl_session_reuse.t > --- a/mail_ssl_session_reuse.t > +++ b/mail_ssl_session_reuse.t > @@ -132,8 +132,6 @@ my $ctx = Net::SSLeay::CTX_new() or die( > > ############################################################################### > > -my ($ssl, $ses); > - > # session reuse: > # > # - only tickets, the default Applied, thanks. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:18:14 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:18:14 +0300 Subject: [PATCH 20 of 20] Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3 In-Reply-To: References: Message-ID: Hello! On Wed, Mar 22, 2023 at 03:11:44PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:15, Maxim Dounin wrote: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1679148869 -10800 > > # Sat Mar 18 17:14:29 2023 +0300 > > # Node ID f6f6a21b1c2a0d88cb2a4993f4c0113a3fb1e019 > > # Parent 782531c3cd79dcf700276e10bef00e524de009d1 > > Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3. > > > > LibreSSL does not support session reuse with TLSv1.3. > > Since LibreSSL is so broken wrt TLSv1.3, I don't think > it deserves annotating every test in separate changed. > The LibreSSL changes could be easily combined: > this is at least p11, p12, p13, p16, p17, p20. That's more about logic of the patch series, see previous responses. Combining some patches is certainly possible, though it will be much harder to manage without per-test logic universally applied to all changes. > > Other broken or missing functionality such as signature > algorithms and certificate authorities can be skipped > in separate commits, though. > > > > > diff --git a/ssl_ocsp.t b/ssl_ocsp.t > > --- a/ssl_ocsp.t > > +++ b/ssl_ocsp.t > > @@ -371,9 +371,15 @@ like(get('ec-end'), qr/200 OK.*SUCCESS/s > > my ($s, $ssl) = get('ec-end'); > > my $ses = Net::SSLeay::get_session($ssl); > > > > +TODO: { > > +local $TODO = 'no TLSv1.3 sessions in LibreSSL' > > + if $t->has_module('LibreSSL') and $version > 0x303; > > + > > like(get('ec-end', ses => $ses), > > qr/200 OK.*SUCCESS:r/s, 'session reused'); > > > > +} > > + > > # revoke with saved session > > > > system("openssl ca -config $d/ca.conf -revoke $d/ec-end.crt " > > @@ -393,9 +399,15 @@ system("openssl ocsp -index $d/certindex > > > > # reusing session with revoked certificate > > > > +TODO: { > > +local $TODO = 'no TLSv1.3 sessions in LibreSSL' > > + if $t->has_module('LibreSSL') and $version > 0x303; > > + > > like(get('ec-end', ses => $ses), > > qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); > > > > +} > > + > > # regression test for self-signed > > > > like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 14:18:30 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 17:18:30 +0300 Subject: [PATCH 00 of 20] tests suite fixes for TLSv1.3 In-Reply-To: References: Message-ID: Hello! On Wed, Mar 22, 2023 at 03:43:12PM +0400, Sergey Kandaurov wrote: > > On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > > > > Hello! > > > > Here are patch series for the test suite to address test failures > > observed with TLSv1.3 enabled with BoringSSL and LibreSSL. > > > > Short summary of the issues seen: > > > > - BoringSSL with TLSv1.3 does not support session reuse via server-side > > session cache, only with tickets. > > > > - BoringSSL with TLSv1.3 does not provide $ssl_session_id. > > > > - LibreSSL with TLSv1.3 does not support session reuse. > > > > - LibreSSL with TLSv1.3 fails to negotiate certificates based on > > signature algorithms supported by the client, and fails with > > "missing rsa certificate" and "unknown pkey type" errors. > > > > - LibreSSL with TLSv1.3 does not send CA lists to the client. > > > > Missing peaces that allow me to run with LibreSSL: > > # HG changeset patch > # User Sergey Kandaurov > # Date 1679485246 -14400 > # Wed Mar 22 15:40:46 2023 +0400 > # Node ID dfe434f295d3da7e3b67bbbafeab245bb591f397 > # Parent 826e00e7c037d617781239963e8b868b6b0de225 > Tests: fixed upstream zone tests with LibreSSL and TLSv1.3. > > LibreSSL does not support session reuse with TLSv1.3. > > diff --git a/stream_upstream_zone_ssl.t b/stream_upstream_zone_ssl.t > --- a/stream_upstream_zone_ssl.t > +++ b/stream_upstream_zone_ssl.t Thanks. I've happen to compile nginx without upstream zone modules as a leftover from some previous tests, and missed these. Added a similar change with TODOs. Full series with all the fixes: # HG changeset patch # User Maxim Dounin # Date 1679498636 -10800 # Wed Mar 22 18:23:56 2023 +0300 # Node ID 8fb46a1a0f3e6ca94e2c59ce50e3043a67cde674 # Parent 1f125771f1a1014dcceb8b765f3dd3153d4552f9 Tests: separate SSL session reuse tests. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. In the generic SSL tests only basic session reuse is now tested, notably with session tickets enabled and a shared SSL session cache. This should make it possible to reuse sessions in all cases (except when it's not supported, such as with LibreSSL with TLSv1.3). Note that session reuse with tickets implies that $ssl_session_id is selected by the client and therefore is not available on the initial connection. Relevant test is modified to handle this. Further, BoringSSL does not use legacy session ID with TLSv1.3 even if it is sent by the client. In contrast, OpenSSL always generates an unique legacy session id, so it is available with TLSv1.3 even if session resumption does not work (such as with old Net::SSLeay and IO::Socket::SSL modules). diff --git a/ssl.t b/ssl.t --- a/ssl.t +++ b/ssl.t @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( plan(skip_all => 'IO::Socket::SSL too old') if $@; my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) - ->has_daemon('openssl')->plan(28); + ->has_daemon('openssl')->plan(21); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -47,7 +47,6 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; log_format ssl $ssl_protocol; @@ -59,6 +58,7 @@ http { ssl_certificate_key inner.key; ssl_certificate inner.crt; ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; ssl_verify_client optional_no_ca; keepalive_requests 1000; @@ -100,57 +100,11 @@ http { } server { - listen 127.0.0.1:8081; - server_name localhost; - - # Special case for enabled "ssl" directive. - - ssl on; - ssl_session_cache builtin; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8082 ssl; - server_name localhost; - - ssl_session_cache builtin:1000; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8083 ssl; - server_name localhost; - - ssl_session_cache none; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { - listen 127.0.0.1:8084 ssl; - server_name localhost; - - ssl_session_cache off; - - location / { - return 200 "body $ssl_session_reused"; - } - } - - server { listen 127.0.0.1:8086 ssl; server_name localhost; ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; ssl_session_timeout 1; location / { @@ -216,59 +170,34 @@ foreach my $name ('localhost', 'inner') or die "Can't create certificate for $name: $!\n"; } -# suppress deprecation warning - -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -my $ctx; +# ssl session reuse -SKIP: { -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +my $ctx = get_ssl_context(); -$ctx = get_ssl_context(); +like(get('/', 8085, $ctx), qr/^body \.$/m, 'session'); -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); - -$ctx = get_ssl_context(); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); +like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); } -$ctx = get_ssl_context(); - -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); - # ssl certificate inheritance -my $s = get_ssl_socket(8081); +my $s = get_ssl_socket(8086); like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); -$s->close(); - $s = get_ssl_socket(8085); like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); -$s->close(); - # session timeout $ctx = get_ssl_context(); @@ -280,8 +209,12 @@ like(get('/', 8086, $ctx), qr/^body \.$/ # embedded variables -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); +$ctx = get_ssl_context(); +like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); +like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); + unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); + like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); SKIP: { @@ -334,6 +267,10 @@ like(`grep -F '[crit]' ${\($t->testdir() ############################################################################### +sub test_tls13 { + return get('/protocol', 8085) =~ /TLSv1.3/; +} + sub get { my ($uri, $port, $ctx) = @_; my $s = get_ssl_socket($port, $ctx) or return; diff --git a/ssl.t b/ssl_session_reuse.t copy from ssl.t copy to ssl_session_reuse.t --- a/ssl.t +++ b/ssl_session_reuse.t @@ -1,10 +1,10 @@ #!/usr/bin/perl -# (C) Sergey Kandaurov # (C) Andrey Zelenkov +# (C) Maxim Dounin # (C) Nginx, Inc. -# Tests for http ssl module. +# Tests for http ssl module, session reuse. ############################################################################### @@ -13,8 +13,6 @@ use strict; use Test::More; -use Socket qw/ CRLF /; - BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; @@ -30,8 +28,8 @@ plan(skip_all => 'IO::Socket::SSL not in eval { IO::Socket::SSL::SSL_VERIFY_NONE(); }; plan(skip_all => 'IO::Socket::SSL too old') if $@; -my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) - ->has_daemon('openssl')->plan(28); +my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/) + ->has_daemon('openssl')->plan(8); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -47,66 +45,37 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; - - log_format ssl $ssl_protocol; server { - listen 127.0.0.1:8085 ssl; - listen 127.0.0.1:8080; + listen 127.0.0.1:8443 ssl; server_name localhost; - ssl_certificate_key inner.key; - ssl_certificate inner.crt; - ssl_session_cache shared:SSL:1m; - ssl_verify_client optional_no_ca; - - keepalive_requests 1000; - location / { return 200 "body $ssl_session_reused"; } - location /id { - return 200 "body $ssl_session_id"; - } - location /cipher { - return 200 "body $ssl_cipher"; - } - location /ciphers { - return 200 "body $ssl_ciphers"; - } - location /client_verify { - return 200 "body $ssl_client_verify"; - } location /protocol { return 200 "body $ssl_protocol"; } - location /issuer { - return 200 "body $ssl_client_i_dn:$ssl_client_i_dn_legacy"; - } - location /subject { - return 200 "body $ssl_client_s_dn:$ssl_client_s_dn_legacy"; - } - location /time { - return 200 "body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain"; - } - - location /body { - add_header X-Body $request_body always; - proxy_pass http://127.0.0.1:8080/; - - access_log %%TESTDIR%%/ssl.log ssl; - } } server { - listen 127.0.0.1:8081; + listen 127.0.0.1:8444 ssl; server_name localhost; - # Special case for enabled "ssl" directive. + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; - ssl on; - ssl_session_cache builtin; + location / { + return 200 "body $ssl_session_reused"; + } + } + + server { + listen 127.0.0.1:8445 ssl; + server_name localhost; + + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -114,10 +83,11 @@ http { } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8446 ssl; server_name localhost; - ssl_session_cache builtin:1000; + ssl_session_cache builtin; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -125,10 +95,11 @@ http { } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8447 ssl; server_name localhost; - ssl_session_cache none; + ssl_session_cache builtin:1000; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -136,10 +107,11 @@ http { } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8448 ssl; server_name localhost; - ssl_session_cache off; + ssl_session_cache none; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -147,11 +119,11 @@ http { } server { - listen 127.0.0.1:8086 ssl; + listen 127.0.0.1:8449 ssl; server_name localhost; - ssl_session_cache shared:SSL:1m; - ssl_session_timeout 1; + ssl_session_cache off; + ssl_session_tickets off; location / { return 200 "body $ssl_session_reused"; @@ -171,44 +143,7 @@ EOF my $d = $t->testdir(); -$t->write_file('ca.conf', <write_file('certserial', '1000'); -$t->write_file('certindex', ''); - -system('openssl req -x509 -new ' - . "-config $d/openssl.conf -subj /CN=issuer/ " - . "-out $d/issuer.crt -keyout $d/issuer.key " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't create certificate for issuer: $!\n"; - -system("openssl req -new " - . "-config $d/openssl.conf -subj /CN=subject/ " - . "-out $d/subject.csr -keyout $d/subject.key " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't create certificate for subject: $!\n"; - -system("openssl ca -batch -config $d/ca.conf " - . "-keyfile $d/issuer.key -cert $d/issuer.crt " - . "-subj /CN=subject/ -in $d/subject.csr -out $d/subject.crt " - . ">>$d/openssl.out 2>&1") == 0 - or die "Can't sign certificate for subject: $!\n"; - -foreach my $name ('localhost', 'inner') { +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.crt -keyout $d/$name.key " @@ -216,124 +151,54 @@ foreach my $name ('localhost', 'inner') or die "Can't create certificate for $name: $!\n"; } -# suppress deprecation warning - -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -my $ctx; - -SKIP: { -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); - -$ctx = get_ssl_context(); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache, should work always +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); - -$ctx = get_ssl_context(); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); -like(get('/', 8081, $ctx), qr/^body \.$/m, 'cache builtin'); -like(get('/', 8081, $ctx), qr/^body r$/m, 'cache builtin reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8082, $ctx), qr/^body \.$/m, 'cache builtin size'); -like(get('/', 8082, $ctx), qr/^body r$/m, 'cache builtin size reused'); +is(test_reuse(8443), 1, 'tickets reused'); +is(test_reuse(8444), 1, 'tickets and cache reused'); +is(test_reuse(8445), 1, 'cache shared reused'); +is(test_reuse(8446), 1, 'cache builtin reused'); +is(test_reuse(8447), 1, 'cache builtin size reused'); } -$ctx = get_ssl_context(); - -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none'); -like(get('/', 8083, $ctx), qr/^body \.$/m, 'cache none not reused'); - -$ctx = get_ssl_context(); - -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off'); -like(get('/', 8084, $ctx), qr/^body \.$/m, 'cache off not reused'); - -# ssl certificate inheritance - -my $s = get_ssl_socket(8081); -like($s->dump_peer_certificate(), qr/CN=localhost/, 'CN'); - -$s->close(); - -$s = get_ssl_socket(8085); -like($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner'); - -$s->close(); - -# session timeout - -$ctx = get_ssl_context(); - -get('/', 8086, $ctx); -select undef, undef, undef, 2.1; - -like(get('/', 8086, $ctx), qr/^body \.$/m, 'session timeout'); - -# embedded variables - -like(get('/id', 8085), qr/^body \w{64}$/m, 'session id'); -unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); -like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); - -SKIP: { -skip 'BoringSSL', 1 if $t->has_module('BoringSSL'); - -like(get('/ciphers', 8085), qr/^body [:\w-]+$/m, 'ciphers'); - -} - -like(get('/client_verify', 8085), qr/^body NONE$/m, 'client verify'); -like(get('/protocol', 8085), qr/^body (TLS|SSL)v(\d|\.)+$/m, 'protocol'); -like(cert('/issuer', 8085), qr!^body CN=issuer:/CN=issuer$!m, 'issuer'); -like(cert('/subject', 8085), qr!^body CN=subject:/CN=subject$!m, 'subject'); -like(cert('/time', 8085), qr/^body [:\s\w]+![:\s\w]+![23]$/m, 'time'); - -# c->read->ready handling bug in ngx_ssl_recv(), triggered with chunked body - -like(get_body('/body', '0123456789', 20, 5), qr/X-Body: (0123456789){100}/, - 'request body chunked'); - -# pipelined requests - -$s = get_ssl_socket(8085); -my $req = < $s) || ""; -$s = undef; -is(() = $r =~ /(200 OK)/g, 1000, 'pipelined requests'); - -# OpenSSL 3.0 error "unexpected eof while reading" seen as a critical error - -ok(get_ssl_socket(8085), 'ssl unexpected eof'); - -# close_notify is sent before lingering close - -is(get_ssl_shutdown(8085), 1, 'ssl shutdown on lingering close'); +is(test_reuse(8448), 0, 'cache none not reused'); +is(test_reuse(8449), 0, 'cache off not reused'); $t->stop(); -like($t->read_file('ssl.log'), qr/^(TLS|SSL)v(\d|\.)+$/m, - 'log ssl variable on lingering close'); - like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); ############################################################################### +sub test_tls13 { + return get('/protocol', 8443) =~ /TLSv1.3/; +} + +sub test_reuse { + my ($port) = @_; + my $ctx = get_ssl_context(); + get('/', $port, $ctx); + return (get('/', $port, $ctx) =~ qr/^body r$/m) ? 1 : 0; +} + sub get { my ($uri, $port, $ctx) = @_; my $s = get_ssl_socket($port, $ctx) or return; @@ -342,30 +207,6 @@ sub get { return $r; } -sub get_body { - my ($uri, $body, $len, $n) = @_; - my $s = get_ssl_socket(8085) or return; - http("GET /body HTTP/1.1" . CRLF - . "Host: localhost" . CRLF - . "Connection: close" . CRLF - . "Transfer-Encoding: chunked" . CRLF . CRLF, - socket => $s, start => 1); - my $chs = unpack("H*", pack("C", length($body) * $len)); - http($chs . CRLF . $body x $len . CRLF, socket => $s, start => 1) - for 1 .. $n; - my $r = http("0" . CRLF . CRLF, socket => $s); - $s->close(); - return $r; -} - -sub cert { - my ($uri, $port) = @_; - my $s = get_ssl_socket($port, undef, - SSL_cert_file => "$d/subject.crt", - SSL_key_file => "$d/subject.key") or return; - http_get($uri, socket => $s); -} - sub get_ssl_context { return IO::Socket::SSL::SSL_Context->new( SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), @@ -402,18 +243,4 @@ sub get_ssl_socket { return $s; } -sub get_ssl_shutdown { - my ($port) = @_; - - my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_fd($ssl, fileno($s)); - Net::SSLeay::connect($ssl) or die("ssl connect"); - Net::SSLeay::write($ssl, 'GET /' . CRLF . 'extra'); - Net::SSLeay::read($ssl); - Net::SSLeay::set_shutdown($ssl, 1); - Net::SSLeay::shutdown($ssl); -} - ############################################################################### # HG changeset patch # User Maxim Dounin # Date 1679518794 -10800 # Wed Mar 22 23:59:54 2023 +0300 # Node ID 670dec53c4cd9ad2f64ff09d9094c44d49998726 # Parent 8fb46a1a0f3e6ca94e2c59ce50e3043a67cde674 Tests: LibreSSL and BoringSSL session reuse with TLSv1.3. LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL with TLSv1.3 only supports session tickets, but not server-side session cache. diff --git a/ssl.t b/ssl.t --- a/ssl.t +++ b/ssl.t @@ -185,6 +185,8 @@ local $TODO = 'no TLSv1.3 sessions, old if $Net::SSLeay::VERSION < 1.88 && test_tls13(); local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); like(get('/', 8085, $ctx), qr/^body r$/m, 'session reused'); @@ -211,8 +213,17 @@ like(get('/', 8086, $ctx), qr/^body \.$/ $ctx = get_ssl_context(); like(get('/id', 8085, $ctx), qr/^body (\w{64})?$/m, 'session id'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); +local $TODO = 'no TLSv1.3 sessions ids in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + like(get('/id', 8085, $ctx), qr/^body \w{64}$/m, 'session id reused'); +} + unlike(http_get('/id'), qr/body \w/, 'session id no ssl'); like(get('/cipher', 8085), qr/^body [\w-]+$/m, 'cipher'); diff --git a/ssl_session_reuse.t b/ssl_session_reuse.t --- a/ssl_session_reuse.t +++ b/ssl_session_reuse.t @@ -170,14 +170,22 @@ local $TODO = 'no TLSv1.3 sessions, old if $Net::SSLeay::VERSION < 1.88 && test_tls13(); local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); is(test_reuse(8443), 1, 'tickets reused'); is(test_reuse(8444), 1, 'tickets and cache reused'); + +TODO: { +local $TODO = 'no TLSv1.3 session cache in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + is(test_reuse(8445), 1, 'cache shared reused'); is(test_reuse(8446), 1, 'cache builtin reused'); is(test_reuse(8447), 1, 'cache builtin size reused'); } +} is(test_reuse(8448), 0, 'cache none not reused'); is(test_reuse(8449), 0, 'cache off not reused'); # HG changeset patch # User Maxim Dounin # Date 1679527437 -10800 # Thu Mar 23 02:23:57 2023 +0300 # Node ID ebef8c3ce0aee7886826da353e285c430ce949cd # Parent 670dec53c4cd9ad2f64ff09d9094c44d49998726 Tests: separate SSL session reuse tests in mail. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. diff --git a/mail_ssl.t b/mail_ssl.t --- a/mail_ssl.t +++ b/mail_ssl.t @@ -37,7 +37,7 @@ eval { exists &Net::SSLeay::P_alpn_selec plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) - ->has_daemon('openssl')->plan(22); + ->has_daemon('openssl')->plan(18); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -51,44 +51,25 @@ events { mail { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; ssl_password_file password; auth_http http://127.0.0.1:8080; # unused - ssl_session_cache none; - server { listen 127.0.0.1:8143; listen 127.0.0.1:8145 ssl; protocol imap; - - ssl_session_cache builtin; } server { - listen 127.0.0.1:8146 ssl; - protocol imap; - - ssl_session_cache off; - } - - server { - listen 127.0.0.1:8147; + listen 127.0.0.1:8148; protocol imap; # Special case for enabled "ssl" directive. ssl on; - ssl_session_cache builtin:1000; - } - server { - listen 127.0.0.1:8148 ssl; - protocol imap; - - ssl_session_cache shared:SSL:1m; ssl_certificate_key inherits.key; ssl_certificate inherits.crt; } @@ -169,46 +150,16 @@ open STDERR, ">&", \*OLDERR; ############################################################################### +my ($s, $ssl); + # simple tests to ensure that nothing broke with ssl_password_file directive -my $s = Test::Nginx::IMAP->new(); +$s = Test::Nginx::IMAP->new(); $s->ok('greeting'); $s->send('1 AUTHENTICATE LOGIN'); $s->check(qr/\+ VXNlcm5hbWU6/, 'login'); -# ssl_session_cache - -my ($ssl, $ses); - -($s, $ssl) = get_ssl_socket(8145); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8145, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(8146); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8146, $ses); -is(Net::SSLeay::session_reused($ssl), 0, 'session not reused'); - -($s, $ssl) = get_ssl_socket(8147); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8147, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(8148); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8148, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); - # ssl_certificate inheritance ($s, $ssl) = get_ssl_socket(8145); @@ -219,7 +170,7 @@ like(Net::SSLeay::dump_peer_certificate( # alpn -ok(get_ssl_socket(8148, undef, ['imap']), 'alpn'); +ok(get_ssl_socket(8148, ['imap']), 'alpn'); SKIP: { $t->{_configure_args} =~ /LibreSSL ([\d\.]+)/; @@ -230,7 +181,7 @@ skip 'OpenSSL too old', 1 if defined $1 TODO: { local $TODO = 'not yet' unless $t->has_version('1.21.4'); -ok(!get_ssl_socket(8148, undef, ['unknown']), 'alpn rejected'); +ok(!get_ssl_socket(8148, ['unknown']), 'alpn rejected'); } @@ -317,11 +268,10 @@ ok(!get_ssl_socket(8148, undef, ['unknow ############################################################################### sub get_ssl_socket { - my ($port, $ses, $alpn) = @_; + my ($port, $alpn) = @_; my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) == 1 or return; diff --git a/mail_ssl.t b/mail_ssl_session_reuse.t copy from mail_ssl.t copy to mail_ssl_session_reuse.t --- a/mail_ssl.t +++ b/mail_ssl_session_reuse.t @@ -1,9 +1,10 @@ #!/usr/bin/perl # (C) Andrey Zelenkov +# (C) Maxim Dounin # (C) Nginx, Inc. -# Tests for mail ssl module. +# Tests for mail ssl module, session reuse. ############################################################################### @@ -16,9 +17,6 @@ BEGIN { use FindBin; chdir($FindBin::Bin use lib 'lib'; use Test::Nginx; -use Test::Nginx::IMAP; -use Test::Nginx::POP3; -use Test::Nginx::SMTP; ############################################################################### @@ -33,11 +31,8 @@ eval { }; plan(skip_all => 'Net::SSLeay not installed') if $@; -eval { exists &Net::SSLeay::P_alpn_selected or die; }; -plan(skip_all => 'Net::SSLeay with OpenSSL ALPN support required') if $@; - -my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/) - ->has_daemon('openssl')->plan(22); +my $t = Test::Nginx->new()->has(qw/mail mail_ssl imap/) + ->has_daemon('openssl')->plan(7); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -49,90 +44,62 @@ events { } mail { - ssl_certificate_key localhost.key; - ssl_certificate localhost.crt; - ssl_session_tickets off; + auth_http http://127.0.0.1:8080; - ssl_password_file password; - - auth_http http://127.0.0.1:8080; # unused - - ssl_session_cache none; + ssl_certificate localhost.crt; + ssl_certificate_key localhost.key; server { - listen 127.0.0.1:8143; - listen 127.0.0.1:8145 ssl; - protocol imap; - - ssl_session_cache builtin; + listen 127.0.0.1:8993 ssl; + protocol imap; } server { - listen 127.0.0.1:8146 ssl; - protocol imap; + listen 127.0.0.1:8994 ssl; + protocol imap; - ssl_session_cache off; + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; } server { - listen 127.0.0.1:8147; - protocol imap; + listen 127.0.0.1:8995 ssl; + protocol imap; - # Special case for enabled "ssl" directive. - - ssl on; - ssl_session_cache builtin:1000; + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; } server { - listen 127.0.0.1:8148 ssl; - protocol imap; - - ssl_session_cache shared:SSL:1m; - ssl_certificate_key inherits.key; - ssl_certificate inherits.crt; - } + listen 127.0.0.1:8996 ssl; + protocol imap; - server { - listen 127.0.0.1:8149; - protocol imap; - - starttls on; - } - - server { - listen 127.0.0.1:8150; - protocol imap; - - starttls only; + ssl_session_cache builtin; + ssl_session_tickets off; } server { - listen 127.0.0.1:8151; - protocol pop3; + listen 127.0.0.1:8997 ssl; + protocol imap; - starttls on; + ssl_session_cache builtin:1000; + ssl_session_tickets off; } server { - listen 127.0.0.1:8152; - protocol pop3; + listen 127.0.0.1:8998 ssl; + protocol imap; - starttls only; + ssl_session_cache none; + ssl_session_tickets off; } server { - listen 127.0.0.1:8153; - protocol smtp; - - starttls on; - } + listen 127.0.0.1:8999 ssl; + protocol imap; - server { - listen 127.0.0.1:8154; - protocol smtp; - - starttls only; + ssl_session_cache off; + ssl_session_tickets off; } } @@ -148,181 +115,55 @@ EOF my $d = $t->testdir(); -foreach my $name ('localhost', 'inherits') { - system("openssl genrsa -out $d/$name.key -passout pass:localhost " - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 - or die "Can't create private key: $!\n"; +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " - . "-out $d/$name.crt " - . "-key $d/$name.key -passin pass:localhost" + . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; } my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); -$t->write_file('password', 'localhost'); -open OLDERR, ">&", \*STDERR; close STDERR; $t->run(); -open STDERR, ">&", \*OLDERR; ############################################################################### -# simple tests to ensure that nothing broke with ssl_password_file directive - -my $s = Test::Nginx::IMAP->new(); -$s->ok('greeting'); - -$s->send('1 AUTHENTICATE LOGIN'); -$s->check(qr/\+ VXNlcm5hbWU6/, 'login'); - -# ssl_session_cache - -my ($ssl, $ses); - -($s, $ssl) = get_ssl_socket(8145); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8145, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(8146); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8146, $ses); -is(Net::SSLeay::session_reused($ssl), 0, 'session not reused'); - -($s, $ssl) = get_ssl_socket(8147); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8147, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(8148); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(8148, $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); - -# ssl_certificate inheritance - -($s, $ssl) = get_ssl_socket(8145); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); - -($s, $ssl) = get_ssl_socket(8148); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); - -# alpn - -ok(get_ssl_socket(8148, undef, ['imap']), 'alpn'); - -SKIP: { -$t->{_configure_args} =~ /LibreSSL ([\d\.]+)/; -skip 'LibreSSL too old', 1 if defined $1 and $1 lt '3.4.0'; -$t->{_configure_args} =~ /OpenSSL ([\d\.]+)/; -skip 'OpenSSL too old', 1 if defined $1 and $1 lt '1.1.0'; - -TODO: { -local $TODO = 'not yet' unless $t->has_version('1.21.4'); - -ok(!get_ssl_socket(8148, undef, ['unknown']), 'alpn rejected'); - -} - -} - -# starttls imap - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8149)); -$s->read(); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache, should work always +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -$s->send('1 AUTHENTICATE LOGIN'); -$s->check(qr/\+ VXNlcm5hbWU6/, 'imap auth before startls on'); - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8149)); -$s->read(); - -$s->send('1 STARTTLS'); -$s->ok('imap starttls on'); - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8150)); -$s->read(); - -$s->send('1 AUTHENTICATE LOGIN'); -$s->check(qr/^\S+ BAD/, 'imap auth before startls only'); - -$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8150)); -$s->read(); - -$s->send('1 STARTTLS'); -$s->ok('imap starttls only'); - -# starttls pop3 - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8151)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/\+ VXNlcm5hbWU6/, 'pop3 auth before startls on'); - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8151)); -$s->read(); - -$s->send('STLS'); -$s->ok('pop3 starttls on'); - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8152)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/^-ERR/, 'pop3 auth before startls only'); - -$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8152)); -$s->read(); - -$s->send('STLS'); -$s->ok('pop3 starttls only'); - -# starttls smtp - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8153)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/^334 VXNlcm5hbWU6/, 'smtp auth before startls on'); - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8153)); -$s->read(); - -$s->send('STARTTLS'); -$s->ok('smtp starttls on'); - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8154)); -$s->read(); - -$s->send('AUTH LOGIN'); -$s->check(qr/^5.. /, 'smtp auth before startls only'); - -$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8154)); -$s->read(); - -$s->send('STARTTLS'); -$s->ok('smtp starttls only'); +is(test_reuse(8993), 1, 'tickets reused'); +is(test_reuse(8994), 1, 'tickets and cache reused'); +is(test_reuse(8995), 1, 'cache shared reused'); +is(test_reuse(8996), 1, 'cache builtin reused'); +is(test_reuse(8997), 1, 'cache builtin size reused'); +is(test_reuse(8998), 0, 'cache none not reused'); +is(test_reuse(8999), 0, 'cache off not reused'); ############################################################################### +sub test_reuse { + my ($port) = @_; + my ($s, $ssl) = get_ssl_socket($port); + Net::SSLeay::read($ssl); + my $ses = Net::SSLeay::get_session($ssl); + ($s, $ssl) = get_ssl_socket($port, $ses); + return Net::SSLeay::session_reused($ssl); +} + sub get_ssl_socket { - my ($port, $ses, $alpn) = @_; + my ($port, $ses) = @_; my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); Net::SSLeay::set_session($ssl, $ses) if defined $ses; - Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) == 1 or return; return ($s, $ssl); # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 28de0345b5cf67a4956a84a8adcbd1c59078ea9e # Parent ebef8c3ce0aee7886826da353e285c430ce949cd Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in mail. LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL with TLSv1.3 only supports session tickets, but not server-side session cache. diff --git a/mail_ssl_session_reuse.t b/mail_ssl_session_reuse.t --- a/mail_ssl_session_reuse.t +++ b/mail_ssl_session_reuse.t @@ -139,16 +139,34 @@ my $ctx = Net::SSLeay::CTX_new() or die( # - only cache none # - only cache off +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + is(test_reuse(8993), 1, 'tickets reused'); is(test_reuse(8994), 1, 'tickets and cache reused'); + +TODO: { +local $TODO = 'no TLSv1.3 session cache in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + is(test_reuse(8995), 1, 'cache shared reused'); is(test_reuse(8996), 1, 'cache builtin reused'); is(test_reuse(8997), 1, 'cache builtin size reused'); + +} +} + is(test_reuse(8998), 0, 'cache none not reused'); is(test_reuse(8999), 0, 'cache off not reused'); ############################################################################### +sub test_tls13 { + my ($s, $ssl) = get_ssl_socket(8993); + return (Net::SSLeay::version($ssl) > 0x303); +} + sub test_reuse { my ($port) = @_; my ($s, $ssl) = get_ssl_socket($port); # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 376c1b24119098b673c5ba1b1b7f09ad7d5511fd # Parent 28de0345b5cf67a4956a84a8adcbd1c59078ea9e Tests: separate SSL session reuse tests in stream. Instead of being mixed with generic SSL tests, session reuse variants are now tested in a separate file. diff --git a/stream_ssl.t b/stream_ssl.t --- a/stream_ssl.t +++ b/stream_ssl.t @@ -37,7 +37,7 @@ plan(skip_all => 'win32') if $^O eq 'MSW my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); -$t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); +$t->plan(5)->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -51,40 +51,35 @@ stream { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; - ssl_session_tickets off; # inherited by server "inherits" ssl_password_file password_stream; server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8443 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache builtin; ssl_password_file password; } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8444 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache off; ssl_password_file password_many; } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8445 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache builtin:1000; ssl_password_file password_fifo; } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8446 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache shared:SSL:1m; ssl_certificate_key inherits.key; ssl_certificate inherits.crt; } @@ -115,7 +110,6 @@ foreach my $name ('localhost', 'inherits or die "Can't create certificate for $name: $!\n"; } - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); $t->write_file('password', 'localhost'); @@ -138,62 +132,35 @@ kill 'INT', $p if $@; ############################################################################### -my ($s, $ssl, $ses); +my ($s, $ssl); -($s, $ssl) = get_ssl_socket(port(8080)); +($s, $ssl) = get_ssl_socket(8443); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); -# ssl_session_cache - -($s, $ssl) = get_ssl_socket(port(8080)); +($s, $ssl) = get_ssl_socket(8444); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8080), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(port(8082)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password many'); -($s, $ssl) = get_ssl_socket(port(8082), $ses); -isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); - -($s, $ssl) = get_ssl_socket(port(8083)); +($s, $ssl) = get_ssl_socket(8445); Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8083), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(port(8084)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8084), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); +like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl password fifo'); # ssl_certificate inheritance -($s, $ssl) = get_ssl_socket(port(8080)); +($s, $ssl) = get_ssl_socket(8443); like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); -($s, $ssl) = get_ssl_socket(port(8084)); +($s, $ssl) = get_ssl_socket(8446); like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); ############################################################################### sub get_ssl_socket { - my ($port, $ses) = @_; + my ($port) = @_; - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); + my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); - Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) or die("ssl connect"); return ($s, $ssl); diff --git a/stream_ssl.t b/stream_ssl_session_reuse.t copy from stream_ssl.t copy to stream_ssl_session_reuse.t --- a/stream_ssl.t +++ b/stream_ssl_session_reuse.t @@ -1,9 +1,10 @@ #!/usr/bin/perl # (C) Sergey Kandaurov +# (C) Maxim Dounin # (C) Nginx, Inc. -# Tests for stream ssl module. +# Tests for stream ssl module, session reuse. ############################################################################### @@ -12,7 +13,6 @@ use strict; use Test::More; -use POSIX qw/ mkfifo /; use Socket qw/ $CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } @@ -33,8 +33,6 @@ eval { }; plan(skip_all => 'Net::SSLeay not installed') if $@; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->has_daemon('openssl'); $t->plan(7)->write_file_expand('nginx.conf', <<'EOF'); @@ -49,44 +47,60 @@ events { stream { %%TEST_GLOBALS_STREAM%% + ssl_certificate localhost.crt; ssl_certificate_key localhost.key; - ssl_certificate localhost.crt; - ssl_session_tickets off; - # inherited by server "inherits" - ssl_password_file password_stream; + server { + listen 127.0.0.1:8443 ssl; + proxy_pass 127.0.0.1:8081; + } server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8444 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache builtin; - ssl_password_file password; + ssl_session_cache shared:SSL:1m; + ssl_session_tickets on; + } + + server { + listen 127.0.0.1:8445 ssl; + proxy_pass 127.0.0.1:8081; + + ssl_session_cache shared:SSL:1m; + ssl_session_tickets off; } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8446 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache off; - ssl_password_file password_many; + ssl_session_cache builtin; + ssl_session_tickets off; } server { - listen 127.0.0.1:8083 ssl; + listen 127.0.0.1:8447 ssl; proxy_pass 127.0.0.1:8081; ssl_session_cache builtin:1000; - ssl_password_file password_fifo; + ssl_session_tickets off; } server { - listen 127.0.0.1:8084 ssl; + listen 127.0.0.1:8448 ssl; proxy_pass 127.0.0.1:8081; - ssl_session_cache shared:SSL:1m; - ssl_certificate_key inherits.key; - ssl_certificate inherits.crt; + ssl_session_cache none; + ssl_session_tickets off; + } + + server { + listen 127.0.0.1:8449 ssl; + proxy_pass 127.0.0.1:8081; + + ssl_session_cache off; + ssl_session_tickets off; } } @@ -101,97 +115,59 @@ distinguished_name = req_distinguished_n EOF my $d = $t->testdir(); -mkfifo("$d/password_fifo", 0700); -foreach my $name ('localhost', 'inherits') { - system("openssl genrsa -out $d/$name.key -passout pass:$name " - . "-aes128 2048 >>$d/openssl.out 2>&1") == 0 - or die "Can't create private key: $!\n"; +foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " - . "-out $d/$name.crt " - . "-key $d/$name.key -passin pass:$name" + . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; } - my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); -$t->write_file('password', 'localhost'); -$t->write_file('password_many', "wrong$CRLF" . "localhost$CRLF"); -$t->write_file('password_stream', 'inherits'); - -my $p = fork(); -exec("echo localhost > $d/password_fifo") if $p == 0; - $t->run_daemon(\&http_daemon); -eval { - open OLDERR, ">&", \*STDERR; close STDERR; - $t->run(); - open STDERR, ">&", \*OLDERR; -}; -kill 'INT', $p if $@; +$t->run(); $t->waitforsocket('127.0.0.1:' . port(8081)); ############################################################################### -my ($s, $ssl, $ses); - -($s, $ssl) = get_ssl_socket(port(8080)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -like(Net::SSLeay::read($ssl), qr/200 OK/, 'ssl'); - -# ssl_session_cache - -($s, $ssl) = get_ssl_socket(port(8080)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8080), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin session reused'); - -($s, $ssl) = get_ssl_socket(port(8082)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8082), $ses); -isnt(Net::SSLeay::session_reused($ssl), 1, 'session not reused'); +# session reuse: +# +# - only tickets, the default +# - tickets and shared cache, should work always +# - only shared cache +# - only builtin cache +# - only builtin cache with explicitly configured size +# - only cache none +# - only cache off -($s, $ssl) = get_ssl_socket(port(8083)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8083), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'builtin size session reused'); - -($s, $ssl) = get_ssl_socket(port(8084)); -Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); -Net::SSLeay::read($ssl); -$ses = Net::SSLeay::get_session($ssl); - -($s, $ssl) = get_ssl_socket(port(8084), $ses); -is(Net::SSLeay::session_reused($ssl), 1, 'shared session reused'); - -# ssl_certificate inheritance - -($s, $ssl) = get_ssl_socket(port(8080)); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=localhost/, 'CN'); - -($s, $ssl) = get_ssl_socket(port(8084)); -like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=inherits/, 'CN inner'); +is(test_reuse(8443), 1, 'tickets reused'); +is(test_reuse(8444), 1, 'tickets and cache reused'); +is(test_reuse(8445), 1, 'cache shared reused'); +is(test_reuse(8446), 1, 'cache builtin reused'); +is(test_reuse(8447), 1, 'cache builtin size reused'); +is(test_reuse(8448), 0, 'cache none not reused'); +is(test_reuse(8449), 0, 'cache off not reused'); ############################################################################### +sub test_reuse { + my ($port) = @_; + my ($s, $ssl) = get_ssl_socket($port); + Net::SSLeay::write($ssl, "GET / HTTP/1.0$CRLF$CRLF"); + Net::SSLeay::read($ssl); + my $ses = Net::SSLeay::get_session($ssl); + ($s, $ssl) = get_ssl_socket($port, $ses); + return Net::SSLeay::session_reused($ssl); +} + sub get_ssl_socket { my ($port, $ses) = @_; - my $s = IO::Socket::INET->new('127.0.0.1:' . $port); + my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); Net::SSLeay::set_session($ssl, $ses) if defined $ses; Net::SSLeay::set_fd($ssl, fileno($s)); # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID b0db313bf025b7647e31355cb41c7fb3563a6c68 # Parent 376c1b24119098b673c5ba1b1b7f09ad7d5511fd Tests: LibreSSL and BoringSSL session reuse with TLSv1.3 in stream. LibreSSL does not support session reuse with TLSv1.3 at all. BoringSSL with TLSv1.3 only supports session tickets, but not server-side session cache. diff --git a/stream_ssl_session_reuse.t b/stream_ssl_session_reuse.t --- a/stream_ssl_session_reuse.t +++ b/stream_ssl_session_reuse.t @@ -144,16 +144,34 @@ my $ctx = Net::SSLeay::CTX_new() or die( # - only cache none # - only cache off +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + is(test_reuse(8443), 1, 'tickets reused'); is(test_reuse(8444), 1, 'tickets and cache reused'); + +TODO: { +local $TODO = 'no TLSv1.3 session cache in BoringSSL' + if $t->has_module('BoringSSL') && test_tls13(); + is(test_reuse(8445), 1, 'cache shared reused'); is(test_reuse(8446), 1, 'cache builtin reused'); is(test_reuse(8447), 1, 'cache builtin size reused'); + +} +} + is(test_reuse(8448), 0, 'cache none not reused'); is(test_reuse(8449), 0, 'cache off not reused'); ############################################################################### +sub test_tls13 { + my ($s, $ssl) = get_ssl_socket(8443); + return (Net::SSLeay::version($ssl) > 0x303); +} + sub test_reuse { my ($port) = @_; my ($s, $ssl) = get_ssl_socket($port); # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 04ce263b25a6e591d39df2eaf2ff5c4ce0fa633e # Parent b0db313bf025b7647e31355cb41c7fb3563a6c68 Tests: BoringSSL does not provide session ids with TLSv1.3. diff --git a/stream_ssl_variables.t b/stream_ssl_variables.t --- a/stream_ssl_variables.t +++ b/stream_ssl_variables.t @@ -103,7 +103,7 @@ like(Net::SSLeay::read($ssl), qr/^\.:(\w my $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8081), $ses); -like(Net::SSLeay::read($ssl), qr/^r:\w{64}:[\w-]+:(TLS|SSL)v(\d|\.)+$/, +like(Net::SSLeay::read($ssl), qr/^r:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables - session reused'); SKIP: { # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID dc59b11f23b2a0df9355efa614f47a21b8786e0e # Parent 04ce263b25a6e591d39df2eaf2ff5c4ce0fa633e Tests: enabled session reuse via TLS session tickets. This fixes tests with TLSv1.3 enabled when using BoringSSL, since for TLSv1.3 it only supports session reuse via TLS session tickets, and not server-side session cache. diff --git a/ssl_certificate.t b/ssl_certificate.t --- a/ssl_certificate.t +++ b/ssl_certificate.t @@ -71,7 +71,7 @@ http { add_header X-SSL $ssl_server_name:$ssl_session_reused; ssl_session_cache shared:SSL:1m; - ssl_session_tickets off; + ssl_session_tickets on; server { listen 127.0.0.1:8080 ssl; diff --git a/stream_ssl_certificate.t b/stream_ssl_certificate.t --- a/stream_ssl_certificate.t +++ b/stream_ssl_certificate.t @@ -68,7 +68,7 @@ stream { } ssl_session_cache shared:SSL:1m; - ssl_session_tickets off; + ssl_session_tickets on; server { listen 127.0.0.1:8080 ssl; # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 28bf82cdf8718b131510ee511812919626ac603c # Parent dc59b11f23b2a0df9355efa614f47a21b8786e0e Tests: restored proper port numbers in ssl_sni_sessions.t. While using port 8080 for SSL connection works, it's usually a bad idea, since 8080 implies plain HTTP, much like 80. For HTTPS we generally use port 8443 instead. Unfortunately, proper port numbers were lost in 952:e9064d691790 and 974:882267679006, while converting tests to run in parallel. diff --git a/ssl_sni_sessions.t b/ssl_sni_sessions.t --- a/ssl_sni_sessions.t +++ b/ssl_sni_sessions.t @@ -39,7 +39,7 @@ http { ssl_certificate localhost.crt; server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8443 ssl; server_name default; ssl_session_tickets off; @@ -51,7 +51,7 @@ http { } server { - listen 127.0.0.1:8080; + listen 127.0.0.1:8443; server_name nocache; ssl_session_tickets off; @@ -63,7 +63,7 @@ http { } server { - listen 127.0.0.1:8081 ssl; + listen 127.0.0.1:8444 ssl; server_name default; ssl_session_ticket_key ticket1.key; @@ -74,7 +74,7 @@ http { } server { - listen 127.0.0.1:8081; + listen 127.0.0.1:8444; server_name tickets; ssl_session_ticket_key ticket2.key; @@ -128,7 +128,7 @@ foreach my $name ('localhost') { $t->run(); plan(skip_all => 'no TLS 1.3 sessions') - if get('default', port(8080), get_ssl_context()) =~ /TLSv1.3/ + if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); $t->plan(6); @@ -139,8 +139,8 @@ plan(skip_all => 'no TLS 1.3 sessions') my $ctx = get_ssl_context(); -like(get('default', port(8080), $ctx), qr!default:\.!, 'default server'); -like(get('default', port(8080), $ctx), qr!default:r!, 'default server reused'); +like(get('default', port(8443), $ctx), qr!default:\.!, 'default server'); +like(get('default', port(8443), $ctx), qr!default:r!, 'default server reused'); # check that sessions are still properly saved and restored # when using an SNI-based virtual server with different session cache; @@ -154,16 +154,16 @@ like(get('default', port(8080), $ctx), q $ctx = get_ssl_context(); -like(get('nocache', port(8080), $ctx), qr!nocache:\.!, 'without cache'); -like(get('nocache', port(8080), $ctx), qr!nocache:r!, 'without cache reused'); +like(get('nocache', port(8443), $ctx), qr!nocache:\.!, 'without cache'); +like(get('nocache', port(8443), $ctx), qr!nocache:r!, 'without cache reused'); # make sure tickets can be used if an SNI-based virtual server # uses a different set of session ticket keys explicitly set $ctx = get_ssl_context(); -like(get('tickets', port(8081), $ctx), qr!tickets:\.!, 'tickets'); -like(get('tickets', port(8081), $ctx), qr!tickets:r!, 'tickets reused'); +like(get('tickets', port(8444), $ctx), qr!tickets:\.!, 'tickets'); +like(get('tickets', port(8444), $ctx), qr!tickets:r!, 'tickets reused'); ############################################################################### # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 558bd1ebd0ef43b81484ad716ec25c349eee55e3 # Parent 28bf82cdf8718b131510ee511812919626ac603c Tests: disabled ssl_sni_sessions.t with LibreSSL and BoringSSL. With TLSv1.3, LibreSSL does not provide session reuse at all, and BoringSSL only supports session tickets, and not server-side session cache. Since the test is focused on session cache usage, it is now skipped. diff --git a/ssl_sni_sessions.t b/ssl_sni_sessions.t --- a/ssl_sni_sessions.t +++ b/ssl_sni_sessions.t @@ -130,6 +130,12 @@ foreach my $name ('localhost') { plan(skip_all => 'no TLS 1.3 sessions') if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +plan(skip_all => 'no TLS 1.3 sessions in LibreSSL') + if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ + && $t->has_module('LibreSSL'); +plan(skip_all => 'no TLS 1.3 session cache in BoringSSL') + if get('default', port(8443), get_ssl_context()) =~ /TLSv1.3/ + && $t->has_module('BoringSSL'); $t->plan(6); # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 9b77114bf006d95f5a1984776a6b375a092cbdfe # Parent 558bd1ebd0ef43b81484ad716ec25c349eee55e3 Tests: fixed proxy_ssl.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/proxy_ssl.t b/proxy_ssl.t --- a/proxy_ssl.t +++ b/proxy_ssl.t @@ -46,6 +46,7 @@ http { location / { add_header X-Session $ssl_session_reused; + add_header X-Protocol $ssl_protocol; } } @@ -109,9 +110,16 @@ foreach my $name ('localhost') { like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl'); like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl 2'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: \./s, 'ssl session new'); + +TODO: { +local $TODO = 'no TLS 1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && http_get('/ssl') =~ /TLSv1.3/; + like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused 2'); +} + SKIP: { skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE}; # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 5a58b9f2c5b33893dcdd67fbfe0231863a43846d # Parent 9b77114bf006d95f5a1984776a6b375a092cbdfe Tests: fixed ssl_session_ticket_key.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/ssl_session_ticket_key.t b/ssl_session_ticket_key.t --- a/ssl_session_ticket_key.t +++ b/ssl_session_ticket_key.t @@ -96,6 +96,10 @@ select undef, undef, undef, 0.5; is(get_ticket_key_name(), $key, 'ticket key match'); select undef, undef, undef, 2.5; + +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + cmp_ok(get_ticket_key_name(), 'ne', $key, 'ticket key next'); ############################################################################### @@ -107,7 +111,7 @@ sub get_ticket_key_name { next: # tag(10) | len{2} | OCTETSTRING(4) | len{2} | ticket(key_name|..) $asn =~ /\xaa\x81($any)\x04\x81($any)($any{16})/g; - return if !defined $3; + return '' if !defined $3; goto next if unpack("C", $1) - unpack("C", $2) != 3; my $key = unpack "H*", $3; Test::Nginx::log_core('||', "ticket key: $key"); @@ -126,6 +130,11 @@ EOF Net::SSLeay::get_session($ssl); } +sub test_tls13 { + my ($s, $ssl) = get_ssl_socket(); + return (Net::SSLeay::version($ssl) > 0x303); +} + sub get_ssl_socket { my $s = IO::Socket::INET->new('127.0.0.1:' . port(8080)); my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID ca3194d2a279bb9b468ce7a9e6628638d367fef1 # Parent 5a58b9f2c5b33893dcdd67fbfe0231863a43846d Tests: fixed ssl_sni.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/ssl_sni.t b/ssl_sni.t --- a/ssl_sni.t +++ b/ssl_sni.t @@ -148,9 +148,13 @@ my $ctx = new IO::Socket::SSL::SSL_Conte like(get('/', 'localhost', 8081, $ctx), qr/^\.:localhost$/m, 'ssl server name'); -SKIP: { -skip 'no TLS 1.3 sessions', 1 if get('/protocol', 'localhost') =~ /TLSv1.3/ - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); +TODO: { +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); like(get('/', 'localhost', 8081, $ctx), qr/^r:localhost$/m, 'ssl server name - reused'); @@ -159,6 +163,10 @@ like(get('/', 'localhost', 8081, $ctx), ############################################################################### +sub test_tls13 { + get('/protocol', 'localhost') =~ /TLSv1.3/; +} + sub get_ssl_socket { my ($host, $port, $ctx) = @_; my $s; # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 85e4f377674975634f1b2e34fc40a5d3b6e54fc0 # Parent ca3194d2a279bb9b468ce7a9e6628638d367fef1 Tests: LibreSSL certificate negotiation with TLSv1.3. LibreSSL fails to negotiate certificates based on signature algorithms when using TLSv1.3, and fails with "missing rsa certificate" and "unknown pkey type" errors. diff --git a/ssl_stapling.t b/ssl_stapling.t --- a/ssl_stapling.t +++ b/ssl_stapling.t @@ -38,7 +38,7 @@ my $t = Test::Nginx->new()->has(qw/http plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL'); -$t->plan(9)->write_file_expand('nginx.conf', <<'EOF'); +$t->plan(10)->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -259,11 +259,25 @@ staple(8449, 'ECDSA'); sleep 1; ok(!staple(8443, 'RSA'), 'staple revoked'); + +TODO: { +local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' + if $t->has_module('LibreSSL') && $version > 0x303; + ok(staple(8443, 'ECDSA'), 'staple success'); +} + ok(!staple(8444, 'RSA'), 'responder revoked'); + +TODO: { +local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' + if $t->has_module('LibreSSL') && $version > 0x303; + ok(staple(8444, 'ECDSA'), 'responder success'); +} + ok(!staple(8445, 'ECDSA'), 'verify - root not trusted'); ok(staple(8446, 'ECDSA', "$d/int.crt"), 'cert store'); @@ -273,6 +287,14 @@ is(staple(8448, 'ECDSA'), '1 0', 'file s ok(!staple(8449, 'ECDSA'), 'ocsp error'); +TODO: { +local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' + if $t->has_module('LibreSSL') && $version > 0x303; + +like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); + +} + ############################################################################### sub staple { # HG changeset patch # User Maxim Dounin # Date 1679527802 -10800 # Thu Mar 23 02:30:02 2023 +0300 # Node ID 647be9c9c22aea06c5a28aa280856edaebd26e21 # Parent 85e4f377674975634f1b2e34fc40a5d3b6e54fc0 Tests: LibreSSL does not send CA lists with TLSv1.3. diff --git a/ssl_verify_client.t b/ssl_verify_client.t --- a/ssl_verify_client.t +++ b/ssl_verify_client.t @@ -55,6 +55,7 @@ http { %%TEST_GLOBALS_HTTP%% add_header X-Verify x$ssl_client_verify:${ssl_client_cert}x; + add_header X-Protocol $ssl_protocol; ssl_session_cache shared:SSL:1m; ssl_session_tickets off; @@ -169,15 +170,24 @@ like(get('optional', '3.example.com'), q SKIP: { skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; +TODO: { +local $TODO = 'broken TLSv1.3 CA list in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + my $ca = join ' ', get('optional', '3.example.com'); is($ca, '/CN=2.example.com', 'no trusted sent'); } +} like(get('optional', undef, 'localhost'), qr/421 Misdirected/, 'misdirected'); ############################################################################### +sub test_tls13 { + get('optional') =~ /TLSv1.3/; +} + sub get { my ($sni, $cert, $host) = @_; diff --git a/stream_ssl_verify_client.t b/stream_ssl_verify_client.t --- a/stream_ssl_verify_client.t +++ b/stream_ssl_verify_client.t @@ -86,6 +86,11 @@ stream { ssl_verify_client optional_no_ca; ssl_client_certificate 2.example.com.crt; } + + server { + listen 127.0.0.1:8084 ssl; + return $ssl_protocol; + } } EOF @@ -126,10 +131,15 @@ like(get(8082, '3.example.com'), qr/SUCC SKIP: { skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; +TODO: { +local $TODO = 'broken TLSv1.3 CA list in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + my $ca = join ' ', get(8082, '3.example.com'); is($ca, '/CN=2.example.com', 'no trusted sent'); } +} $t->stop(); @@ -137,6 +147,10 @@ is($t->read_file('status.log'), "500\n20 ############################################################################### +sub test_tls13 { + get(8084) =~ /TLSv1.3/; +} + sub get { my ($port, $cert) = @_; # HG changeset patch # User Maxim Dounin # Date 1679531073 -10800 # Thu Mar 23 03:24:33 2023 +0300 # Node ID 5e7b95544014db80e0d8c94ca8b6a9b2f14b75cd # Parent 647be9c9c22aea06c5a28aa280856edaebd26e21 Tests: fixed stream_proxy_ssl.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/stream_proxy_ssl.t b/stream_proxy_ssl.t --- a/stream_proxy_ssl.t +++ b/stream_proxy_ssl.t @@ -78,6 +78,8 @@ http { ssl_certificate_key localhost.key; ssl_certificate localhost.crt; + + add_header X-Protocol $ssl_protocol; } } @@ -111,9 +113,16 @@ is(stream('127.0.0.1:' . port(8081))->re is(stream('127.0.0.1:' . port(8081))->read(), '.', 'ssl 2'); is(stream('127.0.0.1:' . port(8082))->read(), '.', 'ssl session new'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + is(stream('127.0.0.1:' . port(8082))->read(), 'r', 'ssl session reused'); is(stream('127.0.0.1:' . port(8082))->read(), 'r', 'ssl session reused 2'); +} + my $s = http('', start => 1); sleep 3; @@ -121,3 +130,9 @@ sleep 3; like(http_get('/', socket => $s), qr/200 OK/, 'proxy connect timeout'); ############################################################################### + +sub test_tls13 { + http_get('/') =~ /TLSv1.3/; +} + +############################################################################### # HG changeset patch # User Maxim Dounin # Date 1679531122 -10800 # Thu Mar 23 03:25:22 2023 +0300 # Node ID 8bec6f886e67e4b093112317cfdc4a97590e221a # Parent 5e7b95544014db80e0d8c94ca8b6a9b2f14b75cd Tests: fixed stream_ssl_variables.t.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/stream_ssl_variables.t b/stream_ssl_variables.t --- a/stream_ssl_variables.t +++ b/stream_ssl_variables.t @@ -101,11 +101,17 @@ is(stream('127.0.0.1:' . port(8080))->re like(Net::SSLeay::read($ssl), qr/^\.:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables'); +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') && test_tls13(); + my $ses = Net::SSLeay::get_session($ssl); ($s, $ssl) = get_ssl_socket(port(8081), $ses); like(Net::SSLeay::read($ssl), qr/^r:(\w{64})?:[\w-]+:(TLS|SSL)v(\d|\.)+$/, 'ssl variables - session reused'); +} + SKIP: { skip 'no sni', 3 unless $t->has_module('sni'); @@ -123,6 +129,11 @@ is(Net::SSLeay::ssl_read_all($ssl), '', ############################################################################### +sub test_tls13 { + ($s, $ssl) = get_ssl_socket(port(8081)); + Net::SSLeay::read($ssl) =~ /TLSv1.3/; +} + sub get_ssl_socket { my ($port, $ses, $name) = @_; # HG changeset patch # User Maxim Dounin # Date 1679531124 -10800 # Thu Mar 23 03:25:24 2023 +0300 # Node ID 1813c2bd9429bff39b8108e58fc4c06f7afe38f5 # Parent 8bec6f886e67e4b093112317cfdc4a97590e221a Tests: cleaned up ssl_ocsp.t. Fixed verbose logging, added $SIG{PIPE} handling to avoid hangs if the server closes connection, fixed SKIP message for BoringSSL. diff --git a/ssl_ocsp.t b/ssl_ocsp.t --- a/ssl_ocsp.t +++ b/ssl_ocsp.t @@ -43,7 +43,8 @@ plan(skip_all => 'Net::SSLeay with OpenS my $t = Test::Nginx->new()->has(qw/http http_ssl sni/)->has_daemon('openssl'); -plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL'); +plan(skip_all => 'no OCSP support in BoringSSL') + if $t->has_module('BoringSSL'); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -416,9 +417,11 @@ sub get { my $cipher = Net::SSLeay::get_cipher($ssl); Test::Nginx::log_core('||', "cipher: $cipher"); my $host = $extra{sni} ? $extra{sni} : 'localhost'; + local $SIG{PIPE} = 'IGNORE'; + log_out("GET /serial HTTP/1.0\nHost: $host\n\n"); Net::SSLeay::write($ssl, "GET /serial HTTP/1.0\nHost: $host\n\n"); my $r = Net::SSLeay::read($ssl); - Test::Nginx::log_core($r); + log_in($r); $s->close(); return $r unless wantarray(); return ($s, $ssl); @@ -496,6 +499,7 @@ sub http_daemon { my $resp; while (<$client>) { + Test::Nginx::log_core('||', $_); $headers .= $_; last if (/^\x0d?\x0a?$/); } # HG changeset patch # User Maxim Dounin # Date 1679531124 -10800 # Thu Mar 23 03:25:24 2023 +0300 # Node ID 0661a3c11e0fccb07f46b4c2878bcd8ee0f77db0 # Parent 1813c2bd9429bff39b8108e58fc4c06f7afe38f5 Tests: removed multiple server certificates from ssl_ocsp.t. Multiple server certificates are not needed to test OCSP verification of client certificates (in contrast to OCSP stapling, where server certificates are verified, and different staples should be correctly returned with different server certificates). And using multiple server certificates causes issues when testing with LibreSSL due to broken sigalgs-based server certificate selection in LibreSSL with TLSv1.3. Accordingly, the test is simplified to do not use multiple server certificates. diff --git a/ssl_ocsp.t b/ssl_ocsp.t --- a/ssl_ocsp.t +++ b/ssl_ocsp.t @@ -63,11 +63,6 @@ http { ssl_verify_depth 2; ssl_client_certificate trusted.crt; - ssl_ciphers DEFAULT:ECCdraft; - - ssl_certificate_key ec.key; - ssl_certificate ec.crt; - ssl_certificate_key rsa.key; ssl_certificate rsa.crt; @@ -273,13 +268,8 @@ system("openssl ocsp -index $d/certindex # server cert/key -system("openssl ecparam -genkey -out $d/ec.key -name prime256v1 " - . ">>$d/openssl.out 2>&1") == 0 or die "Can't create EC pem: $!\n"; -system("openssl genrsa -out $d/rsa.key 2048 >>$d/openssl.out 2>&1") == 0 - or die "Can't create RSA pem: $!\n"; - -foreach my $name ('ec', 'rsa') { - system("openssl req -x509 -new -key $d/$name.key " +foreach my $name ('rsa') { + system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 @@ -288,7 +278,7 @@ foreach my $name ('ec', 'rsa') { $t->run_daemon(\&http_daemon, $t, port(8081)); $t->run_daemon(\&http_daemon, $t, port(8082)); -$t->run()->plan(14); +$t->run()->plan(15); $t->waitforsocket("127.0.0.1:" . port(8081)); $t->waitforsocket("127.0.0.1:" . port(8082)); @@ -297,17 +287,17 @@ my $version = get_version(); ############################################################################### -like(get('RSA', 'end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); +like(get('end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf'); # demonstrate that ocsp int request is failed due to missing resolver -like(get('RSA', 'end', sni => 'resolver'), +like(get('end', sni => 'resolver'), qr/400 Bad.*FAILED:certificate status request failed/s, 'ocsp many failed request'); # demonstrate that ocsp int request is actually made by failing ocsp response -like(get('RSA', 'end', port => 8444), +like(get('end', port => 8444), qr/400 Bad.*FAILED:certificate status request failed/s, 'ocsp many failed'); @@ -323,11 +313,11 @@ system("openssl ocsp -index $d/certindex . ">>$d/openssl.out 2>&1") == 0 or die "Can't create OCSP response: $!\n"; -like(get('RSA', 'end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); +like(get('end', port => 8444), qr/200 OK.*SUCCESS/s, 'ocsp many'); # store into ssl_ocsp_cache -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache store'); # revoke @@ -346,23 +336,23 @@ system("openssl ocsp -index $d/certindex . ">>$d/openssl.out 2>&1") == 0 or die "Can't create OCSP response: $!\n"; -like(get('RSA', 'end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); +like(get('end'), qr/400 Bad.*FAILED:certificate revoked/s, 'revoked'); # with different responder where it's still valid -like(get('RSA', 'end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); +like(get('end', port => 8445), qr/200 OK.*SUCCESS/s, 'ocsp responder'); # with different context to responder where it's still valid -like(get('RSA', 'end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); +like(get('end', sni => 'sni'), qr/200 OK.*SUCCESS/s, 'ocsp context'); # with cached ocsp response it's still valid -like(get('RSA', 'end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); +like(get('end', port => 8446), qr/200 OK.*SUCCESS/s, 'cache lookup'); # ocsp end response signed with invalid (root) cert, expect HTTP 400 -like(get('ECDSA', 'ec-end'), +like(get('ec-end'), qr/400 Bad.*FAILED:certificate status request failed/s, 'root ca not trusted'); @@ -374,12 +364,12 @@ system("openssl ocsp -index $d/certindex . ">>$d/openssl.out 2>&1") == 0 or die "Can't create EC OCSP response: $!\n"; -like(get('ECDSA', 'ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); +like(get('ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa'); -my ($s, $ssl) = get('ECDSA', 'ec-end'); +my ($s, $ssl) = get('ec-end'); my $ses = Net::SSLeay::get_session($ssl); -like(get('ECDSA', 'ec-end', ses => $ses), +like(get('ec-end', ses => $ses), qr/200 OK.*SUCCESS:r/s, 'session reused'); # revoke with saved session @@ -401,19 +391,22 @@ system("openssl ocsp -index $d/certindex # reusing session with revoked certificate -like(get('ECDSA', 'ec-end', ses => $ses), +like(get('ec-end', ses => $ses), qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); # regression test for self-signed -like(get('RSA', 'root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); +like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); + +# check for errors + +like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); ############################################################################### sub get { - my ($type, $cert, %extra) = @_; - $type = 'PSS' if $type eq 'RSA' && $version > 0x0303; - my ($s, $ssl) = get_ssl_socket($type, $cert, %extra); + my ($cert, %extra) = @_; + my ($s, $ssl) = get_ssl_socket($cert, %extra); my $cipher = Net::SSLeay::get_cipher($ssl); Test::Nginx::log_core('||', "cipher: $cipher"); my $host = $extra{sni} ? $extra{sni} : 'localhost'; @@ -428,7 +421,7 @@ sub get { } sub get_ssl_socket { - my ($type, $cert, %extra) = @_; + my ($cert, %extra) = @_; my $ses = $extra{ses}; my $sni = $extra{sni}; my $port = $extra{port} || 8443; @@ -450,18 +443,6 @@ sub get_ssl_socket { my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); - if (defined $type) { - my $ssleay = Net::SSLeay::SSLeay(); - if ($ssleay < 0x1000200f || $ssleay == 0x20000000) { - Net::SSLeay::CTX_set_cipher_list($ctx, $type) - or die("Failed to set cipher list"); - } else { - # SSL_CTRL_SET_SIGALGS_LIST - Net::SSLeay::CTX_ctrl($ctx, 98, 0, $type . '+SHA256') - or die("Failed to set sigalgs"); - } - } - Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") or die if $cert; my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); # HG changeset patch # User Maxim Dounin # Date 1679531124 -10800 # Thu Mar 23 03:25:24 2023 +0300 # Node ID 77957615f50b6ae8ac8ef4ffc2c86813c04c16e6 # Parent 0661a3c11e0fccb07f46b4c2878bcd8ee0f77db0 Tests: fixed ssl_ocsp.t with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/ssl_ocsp.t b/ssl_ocsp.t --- a/ssl_ocsp.t +++ b/ssl_ocsp.t @@ -369,9 +369,15 @@ like(get('ec-end'), qr/200 OK.*SUCCESS/s my ($s, $ssl) = get('ec-end'); my $ses = Net::SSLeay::get_session($ssl); +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and $version > 0x303; + like(get('ec-end', ses => $ses), qr/200 OK.*SUCCESS:r/s, 'session reused'); +} + # revoke with saved session system("openssl ca -config $d/ca.conf -revoke $d/ec-end.crt " @@ -391,9 +397,15 @@ system("openssl ocsp -index $d/certindex # reusing session with revoked certificate +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and $version > 0x303; + like(get('ec-end', ses => $ses), qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked'); +} + # regression test for self-signed like(get('root', port => 8447), qr/200 OK.*SUCCESS/s, 'ocsp one'); # HG changeset patch # User Maxim Dounin # Date 1679531124 -10800 # Thu Mar 23 03:25:24 2023 +0300 # Node ID fa4dd181b78754d69ef3f90a414232d347ecd6cb # Parent 77957615f50b6ae8ac8ef4ffc2c86813c04c16e6 Tests: fixed upstream zone ssl tests with LibreSSL and TLSv1.3. LibreSSL does not support session reuse with TLSv1.3. diff --git a/stream_upstream_zone_ssl.t b/stream_upstream_zone_ssl.t --- a/stream_upstream_zone_ssl.t +++ b/stream_upstream_zone_ssl.t @@ -82,6 +82,19 @@ stream { ssl_certificate localhost.crt; ssl_session_cache builtin; } + + server { + listen 127.0.0.1:8085; + proxy_pass 127.0.0.1:8086; + } + + server { + listen 127.0.0.1:8086 ssl; + return $ssl_protocol; + + ssl_certificate_key localhost.key; + ssl_certificate localhost.crt; + } } EOF @@ -112,13 +125,33 @@ is(stream('127.0.0.1:' . port(8080))->re is(stream('127.0.0.1:' . port(8080))->read(), '.', 'ssl 2'); is(stream('127.0.0.1:' . port(8081))->read(), '.', 'ssl session new'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and test_tls13(); + is(stream('127.0.0.1:' . port(8081))->read(), 'r', 'ssl session reused'); is(stream('127.0.0.1:' . port(8081))->read(), 'r', 'ssl session reused 2'); +} + is(stream('127.0.0.1:' . port(8082))->read(), '.', 'backup ssl'); is(stream('127.0.0.1:' . port(8082))->read(), '.', 'backup ssl 2'); is(stream('127.0.0.1:' . port(8083))->read(), '.', 'backup ssl session new'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and test_tls13(); + is(stream('127.0.0.1:' . port(8083))->read(), 'r', 'backup ssl session reused'); +} + ############################################################################### + +sub test_tls13 { + stream('127.0.0.1:' . port(8085))->read() =~ /TLSv1.3/; +} + +############################################################################### diff --git a/upstream_zone_ssl.t b/upstream_zone_ssl.t --- a/upstream_zone_ssl.t +++ b/upstream_zone_ssl.t @@ -56,6 +56,7 @@ http { location / { add_header X-Session $ssl_session_reused; + add_header X-Protocol $ssl_protocol; } } @@ -114,12 +115,32 @@ foreach my $name ('localhost') { like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl'); like(http_get('/ssl'), qr/200 OK.*X-Session: \./s, 'ssl 2'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: \./s, 'ssl session new'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and test_tls13(); + like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused'); like(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused 2'); +} + like(http_get('/backup'), qr/200 OK.*X-Session: \./s, 'backup'); like(http_get('/backup'), qr/200 OK.*X-Session: \./s, 'backup 2'); like(http_get('/backup_reuse'), qr/200 OK.*X-Session: \./s, 'backup new'); + +TODO: { +local $TODO = 'no TLSv1.3 sessions in LibreSSL' + if $t->has_module('LibreSSL') and test_tls13(); + like(http_get('/backup_reuse'), qr/200 OK.*X-Session: r/s, 'backup reused'); +} + ############################################################################### + +sub test_tls13 { + http_get('/ssl') =~ /TLSv1.3/; +} + +############################################################################### -- Maxim Dounin http://mdounin.ru/ From pluknet at nginx.com Thu Mar 23 15:59:41 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 23 Mar 2023 19:59:41 +0400 Subject: [PATCH 01 of 20] Tests: separate SSL session reuse tests In-Reply-To: References: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> <6B831977-21E8-402B-A254-ACA0A59E7387@nginx.com> Message-ID: <735C14A7-4A60-43EA-8AF5-BD44812B1C9A@nginx.com> > On 23 Mar 2023, at 18:10, Maxim Dounin wrote: > > Hello! > > On Wed, Mar 22, 2023 at 12:57:56PM +0400, Sergey Kandaurov wrote: > >>> On 18 Mar 2023, at 18:14, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1679105686 -10800 >>> # Sat Mar 18 05:14:46 2023 +0300 >>> # Node ID 86c394a226d2a7d463da7a1b7e88375c71c0c69b >>> # Parent 3c9aa6c23fc836725b96cf056d218217a5a81603 >>> Tests: separate SSL session reuse tests. >>> >>> Instead of being mixed with generic SSL tests, session reuse variants >>> are now tested in a separate file. >>> >>> In the generic SSL tests only basic session reuse is now tested, >>> notably with session tickets enabled and a shared SSL session cache. >>> This should make it possible to reuse sessions in all cases (except >>> when it's not supported, such as with LibreSSL with TLSv1.3). >>> >>> Note that session reuse with tickets implies that $ssl_session_id >>> is selected by the client and therefore is not available on the >>> initial connection. Relevant test is modified to handle this. >>> >>> Further, BoringSSL does not use legacy session ID with TLSv1.3 even >>> if it is sent by the client. In contrast, OpenSSL always generates >>> an unique legacy session id, so it is available with TLSv1.3 even if >>> session resumption does not work (such as with old Net::SSLeay and >>> IO::Socket::SSL modules). >> >> Note that TLSv1.3 has only ticket based session resumption. >> BoringSSL has a different notion on using legacy session IDs >> in TLSv1.3, see tls13_create_session_with_ticket() in sources: >> >> // Historically, OpenSSL filled in fake session IDs for ticket-based sessions. >> // Envoy's tests depend on this, although perhaps they shouldn't. >> SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id); >> >> Later, SSL_SESSION_get_id() was additionally annotated in ssl.h: >> >> // As a workaround for some broken applications, BoringSSL sometimes synthesizes >> // arbitrary session IDs for non-ID-based sessions. This behavior may be >> // removed in the future. >> >> As for TLSv1.3 server context, BoringSSL doesn't seem to use "session ID" >> besides echoing the client's legacy_session_id field content in the >> legacy_session_id_echo field of ServerHello/HRR during handshake, >> as mandated in RFC 8446, 4.1.3. So it doesn't settle in the session. > > Yes, that's basically what "BoringSSL does not use legacy session > ID with TLSv1.3..." sentence describes. > >>> >>> diff --git a/ssl.t b/ssl.t >>> --- a/ssl.t >>> +++ b/ssl.t >>> @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( >>> plan(skip_all => 'IO::Socket::SSL too old') if $@; >>> >>> my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) >>> - ->has_daemon('openssl')->plan(28); >>> + ->has_daemon('openssl')->plan(21); >>> >>> $t->write_file_expand('nginx.conf', <<'EOF'); >>> >>> @@ -47,7 +47,6 @@ http { >>> >>> ssl_certificate_key localhost.key; >>> ssl_certificate localhost.crt; >>> - ssl_session_tickets off; >>> >>> log_format ssl $ssl_protocol; >>> >>> @@ -59,6 +58,7 @@ http { >>> ssl_certificate_key inner.key; >>> ssl_certificate inner.crt; >>> ssl_session_cache shared:SSL:1m; >>> + ssl_session_tickets on; >>> ssl_verify_client optional_no_ca; >>> >>> keepalive_requests 1000; >>> @@ -100,57 +100,11 @@ http { >>> } >>> >>> server { >>> - listen 127.0.0.1:8081; >>> - server_name localhost; >>> - >>> - # Special case for enabled "ssl" directive. >>> - >>> - ssl on; >> >> Removing tests for the "ssl" legacy directive doesn't feel right >> now and is out of scope of this change. Please put this back. >> Not being able to test it is fragile and can be left silently >> broken, especially with the upcoming quic merge. > > It used to be tested as a side effect of session reuse tests, and > hence it is now gone. You are correct, yet it's useful to have it in some sort. > It is also tested at least in > ssl_reject_handshake.t, so I tend to think it is enough. > It is, though not very useful, at least now. Currently the tested configuration in ssl_reject_handshake.t is supplemented with "listen .. ssl" in adjacent virtual servers. If the "ssl" directive gets broken and e.g. will stop doing its work, the SSL mode will remain enabled across servers of the listening socket, so this may be left unnoticed. To make it more useful, we can remove "listen .. ssl" on virtual servers. # HG changeset patch # User Sergey Kandaurov # Date 1679586784 -14400 # Thu Mar 23 19:53:04 2023 +0400 # Node ID 938f2657257c24ac2ceb8df39536d330bcc03930 # Parent 5abbc8e2d39dfda3913939a6ae293744f5f80933 Tests: removed ssl from virtual servers in ssl_reject_handshake.t. In particular, this allows to test that the "ssl" directive actually works. diff --git a/ssl_reject_handshake.t b/ssl_reject_handshake.t --- a/ssl_reject_handshake.t +++ b/ssl_reject_handshake.t @@ -59,8 +59,8 @@ http { } server { - listen 127.0.0.1:8080 ssl; - listen 127.0.0.1:8081 ssl; + listen 127.0.0.1:8080; + listen 127.0.0.1:8081; server_name virtual; ssl_certificate localhost.crt; @@ -76,12 +76,12 @@ http { } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8082; server_name virtual1; } server { - listen 127.0.0.1:8082 ssl; + listen 127.0.0.1:8082; server_name virtual2; ssl_reject_handshake on; BTW, the old documentation on the "ssl" directive stated that it "Enables the HTTPS protocol for the given *virtual* server", though it appears to be rather opposite (i.e. for the default one). >> On the other hand, I'm fine with finally removing this directive. >> It was made obsolete in version 1.15.0, released on 05 Jun 2018, >> with the "listen .. ssl" upgrade path available back to 0.7.14, >> released on 01 Sep 2008. > > I think we'll consider this in 1.25.x branch. > > [..] >>> server_name localhost; >>> >>> ssl_session_cache shared:SSL:1m; >>> + ssl_session_tickets on; >>> ssl_session_timeout 1; >>> >>> location / { >>> @@ -216,59 +170,34 @@ foreach my $name ('localhost', 'inner') >>> or die "Can't create certificate for $name: $!\n"; >>> } >>> >>> -# suppress deprecation warning >>> - >>> -open OLDERR, ">&", \*STDERR; close STDERR; >>> $t->run(); >>> -open STDERR, ">&", \*OLDERR; >>> >>> ############################################################################### >>> >>> -my $ctx; >>> +# ssl session reuse >>> >>> -SKIP: { >>> -skip 'no TLS 1.3 sessions', 6 if get('/protocol', 8085) =~ /TLSv1.3/ >>> - && ($Net::SSLeay::VERSION < 1.88 || $IO::Socket::SSL::VERSION < 2.061); >>> +my $ctx = get_ssl_context(); >>> >>> -$ctx = get_ssl_context(); >>> +like(get('/', 8085, $ctx), qr/^body \.$/m, 'session'); >>> >>> -like(get('/', 8085, $ctx), qr/^body \.$/m, 'cache shared'); >>> -like(get('/', 8085, $ctx), qr/^body r$/m, 'cache shared reused'); >>> - >>> -$ctx = get_ssl_context(); >>> +TODO: { >>> +local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay' >>> + if $Net::SSLeay::VERSION < 1.88 && test_tls13(); >>> +local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL' >>> + if $IO::Socket::SSL::VERSION < 2.061 && test_tls13(); >> >> Not sure why do you convert this to TODO. >> >> TODO blocks are used for something we control or able to fix. >> SSL libraries are not something like that, so using SKIP there >> is more appropriate and follows other platform-specific tests. >> Besides that, TODOs plagues diagnostic output for no purpose. >> >> See also Test::More documentation: >> >> When do I use SKIP vs. TODO? >> If it's something the user might not be able to do, use SKIP. This >> includes optional modules that aren't installed, running under an OS >> that doesn't have some feature (like "fork()" or symlinks), or maybe >> you need an Internet connection and one isn't available. >> >> If it's something the programmer hasn't done yet, use TODO. This is >> for any code you haven't written yet, or bugs you have yet to fix, >> but want to put tests in your testing script (always a good idea). > > As you can see from the Test::More documentation, the main > distinction is: SKIP is to be used when something cannot be > tested, and TODO is to use when something doesn't work (but we can > test it). > > In this particular case, things can be tested - but tests will > fail due to lack of support in IO::Socket::SSL / Net::SSLeay. > > Immediate benefits of using TODO here is that a) test output > provides good indication of what actually happens with these > module versions (that is, it does not unexpectedly die or > segfault, but session reuse does not work), b) tests are actually > run and it can be easily spotted if they unexpectedly succeed, and > c) it combines nicely with LibreSSL / BoringSSL TODOs (which are > TODO for similar reasons, see the following patches). > > As the code in question is not under our control, it might be good > enough to use SKIP instead of TODO (and the patch series does this > at least once). But I tend to think that at least in case of > LibreSSL it would be much better to use TODOs, so we'll be able to > see when things are finally fixed in LibreSSL - I would expect > this to happen in a foreseeable future. > Ok, I am partially agree with you (item "a" is the most prominent). Let's leave TODOs. -- Sergey Kandaurov From pluknet at nginx.com Thu Mar 23 15:59:47 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 23 Mar 2023 19:59:47 +0400 Subject: [PATCH 03 of 20] Tests: separate SSL session reuse tests in mail In-Reply-To: References: <97b09b6633f69747c0d6.1679148898@vm-bsd.mdounin.ru> Message-ID: <42C1FBC3-044C-4206-8669-1C9C4A4211A8@nginx.com> > On 23 Mar 2023, at 18:15, Maxim Dounin wrote: > > Hello! > > On Wed, Mar 22, 2023 at 01:46:33PM +0400, Sergey Kandaurov wrote: > >>> On 18 Mar 2023, at 18:14, Maxim Dounin wrote: >>> >>> # HG changeset patch >>> # User Maxim Dounin >>> # Date 1679107816 -10800 >>> # Sat Mar 18 05:50:16 2023 +0300 >>> # Node ID 97b09b6633f69747c0d6ef13c76739bdd6b7f3bb >>> # Parent 125fb8461d88a81a62ccb40d0e205a01ecc759f5 >>> Tests: separate SSL session reuse tests in mail. >>> >>> Instead of being mixed with generic SSL tests, session reuse variants >>> are now tested in a separate file. >>> >>> [..] >>> sub get_ssl_socket { >>> - my ($port, $ses, $alpn) = @_; >>> + my ($port, $ses) = @_; >>> >>> my $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); >>> my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); >>> Net::SSLeay::set_session($ssl, $ses) if defined $ses; >>> - Net::SSLeay::set_alpn_protos($ssl, $alpn) if defined $alpn; >>> Net::SSLeay::set_fd($ssl, fileno($s)); >>> Net::SSLeay::connect($ssl) == 1 or return; >> >> Since this won't fail anymore on ALPN checks, >> you can convert this back to die. > > Strictly speaking, it shouldn't die regardless of whether > connect() can legitimately fail during tests or not. If connect() > fails (for example, due to a bug in some of the session reuse > variants), it still should run other tests. > In general I agree with you. > Fixing this (and other issues, such as missing timeout and SIGPIPE > handling) is out of the scope of this patch series though. Yes, let's postpone this. -- Sergey Kandaurov From pluknet at nginx.com Thu Mar 23 16:01:09 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 23 Mar 2023 20:01:09 +0400 Subject: [PATCH 00 of 20] tests suite fixes for TLSv1.3 In-Reply-To: References: Message-ID: > On 23 Mar 2023, at 18:18, Maxim Dounin wrote: > > Hello! > > On Wed, Mar 22, 2023 at 03:43:12PM +0400, Sergey Kandaurov wrote: > >>> On 18 Mar 2023, at 18:14, Maxim Dounin wrote: >>> >>> Hello! >>> >>> Here are patch series for the test suite to address test failures >>> observed with TLSv1.3 enabled with BoringSSL and LibreSSL. >>> >>> Short summary of the issues seen: >>> >>> - BoringSSL with TLSv1.3 does not support session reuse via server-side >>> session cache, only with tickets. >>> >>> - BoringSSL with TLSv1.3 does not provide $ssl_session_id. >>> >>> - LibreSSL with TLSv1.3 does not support session reuse. >>> >>> - LibreSSL with TLSv1.3 fails to negotiate certificates based on >>> signature algorithms supported by the client, and fails with >>> "missing rsa certificate" and "unknown pkey type" errors. >>> >>> - LibreSSL with TLSv1.3 does not send CA lists to the client. >>> >> >> Missing peaces that allow me to run with LibreSSL: >> >> # HG changeset patch >> # User Sergey Kandaurov >> # Date 1679485246 -14400 >> # Wed Mar 22 15:40:46 2023 +0400 >> # Node ID dfe434f295d3da7e3b67bbbafeab245bb591f397 >> # Parent 826e00e7c037d617781239963e8b868b6b0de225 >> Tests: fixed upstream zone tests with LibreSSL and TLSv1.3. >> >> LibreSSL does not support session reuse with TLSv1.3. >> >> diff --git a/stream_upstream_zone_ssl.t b/stream_upstream_zone_ssl.t >> --- a/stream_upstream_zone_ssl.t >> +++ b/stream_upstream_zone_ssl.t > > Thanks. I've happen to compile nginx without upstream zone > modules as a leftover from some previous tests, and missed these. > Added a similar change with TODOs. > > Full series with all the fixes: Looks good, thanks for your work. -- Sergey Kandaurov From mdounin at mdounin.ru Thu Mar 23 16:52:38 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 19:52:38 +0300 Subject: [PATCH 00 of 20] tests suite fixes for TLSv1.3 In-Reply-To: References: Message-ID: Hello! On Thu, Mar 23, 2023 at 08:01:09PM +0400, Sergey Kandaurov wrote: > > On 23 Mar 2023, at 18:18, Maxim Dounin wrote: > > > > Hello! > > > > On Wed, Mar 22, 2023 at 03:43:12PM +0400, Sergey Kandaurov wrote: > > > >>> On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > >>> > >>> Hello! > >>> > >>> Here are patch series for the test suite to address test failures > >>> observed with TLSv1.3 enabled with BoringSSL and LibreSSL. > >>> > >>> Short summary of the issues seen: > >>> > >>> - BoringSSL with TLSv1.3 does not support session reuse via server-side > >>> session cache, only with tickets. > >>> > >>> - BoringSSL with TLSv1.3 does not provide $ssl_session_id. > >>> > >>> - LibreSSL with TLSv1.3 does not support session reuse. > >>> > >>> - LibreSSL with TLSv1.3 fails to negotiate certificates based on > >>> signature algorithms supported by the client, and fails with > >>> "missing rsa certificate" and "unknown pkey type" errors. > >>> > >>> - LibreSSL with TLSv1.3 does not send CA lists to the client. > >>> > >> > >> Missing peaces that allow me to run with LibreSSL: > >> > >> # HG changeset patch > >> # User Sergey Kandaurov > >> # Date 1679485246 -14400 > >> # Wed Mar 22 15:40:46 2023 +0400 > >> # Node ID dfe434f295d3da7e3b67bbbafeab245bb591f397 > >> # Parent 826e00e7c037d617781239963e8b868b6b0de225 > >> Tests: fixed upstream zone tests with LibreSSL and TLSv1.3. > >> > >> LibreSSL does not support session reuse with TLSv1.3. > >> > >> diff --git a/stream_upstream_zone_ssl.t b/stream_upstream_zone_ssl.t > >> --- a/stream_upstream_zone_ssl.t > >> +++ b/stream_upstream_zone_ssl.t > > > > Thanks. I've happen to compile nginx without upstream zone > > modules as a leftover from some previous tests, and missed these. > > Added a similar change with TODOs. > > > > Full series with all the fixes: > > Looks good, thanks for your work. Pushed to http://mdounin.ru/hg/nginx-tests, thanks for the review. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 17:24:18 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 20:24:18 +0300 Subject: [PATCH 01 of 20] Tests: separate SSL session reuse tests In-Reply-To: <735C14A7-4A60-43EA-8AF5-BD44812B1C9A@nginx.com> References: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> <6B831977-21E8-402B-A254-ACA0A59E7387@nginx.com> <735C14A7-4A60-43EA-8AF5-BD44812B1C9A@nginx.com> Message-ID: Hello! On Thu, Mar 23, 2023 at 07:59:41PM +0400, Sergey Kandaurov wrote: > > On 23 Mar 2023, at 18:10, Maxim Dounin wrote: > > > > On Wed, Mar 22, 2023 at 12:57:56PM +0400, Sergey Kandaurov wrote: > > > >>> On 18 Mar 2023, at 18:14, Maxim Dounin wrote: > >>> > >>> # HG changeset patch > >>> # User Maxim Dounin > >>> # Date 1679105686 -10800 > >>> # Sat Mar 18 05:14:46 2023 +0300 > >>> # Node ID 86c394a226d2a7d463da7a1b7e88375c71c0c69b > >>> # Parent 3c9aa6c23fc836725b96cf056d218217a5a81603 > >>> Tests: separate SSL session reuse tests. > >>> > >>> Instead of being mixed with generic SSL tests, session reuse variants > >>> are now tested in a separate file. > >>> > >>> In the generic SSL tests only basic session reuse is now tested, > >>> notably with session tickets enabled and a shared SSL session cache. > >>> This should make it possible to reuse sessions in all cases (except > >>> when it's not supported, such as with LibreSSL with TLSv1.3). > >>> > >>> Note that session reuse with tickets implies that $ssl_session_id > >>> is selected by the client and therefore is not available on the > >>> initial connection. Relevant test is modified to handle this. > >>> > >>> Further, BoringSSL does not use legacy session ID with TLSv1.3 even > >>> if it is sent by the client. In contrast, OpenSSL always generates > >>> an unique legacy session id, so it is available with TLSv1.3 even if > >>> session resumption does not work (such as with old Net::SSLeay and > >>> IO::Socket::SSL modules). > >> > >> Note that TLSv1.3 has only ticket based session resumption. > >> BoringSSL has a different notion on using legacy session IDs > >> in TLSv1.3, see tls13_create_session_with_ticket() in sources: > >> > >> // Historically, OpenSSL filled in fake session IDs for ticket-based sessions. > >> // Envoy's tests depend on this, although perhaps they shouldn't. > >> SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id); > >> > >> Later, SSL_SESSION_get_id() was additionally annotated in ssl.h: > >> > >> // As a workaround for some broken applications, BoringSSL sometimes synthesizes > >> // arbitrary session IDs for non-ID-based sessions. This behavior may be > >> // removed in the future. > >> > >> As for TLSv1.3 server context, BoringSSL doesn't seem to use "session ID" > >> besides echoing the client's legacy_session_id field content in the > >> legacy_session_id_echo field of ServerHello/HRR during handshake, > >> as mandated in RFC 8446, 4.1.3. So it doesn't settle in the session. > > > > Yes, that's basically what "BoringSSL does not use legacy session > > ID with TLSv1.3..." sentence describes. > > > >>> > >>> diff --git a/ssl.t b/ssl.t > >>> --- a/ssl.t > >>> +++ b/ssl.t > >>> @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( > >>> plan(skip_all => 'IO::Socket::SSL too old') if $@; > >>> > >>> my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) > >>> - ->has_daemon('openssl')->plan(28); > >>> + ->has_daemon('openssl')->plan(21); > >>> > >>> $t->write_file_expand('nginx.conf', <<'EOF'); > >>> > >>> @@ -47,7 +47,6 @@ http { > >>> > >>> ssl_certificate_key localhost.key; > >>> ssl_certificate localhost.crt; > >>> - ssl_session_tickets off; > >>> > >>> log_format ssl $ssl_protocol; > >>> > >>> @@ -59,6 +58,7 @@ http { > >>> ssl_certificate_key inner.key; > >>> ssl_certificate inner.crt; > >>> ssl_session_cache shared:SSL:1m; > >>> + ssl_session_tickets on; > >>> ssl_verify_client optional_no_ca; > >>> > >>> keepalive_requests 1000; > >>> @@ -100,57 +100,11 @@ http { > >>> } > >>> > >>> server { > >>> - listen 127.0.0.1:8081; > >>> - server_name localhost; > >>> - > >>> - # Special case for enabled "ssl" directive. > >>> - > >>> - ssl on; > >> > >> Removing tests for the "ssl" legacy directive doesn't feel right > >> now and is out of scope of this change. Please put this back. > >> Not being able to test it is fragile and can be left silently > >> broken, especially with the upcoming quic merge. > > > > It used to be tested as a side effect of session reuse tests, and > > hence it is now gone. > > You are correct, yet it's useful to have it in some sort. > > > It is also tested at least in > > ssl_reject_handshake.t, so I tend to think it is enough. > > > > It is, though not very useful, at least now. > Currently the tested configuration in ssl_reject_handshake.t is > supplemented with "listen .. ssl" in adjacent virtual servers. > If the "ssl" directive gets broken and e.g. will stop doing its work, > the SSL mode will remain enabled across servers of the listening socket, > so this may be left unnoticed. > To make it more useful, we can remove "listen .. ssl" on virtual servers. > > # HG changeset patch > # User Sergey Kandaurov > # Date 1679586784 -14400 > # Thu Mar 23 19:53:04 2023 +0400 > # Node ID 938f2657257c24ac2ceb8df39536d330bcc03930 > # Parent 5abbc8e2d39dfda3913939a6ae293744f5f80933 > Tests: removed ssl from virtual servers in ssl_reject_handshake.t. > > In particular, this allows to test that the "ssl" directive actually works. > > diff --git a/ssl_reject_handshake.t b/ssl_reject_handshake.t > --- a/ssl_reject_handshake.t > +++ b/ssl_reject_handshake.t > @@ -59,8 +59,8 @@ http { > } > > server { > - listen 127.0.0.1:8080 ssl; > - listen 127.0.0.1:8081 ssl; > + listen 127.0.0.1:8080; > + listen 127.0.0.1:8081; > server_name virtual; > > ssl_certificate localhost.crt; > @@ -76,12 +76,12 @@ http { > } > > server { > - listen 127.0.0.1:8082 ssl; > + listen 127.0.0.1:8082; > server_name virtual1; > } > > server { > - listen 127.0.0.1:8082 ssl; > + listen 127.0.0.1:8082; > server_name virtual2; > > ssl_reject_handshake on; Looks good (except the "virtual servers" term, see below). > BTW, the old documentation on the "ssl" directive stated that it > "Enables the HTTPS protocol for the given *virtual* server", though > it appears to be rather opposite (i.e. for the default one). The term "virtual server" includes everything virtual being run on a given physical server, including IP-based, port-based, and name-based virtual servers. The default server (for a listening socket) is still a virtual server. See, for example, description of the "server" directive: http://nginx.org/en/docs/http/ngx_http_core_module.html#server So, basically, the description of the "ssl" directive you are referring to is perfectly correct. Except the fact that it might additionally state that the directive only works if the server is the default virtual server for the listening socket in question, and does nothing in non-default virtual servers. So a better commit log for the above patch should say that it removes the "ssl" option from listening sockets in non-default [virtual] servers. Something like this should be good enough: : Tests: improved "ssl" directive test in ssl_reject_handshake.t. : : The "ssl" option was removed from listening sockets in non-default : servers. In particular, this allows to test that the "ssl" directive : actually works. [...] -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 17:44:42 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 23 Mar 2023 20:44:42 +0300 Subject: Configurable sleep period for connections In-Reply-To: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> References: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> Message-ID: Hello! On Thu, Mar 23, 2023 at 09:26:48AM +0100, Roy Teeuwen wrote: > We are using NGINX as a proxy / caching layer for a backend > application. Our backend has a relatively slow response time, > ranging between the 100 to 300ms. We want the NGINX proxy to be > as speedy as possible, to do this we have implemented the > following logic: > > - Cache all responses for 5 mins (based on cache control > headers) > - Use stale cache for error's on the backend > - Do a background update for stale cache > > The last part has an issue, namely if a first request reaches > nginx, it will trigger a background request, but other requests > for the same resource will be locked until this background > request is finished instead of still returning the stale cache > that is available. This is caused by the fact that there is a > keepalive on the connection, which locks all subsequent requests > until the background request is finished. Could you please clarify what you are trying to describe? Keepalive on the connection might delay handling of subsequent requests on the same connection (and not other requests to the same resource). Other requests to the same resource might be delayed by the proxy_cache_lock (https://nginx.org/r/proxy_cache_lock), but it is not something in your description, and it only works for new cache elements and has no effect when there is a stale cache item. > The issue that we are facing in this situation is that the > locking is very long, namely 500ms hardcoded. I think it is > caused by this: > https://github.com/nginx/nginx/blob/master/src/core/ngx_connection.c#L703 This looks completely unrelated. A 500ms delay can be seen with proxy_cache_lock as previously mentioned, see here: http://hg.nginx.org/nginx/file/tip/src/http/ngx_http_file_cache.c#l455 But again, it is not expected to appear in the use case you describe. > This means that our relatively slow backend of 100-200ms > actually gets worse than better. > > > Is it an option to make this 500ms a configurable setting > instead of 500ms? Are there any downsides to making this 500ms > lower? I'd be willing to see if we can contribute this. > > > Another option that I'd tried is to set the keepalive to 0, so > that every request is a new connection. In small amounts of > requests this actually seemed to solve the issue, but the moment > that we went to a real life situation, this degraded the > performance massively, so we had to revert this First of all, it might be a good idea to better understand what is the issue you are seeing. Also make sure that you have "proxy_cache_use_stale updating" enabled (https://nginx.org/r/proxy_cache_use_stale). It is designed exactly for the use case you describe, and works quite well in most use cases. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Thu Mar 23 23:56:56 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 24 Mar 2023 02:56:56 +0300 Subject: [PATCH] Mail: fixed handling of blocked client read events in proxy In-Reply-To: <20230322163852.yoqkx7aimwkvzrnf@N00W24XTQX> References: <20230322163852.yoqkx7aimwkvzrnf@N00W24XTQX> Message-ID: Hello! On Wed, Mar 22, 2023 at 08:38:52PM +0400, Roman Arutyunyan wrote: > On Sat, Mar 11, 2023 at 02:24:49PM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1678533841 -10800 > > # Sat Mar 11 14:24:01 2023 +0300 > > # Node ID b97f2b983d1564d29280d03828503edca21a79ee > > # Parent 8771d35d55d0a2b1cefaab04401d6f837f5a05a2 > > Mail: fixed handling of blocked client read events in proxy. > > > > When establishing a connection to the backend, nginx blocks reading > > from the client with ngx_mail_proxy_block_read(). Previously, such > > events were lost, and in some cases this resulted in connection hangs. > > > > Notably, this affected mail_imap_ssl.t on Windows, since the test > > closes connections after requesting authentication, but without > > waiting for any responses (so the connection close events might be > > lost). > > The following patch alowed me to trigger the issue on my laptop. > It slows down the auth http backend and switches event processing to select. > > diff --git a/mail_imap_ssl.t b/mail_imap_ssl.t > --- a/mail_imap_ssl.t > +++ b/mail_imap_ssl.t > @@ -42,6 +42,7 @@ my $t = Test::Nginx->new()->has(qw/mail > daemon off; > > events { > + use select; > } > > mail { > @@ -104,6 +105,7 @@ http { > server { > listen 127.0.0.1:8080; > server_name localhost; > + limit_rate 100; > > location = /mail/auth { > access_log auth.log test; > > > Fix is to post an event to read from the client after connecting to > > the backend if there were blocked events. > > > > diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c > > --- a/src/mail/ngx_mail_proxy_module.c > > +++ b/src/mail/ngx_mail_proxy_module.c > > @@ -327,7 +327,9 @@ ngx_mail_proxy_pop3_handler(ngx_event_t > > c->log->action = NULL; > > ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); > > > > - if (s->buffer->pos < s->buffer->last) { > > + if (s->buffer->pos < s->buffer->last > > + || s->connection->read->ready) > > + { > > ngx_post_event(c->write, &ngx_posted_events); > > } > > > > @@ -486,7 +488,9 @@ ngx_mail_proxy_imap_handler(ngx_event_t > > c->log->action = NULL; > > ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); > > > > - if (s->buffer->pos < s->buffer->last) { > > + if (s->buffer->pos < s->buffer->last > > + || s->connection->read->ready) > > + { > > ngx_post_event(c->write, &ngx_posted_events); > > } > > > > @@ -821,7 +825,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t > > c->log->action = NULL; > > ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); > > > > - if (s->buffer->pos < s->buffer->last) { > > + if (s->buffer->pos < s->buffer->last > > + || s->connection->read->ready) > > + { > > ngx_post_event(c->write, &ngx_posted_events); > > } > > Looks ok Thanks for the review, pushed to http://mdounin.ru/hg/nginx. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Fri Mar 24 00:15:18 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 24 Mar 2023 03:15:18 +0300 Subject: [PATCH] SSL: enabled TLSv1.3 by default In-Reply-To: <20230321131345.cd3hmxihqls46ovw@N00W24XTQX> References: <3496c963b43636104fa7.1679318394@vm-bsd.mdounin.ru> <20230321131345.cd3hmxihqls46ovw@N00W24XTQX> Message-ID: Hello! On Tue, Mar 21, 2023 at 05:13:45PM +0400, Roman Arutyunyan wrote: > Hi, > > On Mon, Mar 20, 2023 at 04:19:54PM +0300, Maxim Dounin wrote: > > # HG changeset patch > > # User Maxim Dounin > > # Date 1678891161 -10800 > > # Wed Mar 15 17:39:21 2023 +0300 > > # Node ID 3496c963b43636104fa794e84969eb077abec6b0 > > # Parent b97f2b983d1564d29280d03828503edca21a79ee > > SSL: enabled TLSv1.3 by default. > > > > diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c > > --- a/src/http/modules/ngx_http_grpc_module.c > > +++ b/src/http/modules/ngx_http_grpc_module.c > > @@ -4473,8 +4473,9 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t > > prev->upstream.ssl_session_reuse, 1); > > > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, > > "DEFAULT"); > > diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c > > --- a/src/http/modules/ngx_http_proxy_module.c > > +++ b/src/http/modules/ngx_http_proxy_module.c > > @@ -3734,8 +3734,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t > > prev->upstream.ssl_session_reuse, 1); > > > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, > > "DEFAULT"); > > diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c > > --- a/src/http/modules/ngx_http_ssl_module.c > > +++ b/src/http/modules/ngx_http_ssl_module.c > > @@ -632,8 +632,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * > > ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); > > > > ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, > > NGX_SSL_BUFSIZE); > > diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c > > --- a/src/http/modules/ngx_http_uwsgi_module.c > > +++ b/src/http/modules/ngx_http_uwsgi_module.c > > @@ -1875,8 +1875,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t > > prev->upstream.ssl_session_reuse, 1); > > > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, > > "DEFAULT"); > > diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c > > --- a/src/mail/ngx_mail_ssl_module.c > > +++ b/src/mail/ngx_mail_ssl_module.c > > @@ -360,8 +360,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, > > prev->prefer_server_ciphers, 0); > > > > ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); > > ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); > > diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c > > --- a/src/stream/ngx_stream_proxy_module.c > > +++ b/src/stream/ngx_stream_proxy_module.c > > @@ -2163,8 +2163,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf > > prev->ssl_session_reuse, 1); > > > > ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); > > > > diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c > > --- a/src/stream/ngx_stream_ssl_module.c > > +++ b/src/stream/ngx_stream_ssl_module.c > > @@ -703,8 +703,9 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf > > prev->prefer_server_ciphers, 0); > > > > ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, > > - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 > > - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); > > + (NGX_CONF_BITMASK_SET > > + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 > > + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); > > > > ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); > > ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > https://mailman.nginx.org/mailman/listinfo/nginx-devel > > Looks fine Thanks for the review, pushed to http://mdounin.ru/hg/nginx. -- Maxim Dounin http://mdounin.ru/ From roy at teeuwen.be Fri Mar 24 08:24:25 2023 From: roy at teeuwen.be (Roy Teeuwen) Date: Fri, 24 Mar 2023 09:24:25 +0100 Subject: Configurable sleep period for connections In-Reply-To: References: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> Message-ID: Hey Maxim, You are absolutely right, I totally forgot about the cache_lock. I have listed our settings below. The reason we are using the cache_lock is to save the backend application to not get 100's of requests when a stale item is invalid. Even if we have use_stale updating, we notice that only the first request will use the stale item, the following requests will do a new request even though there is already a background request going on to refresh the stale item. (This does not happen if we set keepalive to 0, where new connections are being used, but has the performance degradation as mentioned.) This was the reasoning for the cache_lock, but that gives the issue about the 500ms lock, while the item might already be refreshed after 100ms. So is there an option to make the 500ms in the ngx_http_file_cache.c configurable? Are there any downsides to that? Or is there a better alternative Our config: add_header X-Cache-Status $upstream_cache_status always; # tag::proxy_cache[] proxy_cache content-proxy-7b49d8c897-62gk9; proxy_cache_background_update on; proxy_cache_bypass $arg_nocache; proxy_cache_lock on; proxy_cache_use_stale updating error timeout invalid_header http_500 http_502 http_503 http_504; proxy_cache_valid 404 5m; # end::proxy_cache[] # tag::upstream[] proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; # end::upstream[] # This header will be used to indicate a request coming from nginx # With this header set we can return 503 only to nginx when a server is in maintenance mode proxy_set_header X-Content-Cache $hostname; proxy_ssl_server_name on; proxy_read_timeout 10; proxy_connect_timeout 10; proxy_send_timeout 10; Greets, Roy > On 23 Mar 2023, at 18:44, Maxim Dounin wrote: > > Hello! > > On Thu, Mar 23, 2023 at 09:26:48AM +0100, Roy Teeuwen wrote: > >> We are using NGINX as a proxy / caching layer for a backend >> application. Our backend has a relatively slow response time, >> ranging between the 100 to 300ms. We want the NGINX proxy to be >> as speedy as possible, to do this we have implemented the >> following logic: >> >> - Cache all responses for 5 mins (based on cache control >> headers) >> - Use stale cache for error's on the backend >> - Do a background update for stale cache >> >> The last part has an issue, namely if a first request reaches >> nginx, it will trigger a background request, but other requests >> for the same resource will be locked until this background >> request is finished instead of still returning the stale cache >> that is available. This is caused by the fact that there is a >> keepalive on the connection, which locks all subsequent requests >> until the background request is finished. > > Could you please clarify what you are trying to describe? > > Keepalive on the connection might delay handling of subsequent > requests on the same connection (and not other requests to the > same resource). > > Other requests to the same resource might be delayed by the > proxy_cache_lock (https://nginx.org/r/proxy_cache_lock), but it is > not something in your description, and it only works for new cache > elements and has no effect when there is a stale cache item. > >> The issue that we are facing in this situation is that the >> locking is very long, namely 500ms hardcoded. I think it is >> caused by this: >> https://github.com/nginx/nginx/blob/master/src/core/ngx_connection.c#L703 > > This looks completely unrelated. A 500ms delay can be seen with > proxy_cache_lock as previously mentioned, see here: > > http://hg.nginx.org/nginx/file/tip/src/http/ngx_http_file_cache.c#l455 > > But again, it is not expected to appear in the use case you > describe. > >> This means that our relatively slow backend of 100-200ms >> actually gets worse than better. >> >> >> Is it an option to make this 500ms a configurable setting >> instead of 500ms? Are there any downsides to making this 500ms >> lower? I'd be willing to see if we can contribute this. >> >> >> Another option that I'd tried is to set the keepalive to 0, so >> that every request is a new connection. In small amounts of >> requests this actually seemed to solve the issue, but the moment >> that we went to a real life situation, this degraded the >> performance massively, so we had to revert this > > First of all, it might be a good idea to better understand what > is the issue you are seeing. > > Also make sure that you have "proxy_cache_use_stale updating" > enabled (https://nginx.org/r/proxy_cache_use_stale). It is > designed exactly for the use case you describe, and works quite > well in most use cases. > > -- > Maxim Dounin > http://mdounin.ru/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Fri Mar 24 10:18:01 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 24 Mar 2023 13:18:01 +0300 Subject: Configurable sleep period for connections In-Reply-To: References: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> Message-ID: Hello! On Fri, Mar 24, 2023 at 09:24:25AM +0100, Roy Teeuwen wrote: > You are absolutely right, I totally forgot about the cache_lock. > I have listed our settings below. > > The reason we are using the cache_lock is to save the backend > application to not get 100's of requests when a stale item is > invalid. Even if we have use_stale updating, we notice that only > the first request will use the stale item, the following > requests will do a new request even though there is already a > background request going on to refresh the stale item. (This > does not happen if we set keepalive to 0, where new connections > are being used, but has the performance degradation as > mentioned.) This was the reasoning for the cache_lock, but that > gives the issue about the 500ms lock, while the item might > already be refreshed after 100ms. To re-iterate: proxy_cache_lock is not expected to affect requests if there is an existing cache item (and keepalive shouldn't affect proxy_cache_lock in any way; not to mention that the "keepalive" directive, which configures keepalive connections cache to upstream servers, does not accept the "0" value). You may want to dig further into what actually happens in your configuration. I would recommend to start with doing a debug log which shows the described behaviour, and then following the code to find out why the cache lock kicks in when it shouldn't. -- Maxim Dounin http://mdounin.ru/ From pluknet at nginx.com Fri Mar 24 12:38:23 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Fri, 24 Mar 2023 16:38:23 +0400 Subject: [PATCH 01 of 20] Tests: separate SSL session reuse tests In-Reply-To: References: <86c394a226d2a7d463da.1679148896@vm-bsd.mdounin.ru> <6B831977-21E8-402B-A254-ACA0A59E7387@nginx.com> <735C14A7-4A60-43EA-8AF5-BD44812B1C9A@nginx.com> Message-ID: > On 23 Mar 2023, at 21:24, Maxim Dounin wrote: > > Hello! > > On Thu, Mar 23, 2023 at 07:59:41PM +0400, Sergey Kandaurov wrote: > >>> On 23 Mar 2023, at 18:10, Maxim Dounin wrote: >>> >>> On Wed, Mar 22, 2023 at 12:57:56PM +0400, Sergey Kandaurov wrote: >>> >>>>> On 18 Mar 2023, at 18:14, Maxim Dounin wrote: >>>>> >>>>> # HG changeset patch >>>>> # User Maxim Dounin >>>>> # Date 1679105686 -10800 >>>>> # Sat Mar 18 05:14:46 2023 +0300 >>>>> # Node ID 86c394a226d2a7d463da7a1b7e88375c71c0c69b >>>>> # Parent 3c9aa6c23fc836725b96cf056d218217a5a81603 >>>>> Tests: separate SSL session reuse tests. >>>>> >>>>> Instead of being mixed with generic SSL tests, session reuse variants >>>>> are now tested in a separate file. >>>>> >>>>> In the generic SSL tests only basic session reuse is now tested, >>>>> notably with session tickets enabled and a shared SSL session cache. >>>>> This should make it possible to reuse sessions in all cases (except >>>>> when it's not supported, such as with LibreSSL with TLSv1.3). >>>>> >>>>> Note that session reuse with tickets implies that $ssl_session_id >>>>> is selected by the client and therefore is not available on the >>>>> initial connection. Relevant test is modified to handle this. >>>>> >>>>> Further, BoringSSL does not use legacy session ID with TLSv1.3 even >>>>> if it is sent by the client. In contrast, OpenSSL always generates >>>>> an unique legacy session id, so it is available with TLSv1.3 even if >>>>> session resumption does not work (such as with old Net::SSLeay and >>>>> IO::Socket::SSL modules). >>>> >>>> Note that TLSv1.3 has only ticket based session resumption. >>>> BoringSSL has a different notion on using legacy session IDs >>>> in TLSv1.3, see tls13_create_session_with_ticket() in sources: >>>> >>>> // Historically, OpenSSL filled in fake session IDs for ticket-based sessions. >>>> // Envoy's tests depend on this, although perhaps they shouldn't. >>>> SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id); >>>> >>>> Later, SSL_SESSION_get_id() was additionally annotated in ssl.h: >>>> >>>> // As a workaround for some broken applications, BoringSSL sometimes synthesizes >>>> // arbitrary session IDs for non-ID-based sessions. This behavior may be >>>> // removed in the future. >>>> >>>> As for TLSv1.3 server context, BoringSSL doesn't seem to use "session ID" >>>> besides echoing the client's legacy_session_id field content in the >>>> legacy_session_id_echo field of ServerHello/HRR during handshake, >>>> as mandated in RFC 8446, 4.1.3. So it doesn't settle in the session. >>> >>> Yes, that's basically what "BoringSSL does not use legacy session >>> ID with TLSv1.3..." sentence describes. >>> >>>>> >>>>> diff --git a/ssl.t b/ssl.t >>>>> --- a/ssl.t >>>>> +++ b/ssl.t >>>>> @@ -31,7 +31,7 @@ eval { IO::Socket::SSL::SSL_VERIFY_NONE( >>>>> plan(skip_all => 'IO::Socket::SSL too old') if $@; >>>>> >>>>> my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy/) >>>>> - ->has_daemon('openssl')->plan(28); >>>>> + ->has_daemon('openssl')->plan(21); >>>>> >>>>> $t->write_file_expand('nginx.conf', <<'EOF'); >>>>> >>>>> @@ -47,7 +47,6 @@ http { >>>>> >>>>> ssl_certificate_key localhost.key; >>>>> ssl_certificate localhost.crt; >>>>> - ssl_session_tickets off; >>>>> >>>>> log_format ssl $ssl_protocol; >>>>> >>>>> @@ -59,6 +58,7 @@ http { >>>>> ssl_certificate_key inner.key; >>>>> ssl_certificate inner.crt; >>>>> ssl_session_cache shared:SSL:1m; >>>>> + ssl_session_tickets on; >>>>> ssl_verify_client optional_no_ca; >>>>> >>>>> keepalive_requests 1000; >>>>> @@ -100,57 +100,11 @@ http { >>>>> } >>>>> >>>>> server { >>>>> - listen 127.0.0.1:8081; >>>>> - server_name localhost; >>>>> - >>>>> - # Special case for enabled "ssl" directive. >>>>> - >>>>> - ssl on; >>>> >>>> Removing tests for the "ssl" legacy directive doesn't feel right >>>> now and is out of scope of this change. Please put this back. >>>> Not being able to test it is fragile and can be left silently >>>> broken, especially with the upcoming quic merge. >>> >>> It used to be tested as a side effect of session reuse tests, and >>> hence it is now gone. >> >> You are correct, yet it's useful to have it in some sort. >> >>> It is also tested at least in >>> ssl_reject_handshake.t, so I tend to think it is enough. >>> >> >> It is, though not very useful, at least now. >> Currently the tested configuration in ssl_reject_handshake.t is >> supplemented with "listen .. ssl" in adjacent virtual servers. >> If the "ssl" directive gets broken and e.g. will stop doing its work, >> the SSL mode will remain enabled across servers of the listening socket, >> so this may be left unnoticed. >> To make it more useful, we can remove "listen .. ssl" on virtual servers. >> >> # HG changeset patch >> # User Sergey Kandaurov >> # Date 1679586784 -14400 >> # Thu Mar 23 19:53:04 2023 +0400 >> # Node ID 938f2657257c24ac2ceb8df39536d330bcc03930 >> # Parent 5abbc8e2d39dfda3913939a6ae293744f5f80933 >> Tests: removed ssl from virtual servers in ssl_reject_handshake.t. >> >> In particular, this allows to test that the "ssl" directive actually works. >> >> diff --git a/ssl_reject_handshake.t b/ssl_reject_handshake.t >> --- a/ssl_reject_handshake.t >> +++ b/ssl_reject_handshake.t >> @@ -59,8 +59,8 @@ http { >> } >> >> server { >> - listen 127.0.0.1:8080 ssl; >> - listen 127.0.0.1:8081 ssl; >> + listen 127.0.0.1:8080; >> + listen 127.0.0.1:8081; >> server_name virtual; >> >> ssl_certificate localhost.crt; >> @@ -76,12 +76,12 @@ http { >> } >> >> server { >> - listen 127.0.0.1:8082 ssl; >> + listen 127.0.0.1:8082; >> server_name virtual1; >> } >> >> server { >> - listen 127.0.0.1:8082 ssl; >> + listen 127.0.0.1:8082; >> server_name virtual2; >> >> ssl_reject_handshake on; > > Looks good (except the "virtual servers" term, see below). > >> BTW, the old documentation on the "ssl" directive stated that it >> "Enables the HTTPS protocol for the given *virtual* server", though >> it appears to be rather opposite (i.e. for the default one). > > The term "virtual server" includes everything virtual being run on > a given physical server, including IP-based, port-based, and > name-based virtual servers. The default server (for a listening > socket) is still a virtual server. See, for example, description > of the "server" directive: > > http://nginx.org/en/docs/http/ngx_http_core_module.html#server > > So, basically, the description of the "ssl" directive you are > referring to is perfectly correct. Except the fact that it might > additionally state that the directive only works if the server is > the default virtual server for the listening socket in question, > and does nothing in non-default virtual servers. > > So a better commit log for the above patch should say that it > removes the "ssl" option from listening sockets in non-default > [virtual] servers. Something like this should be good enough: > > : Tests: improved "ssl" directive test in ssl_reject_handshake.t. > : > : The "ssl" option was removed from listening sockets in non-default > : servers. In particular, this allows to test that the "ssl" directive > : actually works. > Thanks, applied and pushed, together with the whole series. -- Sergey Kandaurov From pluknet at nginx.com Fri Mar 24 13:18:09 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Fri, 24 Mar 2023 13:18:09 +0000 Subject: [nginx] Mail: fixed handling of blocked client read events in proxy. Message-ID: details: https://hg.nginx.org/nginx/rev/a5e6e8510634 branches: changeset: 8151:a5e6e8510634 user: Maxim Dounin date: Fri Mar 24 02:53:21 2023 +0300 description: Mail: fixed handling of blocked client read events in proxy. When establishing a connection to the backend, nginx blocks reading from the client with ngx_mail_proxy_block_read(). Previously, such events were lost, and in some cases this resulted in connection hangs. Notably, this affected mail_imap_ssl.t on Windows, since the test closes connections after requesting authentication, but without waiting for any responses (so the connection close events might be lost). Fix is to post an event to read from the client after connecting to the backend if there were blocked events. diffstat: src/mail/ngx_mail_proxy_module.c | 12 +++++++++--- 1 files changed, 9 insertions(+), 3 deletions(-) diffs (36 lines): diff -r 8771d35d55d0 -r a5e6e8510634 src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c Fri Mar 10 07:43:50 2023 +0300 +++ b/src/mail/ngx_mail_proxy_module.c Fri Mar 24 02:53:21 2023 +0300 @@ -327,7 +327,9 @@ ngx_mail_proxy_pop3_handler(ngx_event_t c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); - if (s->buffer->pos < s->buffer->last) { + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { ngx_post_event(c->write, &ngx_posted_events); } @@ -486,7 +488,9 @@ ngx_mail_proxy_imap_handler(ngx_event_t c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); - if (s->buffer->pos < s->buffer->last) { + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { ngx_post_event(c->write, &ngx_posted_events); } @@ -821,7 +825,9 @@ ngx_mail_proxy_smtp_handler(ngx_event_t c->log->action = NULL; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); - if (s->buffer->pos < s->buffer->last) { + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { ngx_post_event(c->write, &ngx_posted_events); } From pluknet at nginx.com Fri Mar 24 13:18:12 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Fri, 24 Mar 2023 13:18:12 +0000 Subject: [nginx] SSL: enabled TLSv1.3 by default. Message-ID: details: https://hg.nginx.org/nginx/rev/d1cf09451ae8 branches: changeset: 8152:d1cf09451ae8 user: Maxim Dounin date: Fri Mar 24 02:57:43 2023 +0300 description: SSL: enabled TLSv1.3 by default. diffstat: src/http/modules/ngx_http_grpc_module.c | 5 +++-- src/http/modules/ngx_http_proxy_module.c | 5 +++-- src/http/modules/ngx_http_ssl_module.c | 5 +++-- src/http/modules/ngx_http_uwsgi_module.c | 5 +++-- src/mail/ngx_mail_ssl_module.c | 5 +++-- src/stream/ngx_stream_proxy_module.c | 5 +++-- src/stream/ngx_stream_ssl_module.c | 5 +++-- 7 files changed, 21 insertions(+), 14 deletions(-) diffs (105 lines): diff -r a5e6e8510634 -r d1cf09451ae8 src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/http/modules/ngx_http_grpc_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -4473,8 +4473,9 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t prev->upstream.ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff -r a5e6e8510634 -r d1cf09451ae8 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/http/modules/ngx_http_proxy_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -3734,8 +3734,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t prev->upstream.ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff -r a5e6e8510634 -r d1cf09451ae8 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/http/modules/ngx_http_ssl_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -632,8 +632,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, NGX_SSL_BUFSIZE); diff -r a5e6e8510634 -r d1cf09451ae8 src/http/modules/ngx_http_uwsgi_module.c --- a/src/http/modules/ngx_http_uwsgi_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/http/modules/ngx_http_uwsgi_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -1875,8 +1875,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t prev->upstream.ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff -r a5e6e8510634 -r d1cf09451ae8 src/mail/ngx_mail_ssl_module.c --- a/src/mail/ngx_mail_ssl_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/mail/ngx_mail_ssl_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -360,8 +360,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, prev->prefer_server_ciphers, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); diff -r a5e6e8510634 -r d1cf09451ae8 src/stream/ngx_stream_proxy_module.c --- a/src/stream/ngx_stream_proxy_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/stream/ngx_stream_proxy_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -2163,8 +2163,9 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf prev->ssl_session_reuse, 1); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff -r a5e6e8510634 -r d1cf09451ae8 src/stream/ngx_stream_ssl_module.c --- a/src/stream/ngx_stream_ssl_module.c Fri Mar 24 02:53:21 2023 +0300 +++ b/src/stream/ngx_stream_ssl_module.c Fri Mar 24 02:57:43 2023 +0300 @@ -703,8 +703,9 @@ ngx_stream_ssl_merge_conf(ngx_conf_t *cf prev->prefer_server_ciphers, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 - |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); From maxim at nginx.com Fri Mar 24 15:30:33 2023 From: maxim at nginx.com (Maxim Konovalov) Date: Fri, 24 Mar 2023 08:30:33 -0700 Subject: [PATCH] Added TLSv1.3 to the default value of ssl_protocols and friends In-Reply-To: <477d0fe1e6cb95533ffb.1679383149@ORK-ML-00007151> References: <477d0fe1e6cb95533ffb.1679383149@ORK-ML-00007151> Message-ID: Hi Yar, On 21.03.2023 00:19, Yaroslav Zhuravlev wrote: > diff --git a/xml/en/docs/http/ngx_http_grpc_module.xml b/xml/en/docs/http/ngx_http_grpc_module.xml > --- a/xml/en/docs/http/ngx_http_grpc_module.xml > +++ b/xml/en/docs/http/ngx_http_grpc_module.xml [...] > @@ -633,7 +633,7 @@ > [TLSv1.1] > [TLSv1.2] > [TLSv1.3] > -TLSv1 TLSv1.1 TLSv1.2 > +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 > http > server > location It makes sense to add a history note here and in other modules that have ssl_protocol directive. Maxim -- Maxim Konovalov From claireecf at gmail.com Sat Mar 25 03:59:16 2023 From: claireecf at gmail.com (claire liu) Date: Fri, 24 Mar 2023 22:59:16 -0500 Subject: Question about stream multiplexing in Nginx and Nginx-quic Message-ID: Hello Dev Team, Would you mind confirming if Nginx and Nginx-quic support stream multiplexing? How does Nginx handle multiplexed-object webpages? Does it respond data to client from multiple streams simultaneously? Thank you very much. Cheers, Claire -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Sat Mar 25 11:25:16 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 25 Mar 2023 14:25:16 +0300 Subject: Question about stream multiplexing in Nginx and Nginx-quic In-Reply-To: References: Message-ID: Hello! On Fri, Mar 24, 2023 at 10:59:16PM -0500, claire liu wrote: > Hello Dev Team, > > Would you mind confirming if Nginx and Nginx-quic support stream > multiplexing? How does Nginx handle multiplexed-object webpages? Does it > respond data to client from multiple streams simultaneously? Thank you very > much. For user-level questions please use the nginx@ mailing list instead. Thank you for understanding. -- Maxim Dounin http://mdounin.ru/ From u5.horie at gmail.com Mon Mar 27 00:25:56 2023 From: u5.horie at gmail.com (u5h) Date: Mon, 27 Mar 2023 09:25:56 +0900 Subject: Configurable sleep period for connections In-Reply-To: References: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> Message-ID: Hi, I had a same issue in those days. Did you try the proxy_cache_lock_timeout? https://forum.nginx.org/read.php?2,276344,276349#msg-276349 But the below article said if you reduce simply the once busy loop time, it may not resolve this problem for which based on the nginx event notification mechanism in case it has many concurrently same content request. https://blog.lumen.com/pulling-back-the-curtain-development-and-testing-for-low-latency-dash-support/ By the way, we might have been better to use nginx at mailing such a user level discussion. — Yugo Horie On Fri, Mar 24, 2023 at 19:18 Maxim Dounin wrote: > Hello! > > On Fri, Mar 24, 2023 at 09:24:25AM +0100, Roy Teeuwen wrote: > > > You are absolutely right, I totally forgot about the cache_lock. > > I have listed our settings below. > > > > The reason we are using the cache_lock is to save the backend > > application to not get 100's of requests when a stale item is > > invalid. Even if we have use_stale updating, we notice that only > > the first request will use the stale item, the following > > requests will do a new request even though there is already a > > background request going on to refresh the stale item. (This > > does not happen if we set keepalive to 0, where new connections > > are being used, but has the performance degradation as > > mentioned.) This was the reasoning for the cache_lock, but that > > gives the issue about the 500ms lock, while the item might > > already be refreshed after 100ms. > > To re-iterate: proxy_cache_lock is not expected to affect requests > if there is an existing cache item (and keepalive shouldn't affect > proxy_cache_lock in any way; not to mention that the "keepalive" > directive, which configures keepalive connections cache to > upstream servers, does not accept the "0" value). > > You may want to dig further into what actually happens in your > configuration. I would recommend to start with doing a debug log > which shows the described behaviour, and then following the code > to find out why the cache lock kicks in when it shouldn't. > > -- > Maxim Dounin > http://mdounin.ru/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From roy at teeuwen.be Mon Mar 27 06:59:02 2023 From: roy at teeuwen.be (Roy Teeuwen) Date: Mon, 27 Mar 2023 08:59:02 +0200 Subject: Configurable sleep period for connections In-Reply-To: References: <40c63798-3ab7-472b-bf85-ef154b4fc9b3@Spark> Message-ID: <6EA0B072-953A-4097-9402-162D62DC7E64@teeuwen.be> I'm going to try to and create an integration test for it so that I can show the setup doing the unexpected 500ms locking for stale+1 requests. I can then set the debug level and if I can't figure it out I will post back here with the integration tests. The reason I posted on the devel list was because I wanted to adjusted the 500ms lock / sleep period to be configurable, but seeing as I made the wrong assumption it turned into a user discussion, sorry! Greets, Roy > On 27 Mar 2023, at 02:25, u5h wrote: > > Hi, I had a same issue in those days. > Did you try the proxy_cache_lock_timeout? > > https://forum.nginx.org/read.php?2,276344,276349#msg-276349 > > But the below article said if you reduce simply the once busy loop time, it may not resolve this problem for which based on the nginx event notification mechanism in case it has many concurrently same content request. > > https://blog.lumen.com/pulling-back-the-curtain-development-and-testing-for-low-latency-dash-support/ > > By the way, we might have been better to use nginx at mailing such a user level discussion. > > — > Yugo Horie > > On Fri, Mar 24, 2023 at 19:18 Maxim Dounin > wrote: >> Hello! >> >> On Fri, Mar 24, 2023 at 09:24:25AM +0100, Roy Teeuwen wrote: >> >> > You are absolutely right, I totally forgot about the cache_lock. >> > I have listed our settings below. >> > >> > The reason we are using the cache_lock is to save the backend >> > application to not get 100's of requests when a stale item is >> > invalid. Even if we have use_stale updating, we notice that only >> > the first request will use the stale item, the following >> > requests will do a new request even though there is already a >> > background request going on to refresh the stale item. (This >> > does not happen if we set keepalive to 0, where new connections >> > are being used, but has the performance degradation as >> > mentioned.) This was the reasoning for the cache_lock, but that >> > gives the issue about the 500ms lock, while the item might >> > already be refreshed after 100ms. >> >> To re-iterate: proxy_cache_lock is not expected to affect requests >> if there is an existing cache item (and keepalive shouldn't affect >> proxy_cache_lock in any way; not to mention that the "keepalive" >> directive, which configures keepalive connections cache to >> upstream servers, does not accept the "0" value). >> >> You may want to dig further into what actually happens in your >> configuration. I would recommend to start with doing a debug log >> which shows the described behaviour, and then following the code >> to find out why the cache lock kicks in when it shouldn't. >> >> -- >> Maxim Dounin >> http://mdounin.ru/ >> _______________________________________________ >> nginx-devel mailing list >> nginx-devel at nginx.org >> https://mailman.nginx.org/mailman/listinfo/nginx-devel > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From Michael.Kourlas at solace.com Mon Mar 27 16:19:27 2023 From: Michael.Kourlas at solace.com (Michael Kourlas) Date: Mon, 27 Mar 2023 16:19:27 +0000 Subject: [PATCH] HTTP: Add new uri_normalization_percent_decode option Message-ID: Hello, Thanks for your comments! Sorry for the delay in responding -- I was on vacation. > As far as I understand, it will irreversibly corrupt URIs with > double-encoded reserved characters. For example, "%252F" will > become "%2F" when proxying in the following configuration: > > location /foo/ { > proxy_pass http://upstream/foo/; > } I believe you are correct. When the "all-except-reserved" option is used, nginx will not recognize, during the re-encoding step, that the "%" in "%2F" was originally percent-encoded and not part of a percent-encoded "/". However, I think this issue can be addressed by renaming "all-except-reserved" to "all-except-percent-and-reserved" and adding "%" to the corresponding set of characters that is not decoded or encoded automatically when that option is used. "%252F" will then be left untouched throughout the entire process. > Further, requests to static files with (properly escaped) reserved > characters will simply fail, because nginx won't decode these > characters. For example, in the following trivial configuration a > request to "/foo%3Fbar" won't be decoded to match "/foo?bar" file > under the document root: > > location / { > # static files > } Yes, I agree it doesn't make sense to keep "%" and reserved characters percent-encoded when performing a filesystem lookup, because at that point we can be certain they're not being used as delimiters or anything other than data. However, I think this issue can be addressed by decoding any remaining percent-encoded characters at the point of filesystem lookup, as well as any other place where you think it is appropriate. (Of course, this should only be done when using the "all-except-percent-and-reserved" option, and only with "%" and reserved characters, to avoid double-decoding double-encoded characters.) > Please also note that the configuration directive you've > introduced in this patch applies to URI parsing from not-yet-final > server block (see [1] for details), but the configuration from the > final server block will be used for URI escaping. These > configuration can be different, and this might result in various > additional issues. I think this issue can be addressed by making the new directive available only in http blocks. This ensures that all virtual servers share the same configuration. If you agree with these suggestions, I'd be happy to submit an updated patch. Reserved characters are often used as delimiters in APIs, and I believe it is important that nginx be able to distinguish between usages as delimiters and usages as data. Thanks, Michael Kourlas ________________________________ Confidentiality notice This e-mail message and any attachment hereto contain confidential information which may be privileged and which is intended for the exclusive use of its addressee(s). If you receive this message in error, please inform sender immediately and destroy any copy thereof. Furthermore, any disclosure, distribution or copying of this message and/or any attachment hereto without the consent of the sender is strictly prohibited. Thank you. From pluknet at nginx.com Mon Mar 27 19:35:13 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 27 Mar 2023 19:35:13 +0000 Subject: [nginx] Gzip: compatibility with recent zlib-ng versions. Message-ID: details: https://hg.nginx.org/nginx/rev/fcb2333c9982 branches: changeset: 8153:fcb2333c9982 user: Maxim Dounin date: Mon Mar 27 21:25:05 2023 +0300 description: Gzip: compatibility with recent zlib-ng versions. It now uses custom alloc_aligned() wrapper for all allocations, therefore all allocations are larger than expected by (64 + sizeof(void*)). Further, they are seen as allocations of 1 element. Relevant calculations were adjusted to reflect this, and state allocation is now protected with a flag to avoid misinterpreting other allocations as the zlib deflate_state allocation. Further, it no longer forces window bits to 13 on compression level 1, so the comment was adjusted to reflect this. diffstat: src/http/modules/ngx_http_gzip_filter_module.c | 18 ++++++++++++------ 1 files changed, 12 insertions(+), 6 deletions(-) diffs (54 lines): diff -r d1cf09451ae8 -r fcb2333c9982 src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c Fri Mar 24 02:57:43 2023 +0300 +++ b/src/http/modules/ngx_http_gzip_filter_module.c Mon Mar 27 21:25:05 2023 +0300 @@ -57,6 +57,7 @@ typedef struct { unsigned nomem:1; unsigned buffering:1; unsigned zlib_ng:1; + unsigned state_allocated:1; size_t zin; size_t zout; @@ -514,9 +515,10 @@ ngx_http_gzip_filter_memory(ngx_http_req } else { /* * Another zlib variant, https://github.com/zlib-ng/zlib-ng. - * It forces window bits to 13 for fast compression level, - * uses 16-byte padding in one of window-sized buffers, and - * uses 128K hash. + * It used to force window bits to 13 for fast compression level, + * uses (64 + sizeof(void*)) additional space on all allocations + * for alignment, 16-byte padding in one of window-sized buffers, + * and 128K hash. */ if (conf->level == 1) { @@ -524,7 +526,8 @@ ngx_http_gzip_filter_memory(ngx_http_req } ctx->allocated = 8192 + 16 + (1 << (wbits + 2)) - + 131072 + (1 << (memlevel + 8)); + + 131072 + (1 << (memlevel + 8)) + + 4 * (64 + sizeof(void*)); ctx->zlib_ng = 1; } } @@ -926,13 +929,16 @@ ngx_http_gzip_filter_alloc(void *opaque, alloc = items * size; - if (items == 1 && alloc % 512 != 0 && alloc < 8192) { - + if (items == 1 && alloc % 512 != 0 && alloc < 8192 + && !ctx->state_allocated) + { /* * The zlib deflate_state allocation, it takes about 6K, * we allocate 8K. Other allocations are divisible by 512. */ + ctx->state_allocated = 1; + alloc = 8192; } From thresh at nginx.com Mon Mar 27 23:27:14 2023 From: thresh at nginx.com (=?iso-8859-1?q?Konstantin_Pavlov?=) Date: Mon, 27 Mar 2023 16:27:14 -0700 Subject: [PATCH] Linux packages: specified priority for Amazon Linux Message-ID: <9741a500247ec50eaf5a.1679959634@QGCD7XG9R9> # HG changeset patch # User Konstantin Pavlov # Date 1679959544 25200 # Mon Mar 27 16:25:44 2023 -0700 # Node ID 9741a500247ec50eaf5a4043a270fc097e0345c1 # Parent 23d3cabaab95fb09ea40f113759f4eaed99ec9d7 Linux packages: specified priority for Amazon Linux. This makes nginx.org repositories preferred when installing nginx packages. Currently, both Amazon Linux 2 and 2023 repositories have a priority of 10, so any number lower than that makes our packages preferred. diff -r 23d3cabaab95 -r 9741a500247e xml/en/linux_packages.xml --- a/xml/en/linux_packages.xml Mon Mar 20 11:20:57 2023 -0700 +++ b/xml/en/linux_packages.xml Mon Mar 27 16:25:44 2023 -0700 @@ -7,7 +7,7 @@
+ rev="84">
@@ -548,6 +548,7 @@ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 [nginx-mainline] name=nginx mainline repo @@ -556,6 +557,7 @@ gpgcheck=1 enabled=0 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 To set up the yum repository for Amazon Linux 2023, create the file named @@ -570,6 +572,7 @@ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 [nginx-mainline] name=nginx mainline repo @@ -578,6 +581,7 @@ gpgcheck=1 enabled=0 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 By default, the repository for stable nginx packages is used. diff -r 23d3cabaab95 -r 9741a500247e xml/ru/linux_packages.xml --- a/xml/ru/linux_packages.xml Mon Mar 20 11:20:57 2023 -0700 +++ b/xml/ru/linux_packages.xml Mon Mar 27 16:25:44 2023 -0700 @@ -7,7 +7,7 @@
+ rev="84">
@@ -545,6 +545,7 @@ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 [nginx-mainline] name=nginx mainline repo @@ -553,6 +554,7 @@ gpgcheck=1 enabled=0 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 Для подключения yum-репозитория для Amazon Linux 2023 создайте файл с именем @@ -567,6 +569,7 @@ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 [nginx-mainline] name=nginx mainline repo @@ -575,6 +578,7 @@ gpgcheck=1 enabled=0 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true +priority=9 По умолчанию используется репозиторий для стабильной версии nginx. From arut at nginx.com Tue Mar 28 13:36:47 2023 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Tue, 28 Mar 2023 17:36:47 +0400 Subject: [PATCH] QUIC: style Message-ID: <24daeb3703817af6fc17.1680010607@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1678895835 -14400 # Wed Mar 15 19:57:15 2023 +0400 # Branch quic # Node ID 24daeb3703817af6fc17d7281fa06c7dc1c4f0a5 # Parent 13d43a278510f131101c7b19d87455a0171ebe2f QUIC: style. diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_event_quic_transport.c --- a/src/event/quic/ngx_event_quic_transport.c +++ b/src/event/quic/ngx_event_quic_transport.c @@ -576,7 +576,7 @@ ngx_quic_payload_size(ngx_quic_header_t { size_t len; - if ngx_quic_short_pkt(pkt->flags) { + if (ngx_quic_short_pkt(pkt->flags)) { len = 1 + pkt->dcid.len + pkt->num_len + EVP_GCM_TLS_TAG_LEN; if (len > pkt_len) { From pluknet at nginx.com Tue Mar 28 13:48:39 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 28 Mar 2023 17:48:39 +0400 Subject: [PATCH] QUIC: style In-Reply-To: <24daeb3703817af6fc17.1680010607@arut-laptop> References: <24daeb3703817af6fc17.1680010607@arut-laptop> Message-ID: <156C34A2-8192-40CD-8F99-938A1A954A9C@nginx.com> > On 28 Mar 2023, at 17:36, Roman Arutyunyan wrote: > > # HG changeset patch > # User Roman Arutyunyan > # Date 1678895835 -14400 > # Wed Mar 15 19:57:15 2023 +0400 > # Branch quic > # Node ID 24daeb3703817af6fc17d7281fa06c7dc1c4f0a5 > # Parent 13d43a278510f131101c7b19d87455a0171ebe2f > QUIC: style. > > diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_event_quic_transport.c > --- a/src/event/quic/ngx_event_quic_transport.c > +++ b/src/event/quic/ngx_event_quic_transport.c > @@ -576,7 +576,7 @@ ngx_quic_payload_size(ngx_quic_header_t > { > size_t len; > > - if ngx_quic_short_pkt(pkt->flags) { > + if (ngx_quic_short_pkt(pkt->flags)) { > > len = 1 + pkt->dcid.len + pkt->num_len + EVP_GCM_TLS_TAG_LEN; > if (len > pkt_len) { Looks good. -- Sergey Kandaurov From mdounin at mdounin.ru Tue Mar 28 14:08:52 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 28 Mar 2023 17:08:52 +0300 Subject: [PATCH] HTTP: Add new uri_normalization_percent_decode option In-Reply-To: References: Message-ID: Hello! On Mon, Mar 27, 2023 at 04:19:27PM +0000, Michael Kourlas via nginx-devel wrote: > > As far as I understand, it will irreversibly corrupt URIs with > > double-encoded reserved characters. For example, "%252F" will > > become "%2F" when proxying in the following configuration: > > > > location /foo/ { > > proxy_pass http://upstream/foo/; > > } > > I believe you are correct. When the "all-except-reserved" option is used, nginx > will not recognize, during the re-encoding step, that the "%" in "%2F" was > originally percent-encoded and not part of a percent-encoded "/". > > However, I think this issue can be addressed by renaming "all-except-reserved" > to "all-except-percent-and-reserved" and adding "%" to the corresponding set of > characters that is not decoded or encoded automatically when that option is > used. "%252F" will then be left untouched throughout the entire process. So it will prevent access to the files with "%", such as in "foo%bar", unless specifically handled (see below). > > Further, requests to static files with (properly escaped) reserved > > characters will simply fail, because nginx won't decode these > > characters. For example, in the following trivial configuration a > > request to "/foo%3Fbar" won't be decoded to match "/foo?bar" file > > under the document root: > > > > location / { > > # static files > > } > > Yes, I agree it doesn't make sense to keep "%" and reserved characters > percent-encoded when performing a filesystem lookup, because at that point we > can be certain they're not being used as delimiters or anything other than > data. > > However, I think this issue can be addressed by decoding any remaining > percent-encoded characters at the point of filesystem lookup, as well as any > other place where you think it is appropriate. (Of course, this should only be > done when using the "all-except-percent-and-reserved" option, and only with "%" > and reserved characters, to avoid double-decoding double-encoded characters.) This implies, basically, that there are 3 forms of the request URI: 1) fully encoded, as in $request_uri, 2) fully decoded, as in $uri now, and 3) "all-except-percent-and-reserved". To implement this correctly, it needs clear definition when each form is used, and it is going to be a non-trivial task to do this safely. For example, using fully decoded form for file system lookups while using "all-except-percent-and-reserved" form for location matching is going to be unsafe: restrictions like "location /private/ { deny all; }" can be easily bypassed. The same issue applies to other cases not involving file system access directly in nginx: for example, consider $fastcgi_script_name in FastCGI proxying. > > Please also note that the configuration directive you've > > introduced in this patch applies to URI parsing from not-yet-final > > server block (see [1] for details), but the configuration from the > > final server block will be used for URI escaping. These > > configuration can be different, and this might result in various > > additional issues. > > I think this issue can be addressed by making the new directive available only > in http blocks. This ensures that all virtual servers share the same > configuration. This approach basically precludes using different settings in different server blocks, even completely independent, such as IP-based virtual servers, and is not generally the way to go. Instead, this should be implemented correctly, if at all. > If you agree with these suggestions, I'd be happy to submit an updated patch. > > Reserved characters are often used as delimiters in APIs, and I believe it is > important that nginx be able to distinguish between usages as delimiters and > usages as data. First of all, you may want to focus on the logic you are going to implement. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue Mar 28 14:14:46 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 28 Mar 2023 17:14:46 +0300 Subject: nginx 1.23.4 changes draft Message-ID: Hello! Changes with nginx 1.23.4 28 Mar 2023 *) Change: now TLSv1.3 protocol is enabled by default. *) Change: now nginx issues a warning if protocol parameters of a listening socket are redefined. *) Change: now nginx closes connections with lingering if pipelining was used by the client. *) Feature: byte ranges support in the ngx_http_gzip_static_module. *) Bugfix: port ranges in the "listen" directive did not work; the bug had appeared in 1.23.3. Thanks to Valentin Bartenev. *) Bugfix: incorrect location might be chosen to process a request if a prefix location longer than 255 characters was used in the configuration. *) Bugfix: non-ASCII characters in file names on Windows were not supported by the ngx_http_autoindex_module, the ngx_http_dav_module, and the "include" directive. *) Change: the logging level of the "data length too long", "length too short", "bad legacy version", "no shared signature algorithms", "bad digest length", "missing sigalgs extension", "encrypted length too long", "bad length", "bad key update", "mixed handshake and non handshake data", "ccs received early", "data between ccs and finished", "packet length too long", "too many warn alerts", "record too small", and "got a fin before a ccs" SSL errors has been lowered from "crit" to "info". *) Bugfix: a socket leak might occur when using HTTP/2 and the "error_page" directive to redirect errors with code 400. *) Bugfix: messages about logging to syslog errors did not contain information that the errors happened while logging to syslog. Thanks to Safar Safarly. *) Workaround: "gzip filter failed to use preallocated memory" alerts appeared in logs when using zlib-ng. *) Bugfix: in the mail proxy server. Изменения в nginx 1.23.4 28.03.2023 *) Изменение: теперь протокол TLSv1.3 разрешён по умолчанию. *) Изменение: теперь nginx выдаёт предупреждение при переопределении параметров listen-сокета, задающих используемые протоколы. *) Изменение: теперь, если клиент использует pipelining, nginx закрывает соединения с ожиданием дополнительных данных (lingering close). *) Добавление: поддержка byte ranges для ответов модуля ngx_http_gzip_static_module. *) Исправление: диапазоны портов в директиве listen не работали; ошибка появилась в nginx 1.23.3. Спасибо Валентину Бартеневу. *) Исправление: для обработки запроса мог быть выбран неверный location, если в конфигурации использовался префиксный location длиннее 255 символов. *) Исправление: не-ASCII символы в именах файлов на Windows не поддерживались модулями ngx_http_autoindex_module и ngx_http_dav_module, а также директивой include. *) Изменение: уровень логгирования ошибок SSL "data length too long", "length too short", "bad legacy version", "no shared signature algorithms", "bad digest length", "missing sigalgs extension", "encrypted length too long", "bad length", "bad key update", "mixed handshake and non handshake data", "ccs received early", "data between ccs and finished", "packet length too long", "too many warn alerts", "record too small", и "got a fin before a ccs" понижен с уровня crit до info. *) Исправление: при использовании HTTP/2 и директивы error_page для перенаправления ошибок с кодом 400 могла происходить утечка сокетов. *) Исправление: сообщения об ошибках записи в syslog не содержали информации о том, ошибки происходили в процессе записи в syslog. Спасибо Safar Safarly. *) Изменение: при использовании zlib-ng в логах появлялись сообщения "gzip filter failed to use preallocated memory". *) Исправление: в почтовом прокси-сервере. -- Maxim Dounin http://mdounin.ru/ From yar at nginx.com Tue Mar 28 14:37:31 2023 From: yar at nginx.com (Yaroslav Zhuravlev) Date: Tue, 28 Mar 2023 15:37:31 +0100 Subject: [PATCH] Added TLSv1.3 to the default value of ssl_protocols and friends In-Reply-To: References: <477d0fe1e6cb95533ffb.1679383149@ORK-ML-00007151> Message-ID: <23389F73-D1F7-4042-A2F4-B4E1AB47143E@nginx.com> > On 24 Mar 2023, at 15:30, Maxim Konovalov wrote: > > Hi Yar, > > On 21.03.2023 00:19, Yaroslav Zhuravlev wrote: >> diff --git a/xml/en/docs/http/ngx_http_grpc_module.xml b/xml/en/docs/http/ngx_http_grpc_module.xml >> --- a/xml/en/docs/http/ngx_http_grpc_module.xml >> +++ b/xml/en/docs/http/ngx_http_grpc_module.xml > [...] >> @@ -633,7 +633,7 @@ >> [TLSv1.1] >> [TLSv1.2] >> [TLSv1.3] >> -TLSv1 TLSv1.1 TLSv1.2 >> +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 >> http >> server >> location > > It makes sense to add a history note here and in other modules that have ssl_protocol directive. [...] Hi Maxim, Thank you for the feedback, the patch was accordingly updated: # HG changeset patch # User Yaroslav Zhuravlev # Date 1678486627 0 # Fri Mar 10 22:17:07 2023 +0000 # Node ID 6096d51ff4d2abecc67b47bc09cfbf03be69f7b0 # Parent ac7518a1fe1c74daba708e30405a9b5d33f606e1 Added TLSv1.3 to the default value of ssl_protocols and friends. diff --git a/xml/en/docs/http/configuring_https_servers.xml b/xml/en/docs/http/configuring_https_servers.xml --- a/xml/en/docs/http/configuring_https_servers.xml +++ b/xml/en/docs/http/configuring_https_servers.xml @@ -8,7 +8,7 @@
@@ -31,7 +31,7 @@ server_name www.example.com; ssl_certificate www.example.com.crt; ssl_certificate_key www.example.com.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ... } @@ -59,7 +59,7 @@ can be used to limit connections to include only the strong versions and ciphers of SSL/TLS. By default nginx uses -“ssl_protocols TLSv1 TLSv1.1 TLSv1.2” +“ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3” and “ssl_ciphers HIGH:!aNULL:!MD5”, so configuring them explicitly is generally not needed. Note that default values of these directives were @@ -110,7 +110,7 @@ ssl_certificate www.example.com.crt; ssl_certificate_key www.example.com.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ... @@ -446,6 +446,11 @@ +Version 1.23.4 and later: the default SSL protocols are TLSv1, +TLSv1.1, TLSv1.2, and TLSv1.3 (if supported by the OpenSSL library). + + + Version 1.9.1 and later: the default SSL protocols are TLSv1, TLSv1.1, and TLSv1.2 (if supported by the OpenSSL library). diff --git a/xml/en/docs/http/ngx_http_grpc_module.xml b/xml/en/docs/http/ngx_http_grpc_module.xml --- a/xml/en/docs/http/ngx_http_grpc_module.xml +++ b/xml/en/docs/http/ngx_http_grpc_module.xml @@ -10,7 +10,7 @@ + rev="9">
@@ -633,7 +633,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server location @@ -642,6 +642,13 @@ Enables the specified protocols for requests to a gRPC SSL server. + + +The TLSv1.3 parameter is used by default +since 1.23.4. + + + diff --git a/xml/en/docs/http/ngx_http_proxy_module.xml b/xml/en/docs/http/ngx_http_proxy_module.xml --- a/xml/en/docs/http/ngx_http_proxy_module.xml +++ b/xml/en/docs/http/ngx_http_proxy_module.xml @@ -10,7 +10,7 @@ + rev="76">
@@ -2096,7 +2096,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server location @@ -2106,6 +2106,13 @@ Enables the specified protocols for requests to a proxied HTTPS server. + + +The TLSv1.3 parameter is used by default +since 1.23.4. + + + diff --git a/xml/en/docs/http/ngx_http_ssl_module.xml b/xml/en/docs/http/ngx_http_ssl_module.xml --- a/xml/en/docs/http/ngx_http_ssl_module.xml +++ b/xml/en/docs/http/ngx_http_ssl_module.xml @@ -10,7 +10,7 @@ + rev="60">
@@ -76,7 +76,7 @@ listen 443 ssl; keepalive_timeout 70; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; ssl_certificate /usr/local/nginx/conf/cert.pem; ssl_certificate_key /usr/local/nginx/conf/cert.key; @@ -595,7 +595,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server @@ -609,6 +609,10 @@ The TLSv1.3 parameter (1.13.0) works only when OpenSSL 1.1.1 or higher is used. + +The TLSv1.3 parameter is used by default +since 1.23.4. + diff --git a/xml/en/docs/http/ngx_http_uwsgi_module.xml b/xml/en/docs/http/ngx_http_uwsgi_module.xml --- a/xml/en/docs/http/ngx_http_uwsgi_module.xml +++ b/xml/en/docs/http/ngx_http_uwsgi_module.xml @@ -10,7 +10,7 @@ + rev="50">
@@ -1546,7 +1546,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server location @@ -1556,6 +1556,13 @@ Enables the specified protocols for requests to a secured uwsgi server. + + +The TLSv1.3 parameter is used by default +since 1.23.4. + + + diff --git a/xml/en/docs/mail/ngx_mail_ssl_module.xml b/xml/en/docs/mail/ngx_mail_ssl_module.xml --- a/xml/en/docs/mail/ngx_mail_ssl_module.xml +++ b/xml/en/docs/mail/ngx_mail_ssl_module.xml @@ -10,7 +10,7 @@ + rev="27">
@@ -69,7 +69,7 @@ server { listen 993 ssl; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; ssl_certificate /usr/local/nginx/conf/cert.pem; ssl_certificate_key /usr/local/nginx/conf/cert.key; @@ -420,7 +420,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 mail server @@ -434,6 +434,10 @@ The TLSv1.3 parameter (1.13.0) works only when OpenSSL 1.1.1 or higher is used. + +The TLSv1.3 parameter is used by default +since 1.23.4. + diff --git a/xml/en/docs/stream/ngx_stream_proxy_module.xml b/xml/en/docs/stream/ngx_stream_proxy_module.xml --- a/xml/en/docs/stream/ngx_stream_proxy_module.xml +++ b/xml/en/docs/stream/ngx_stream_proxy_module.xml @@ -9,7 +9,7 @@ + rev="32">
@@ -543,7 +543,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 stream server @@ -551,6 +551,13 @@ Enables the specified protocols for connections to a proxied server. + + +The TLSv1.3 parameter is used by default +since 1.23.4. + + + diff --git a/xml/en/docs/stream/ngx_stream_ssl_module.xml b/xml/en/docs/stream/ngx_stream_ssl_module.xml --- a/xml/en/docs/stream/ngx_stream_ssl_module.xml +++ b/xml/en/docs/stream/ngx_stream_ssl_module.xml @@ -9,7 +9,7 @@ + rev="32">
@@ -62,7 +62,7 @@ server { listen 12345 ssl; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; ssl_certificate /usr/local/nginx/conf/cert.pem; ssl_certificate_key /usr/local/nginx/conf/cert.key; @@ -444,7 +444,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 stream server @@ -458,6 +458,10 @@ The TLSv1.3 parameter (1.13.0) works only when OpenSSL 1.1.1 or higher is used. + +The TLSv1.3 parameter is used by default +since 1.23.4. + diff --git a/xml/ru/docs/http/configuring_https_servers.xml b/xml/ru/docs/http/configuring_https_servers.xml --- a/xml/ru/docs/http/configuring_https_servers.xml +++ b/xml/ru/docs/http/configuring_https_servers.xml @@ -8,7 +8,7 @@
@@ -30,7 +30,7 @@ server_name www.example.com; ssl_certificate www.example.com.crt; ssl_certificate_key www.example.com.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ... } @@ -58,7 +58,7 @@ можно ограничить соединения использованием только “сильных” версий и шифров SSL/TLS. По умолчанию nginx использует -“ssl_protocols TLSv1 TLSv1.1 TLSv1.2” и +“ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3” и “ssl_ciphers HIGH:!aNULL:!MD5”, поэтому их явная настройка в общем случае не требуется. Следует отметить, что значения по умолчанию этих директив несколько раз @@ -108,7 +108,7 @@ ssl_certificate www.example.com.crt; ssl_certificate_key www.example.com.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ... @@ -445,6 +445,11 @@ +Версия 1.23.4 и более поздние: протоколами SSL по умолчанию являются +TLSv1, TLSv1.1, TLSv1.2 и TLSv1.3 (если поддерживается библиотекой OpenSSL). + + + Версия 1.9.1 и более поздние: протоколами SSL по умолчанию являются TLSv1, TLSv1.1 и TLSv1.2 (если поддерживается библиотекой OpenSSL). diff --git a/xml/ru/docs/http/ngx_http_grpc_module.xml b/xml/ru/docs/http/ngx_http_grpc_module.xml --- a/xml/ru/docs/http/ngx_http_grpc_module.xml +++ b/xml/ru/docs/http/ngx_http_grpc_module.xml @@ -10,7 +10,7 @@ + rev="9">
@@ -632,7 +632,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server location @@ -641,6 +641,13 @@ Разрешает указанные протоколы для запросов к gRPC SSL-серверу. + + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + + + diff --git a/xml/ru/docs/http/ngx_http_proxy_module.xml b/xml/ru/docs/http/ngx_http_proxy_module.xml --- a/xml/ru/docs/http/ngx_http_proxy_module.xml +++ b/xml/ru/docs/http/ngx_http_proxy_module.xml @@ -10,7 +10,7 @@ + rev="76">
@@ -2098,7 +2098,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server location @@ -2108,6 +2108,13 @@ Разрешает указанные протоколы для запросов к проксируемому HTTPS-серверу. + + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + + + diff --git a/xml/ru/docs/http/ngx_http_ssl_module.xml b/xml/ru/docs/http/ngx_http_ssl_module.xml --- a/xml/ru/docs/http/ngx_http_ssl_module.xml +++ b/xml/ru/docs/http/ngx_http_ssl_module.xml @@ -10,7 +10,7 @@ + rev="60">
@@ -76,7 +76,7 @@ listen 443 ssl; keepalive_timeout 70; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; ssl_certificate /usr/local/nginx/conf/cert.pem; ssl_certificate_key /usr/local/nginx/conf/cert.key; @@ -600,7 +600,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server @@ -614,6 +614,10 @@ Параметр TLSv1.3 (1.13.0) работает только при использовании OpenSSL 1.1.1 и выше. + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + diff --git a/xml/ru/docs/http/ngx_http_uwsgi_module.xml b/xml/ru/docs/http/ngx_http_uwsgi_module.xml --- a/xml/ru/docs/http/ngx_http_uwsgi_module.xml +++ b/xml/ru/docs/http/ngx_http_uwsgi_module.xml @@ -10,7 +10,7 @@ + rev="50">
@@ -1542,7 +1542,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 http server location @@ -1552,6 +1552,13 @@ Разрешает указанные протоколы для запросов к suwsgi-серверу. + + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + + + diff --git a/xml/ru/docs/mail/ngx_mail_ssl_module.xml b/xml/ru/docs/mail/ngx_mail_ssl_module.xml --- a/xml/ru/docs/mail/ngx_mail_ssl_module.xml +++ b/xml/ru/docs/mail/ngx_mail_ssl_module.xml @@ -10,7 +10,7 @@ + rev="27">
@@ -69,7 +69,7 @@ server { listen 993 ssl; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; ssl_certificate /usr/local/nginx/conf/cert.pem; ssl_certificate_key /usr/local/nginx/conf/cert.key; @@ -422,7 +422,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 mail server @@ -436,6 +436,10 @@ Параметр TLSv1.3 (1.13.0) работает только при использовании OpenSSL 1.1.1 и выше. + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + diff --git a/xml/ru/docs/stream/ngx_stream_proxy_module.xml b/xml/ru/docs/stream/ngx_stream_proxy_module.xml --- a/xml/ru/docs/stream/ngx_stream_proxy_module.xml +++ b/xml/ru/docs/stream/ngx_stream_proxy_module.xml @@ -9,7 +9,7 @@ + rev="32">
@@ -543,7 +543,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 stream server @@ -551,6 +551,13 @@ Разрешает указанные протоколы для соединений с проксируемым сервером. + + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + + + diff --git a/xml/ru/docs/stream/ngx_stream_ssl_module.xml b/xml/ru/docs/stream/ngx_stream_ssl_module.xml --- a/xml/ru/docs/stream/ngx_stream_ssl_module.xml +++ b/xml/ru/docs/stream/ngx_stream_ssl_module.xml @@ -9,7 +9,7 @@ + rev="32">
@@ -62,7 +62,7 @@ server { listen 12345 ssl; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; ssl_certificate /usr/local/nginx/conf/cert.pem; ssl_certificate_key /usr/local/nginx/conf/cert.key; @@ -447,7 +447,7 @@ [TLSv1.1] [TLSv1.2] [TLSv1.3] -TLSv1 TLSv1.1 TLSv1.2 +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 stream server @@ -461,6 +461,10 @@ Параметр TLSv1.3 (1.13.0) работает только при использовании OpenSSL 1.1.1 и выше. + +Параметр TLSv1.3 используется по умолчанию +начиная с 1.23.4. + From pluknet at nginx.com Tue Mar 28 14:39:41 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 28 Mar 2023 18:39:41 +0400 Subject: nginx 1.23.4 changes draft In-Reply-To: References: Message-ID: <86F4BD59-693D-4810-9694-B671A99E2C28@nginx.com> > On 28 Mar 2023, at 18:14, Maxim Dounin wrote: > > Hello! > > Changes with nginx 1.23.4 28 Mar 2023 > > *) Change: now TLSv1.3 protocol is enabled by default. > > *) Change: now nginx issues a warning if protocol parameters of a > listening socket are redefined. > > *) Change: now nginx closes connections with lingering if pipelining was > used by the client. > > *) Feature: byte ranges support in the ngx_http_gzip_static_module. > > *) Bugfix: port ranges in the "listen" directive did not work; the bug > had appeared in 1.23.3. > Thanks to Valentin Bartenev. > > *) Bugfix: incorrect location might be chosen to process a request if a > prefix location longer than 255 characters was used in the > configuration. > > *) Bugfix: non-ASCII characters in file names on Windows were not > supported by the ngx_http_autoindex_module, the ngx_http_dav_module, > and the "include" directive. > > *) Change: the logging level of the "data length too long", "length too > short", "bad legacy version", "no shared signature algorithms", "bad > digest length", "missing sigalgs extension", "encrypted length too > long", "bad length", "bad key update", "mixed handshake and non > handshake data", "ccs received early", "data between ccs and > finished", "packet length too long", "too many warn alerts", "record > too small", and "got a fin before a ccs" SSL errors has been lowered > from "crit" to "info". > > *) Bugfix: a socket leak might occur when using HTTP/2 and the > "error_page" directive to redirect errors with code 400. > > *) Bugfix: messages about logging to syslog errors did not contain > information that the errors happened while logging to syslog. > Thanks to Safar Safarly. > > *) Workaround: "gzip filter failed to use preallocated memory" alerts > appeared in logs when using zlib-ng. > > *) Bugfix: in the mail proxy server. > > > Изменения в nginx 1.23.4 28.03.2023 > > *) Изменение: теперь протокол TLSv1.3 разрешён по умолчанию. > > *) Изменение: теперь nginx выдаёт предупреждение при переопределении > параметров listen-сокета, задающих используемые протоколы. > > *) Изменение: теперь, если клиент использует pipelining, nginx закрывает > соединения с ожиданием дополнительных данных (lingering close). > > *) Добавление: поддержка byte ranges для ответов модуля > ngx_http_gzip_static_module. > > *) Исправление: диапазоны портов в директиве listen не работали; ошибка > появилась в nginx 1.23.3. "nginx" is usually omitted here (per changes grep) > Спасибо Валентину Бартеневу. > > *) Исправление: для обработки запроса мог быть выбран неверный location, > если в конфигурации использовался префиксный location длиннее 255 > символов. > > *) Исправление: не-ASCII символы в именах файлов на Windows не > поддерживались модулями ngx_http_autoindex_module и > ngx_http_dav_module, а также директивой include. > > *) Изменение: уровень логгирования ошибок SSL "data length too long", > "length too short", "bad legacy version", "no shared signature > algorithms", "bad digest length", "missing sigalgs extension", > "encrypted length too long", "bad length", "bad key update", "mixed > handshake and non handshake data", "ccs received early", "data > between ccs and finished", "packet length too long", "too many warn > alerts", "record too small", и "got a fin before a ccs" понижен с > уровня crit до info. > > *) Исправление: при использовании HTTP/2 и директивы error_page для > перенаправления ошибок с кодом 400 могла происходить утечка сокетов. > > *) Исправление: сообщения об ошибках записи в syslog не содержали > информации о том, ошибки происходили в процессе записи в syslog. "о том, что" > Спасибо Safar Safarly. > > *) Изменение: при использовании zlib-ng в логах появлялись сообщения > "gzip filter failed to use preallocated memory". > > *) Исправление: в почтовом прокси-сервере. > Otherwise, looks good. -- Sergey Kandaurov From arut at nginx.com Tue Mar 28 14:51:37 2023 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Tue, 28 Mar 2023 18:51:37 +0400 Subject: [PATCH 0 of 3] QUIC path MTU discovery Message-ID: This implementation does not rely on ICMP PTB messages. Instead, QUIC packet acknowledgement is used to make sure the MTU is suppored by the path ([1], [2]). A lost packet, as detected by QUIC, indicates the MTU is too big. Existing QUIC implementations use different approaches to size selection while probing MTU. Options are: - hardcoded MTU values array - linear search - binary search A review of size search methods is available in [3]. The algorithm implemented first tries local MTU, followed by a binary search in case the local MTU was too big. [1] https://datatracker.ietf.org/doc/html/rfc9000#name-datagram-packetization-laye [2] https://datatracker.ietf.org/doc/html/rfc8899 [3] https://www.youtube.com/watch?v=5kIe3pbK84A From arut at nginx.com Tue Mar 28 14:51:38 2023 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Tue, 28 Mar 2023 18:51:38 +0400 Subject: [PATCH 1 of 3] QUIC: changed path validation timeout In-Reply-To: References: Message-ID: # HG changeset patch # User Roman Arutyunyan # Date 1679925333 -14400 # Mon Mar 27 17:55:33 2023 +0400 # Branch quic # Node ID f76e83412133085a6c82fce2c3e15b2c34a6e959 # Parent 5fd628b89bb7fb5c95afa1dc914385f7ab79f6a3 QUIC: changed path validation timeout. Path validation packets containing PATH_CHALLENGE frames are sent separately from regular frame queue, because of the need to use a decicated path and pad the packets. The packets are also resent separately from the regular probe/lost detection mechanism. A path validation packet is resent 3 times, each time after PTO expiration. Assuming constant PTO, the overall maximum waiting time is 3 * PTO. According to RFC 9000, 8.2.4. Failed Path Validation, the following value is recommended as a validation timeout: A value of three times the larger of the current PTO or the PTO for the new path (using kInitialRtt, as defined in [QUIC-RECOVERY]) is RECOMMENDED. The change adds PTO of the new path to the equation as the lower bound. Also, max_ack_delay is now always accounted for, unlike previously, when it was only used when there are packets in flight. As mentioned before, PACH_CHALLENGE is not considered in-flight by nginx since it's processed separately, but technically it is. diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -14,6 +14,7 @@ static void ngx_quic_set_connection_path ngx_quic_path_t *path); static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path); +static ngx_msec_t ngx_quic_path_pto(ngx_connection_t *c); static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path); static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag); @@ -487,7 +488,6 @@ static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path) { ngx_msec_t pto; - ngx_quic_send_ctx_t *ctx; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); @@ -509,8 +509,7 @@ ngx_quic_validate_path(ngx_connection_t return NGX_ERROR; } - ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); - pto = ngx_quic_pto(c, ctx); + pto = ngx_quic_path_pto(c); path->expires = ngx_current_msec + pto; path->tries = NGX_QUIC_PATH_RETRIES; @@ -523,6 +522,33 @@ ngx_quic_validate_path(ngx_connection_t } +static ngx_msec_t +ngx_quic_path_pto(ngx_connection_t *c) +{ + + ngx_msec_t duration; + ngx_quic_connection_t *qc; + + /* + * RFC 9000, 8.2.4. Failed Path Validation + * + * A value of three times the larger of the current PTO + * or the PTO for the new path (using kInitialRtt, as defined + * in [QUIC-RECOVERY]) is RECOMMENDED. + */ + + qc = ngx_quic_get_connection(c); + + duration = qc->avg_rtt + 4 * qc->rttvar; + + if (duration < 1000) { + duration = 1000; + } + + return duration + qc->ctp.max_ack_delay; +} + + static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path) { @@ -571,14 +597,12 @@ ngx_quic_path_validation_handler(ngx_eve ngx_msec_int_t left, next, pto; ngx_quic_path_t *path, *bkp; ngx_connection_t *c; - ngx_quic_send_ctx_t *ctx; ngx_quic_connection_t *qc; c = ev->data; qc = ngx_quic_get_connection(c); - ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); - pto = ngx_quic_pto(c, ctx); + pto = ngx_quic_path_pto(c); next = -1; now = ngx_current_msec; From arut at nginx.com Tue Mar 28 14:51:39 2023 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Tue, 28 Mar 2023 18:51:39 +0400 Subject: [PATCH 2 of 3] QUIC: allowed ngx_quic_frame_sendto() to return NGX_AGAIN In-Reply-To: References: Message-ID: # HG changeset patch # User Roman Arutyunyan # Date 1679920727 -14400 # Mon Mar 27 16:38:47 2023 +0400 # Branch quic # Node ID c686c97f4abd6e1ca9a2cc2324d5a24f3d035c58 # Parent f76e83412133085a6c82fce2c3e15b2c34a6e959 QUIC: allowed ngx_quic_frame_sendto() to return NGX_AGAIN. Previously, NGX_AGAIN returned by ngx_send() was treated by ngx_quic_frame_sendto() as error, which triggered errors in its callers. However, a blocked socket is not an error. Now NGX_AGAIN is passed as is to the ngx_quic_frame_sendto() callers, which can safely ignore it. diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -46,7 +46,7 @@ ngx_quic_handle_path_challenge_frame(ngx * An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame * to at least the smallest allowed maximum datagram size of 1200 bytes. */ - if (ngx_quic_frame_sendto(c, &frame, 1200, pkt->path) != NGX_OK) { + if (ngx_quic_frame_sendto(c, &frame, 1200, pkt->path) == NGX_ERROR) { return NGX_ERROR; } @@ -575,13 +575,13 @@ ngx_quic_send_path_challenge(ngx_connect */ /* same applies to PATH_RESPONSE frames */ - if (ngx_quic_frame_sendto(c, &frame, 1200, path) != NGX_OK) { + if (ngx_quic_frame_sendto(c, &frame, 1200, path) == NGX_ERROR) { return NGX_ERROR; } ngx_memcpy(frame.u.path_challenge.data, path->challenge2, 8); - if (ngx_quic_frame_sendto(c, &frame, 1200, path) != NGX_OK) { + if (ngx_quic_frame_sendto(c, &frame, 1200, path) == NGX_ERROR) { return NGX_ERROR; } diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c --- a/src/event/quic/ngx_event_quic_output.c +++ b/src/event/quic/ngx_event_quic_output.c @@ -1269,7 +1269,7 @@ ngx_quic_frame_sendto(ngx_connection_t * sent = ngx_quic_send(c, res.data, res.len, path->sockaddr, path->socklen); if (sent < 0) { - return NGX_ERROR; + return sent; } path->sent += sent; From arut at nginx.com Tue Mar 28 14:51:40 2023 From: arut at nginx.com (=?iso-8859-1?q?Roman_Arutyunyan?=) Date: Tue, 28 Mar 2023 18:51:40 +0400 Subject: [PATCH 3 of 3] QUIC: path MTU discovery In-Reply-To: References: Message-ID: <13d43a278510f131101c.1680015100@arut-laptop> # HG changeset patch # User Roman Arutyunyan # Date 1679993500 -14400 # Tue Mar 28 12:51:40 2023 +0400 # Branch quic # Node ID 13d43a278510f131101c7b19d87455a0171ebe2f # Parent c686c97f4abd6e1ca9a2cc2324d5a24f3d035c58 QUIC: path MTU discovery. MTU selection starts by probing the maximum allowed MTU first. After that, binary search is used to find the path MTU. Maximum allowed MTU is calculated as the minimum of max_udp_payload for client and server, and local interface MTU. diff --git a/auto/unix b/auto/unix --- a/auto/unix +++ b/auto/unix @@ -448,6 +448,54 @@ ngx_feature_test="setsockopt(0, IPPROTO_ . auto/feature +# IP packet fragmentation flags + +ngx_feature="IP_DONTFRAG" +ngx_feature_name="NGX_HAVE_IP_DONTFRAG" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="getsockopt(0, IPPROTO_IP, IP_DONTFRAG, NULL, 0)" +. auto/feature + + +ngx_feature="IPV6_DONTFRAG" +ngx_feature_name="NGX_HAVE_IPV6_DONTFRAG" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="getsockopt(0, IPPROTO_IPV6, IPV6_DONTFRAG, NULL, 0)" +. auto/feature + + +# Linux MTU flags + +ngx_feature="IP_PMTUDISC_DO" +ngx_feature_name="NGX_HAVE_IP_PMTUDISC_DO" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="getsockopt(0, IPPROTO_IP, IP_PMTUDISC_DO, NULL, 0)" +. auto/feature + + +ngx_feature="IPV6_PMTUDISC_DO" +ngx_feature_name="NGX_HAVE_IPV6_PMTUDISC_DO" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="getsockopt(0, IPPROTO_IPV6, IPV6_PMTUDISC_DO, NULL, 0)" +. auto/feature + + ngx_feature="TCP_DEFER_ACCEPT" ngx_feature_name="NGX_HAVE_DEFERRED_ACCEPT" ngx_feature_run=no @@ -920,6 +968,19 @@ ngx_feature_test="int i = FIONREAD; prin . auto/feature +ngx_feature="ioctl(SIOCGIFMTU)" +ngx_feature_name="NGX_HAVE_SIOCGIFMTU" +ngx_feature_run=no +ngx_feature_incs="#include + #include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int i = SIOCGIFMTU; struct ifreq ifr; + ifr.ifr_name[0] = 'e'; printf(\"%d\", i)" +. auto/feature + + ngx_feature="struct tm.tm_gmtoff" ngx_feature_name="NGX_HAVE_GMTOFF" ngx_feature_run=no @@ -1002,3 +1063,17 @@ ngx_feature_test='struct addrinfo *res; if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 1; freeaddrinfo(res)' . auto/feature + + +ngx_feature="getifaddrs()" +ngx_feature_name="NGX_HAVE_GETIFADDRS" +ngx_feature_run=no +ngx_feature_incs="#include + #include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test='struct ifaddrs *ifaddr; + if (getifaddrs(&ifaddr) != 0) return 1; + freeifaddrs(ifaddr)' +. auto/feature diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -1010,6 +1010,74 @@ ngx_configure_listening_sockets(ngx_cycl } #endif + +#if (NGX_HAVE_IP_PMTUDISC_DO) + + if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET) { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IP, IP_PMTUDISC_DO, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IP_PMTUDISC_DO) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#elif (NGX_HAVE_IP_DONTFRAG) + + if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET) { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IP, IP_DONTFRAG, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IP_DONTFRAG) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_PMTUDISC_DO) + + if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET6) { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_PMTUDISC_DO, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IPV6_PMTUDISC_DO) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#elif (NGX_HAVE_INET6 && NGX_HAVE_IPV6_DONTFRAG) + + if (ls[i].quic && ls[i].sockaddr->sa_family == AF_INET6) { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_DONTFRAG, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IPV6_DONTFRAG) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#endif } return; @@ -1507,6 +1575,10 @@ ngx_connection_error(ngx_connection_t *c } #endif + if (err == NGX_EMSGSIZE && c->log_error == NGX_ERROR_IGNORE_EMSGSIZE) { + return 0; + } + if (err == 0 || err == NGX_ECONNRESET #if (NGX_WIN32) @@ -1524,6 +1596,7 @@ ngx_connection_error(ngx_connection_t *c { switch (c->log_error) { + case NGX_ERROR_IGNORE_EMSGSIZE: case NGX_ERROR_IGNORE_EINVAL: case NGX_ERROR_IGNORE_ECONNRESET: case NGX_ERROR_INFO: diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -97,7 +97,8 @@ typedef enum { NGX_ERROR_ERR, NGX_ERROR_INFO, NGX_ERROR_IGNORE_ECONNRESET, - NGX_ERROR_IGNORE_EINVAL + NGX_ERROR_IGNORE_EINVAL, + NGX_ERROR_IGNORE_EMSGSIZE } ngx_connection_log_error_e; diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -10,8 +10,17 @@ #include +#define NGX_QUIC_UDP4_MAX_PACKET 65535 +#define NGX_QUIC_UDP4_HEADER_SIZE 28 + +#define NGX_QUIC_UDP6_MAX_PAYLOAD 65535 +#define NGX_QUIC_UDP6_HEADER_SIZE 48 + + static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); +static ssize_t ngx_quic_get_local_mtu(ngx_connection_t *c, + struct sockaddr *sockaddr); static ngx_int_t ngx_quic_handle_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt); static void ngx_quic_input_handler(ngx_event_t *rev); @@ -149,11 +158,6 @@ ngx_quic_apply_transport_params(ngx_conn ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic maximum packet size is invalid"); return NGX_ERROR; - - } else if (ctp->max_udp_payload_size > ngx_quic_max_udp_payload(c)) { - ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c); - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic client maximum packet size truncated"); } if (ctp->active_connection_id_limit < 2) { @@ -228,6 +232,7 @@ static ngx_quic_connection_t * ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, ngx_quic_header_t *pkt) { + ssize_t mtu; ngx_uint_t i; ngx_quic_tp_t *ctp; ngx_quic_connection_t *qc; @@ -297,7 +302,7 @@ ngx_quic_new_connection(ngx_connection_t ctp = &qc->ctp; /* defaults to be used before actual client parameters are received */ - ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c); + ctp->max_udp_payload_size = NGX_QUIC_MAX_UDP_PAYLOAD_SIZE; ctp->ack_delay_exponent = NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT; ctp->max_ack_delay = NGX_QUIC_DEFAULT_MAX_ACK_DELAY; ctp->active_connection_id_limit = 2; @@ -317,6 +322,18 @@ ngx_quic_new_connection(ngx_connection_t qc->congestion.ssthresh = (size_t) -1; qc->congestion.recovery_start = ngx_current_msec; + qc->max_mtu = ngx_min(qc->tp.max_udp_payload_size, + qc->ctp.max_udp_payload_size); + + mtu = ngx_quic_get_local_mtu(c, c->local_sockaddr); + if (mtu == NGX_ERROR) { + return NULL; + } + + if (mtu > 0 && (size_t) mtu < qc->max_mtu) { + qc->max_mtu = mtu; + } + if (pkt->validated && pkt->retried) { qc->tp.retry_scid.len = pkt->dcid.len; qc->tp.retry_scid.data = ngx_pstrdup(c->pool, &pkt->dcid); @@ -347,6 +364,90 @@ ngx_quic_new_connection(ngx_connection_t } +static ssize_t +ngx_quic_get_local_mtu(ngx_connection_t *c, struct sockaddr *sockaddr) +{ +#if (NGX_HAVE_GETIFADDRS && NGX_HAVE_SIOCGIFMTU) + + size_t mtu; + struct ifreq ifr; + struct ifaddrs *ifaddrs, *ifa; + + if (sockaddr->sa_family != AF_INET +#if (NGX_HAVE_INET6) + && sockaddr->sa_family != AF_INET6 +#endif + ) + { + return NGX_DECLINED; + } + + if (getifaddrs(&ifaddrs) == -1) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "getifaddrs() failed"); + return NGX_ERROR; + } + + for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) { + continue; + } + + if (ngx_cmp_sockaddr(sockaddr, 0, ifa->ifa_addr, 0, 0) != NGX_OK) { + continue; + } + + ngx_memzero(&ifr, sizeof(struct ifreq)); + strcpy(ifr.ifr_name, ifa->ifa_name); + + freeifaddrs(ifaddrs); + + if (ioctl(c->fd, SIOCGIFMTU, &ifr)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "ioctl(SIOCGIFMTU) failed"); + return NGX_ERROR; + } + + mtu = ifr.ifr_mtu; + + if (sockaddr->sa_family == AF_INET) { + if (mtu > NGX_QUIC_UDP4_MAX_PACKET) { + mtu = NGX_QUIC_UDP4_MAX_PACKET; + } + + if (mtu <= NGX_QUIC_UDP4_HEADER_SIZE) { + return NGX_DECLINED; + } + + mtu -= NGX_QUIC_UDP4_HEADER_SIZE; + +#if (NGX_HAVE_INET6) + } else { /* sockaddr->sa_family == AF_INET6 */ + + if (mtu <= NGX_QUIC_UDP6_HEADER_SIZE) { + return NGX_DECLINED; + } + + mtu -= NGX_QUIC_UDP6_HEADER_SIZE; + + if (mtu > NGX_QUIC_UDP6_MAX_PAYLOAD) { + mtu = NGX_QUIC_UDP6_MAX_PAYLOAD; + } +#endif + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic local mtu:%uz", mtu); + + return mtu; + } + + freeifaddrs(ifaddrs); + +#endif + + return NGX_DECLINED; +} + + static ngx_int_t ngx_quic_handle_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt) { diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c --- a/src/event/quic/ngx_event_quic_ack.c +++ b/src/event/quic/ngx_event_quic_ack.c @@ -229,6 +229,12 @@ ngx_quic_handle_ack_frame_range(ngx_conn qc = ngx_quic_get_connection(c); + if (ctx->level == ssl_encryption_application) { + if (ngx_quic_handle_path_mtu_ack(c, qc->path, min, max) != NGX_OK) { + return NGX_ERROR; + } + } + st->max_pn = NGX_TIMER_INFINITE; found = 0; diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h --- a/src/event/quic/ngx_event_quic_connection.h +++ b/src/event/quic/ngx_event_quic_connection.h @@ -89,14 +89,21 @@ struct ngx_quic_path_s { ngx_sockaddr_t sa; socklen_t socklen; ngx_quic_client_id_t *cid; - ngx_msec_t expires; - ngx_uint_t tries; + ngx_msec_t valid_expires; + ngx_msec_t mtu_expires; + ngx_uint_t valid_tries; + ngx_uint_t mtu_tries; + ngx_uint_t mtu_steps; ngx_uint_t tag; + size_t mtu; + size_t mtud; + size_t max_mtu; off_t sent; off_t received; u_char challenge1[8]; u_char challenge2[8]; uint64_t seqnum; + uint64_t mtu_pnum[NGX_QUIC_PATH_RETRIES]; ngx_str_t addr_text; u_char text[NGX_SOCKADDR_STRLEN]; unsigned validated:1; @@ -206,6 +213,8 @@ struct ngx_quic_connection_s { uint64_t server_seqnum; uint64_t path_seqnum; + size_t max_mtu; + ngx_quic_tp_t tp; ngx_quic_tp_t ctp; diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -10,6 +10,10 @@ #include +#define NGX_QUIC_MAX_MTU_STEPS 7 +#define NGX_QUIC_MTU_PRECISION 4 + + static void ngx_quic_set_connection_path(ngx_connection_t *c, ngx_quic_path_t *path); static ngx_int_t ngx_quic_validate_path(ngx_connection_t *c, @@ -17,7 +21,13 @@ static ngx_int_t ngx_quic_validate_path( static ngx_msec_t ngx_quic_path_pto(ngx_connection_t *c); static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path); +static ngx_int_t ngx_quic_expire_path_mtu(ngx_connection_t *c, + ngx_quic_path_t *path, ngx_msec_int_t *next); +static ngx_int_t ngx_quic_expire_path(ngx_connection_t *c, + ngx_quic_path_t *path, ngx_msec_int_t *next); static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag); +static ngx_int_t ngx_quic_send_path_mtu_probe(ngx_connection_t *c, + ngx_quic_path_t *path); ngx_int_t @@ -170,6 +180,10 @@ valid: path->validating = 0; path->limited = 0; + if (ngx_quic_discover_path_mtu(c, path) != NGX_OK) { + return NGX_ERROR; + } + return NGX_OK; } @@ -208,6 +222,8 @@ ngx_quic_new_path(ngx_connection_t *c, path->limited = 1; + path->mtu = NGX_QUIC_MIN_INITIAL_SIZE; + path->seqnum = qc->path_seqnum++; path->sockaddr = &path->sa.sockaddr; @@ -505,14 +521,14 @@ ngx_quic_validate_path(ngx_connection_t return NGX_ERROR; } + path->valid_tries = 0; + if (ngx_quic_send_path_challenge(c, path) != NGX_OK) { return NGX_ERROR; } pto = ngx_quic_path_pto(c); - - path->expires = ngx_current_msec + pto; - path->tries = NGX_QUIC_PATH_RETRIES; + path->valid_expires = ngx_current_msec + pto; if (!qc->path_validation.timer_set) { ngx_add_timer(&qc->path_validation, pto); @@ -556,7 +572,7 @@ ngx_quic_send_path_challenge(ngx_connect ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic path seq:%uL send path_challenge tries:%ui", - path->seqnum, path->tries); + path->seqnum, path->valid_tries); ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); @@ -592,20 +608,16 @@ ngx_quic_send_path_challenge(ngx_connect void ngx_quic_path_validation_handler(ngx_event_t *ev) { - ngx_msec_t now; ngx_queue_t *q; - ngx_msec_int_t left, next, pto; - ngx_quic_path_t *path, *bkp; + ngx_msec_int_t next; + ngx_quic_path_t *path; ngx_connection_t *c; ngx_quic_connection_t *qc; c = ev->data; qc = ngx_quic_get_connection(c); - pto = ngx_quic_path_pto(c); - next = -1; - now = ngx_current_msec; q = ngx_queue_head(&qc->paths); @@ -614,76 +626,12 @@ ngx_quic_path_validation_handler(ngx_eve path = ngx_queue_data(q, ngx_quic_path_t, queue); q = ngx_queue_next(q); - if (!path->validating) { - continue; - } - - left = path->expires - now; - - if (left > 0) { - - if (next == -1 || left < next) { - next = left; - } - - continue; - } - - if (--path->tries) { - path->expires = ngx_current_msec + pto; - - if (next == -1 || pto < next) { - next = pto; - } - - /* retransmit */ - (void) ngx_quic_send_path_challenge(c, path); - - continue; + if (ngx_quic_expire_path_mtu(c, path, &next) != NGX_OK) { + ngx_quic_close_connection(c, NGX_ERROR); + return; } - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, - "quic path seq:%uL validation failed", path->seqnum); - - /* found expired path */ - - path->validated = 0; - path->validating = 0; - path->limited = 1; - - - /* RFC 9000, 9.3.2. On-Path Address Spoofing - * - * To protect the connection from failing due to such a spurious - * migration, an endpoint MUST revert to using the last validated - * peer address when validation of a new peer address fails. - */ - - if (qc->path == path) { - /* active path validation failed */ - - bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP); - - if (bkp == NULL) { - qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH; - qc->error_reason = "no viable path"; - ngx_quic_close_connection(c, NGX_ERROR); - return; - } - - qc->path = bkp; - qc->path->tag = NGX_QUIC_PATH_ACTIVE; - - ngx_quic_set_connection_path(c, qc->path); - - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "quic path seq:%uL addr:%V is restored from backup", - qc->path->seqnum, &qc->path->addr_text); - - ngx_quic_path_dbg(c, "is active", qc->path); - } - - if (ngx_quic_free_path(c, path) != NGX_OK) { + if (ngx_quic_expire_path(c, path, &next) != NGX_OK) { ngx_quic_close_connection(c, NGX_ERROR); return; } @@ -693,3 +641,290 @@ ngx_quic_path_validation_handler(ngx_eve ngx_add_timer(&qc->path_validation, next); } } + + +static ngx_int_t +ngx_quic_expire_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path, + ngx_msec_int_t *next) +{ + ngx_int_t rc; + ngx_msec_t now; + ngx_msec_int_t left, pto; + + if (!path->mtud) { + return NGX_OK; + } + + now = ngx_current_msec; + + left = path->mtu_expires - now; + + if (left > 0) { + + if (*next == -1 || left < *next) { + *next = left; + } + + return NGX_OK; + } + + if (++path->mtu_tries < NGX_QUIC_PATH_RETRIES) { + pto = ngx_quic_path_pto(c); + + path->mtu_expires = ngx_current_msec + pto; + + if (*next == -1 || pto < *next) { + *next = pto; + } + + rc = ngx_quic_send_path_mtu_probe(c, path); + if (rc != NGX_DECLINED) { + return rc; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic path seq:%uL mtu probe failed", path->seqnum); + + path->max_mtu = path->mtud; + path->mtud = 0; + + return ngx_quic_discover_path_mtu(c, path); +} + + +static ngx_int_t +ngx_quic_expire_path(ngx_connection_t *c, ngx_quic_path_t *path, + ngx_msec_int_t *next) +{ + ngx_msec_t now; + ngx_msec_int_t left, pto; + ngx_quic_path_t *bkp; + ngx_quic_connection_t *qc; + + if (!path->validating) { + return NGX_OK; + } + + qc = ngx_quic_get_connection(c); + + now = ngx_current_msec; + + left = path->valid_expires - now; + + if (left > 0) { + + if (*next == -1 || left < *next) { + *next = left; + } + + return NGX_OK; + } + + if (++path->valid_tries < NGX_QUIC_PATH_RETRIES) { + pto = ngx_quic_path_pto(c); + + path->valid_expires = ngx_current_msec + pto; + + if (*next == -1 || pto < *next) { + *next = pto; + } + + /* retransmit */ + (void) ngx_quic_send_path_challenge(c, path); + + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic path seq:%uL validation failed", path->seqnum); + + /* found expired path */ + + path->validated = 0; + path->validating = 0; + path->limited = 1; + + + /* RFC 9000, 9.3.2. On-Path Address Spoofing + * + * To protect the connection from failing due to such a spurious + * migration, an endpoint MUST revert to using the last validated + * peer address when validation of a new peer address fails. + */ + + if (qc->path == path) { + /* active path validation failed */ + + bkp = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP); + + if (bkp == NULL) { + qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH; + qc->error_reason = "no viable path"; + return NGX_ERROR; + } + + qc->path = bkp; + qc->path->tag = NGX_QUIC_PATH_ACTIVE; + + ngx_quic_set_connection_path(c, qc->path); + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic path seq:%uL addr:%V is restored from backup", + qc->path->seqnum, &qc->path->addr_text); + + ngx_quic_path_dbg(c, "is active", qc->path); + } + + if (ngx_quic_free_path(c, path) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_quic_discover_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path) +{ + ngx_int_t rc; + ngx_uint_t i; + ngx_msec_t pto; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + +again: + + if (path->mtu_steps == 0) { + path->max_mtu = qc->max_mtu; + path->mtud = path->max_mtu; + + } else if (path->mtu_steps >= NGX_QUIC_MAX_MTU_STEPS + || (path->max_mtu - path->mtu) <= NGX_QUIC_MTU_PRECISION) + { + return NGX_OK; + + } else { + path->mtud = (path->mtu + path->max_mtu) / 2; + } + + path->mtu_steps++; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic initiated mtu discovery of path seq:%uL", + path->seqnum); + + for (i = 0; i < NGX_QUIC_PATH_RETRIES; i++) { + path->mtu_pnum[i] = NGX_QUIC_UNSET_PN; + } + + path->mtu_tries = 0; + + rc = ngx_quic_send_path_mtu_probe(c, path); + + if (rc == NGX_DECLINED) { + path->max_mtu = path->mtud; + path->mtud = 0; + goto again; + } + + if (rc == NGX_ERROR) { + path->mtud = 0; + return NGX_ERROR; + } + + /* rc == NGX_OK */ + + pto = ngx_quic_path_pto(c); + path->mtu_expires = ngx_current_msec + pto; + + if (!qc->path_validation.timer_set) { + ngx_add_timer(&qc->path_validation, pto); + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path) +{ + ngx_int_t rc; + ngx_uint_t log_error; + ngx_quic_frame_t frame; + ngx_quic_send_ctx_t *ctx; + ngx_quic_connection_t *qc; + + ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); + + frame.level = ssl_encryption_application; + frame.type = NGX_QUIC_FT_PING; + + qc = ngx_quic_get_connection(c); + ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); + path->mtu_pnum[path->mtu_tries] = ctx->pnum; + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic path seq:%uL send mtu probe " + "size:%uz pnum:%uL tries:%ui", + path->seqnum, path->mtud, ctx->pnum, path->mtu_tries); + + log_error = c->log_error; + c->log_error = NGX_ERROR_IGNORE_EMSGSIZE; + + rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path); + c->log_error = log_error; + + if (rc == NGX_ERROR) { + if (c->write->error) { + c->write->error = 0; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic rejected mtu probe of path seq:%uL", + path->seqnum); + + return NGX_DECLINED; + } + + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_quic_handle_path_mtu_ack(ngx_connection_t *c, ngx_quic_path_t *path, + uint64_t min, uint64_t max) +{ + uint64_t pnum; + ngx_uint_t i; + + if (!path->mtud) { + return NGX_OK; + } + + for (i = 0; i < NGX_QUIC_PATH_RETRIES; i++) { + pnum = path->mtu_pnum[i]; + + if (pnum == NGX_QUIC_UNSET_PN) { + break; + } + + if (pnum < min || pnum > max) { + continue; + } + + path->mtu = path->mtud; + path->mtud = 0; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic path seq:%uL mtu ack size:%uz", + path->seqnum, path->mtu); + + return ngx_quic_discover_path_mtu(c, path); + } + + return NGX_OK; +} diff --git a/src/event/quic/ngx_event_quic_migration.h b/src/event/quic/ngx_event_quic_migration.h --- a/src/event/quic/ngx_event_quic_migration.h +++ b/src/event/quic/ngx_event_quic_migration.h @@ -39,4 +39,9 @@ ngx_int_t ngx_quic_handle_migration(ngx_ void ngx_quic_path_validation_handler(ngx_event_t *ev); +ngx_int_t ngx_quic_discover_path_mtu(ngx_connection_t *c, + ngx_quic_path_t *path); +ngx_int_t ngx_quic_handle_path_mtu_ack(ngx_connection_t *c, + ngx_quic_path_t *path, uint64_t min, uint64_t max); + #endif /* _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_ */ diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c --- a/src/event/quic/ngx_event_quic_output.c +++ b/src/event/quic/ngx_event_quic_output.c @@ -10,9 +10,6 @@ #include -#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT 1252 -#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6 1232 - #define NGX_QUIC_MAX_UDP_SEGMENT_BUF 65487 /* 65K - IPv6 header */ #define NGX_QUIC_MAX_SEGMENTS 64 /* UDP_MAX_SEGMENTS */ @@ -61,21 +58,6 @@ static size_t ngx_quic_path_limit(ngx_co size_t size); -size_t -ngx_quic_max_udp_payload(ngx_connection_t *c) -{ - /* TODO: path MTU discovery */ - -#if (NGX_HAVE_INET6) - if (c->sockaddr->sa_family == AF_INET6) { - return NGX_QUIC_MAX_UDP_PAYLOAD_OUT6; - } -#endif - - return NGX_QUIC_MAX_UDP_PAYLOAD_OUT; -} - - ngx_int_t ngx_quic_output(ngx_connection_t *c) { @@ -142,10 +124,7 @@ ngx_quic_create_datagrams(ngx_connection p = dst; - len = ngx_min(qc->ctp.max_udp_payload_size, - NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); - - len = ngx_quic_path_limit(c, path, len); + len = ngx_quic_path_limit(c, path, path->mtu); pad = ngx_quic_get_padding_level(c); @@ -271,17 +250,19 @@ ngx_quic_allow_segmentation(ngx_connecti { size_t bytes, len; ngx_queue_t *q; + ngx_quic_path_t *path; ngx_quic_frame_t *f; ngx_quic_send_ctx_t *ctx; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); + path = qc->path; if (!qc->conf->gso_enabled) { return 0; } - if (qc->path->limited) { + if (path->limited) { /* don't even try to be faster on non-validated paths */ return 0; } @@ -299,9 +280,7 @@ ngx_quic_allow_segmentation(ngx_connecti ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); bytes = 0; - - len = ngx_min(qc->ctp.max_udp_payload_size, - NGX_QUIC_MAX_UDP_SEGMENT_BUF); + len = path->mtu; for (q = ngx_queue_head(&ctx->frames); q != ngx_queue_sentinel(&ctx->frames); @@ -345,8 +324,7 @@ ngx_quic_create_segments(ngx_connection_ return NGX_ERROR; } - segsize = ngx_min(qc->ctp.max_udp_payload_size, - NGX_QUIC_MAX_UDP_SEGMENT_BUF); + segsize = ngx_min(path->mtu, NGX_QUIC_MAX_UDP_SEGMENT_BUF); p = dst; end = dst + sizeof(dst); diff --git a/src/event/quic/ngx_event_quic_output.h b/src/event/quic/ngx_event_quic_output.h --- a/src/event/quic/ngx_event_quic_output.h +++ b/src/event/quic/ngx_event_quic_output.h @@ -12,8 +12,6 @@ #include -size_t ngx_quic_max_udp_payload(ngx_connection_t *c); - ngx_int_t ngx_quic_output(ngx_connection_t *c); ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c, diff --git a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c --- a/src/event/quic/ngx_event_quic_ssl.c +++ b/src/event/quic/ngx_event_quic_ssl.c @@ -499,6 +499,10 @@ ngx_quic_crypto_input(ngx_connection_t * return NGX_ERROR; } + if (ngx_quic_discover_path_mtu(c, qc->path) != NGX_OK) { + return NGX_ERROR; + } + if (ngx_quic_init_streams(c) != NGX_OK) { return NGX_ERROR; } diff --git a/src/os/unix/ngx_darwin_config.h b/src/os/unix/ngx_darwin_config.h --- a/src/os/unix/ngx_darwin_config.h +++ b/src/os/unix/ngx_darwin_config.h @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/os/unix/ngx_errno.h b/src/os/unix/ngx_errno.h --- a/src/os/unix/ngx_errno.h +++ b/src/os/unix/ngx_errno.h @@ -54,6 +54,7 @@ typedef int ngx_err_t; #define NGX_ENOMOREFILES 0 #define NGX_ELOOP ELOOP #define NGX_EBADF EBADF +#define NGX_EMSGSIZE EMSGSIZE #if (NGX_HAVE_OPENAT) #define NGX_EMLINK EMLINK diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h --- a/src/os/unix/ngx_freebsd_config.h +++ b/src/os/unix/ngx_freebsd_config.h @@ -48,6 +48,9 @@ #include /* setproctitle() before 4.1 */ #include #include +#include +#include +#include #include diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h --- a/src/os/unix/ngx_linux_config.h +++ b/src/os/unix/ngx_linux_config.h @@ -54,6 +54,8 @@ #include #include #include /* uname() */ +#include +#include #include diff --git a/src/os/unix/ngx_posix_config.h b/src/os/unix/ngx_posix_config.h --- a/src/os/unix/ngx_posix_config.h +++ b/src/os/unix/ngx_posix_config.h @@ -140,6 +140,17 @@ typedef struct aiocb ngx_aiocb_t; #endif +#if (NGX_HAVE_SIOCGIFMTU) +#include +#include +#endif + + +#if (NGX_HAVE_GETIFADDRS) +#include +#endif + + #define NGX_LISTEN_BACKLOG 511 #define ngx_debug_init() diff --git a/src/os/unix/ngx_solaris_config.h b/src/os/unix/ngx_solaris_config.h --- a/src/os/unix/ngx_solaris_config.h +++ b/src/os/unix/ngx_solaris_config.h @@ -88,6 +88,17 @@ #endif +#if (NGX_HAVE_SIOCGIFMTU) +#include +#include +#endif + + +#if (NGX_HAVE_GETIFADDRS) +#include +#endif + + #define NGX_LISTEN_BACKLOG 511 From mdounin at mdounin.ru Tue Mar 28 15:11:24 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 28 Mar 2023 18:11:24 +0300 Subject: nginx 1.23.4 changes draft In-Reply-To: <86F4BD59-693D-4810-9694-B671A99E2C28@nginx.com> References: <86F4BD59-693D-4810-9694-B671A99E2C28@nginx.com> Message-ID: Hello! On Tue, Mar 28, 2023 at 06:39:41PM +0400, Sergey Kandaurov wrote: > > On 28 Mar 2023, at 18:14, Maxim Dounin wrote: > > > > Hello! > > > > Changes with nginx 1.23.4 28 Mar 2023 > > > > *) Change: now TLSv1.3 protocol is enabled by default. > > > > *) Change: now nginx issues a warning if protocol parameters of a > > listening socket are redefined. > > > > *) Change: now nginx closes connections with lingering if pipelining was > > used by the client. > > > > *) Feature: byte ranges support in the ngx_http_gzip_static_module. > > > > *) Bugfix: port ranges in the "listen" directive did not work; the bug > > had appeared in 1.23.3. > > Thanks to Valentin Bartenev. > > > > *) Bugfix: incorrect location might be chosen to process a request if a > > prefix location longer than 255 characters was used in the > > configuration. > > > > *) Bugfix: non-ASCII characters in file names on Windows were not > > supported by the ngx_http_autoindex_module, the ngx_http_dav_module, > > and the "include" directive. > > > > *) Change: the logging level of the "data length too long", "length too > > short", "bad legacy version", "no shared signature algorithms", "bad > > digest length", "missing sigalgs extension", "encrypted length too > > long", "bad length", "bad key update", "mixed handshake and non > > handshake data", "ccs received early", "data between ccs and > > finished", "packet length too long", "too many warn alerts", "record > > too small", and "got a fin before a ccs" SSL errors has been lowered > > from "crit" to "info". > > > > *) Bugfix: a socket leak might occur when using HTTP/2 and the > > "error_page" directive to redirect errors with code 400. > > > > *) Bugfix: messages about logging to syslog errors did not contain > > information that the errors happened while logging to syslog. > > Thanks to Safar Safarly. > > > > *) Workaround: "gzip filter failed to use preallocated memory" alerts > > appeared in logs when using zlib-ng. > > > > *) Bugfix: in the mail proxy server. > > > > > > Изменения в nginx 1.23.4 28.03.2023 > > > > *) Изменение: теперь протокол TLSv1.3 разрешён по умолчанию. > > > > *) Изменение: теперь nginx выдаёт предупреждение при переопределении > > параметров listen-сокета, задающих используемые протоколы. > > > > *) Изменение: теперь, если клиент использует pipelining, nginx закрывает > > соединения с ожиданием дополнительных данных (lingering close). > > > > *) Добавление: поддержка byte ranges для ответов модуля > > ngx_http_gzip_static_module. > > > > *) Исправление: диапазоны портов в директиве listen не работали; ошибка > > появилась в nginx 1.23.3. > > "nginx" is usually omitted here (per changes grep) Thanks, fixed. > > Спасибо Валентину Бартеневу. > > > > *) Исправление: для обработки запроса мог быть выбран неверный location, > > если в конфигурации использовался префиксный location длиннее 255 > > символов. > > > > *) Исправление: не-ASCII символы в именах файлов на Windows не > > поддерживались модулями ngx_http_autoindex_module и > > ngx_http_dav_module, а также директивой include. > > > > *) Изменение: уровень логгирования ошибок SSL "data length too long", > > "length too short", "bad legacy version", "no shared signature > > algorithms", "bad digest length", "missing sigalgs extension", > > "encrypted length too long", "bad length", "bad key update", "mixed > > handshake and non handshake data", "ccs received early", "data > > between ccs and finished", "packet length too long", "too many warn > > alerts", "record too small", и "got a fin before a ccs" понижен с > > уровня crit до info. > > > > *) Исправление: при использовании HTTP/2 и директивы error_page для > > перенаправления ошибок с кодом 400 могла происходить утечка сокетов. > > > > *) Исправление: сообщения об ошибках записи в syslog не содержали > > информации о том, ошибки происходили в процессе записи в syslog. > > "о том, что" Fixed, thanks. > > Спасибо Safar Safarly. > > > > *) Изменение: при использовании zlib-ng в логах появлялись сообщения > > "gzip filter failed to use preallocated memory". > > > > *) Исправление: в почтовом прокси-сервере. > > > > Otherwise, looks good. Thanks, pushed to: http://mdounin.ru/hg/nginx http://mdounin.ru/hg/nginx.org Release files: http://mdounin.ru/temp/nginx-1.23.4.tar.gz http://mdounin.ru/temp/nginx-1.23.4.tar.gz.asc http://mdounin.ru/temp/nginx-1.23.4.zip http://mdounin.ru/temp/nginx-1.23.4.zip.asc -- Maxim Dounin http://mdounin.ru/ From thresh at nginx.com Tue Mar 28 16:04:58 2023 From: thresh at nginx.com (Konstantin Pavlov) Date: Tue, 28 Mar 2023 16:04:58 +0000 Subject: [nginx] Updated OpenSSL used for win32 builds. Message-ID: details: https://hg.nginx.org/nginx/rev/09affff4fd35 branches: changeset: 8154:09affff4fd35 user: Maxim Dounin date: Tue Mar 28 02:25:55 2023 +0300 description: Updated OpenSSL used for win32 builds. diffstat: misc/GNUmakefile | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r fcb2333c9982 -r 09affff4fd35 misc/GNUmakefile --- a/misc/GNUmakefile Mon Mar 27 21:25:05 2023 +0300 +++ b/misc/GNUmakefile Tue Mar 28 02:25:55 2023 +0300 @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-1.1.1s +OPENSSL = openssl-1.1.1t ZLIB = zlib-1.2.13 PCRE = pcre2-10.39 From thresh at nginx.com Tue Mar 28 16:05:01 2023 From: thresh at nginx.com (Konstantin Pavlov) Date: Tue, 28 Mar 2023 16:05:01 +0000 Subject: [nginx] nginx-1.23.4-RELEASE Message-ID: details: https://hg.nginx.org/nginx/rev/ac779115ed6e branches: changeset: 8155:ac779115ed6e user: Maxim Dounin date: Tue Mar 28 18:01:53 2023 +0300 description: nginx-1.23.4-RELEASE diffstat: docs/xml/nginx/changes.xml | 157 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 157 insertions(+), 0 deletions(-) diffs (167 lines): diff -r 09affff4fd35 -r ac779115ed6e docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml Tue Mar 28 02:25:55 2023 +0300 +++ b/docs/xml/nginx/changes.xml Tue Mar 28 18:01:53 2023 +0300 @@ -5,6 +5,163 @@ + + + + +теперь протокол TLSv1.3 разрешён по умолчанию. + + +now TLSv1.3 protocol is enabled by default. + + + + + +теперь nginx выдаёт предупреждение +при переопределении параметров listen-сокета, задающих используемые протоколы. + + +now nginx issues a warning +if protocol parameters of a listening socket are redefined. + + + + + +теперь, если клиент использует pipelining, +nginx закрывает соединения с ожиданием дополнительных данных (lingering close). + + +now nginx closes connections with lingering +if pipelining was used by the client. + + + + + +поддержка byte ranges для ответов модуля ngx_http_gzip_static_module. + + +byte ranges support in the ngx_http_gzip_static_module. + + + + + +диапазоны портов в директиве listen не работали; +ошибка появилась в 1.23.3.
+Спасибо Валентину Бартеневу. +
+ +port ranges in the "listen" directive did not work; +the bug had appeared in 1.23.3.
+Thanks to Valentin Bartenev. +
+
+ + + +для обработки запроса мог быть выбран неверный location, +если в конфигурации использовался +префиксный location длиннее 255 символов. + + +incorrect location might be chosen to process a request +if a prefix location longer than 255 characters +was used in the configuration. + + + + + +не-ASCII символы в именах файлов на Windows +не поддерживались модулями ngx_http_autoindex_module и +ngx_http_dav_module, а также директивой include. + + +non-ASCII characters in file names on Windows were not supported +by the ngx_http_autoindex_module, the ngx_http_dav_module, +and the "include" directive. + + + + + +уровень логгирования ошибок SSL +"data length too long", "length too short", "bad legacy version", +"no shared signature algorithms", "bad digest length", +"missing sigalgs extension", "encrypted length too long", +"bad length", "bad key update", "mixed handshake and non handshake data", +"ccs received early", "data between ccs and finished", +"packet length too long", "too many warn alerts", "record too small", +и "got a fin before a ccs" +понижен с уровня crit до info. + + +the logging level of the +"data length too long", "length too short", "bad legacy version", +"no shared signature algorithms", "bad digest length", +"missing sigalgs extension", "encrypted length too long", +"bad length", "bad key update", "mixed handshake and non handshake data", +"ccs received early", "data between ccs and finished", +"packet length too long", "too many warn alerts", "record too small", +and "got a fin before a ccs" SSL errors +has been lowered from "crit" to "info". + + + + + +при использовании HTTP/2 и директивы error_page +для перенаправления ошибок с кодом 400 +могла происходить утечка сокетов. + + +a socket leak might occur +when using HTTP/2 and the "error_page" directive +to redirect errors with code 400. + + + + + +сообщения об ошибках записи в syslog +не содержали информации о том, что +ошибки происходили в процессе записи в syslog.
+Спасибо Safar Safarly. +
+ +messages about logging to syslog errors +did not contain information +that the errors happened while logging to syslog.
+Thanks to Safar Safarly. +
+
+ + + +при использовании zlib-ng +в логах появлялись сообщения "gzip filter failed to use preallocated memory". + + +"gzip filter failed to use preallocated memory" alerts appeared in logs +when using zlib-ng. + + + + + +в почтовом прокси-сервере. + + +in the mail proxy server. + + + +
+ + From thresh at nginx.com Tue Mar 28 16:05:04 2023 From: thresh at nginx.com (Konstantin Pavlov) Date: Tue, 28 Mar 2023 16:05:04 +0000 Subject: [nginx] release-1.23.4 tag Message-ID: details: https://hg.nginx.org/nginx/rev/5f1d05a21287 branches: changeset: 8156:5f1d05a21287 user: Maxim Dounin date: Tue Mar 28 18:01:54 2023 +0300 description: release-1.23.4 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff -r ac779115ed6e -r 5f1d05a21287 .hgtags --- a/.hgtags Tue Mar 28 18:01:53 2023 +0300 +++ b/.hgtags Tue Mar 28 18:01:54 2023 +0300 @@ -471,3 +471,4 @@ 5da2c0902e8e2aa4534008a582a60c61c135960e a63d0a70afea96813ba6667997bc7d68b5863f0d release-1.23.1 aa901551a7ebad1e8b0f8c11cb44e3424ba29707 release-1.23.2 ff3afd1ce6a6b65057741df442adfaa71a0e2588 release-1.23.3 +ac779115ed6ee4f3039e9aea414a54e560450ee2 release-1.23.4 From pluknet at nginx.com Tue Mar 28 16:37:02 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 28 Mar 2023 20:37:02 +0400 Subject: [PATCH] Added TLSv1.3 to the default value of ssl_protocols and friends In-Reply-To: <23389F73-D1F7-4042-A2F4-B4E1AB47143E@nginx.com> References: <477d0fe1e6cb95533ffb.1679383149@ORK-ML-00007151> <23389F73-D1F7-4042-A2F4-B4E1AB47143E@nginx.com> Message-ID: <270BD3ED-74F2-433B-BFB6-8E200EBDDC67@nginx.com> > On 28 Mar 2023, at 18:37, Yaroslav Zhuravlev wrote: > > > >> On 24 Mar 2023, at 15:30, Maxim Konovalov wrote: >> >> Hi Yar, >> >> On 21.03.2023 00:19, Yaroslav Zhuravlev wrote: >>> diff --git a/xml/en/docs/http/ngx_http_grpc_module.xml b/xml/en/docs/http/ngx_http_grpc_module.xml >>> --- a/xml/en/docs/http/ngx_http_grpc_module.xml >>> +++ b/xml/en/docs/http/ngx_http_grpc_module.xml >> [...] >>> @@ -633,7 +633,7 @@ >>> [TLSv1.1] >>> [TLSv1.2] >>> [TLSv1.3] >>> -TLSv1 TLSv1.1 TLSv1.2 >>> +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 >>> http >>> server >>> location >> >> It makes sense to add a history note here and in other modules that have ssl_protocol directive. > > [...] > > Hi Maxim, > > Thank you for the feedback, the patch was accordingly updated: > > # HG changeset patch > # User Yaroslav Zhuravlev > # Date 1678486627 0 > # Fri Mar 10 22:17:07 2023 +0000 > # Node ID 6096d51ff4d2abecc67b47bc09cfbf03be69f7b0 > # Parent ac7518a1fe1c74daba708e30405a9b5d33f606e1 > Added TLSv1.3 to the default value of ssl_protocols and friends. > > diff --git a/xml/en/docs/http/configuring_https_servers.xml b/xml/en/docs/http/configuring_https_servers.xml > --- a/xml/en/docs/http/configuring_https_servers.xml > +++ b/xml/en/docs/http/configuring_https_servers.xml > @@ -8,7 +8,7 @@ >
link="/en/docs/http/configuring_https_servers.html" > lang="en" > - rev="13" > + rev="14" > author="Igor Sysoev" > editor="Brian Mercer"> > > @@ -31,7 +31,7 @@ > server_name www.example.com; > ssl_certificate www.example.com.crt; > ssl_certificate_key www.example.com.key; > - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; > + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; > ssl_ciphers HIGH:!aNULL:!MD5; > ... > } > @@ -59,7 +59,7 @@ > can be used to limit connections > to include only the strong versions and ciphers of SSL/TLS. > By default nginx uses > -“ssl_protocols TLSv1 TLSv1.1 TLSv1.2” > +“ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3” > and “ssl_ciphers HIGH:!aNULL:!MD5”, > so configuring them explicitly is generally not needed. > Note that default values of these directives were > @@ -110,7 +110,7 @@ > > ssl_certificate www.example.com.crt; > ssl_certificate_key www.example.com.key; > - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; > + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; > ssl_ciphers HIGH:!aNULL:!MD5; > ... > > @@ -446,6 +446,11 @@ > > > > +Version 1.23.4 and later: the default SSL protocols are TLSv1, > +TLSv1.1, TLSv1.2, and TLSv1.3 (if supported by the OpenSSL library). > + > + > + > Version 1.9.1 and later: the default SSL protocols are TLSv1, > TLSv1.1, and TLSv1.2 (if supported by the OpenSSL library). > > diff --git a/xml/en/docs/http/ngx_http_grpc_module.xml b/xml/en/docs/http/ngx_http_grpc_module.xml > --- a/xml/en/docs/http/ngx_http_grpc_module.xml > +++ b/xml/en/docs/http/ngx_http_grpc_module.xml > @@ -10,7 +10,7 @@ > link="/en/docs/http/ngx_http_grpc_module.html" > lang="en" > - rev="8"> > + rev="9"> > >
> > @@ -633,7 +633,7 @@ > [TLSv1.1] > [TLSv1.2] > [TLSv1.3] > -TLSv1 TLSv1.1 TLSv1.2 > +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 > http > server > location > @@ -642,6 +642,13 @@ > Enables the specified protocols for requests to a gRPC SSL server. > > > + > + > +The TLSv1.3 parameter is used by default > +since 1.23.4. > + > + > + > > > > diff --git a/xml/en/docs/http/ngx_http_proxy_module.xml b/xml/en/docs/http/ngx_http_proxy_module.xml > --- a/xml/en/docs/http/ngx_http_proxy_module.xml > +++ b/xml/en/docs/http/ngx_http_proxy_module.xml > @@ -10,7 +10,7 @@ > link="/en/docs/http/ngx_http_proxy_module.html" > lang="en" > - rev="75"> > + rev="76"> > >
> > @@ -2096,7 +2096,7 @@ > [TLSv1.1] > [TLSv1.2] > [TLSv1.3] > -TLSv1 TLSv1.1 TLSv1.2 > +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 > http > server > location > @@ -2106,6 +2106,13 @@ > Enables the specified protocols for requests to a proxied HTTPS server. > > > + > + > +The TLSv1.3 parameter is used by default > +since 1.23.4. > + > + > + > > > > diff --git a/xml/en/docs/http/ngx_http_ssl_module.xml b/xml/en/docs/http/ngx_http_ssl_module.xml > --- a/xml/en/docs/http/ngx_http_ssl_module.xml > +++ b/xml/en/docs/http/ngx_http_ssl_module.xml > @@ -10,7 +10,7 @@ > link="/en/docs/http/ngx_http_ssl_module.html" > lang="en" > - rev="59"> > + rev="60"> > >
> > @@ -76,7 +76,7 @@ > listen 443 ssl; > keepalive_timeout 70; > > - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; > + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; > ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5; > ssl_certificate /usr/local/nginx/conf/cert.pem; > ssl_certificate_key /usr/local/nginx/conf/cert.key; > @@ -595,7 +595,7 @@ > [TLSv1.1] > [TLSv1.2] > [TLSv1.3] > -TLSv1 TLSv1.1 TLSv1.2 > +TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 > http > server > > @@ -609,6 +609,10 @@ > The TLSv1.3 parameter (1.13.0) works only when > OpenSSL 1.1.1 or higher is used. > > + > +The TLSv1.3 parameter is used by default > +since 1.23.4. > + > > > [..] Looks good for me. -- Sergey Kandaurov From pluknet at nginx.com Wed Mar 29 00:05:20 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 29 Mar 2023 04:05:20 +0400 Subject: [PATCH] Tests: run syslog and error_log tests on win32 In-Reply-To: References: Message-ID: <1D82EDDB-380F-4A1C-BD04-3ECC6928DA8F@nginx.com> > On 22 Mar 2023, at 05:02, Maxim Dounin wrote: > > Hello! > > On Mon, Mar 20, 2023 at 06:13:34PM +0400, Sergey Kandaurov wrote: > >> # HG changeset patch >> # User Sergey Kandaurov >> # Date 1679321601 -14400 >> # Mon Mar 20 18:13:21 2023 +0400 >> # Node ID f3225ad9300ee2c11c0dec54b9605e67060b7347 >> # Parent 1e1d0f3874b0c5b1e399ec76b0198b5c9c265a36 >> Tests: run syslog and error_log tests on win32. >> >> They are supposed to work well now, no reason to skip them. > > It might be a good idea to explicitly mention why it wasn't > supposed to work. It wasn't supposed to work due to stderr tests, now fixed. See full description below in the updated series. > >> An exception is logging to stderr. > > And why stderr is not supposed to. > > (Just in case, the culprit is CreateProcess(CREATE_NO_WINDOW) used > when starting worker processes, which creates a process without a > console. This probably can be improved to preserve console if > logging to stderr is used.) Thanks, applied to description. > >> >> diff --git a/error_log.t b/error_log.t >> --- a/error_log.t >> +++ b/error_log.t >> @@ -22,8 +22,6 @@ use Test::Nginx; >> select STDERR; $| = 1; >> select STDOUT; $| = 1; >> >> -plan(skip_all => 'win32') if $^O eq 'MSWin32'; >> - >> my $t = Test::Nginx->new()->has(qw/http limit_req/) >> ->plan(25)->write_file_expand('nginx.conf', <<'EOF'); >> >> @@ -110,7 +108,7 @@ EOF >> open OLDERR, ">&", \*STDERR; >> open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; >> open my $stderr, '<', $t->testdir() . '/stderr' >> - or die "Can't open stderr file: $!"; >> + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; > > It shouldn't be a problem to open the file on Windows. Though > will require explicitly closing it at the end of the test, so the > test suite will be able to remove the test directory in > destructor. > > (In other tests this will also require reordering of daemon > startup, to avoid leaking the descriptor to daemons.) Applied too. >> [..] >> @@ -255,13 +259,16 @@ sub syslog_daemon { >> LocalAddr => "127.0.0.1:$port" >> ); >> >> - open my $fh, '>', $t->testdir() . '/' . $file; >> - select $fh; $| = 1; >> + my $path = $t->testdir() . '/' . $file; >> + open my $fh, '>', $path; >> + close $fh; >> >> while (1) { >> my $buffer; >> $s->recv($buffer, 4096); >> + open $fh, '>>', $path; >> print $fh $buffer . "\n"; >> + close $fh; >> } > > It might be a good idea to clarify in the commit log why this is > needed. (Notably, that pseudo-processes created by Perl's fork() > emulation on win32 cannot be gracefully stopped when they are > blocked in a system call, and using kill('KILL') as nginx does > seems to leave the files open.) Well, I don't like this kind of open/close tango. Reading syslog can be done in the main process, without invoking pseudo-processes with their limitations. This should work with minor number of syslog messages as seen in tests. Below is updated series, syslog part is extracted into a separate change. # HG changeset patch # User Sergey Kandaurov # Date 1680046138 -14400 # Wed Mar 29 03:28:58 2023 +0400 # Node ID be1832cdf571d465aed741b7dcbd583cedc43365 # Parent 0351dee227a8341e442feeb03920a46b259adeb5 Tests: revised reading syslog messages with test daemon. On win32, terminating test daemons blocked by a system call leads to leaking file descriptors, which breaks temp directory removal. See limitations of pseudo-processes used in Perl's fork() emulation. Now it is replaced with reading syslog in the main process. diff --git a/mail_error_log.t b/mail_error_log.t --- a/mail_error_log.t +++ b/mail_error_log.t @@ -90,12 +90,16 @@ open my $stderr, '<', $t->testdir() . '/ or die "Can't open stderr file: $!"; $t->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon); -$t->run_daemon(\&syslog_daemon, port(8981), $t, 's_glob.log'); -$t->run_daemon(\&syslog_daemon, port(8982), $t, 's_info.log'); + +my ($s_glob, $s_info) = map { + IO::Socket::INET->new( + Proto => 'udp', + LocalAddr => '127.0.0.1:' . port($_) + ) + or die "Can't open syslog socket: $!"; +} 8981, 8982; $t->waitforsocket('127.0.0.1:' . port(8144)); -$t->waitforfile($t->testdir . '/s_glob.log'); -$t->waitforfile($t->testdir . '/s_info.log'); $t->run(); @@ -124,16 +128,16 @@ is(lines($t, 'stderr', '[debug]'), 0, 's like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); like($t->read_file('e_glob2.log'), qr!nginx/[.0-9]+!, 'error global 2'); -is_deeply(levels($t, 'e_glob.log'), levels($t, 'e_glob2.log'), - 'multiple error global'); +is_deeply(levels($t->read_file('e_glob.log')), + levels($t->read_file('e_glob2.log')), 'multiple error global'); # syslog parse_syslog_message('syslog', get_syslog()); -is_deeply(levels($t, 's_glob.log'), levels($t, 'e_glob.log'), +is_deeply(levels(read_syslog($s_glob)), levels($t->read_file('e_glob.log')), 'global syslog messages'); -is_deeply(levels($t, 's_info.log'), levels($t, 'e_info.log'), +is_deeply(levels(read_syslog($s_info)), levels($t->read_file('e_info.log')), 'mail syslog messages'); ############################################################################### @@ -153,10 +157,10 @@ sub lines { } sub levels { - my ($t, $file) = @_; + my ($file) = @_; my %levels_hash; - map { $levels_hash{$_}++; } ($t->read_file($file) =~ /(\[\w+\])/g); + map { $levels_hash{$_}++; } ($file =~ /(\[\w+\])/g); return \%levels_hash; } @@ -193,6 +197,19 @@ sub get_syslog { return $data; } +sub read_syslog { + my ($s) = @_; + my $data = ''; + + IO::Select->new($s)->can_read(1); + while (IO::Select->new($s)->can_read(0.1)) { + my $buffer; + sysread($s, $buffer, 4096); + $data .= $buffer; + } + return $data; +} + sub parse_syslog_message { my ($desc, $line) = @_; @@ -246,23 +263,3 @@ SKIP: { } ############################################################################### - -sub syslog_daemon { - my ($port, $t, $file) = @_; - - my $s = IO::Socket::INET->new( - Proto => 'udp', - LocalAddr => "127.0.0.1:$port" - ); - - open my $fh, '>', $t->testdir() . '/' . $file; - select $fh; $| = 1; - - while (1) { - my $buffer; - $s->recv($buffer, 4096); - print $fh $buffer . "\n"; - } -} - -############################################################################### diff --git a/stream_error_log.t b/stream_error_log.t --- a/stream_error_log.t +++ b/stream_error_log.t @@ -78,12 +78,16 @@ open my $stderr, '<', $t->testdir() . '/ or die "Can't open stderr file: $!"; $t->run_daemon(\&stream_daemon); -$t->run_daemon(\&syslog_daemon, port(8983), $t, 's_glob.log'); -$t->run_daemon(\&syslog_daemon, port(8984), $t, 's_stream.log'); + +my ($s_glob, $s_stream) = map { + IO::Socket::INET->new( + Proto => 'udp', + LocalAddr => '127.0.0.1:' . port($_) + ) + or die "Can't open syslog socket: $!"; +} 8983, 8984; $t->waitforsocket('127.0.0.1:' . port(8081)); -$t->waitforfile($t->testdir . '/s_glob.log'); -$t->waitforfile($t->testdir . '/s_stream.log'); $t->run(); @@ -111,17 +115,17 @@ is(lines($t, 'stderr', '[debug]'), 0, 's like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); like($t->read_file('e_glob2.log'), qr!nginx/[.0-9]+!, 'error global 2'); -is_deeply(levels($t, 'e_glob.log'), levels($t, 'e_glob2.log'), - 'multiple error global'); +is_deeply(levels($t->read_file('e_glob.log')), + levels($t->read_file('e_glob2.log')), 'multiple error global'); # syslog parse_syslog_message('syslog', get_syslog('data2', '127.0.0.1:' . port(8082), port(8985))); -is_deeply(levels($t, 's_glob.log'), levels($t, 'e_glob.log'), +is_deeply(levels(read_syslog($s_glob)), levels($t->read_file('e_glob.log')), 'global syslog messages'); -is_deeply(levels($t, 's_stream.log'), levels($t, 'e_stream.log'), +is_deeply(levels(read_syslog($s_stream)), levels($t->read_file('e_stream.log')), 'stream syslog messages'); # error_log context @@ -161,10 +165,10 @@ sub lines { } sub levels { - my ($t, $file) = @_; + my ($file) = @_; my %levels_hash; - map { $levels_hash{$_}++; } ($t->read_file($file) =~ /(\[\w+\])/g); + map { $levels_hash{$_}++; } ($file =~ /(\[\w+\])/g); return \%levels_hash; } @@ -202,6 +206,19 @@ sub get_syslog { return $data; } +sub read_syslog { + my ($s) = @_; + my $data = ''; + + IO::Select->new($s)->can_read(1); + while (IO::Select->new($s)->can_read(0.1)) { + my $buffer; + sysread($s, $buffer, 4096); + $data .= $buffer; + } + return $data; +} + sub parse_syslog_message { my ($desc, $line) = @_; @@ -256,24 +273,6 @@ SKIP: { ############################################################################### -sub syslog_daemon { - my ($port, $t, $file) = @_; - - my $s = IO::Socket::INET->new( - Proto => 'udp', - LocalAddr => "127.0.0.1:$port" - ); - - open my $fh, '>', $t->testdir() . '/' . $file; - select $fh; $| = 1; - - while (1) { - my $buffer; - $s->recv($buffer, 4096); - print $fh $buffer . "\n"; - } -} - sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', diff --git a/syslog.t b/syslog.t --- a/syslog.t +++ b/syslog.t @@ -25,8 +25,6 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(62); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -142,13 +140,13 @@ http { EOF -$t->run_daemon(\&syslog_daemon, port(8981), $t, 's_glob.log'); -$t->run_daemon(\&syslog_daemon, port(8982), $t, 's_http.log'); -$t->run_daemon(\&syslog_daemon, port(8983), $t, 's_if.log'); - -$t->waitforfile($t->testdir . '/s_glob.log'); -$t->waitforfile($t->testdir . '/s_http.log'); -$t->waitforfile($t->testdir . '/s_if.log'); +my ($s_glob, $s_http, $s_if) = map { + IO::Socket::INET->new( + Proto => 'udp', + LocalAddr => '127.0.0.1:' . port($_) + ) + or die "Can't open syslog socket: $!"; +} 8981, 8982, 8983; $t->run(); @@ -186,7 +184,6 @@ is($lines[1], $lines[2], 'error_log many # error_log log levels SKIP: { - skip "no --with-debug", 1 unless $t->has_module('--with-debug'); isnt(syslog_lines('/debug', '[debug]'), 0, 'debug'); @@ -210,8 +207,10 @@ is(syslog_lines('/high', '[error]'), 1, # check for the presence of the syslog messages in the global and http contexts -is_deeply(levels($t, 's_glob.log'), levels($t, 'f_glob.log'), 'master syslog'); -is_deeply(levels($t, 's_http.log'), levels($t, 'f_http.log'), 'http syslog'); +is_deeply(levels(read_syslog($s_glob)), levels($t->read_file('f_glob.log')), + 'master syslog'); +is_deeply(levels(read_syslog($s_http)), levels($t->read_file('f_http.log')), + 'http syslog'); http_get('/if'); http_get('/if/empty?logme='); @@ -219,11 +218,11 @@ http_get('/if/zero?logme=0'); http_get('/if/good?logme=1'); http_get('/if/work?logme=yes'); -get_syslog('/a'); +my $syslog_if = read_syslog($s_if); -like($t->read_file('s_if.log'), qr/good:404/s, 'syslog if success'); -like($t->read_file('s_if.log'), qr/work:404/s, 'syslog if success 2'); -unlike($t->read_file('s_if.log'), qr/(if:|empty:|zero:)404/, 'syslog if fail'); +like($syslog_if, qr/good:404/s, 'syslog if success'); +like($syslog_if, qr/work:404/s, 'syslog if success 2'); +unlike($syslog_if, qr/(if:|empty:|zero:)404/, 'syslog if fail'); like(get_syslog('/nohostname'), qr/^<(\d{1,3})> # PRI @@ -258,10 +257,10 @@ sub syslog_lines { } sub levels { - my ($t, $file) = @_; + my ($file) = @_; my %levels_hash; - map { $levels_hash{$_}++; } ($t->read_file($file) =~ /(\[\w+\])/g); + map { $levels_hash{$_}++; } ($file =~ /(\[\w+\])/g); return \%levels_hash; } @@ -281,6 +280,19 @@ sub get_syslog { return $data; } +sub read_syslog { + my ($s) = @_; + my $data = ''; + + IO::Select->new($s)->can_read(1); + while (IO::Select->new($s)->can_read(0.1)) { + my $buffer; + sysread($s, $buffer, 4096); + $data .= $buffer; + } + return $data; +} + sub parse_syslog_message { my ($desc, $line) = @_; @@ -331,23 +343,3 @@ sub parse_syslog_message { } ############################################################################### - -sub syslog_daemon { - my ($port, $t, $file) = @_; - - my $s = IO::Socket::INET->new( - Proto => 'udp', - LocalAddr => "127.0.0.1:$port" - ); - - open my $fh, '>', $t->testdir() . '/' . $file; - select $fh; $| = 1; - - while (1) { - my $buffer; - $s->recv($buffer, 4096); - print $fh $buffer . "\n"; - } -} - -############################################################################### # HG changeset patch # User Sergey Kandaurov # Date 1680046139 -14400 # Wed Mar 29 03:28:59 2023 +0400 # Node ID 98fb93f9328c15b432d04fc372e0614c2c720f0a # Parent be1832cdf571d465aed741b7dcbd583cedc43365 Tests: allow to run error_log tests on win32. Tests are supposed to work now with stderr parts isolated. In particular, leaking stderr file descriptor was fixed to allow Test::Nginx destructor to remove temp directory. Logging to stderr doesn't currently work in worker processes on win32. This is caused by CreateProcess(CREATE_NO_WINDOW). Such tests are marked TODO. diff --git a/error_log.t b/error_log.t --- a/error_log.t +++ b/error_log.t @@ -22,8 +22,6 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/http limit_req/) ->plan(25)->write_file_expand('nginx.conf', <<'EOF'); @@ -123,40 +121,69 @@ open STDERR, ">&", \*OLDERR; http_get('/'); SKIP: { - skip "no --with-debug", 3 unless $t->has_module('--with-debug'); http_get('/debug'); isnt(lines($t, 'e_debug_debug.log', '[debug]'), 0, 'file debug debug'); is(lines($t, 'e_debug_info.log', '[debug]'), 0, 'file debug info'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + isnt(lines($t, 'stderr', '[debug]'), 0, 'stderr debug'); } +} + http_get('/info'); is(lines($t, 'e_info_debug.log', '[info]'), 1, 'file info debug'); is(lines($t, 'e_info_info.log', '[info]'), 1, 'file info info'); is(lines($t, 'e_info_notice.log', '[info]'), 0, 'file info notice'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[info]'), 1, 'stderr info'); +} + http_get('/notice'); is(lines($t, 'e_notice_info.log', '[notice]'), 1, 'file notice info'); is(lines($t, 'e_notice_notice.log', '[notice]'), 1, 'file notice notice'); is(lines($t, 'e_notice_warn.log', '[notice]'), 0, 'file notice warn'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[notice]'), 1, 'stderr notice'); +} + http_get('/warn'); is(lines($t, 'e_warn_notice.log', '[warn]'), 1, 'file warn notice'); is(lines($t, 'e_warn_warn.log', '[warn]'), 1, 'file warn warn'); is(lines($t, 'e_warn_error.log', '[warn]'), 0, 'file warn error'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[warn]'), 1, 'stderr warn'); +} + http_get('/error'); is(lines($t, 'e_error_warn.log', '[error]'), 1, 'file error warn'); is(lines($t, 'e_error_error.log', '[error]'), 1, 'file error error'); is(lines($t, 'e_error_alert.log', '[error]'), 0, 'file error alert'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + is(lines($t, 'stderr', '[error]'), 1, 'stderr error'); +} + # count log messages emitted with various error_log levels http_get('/file_low'); @@ -168,6 +195,9 @@ is(lines($t, 'e_multi.log', '[error]'), http_get('/file_high'); is(lines($t, 'e_multi_high.log', '[error]'), 1, 'file high'); +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + http_get('/stderr_low'); is(lines($t, 'stderr', '[error]'), 2, 'stderr low'); @@ -177,6 +207,10 @@ is(lines($t, 'stderr', '[error]'), 2, 's http_get('/stderr_high'); is(lines($t, 'stderr', '[error]'), 1, 'stderr high'); +} + +close $stderr; + ############################################################################### sub lines { diff --git a/mail_error_log.t b/mail_error_log.t --- a/mail_error_log.t +++ b/mail_error_log.t @@ -26,8 +26,6 @@ use Test::Nginx::IMAP; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/mail imap http rewrite/); $t->plan(30)->write_file_expand('nginx.conf', <<'EOF'); @@ -84,13 +82,13 @@ http { EOF +$t->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon); + open OLDERR, ">&", \*STDERR; open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; open my $stderr, '<', $t->testdir() . '/stderr' or die "Can't open stderr file: $!"; -$t->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon); - my ($s_glob, $s_info) = map { IO::Socket::INET->new( Proto => 'udp', @@ -121,7 +119,14 @@ isnt(lines($t, 'e_debug.log', '[debug]') isnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info'); is(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + isnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info'); + +} + is(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info'); # multiple error_log @@ -140,6 +145,8 @@ is_deeply(levels(read_syslog($s_glob)), is_deeply(levels(read_syslog($s_info)), levels($t->read_file('e_info.log')), 'mail syslog messages'); +close $stderr; + ############################################################################### sub lines { diff --git a/stream_error_log.t b/stream_error_log.t --- a/stream_error_log.t +++ b/stream_error_log.t @@ -26,8 +26,6 @@ use Test::Nginx::Stream qw/ stream /; select STDERR; $| = 1; select STDOUT; $| = 1; -plan(skip_all => 'win32') if $^O eq 'MSWin32'; - my $t = Test::Nginx->new()->has(qw/stream/)->plan(34); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -72,13 +70,13 @@ stream { EOF +$t->run_daemon(\&stream_daemon); + open OLDERR, ">&", \*STDERR; open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; open my $stderr, '<', $t->testdir() . '/stderr' or die "Can't open stderr file: $!"; -$t->run_daemon(\&stream_daemon); - my ($s_glob, $s_stream) = map { IO::Socket::INET->new( Proto => 'udp', @@ -108,7 +106,14 @@ isnt(lines($t, 'e_debug.log', '[debug]') isnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info'); is(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + isnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info'); + +} + is(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info'); # multiple error_log @@ -139,8 +144,14 @@ my $msg = 'no live upstreams while conne unlike($t->read_file('e_glob.log'), qr/$msg/ms, 'stream error in global'); like($t->read_file('e_info.log'), qr/$msg/ms, 'stream error in info'); +unlike($t->read_file('e_emerg.log'), qr/$msg/ms, 'stream error in emerg'); + +TODO: { +local $TODO = 'win32' if $^O eq 'MSWin32'; + like($t->read_file('stderr'), qr/$msg/ms, 'stream error in info stderr'); -unlike($t->read_file('e_emerg.log'), qr/$msg/ms, 'stream error in emerg'); + +} $msg = "bytes from/to client:5/4, bytes from/to upstream:4/5"; @@ -148,6 +159,8 @@ like($t->read_file('e_stream.log'), qr/$ } +close $stderr; + ############################################################################### sub lines { -- Sergey Kandaurov From mdounin at mdounin.ru Wed Mar 29 01:29:21 2023 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 29 Mar 2023 04:29:21 +0300 Subject: [PATCH] Tests: run syslog and error_log tests on win32 In-Reply-To: <1D82EDDB-380F-4A1C-BD04-3ECC6928DA8F@nginx.com> References: <1D82EDDB-380F-4A1C-BD04-3ECC6928DA8F@nginx.com> Message-ID: Hello! On Wed, Mar 29, 2023 at 04:05:20AM +0400, Sergey Kandaurov wrote: > > > On 22 Mar 2023, at 05:02, Maxim Dounin wrote: > > > > Hello! > > > > On Mon, Mar 20, 2023 at 06:13:34PM +0400, Sergey Kandaurov wrote: > > > >> # HG changeset patch > >> # User Sergey Kandaurov > >> # Date 1679321601 -14400 > >> # Mon Mar 20 18:13:21 2023 +0400 > >> # Node ID f3225ad9300ee2c11c0dec54b9605e67060b7347 > >> # Parent 1e1d0f3874b0c5b1e399ec76b0198b5c9c265a36 > >> Tests: run syslog and error_log tests on win32. > >> > >> They are supposed to work well now, no reason to skip them. > > > > It might be a good idea to explicitly mention why it wasn't > > supposed to work. > > It wasn't supposed to work due to stderr tests, now fixed. > See full description below in the updated series. Not really. It was originally disabled because the test uses shared memory, which wasn't functional on Windows with ASLR till nginx 1.9.0. changeset: 334:c0bd624db067 user: Maxim Dounin date: Mon Sep 16 23:55:04 2013 +0400 summary: Tests: disable error_log.t on win32 as it uses shared memory. Since nginx 1.9.0 the test can be enabled, assuming other issues, such as stderr tests, are fixed. > > > >> An exception is logging to stderr. > > > > And why stderr is not supposed to. > > > > (Just in case, the culprit is CreateProcess(CREATE_NO_WINDOW) used > > when starting worker processes, which creates a process without a > > console. This probably can be improved to preserve console if > > logging to stderr is used.) > > Thanks, applied to description. > > > > >> > >> diff --git a/error_log.t b/error_log.t > >> --- a/error_log.t > >> +++ b/error_log.t > >> @@ -22,8 +22,6 @@ use Test::Nginx; > >> select STDERR; $| = 1; > >> select STDOUT; $| = 1; > >> > >> -plan(skip_all => 'win32') if $^O eq 'MSWin32'; > >> - > >> my $t = Test::Nginx->new()->has(qw/http limit_req/) > >> ->plan(25)->write_file_expand('nginx.conf', <<'EOF'); > >> > >> @@ -110,7 +108,7 @@ EOF > >> open OLDERR, ">&", \*STDERR; > >> open STDERR, '>', $t->testdir() . '/stderr' or die "Can't reopen STDERR: $!"; > >> open my $stderr, '<', $t->testdir() . '/stderr' > >> - or die "Can't open stderr file: $!"; > >> + or die "Can't open stderr file: $!" unless $^O eq 'MSWin32'; > > > > It shouldn't be a problem to open the file on Windows. Though > > will require explicitly closing it at the end of the test, so the > > test suite will be able to remove the test directory in > > destructor. > > > > (In other tests this will also require reordering of daemon > > startup, to avoid leaking the descriptor to daemons.) > > Applied too. > > >> [..] > >> @@ -255,13 +259,16 @@ sub syslog_daemon { > >> LocalAddr => "127.0.0.1:$port" > >> ); > >> > >> - open my $fh, '>', $t->testdir() . '/' . $file; > >> - select $fh; $| = 1; > >> + my $path = $t->testdir() . '/' . $file; > >> + open my $fh, '>', $path; > >> + close $fh; > >> > >> while (1) { > >> my $buffer; > >> $s->recv($buffer, 4096); > >> + open $fh, '>>', $path; > >> print $fh $buffer . "\n"; > >> + close $fh; > >> } > > > > It might be a good idea to clarify in the commit log why this is > > needed. (Notably, that pseudo-processes created by Perl's fork() > > emulation on win32 cannot be gracefully stopped when they are > > blocked in a system call, and using kill('KILL') as nginx does > > seems to leave the files open.) > > Well, I don't like this kind of open/close tango. > Reading syslog can be done in the main process, without > invoking pseudo-processes with their limitations. > This should work with minor number of syslog messages > as seen in tests. Below is updated series, syslog part > is extracted into a separate change. > > # HG changeset patch > # User Sergey Kandaurov > # Date 1680046138 -14400 > # Wed Mar 29 03:28:58 2023 +0400 > # Node ID be1832cdf571d465aed741b7dcbd583cedc43365 > # Parent 0351dee227a8341e442feeb03920a46b259adeb5 > Tests: revised reading syslog messages with test daemon. > > On win32, terminating test daemons blocked by a system call leads > to leaking file descriptors, which breaks temp directory removal. > See limitations of pseudo-processes used in Perl's fork() emulation. > Now it is replaced with reading syslog in the main process. > > diff --git a/mail_error_log.t b/mail_error_log.t > --- a/mail_error_log.t > +++ b/mail_error_log.t > @@ -90,12 +90,16 @@ open my $stderr, '<', $t->testdir() . '/ > or die "Can't open stderr file: $!"; > > $t->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon); > -$t->run_daemon(\&syslog_daemon, port(8981), $t, 's_glob.log'); > -$t->run_daemon(\&syslog_daemon, port(8982), $t, 's_info.log'); > + > +my ($s_glob, $s_info) = map { > + IO::Socket::INET->new( > + Proto => 'udp', > + LocalAddr => '127.0.0.1:' . port($_) > + ) > + or die "Can't open syslog socket: $!"; > +} 8981, 8982; > > $t->waitforsocket('127.0.0.1:' . port(8144)); > -$t->waitforfile($t->testdir . '/s_glob.log'); > -$t->waitforfile($t->testdir . '/s_info.log'); > > $t->run(); > > @@ -124,16 +128,16 @@ is(lines($t, 'stderr', '[debug]'), 0, 's > > like($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global'); > like($t->read_file('e_glob2.log'), qr!nginx/[.0-9]+!, 'error global 2'); > -is_deeply(levels($t, 'e_glob.log'), levels($t, 'e_glob2.log'), > - 'multiple error global'); > +is_deeply(levels($t->read_file('e_glob.log')), > + levels($t->read_file('e_glob2.log')), 'multiple error global'); > > # syslog > > parse_syslog_message('syslog', get_syslog()); > > -is_deeply(levels($t, 's_glob.log'), levels($t, 'e_glob.log'), > +is_deeply(levels(read_syslog($s_glob)), levels($t->read_file('e_glob.log')), > 'global syslog messages'); > -is_deeply(levels($t, 's_info.log'), levels($t, 'e_info.log'), > +is_deeply(levels(read_syslog($s_info)), levels($t->read_file('e_info.log')), > 'mail syslog messages'); > > ############################################################################### > @@ -153,10 +157,10 @@ sub lines { > } > > sub levels { > - my ($t, $file) = @_; > + my ($file) = @_; > my %levels_hash; > > - map { $levels_hash{$_}++; } ($t->read_file($file) =~ /(\[\w+\])/g); > + map { $levels_hash{$_}++; } ($file =~ /(\[\w+\])/g); > > return \%levels_hash; > } > @@ -193,6 +197,19 @@ sub get_syslog { > return $data; > } > > +sub read_syslog { > + my ($s) = @_; > + my $data = ''; > + > + IO::Select->new($s)->can_read(1); > + while (IO::Select->new($s)->can_read(0.1)) { > + my $buffer; > + sysread($s, $buffer, 4096); > + $data .= $buffer; > + } > + return $data; > +} > + > sub parse_syslog_message { > my ($desc, $line) = @_; > > @@ -246,23 +263,3 @@ SKIP: { > } > > ############################################################################### > - > -sub syslog_daemon { > - my ($port, $t, $file) = @_; > - > - my $s = IO::Socket::INET->new( > - Proto => 'udp', > - LocalAddr => "127.0.0.1:$port" > - ); > - > - open my $fh, '>', $t->testdir() . '/' . $file; > - select $fh; $| = 1; > - > - while (1) { > - my $buffer; > - $s->recv($buffer, 4096); > - print $fh $buffer . "\n"; > - } > -} > - > -############################################################################### Well, I can't say I like this approach. While there are some issues with pseudo-processes on win32, in general test daemons are much easier to read and understand than using some socket intermixed with the test code. Also note that the particular approach taken implies noticeably more per-test code, such as in: -is_deeply(levels($t, 's_glob.log'), levels($t, 'e_glob.log'), +is_deeply(levels(read_syslog($s_glob)), levels($t->read_file('e_glob.log')), While probably this can be optimized away (either by moving read_syslog() into levels(), or by calling it separately to save data to the file and then using levels() unmodified), it might be easier to keep the existing code with test daemons instead. If the open()/write()/close() approach makes you feel uncomfortable, abstracting it into a function might be the way to go. Something similar to $t->write_file() but with append should do the trick. [...] -- Maxim Dounin http://mdounin.ru/ From xeioex at nginx.com Wed Mar 29 04:38:51 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 29 Mar 2023 04:38:51 +0000 Subject: [njs] Added "zlib" module. Message-ID: details: https://hg.nginx.org/njs/rev/5e7fc8efebdc branches: changeset: 2077:5e7fc8efebdc user: Dmitry Volyntsev date: Mon Mar 27 22:41:27 2023 -0700 description: Added "zlib" module. - zlib.deflateRawSync(string|buffer, options?) compresses data using deflate, and do not append a zlib header, returns Buffer. - zlib.deflateSync(string|buffer, options?) compresses data using deflate, returns Buffer. - zlib.inflateRawSync(string|buffer) decompresses a raw deflate stream, returns Buffer. - zlib.inflateSync(string|buffer) decompresses a deflate stream, return Buffer. diffstat: auto/help | 4 + auto/modules | 8 + auto/options | 2 + auto/summary | 4 + auto/zlib | 61 ++++ configure | 1 + external/njs_zlib_module.c | 566 +++++++++++++++++++++++++++++++++++++++++++++ nginx/config | 5 +- nginx/config.make | 2 +- nginx/ngx_js.c | 2 + src/test/njs_unit_test.c | 79 ++++++ 11 files changed, 731 insertions(+), 3 deletions(-) diffs (867 lines): diff -r ec007866a53b -r 5e7fc8efebdc auto/help --- a/auto/help Wed Mar 22 15:22:37 2023 +0200 +++ b/auto/help Mon Mar 27 22:41:27 2023 -0700 @@ -39,6 +39,10 @@ default: "$NJS_LD_OPT" enabled libxml2 dependant code is not built as a part of libnjs.a. + --no-zlib disabled zlib discovery. When this option is + enabled zlib dependant code is not built as a + part of libnjs.a. + --address-sanitizer=YES enables build with address sanitizer, \ default: "$NJS_ADDRESS_SANITIZER" --addr2line=YES enables native function symbolization, \ diff -r ec007866a53b -r 5e7fc8efebdc auto/modules --- a/auto/modules Wed Mar 22 15:22:37 2023 +0200 +++ b/auto/modules Mon Mar 27 22:41:27 2023 -0700 @@ -29,6 +29,14 @@ if [ $NJS_LIBXML2 = YES -a $NJS_HAVE_LIB . auto/module fi +if [ $NJS_ZLIB = YES -a $NJS_HAVE_ZLIB = YES ]; then + njs_module_name=njs_zlib_module + njs_module_incs= + njs_module_srcs=external/njs_zlib_module.c + + . auto/module +fi + njs_module_name=njs_fs_module njs_module_incs= njs_module_srcs=external/njs_fs_module.c diff -r ec007866a53b -r 5e7fc8efebdc auto/options --- a/auto/options Wed Mar 22 15:22:37 2023 +0200 +++ b/auto/options Mon Mar 27 22:41:27 2023 -0700 @@ -17,6 +17,7 @@ NJS_TEST262=YES NJS_OPENSSL=YES NJS_LIBXML2=YES +NJS_ZLIB=YES NJS_PCRE=YES NJS_TRY_PCRE2=YES @@ -50,6 +51,7 @@ do --no-openssl) NJS_OPENSSL=NO ;; --no-libxml2) NJS_LIBXML2=NO ;; + --no-zlib) NJS_ZLIB=NO ;; --no-pcre) NJS_PCRE=NO ;; --no-pcre2) NJS_TRY_PCRE2=NO ;; diff -r ec007866a53b -r 5e7fc8efebdc auto/summary --- a/auto/summary Wed Mar 22 15:22:37 2023 +0200 +++ b/auto/summary Mon Mar 27 22:41:27 2023 -0700 @@ -26,6 +26,10 @@ if [ $NJS_HAVE_LIBXML2 = YES ]; then echo " + using libxml2 library: $NJS_LIBXML2_LIB" fi +if [ $NJS_HAVE_ZLIB = YES ]; then + echo " + using zlib library: $NJS_ZLIB_LIB" +fi + if [ $NJS_HAVE_COMPUTED_GOTO = YES ]; then echo " + using computed goto" fi diff -r ec007866a53b -r 5e7fc8efebdc auto/zlib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/auto/zlib Mon Mar 27 22:41:27 2023 -0700 @@ -0,0 +1,61 @@ + +# Copyright (C) Dmitry Volyntsev +# Copyright (C) NGINX, Inc. + +NJS_ZLIB_LIB= +NJS_HAVE_ZLIB=NO + +if [ $NJS_ZLIB = YES ]; then + njs_found=no + njs_feature_name=NJS_HAVE_ZLIB + njs_feature_run=no + njs_feature_test="#include + + int main() { + int rc; + z_stream z; + + rc = deflate(&z, Z_NO_FLUSH); + + return (rc == Z_OK) ? 0 : 1; + }" + + + if /bin/sh -c "(pkg-config zlib --exists)" >> $NJS_AUTOCONF_ERR 2>&1; then + + # pkg-config + + njs_feature="zlib via pkg-config" + njs_feature_incs=`pkg-config zlib --cflags | sed -n -e 's/.*-I *\([^ ][^ ]*\).*/\1/p'` + njs_feature_libs=`pkg-config zlib --libs` + + . auto/feature + fi + + if [ $njs_found = no ]; then + + njs_feature="zlib" + njs_feature_libs="-lz" + + . auto/feature + fi + + if [ $njs_found = yes ]; then + njs_feature="zlib version" + njs_feature_name=NJS_ZLIB_VERSION + njs_feature_run=value + njs_feature_test="#include + #include + + int main() { + printf(\"\\\"%s\\\"\", zlibVersion()); + return 0; + }" + . auto/feature + + NJS_HAVE_ZLIB=YES + NJS_ZLIB_LIB="$njs_feature_libs" + NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs" + NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" + fi +fi diff -r ec007866a53b -r 5e7fc8efebdc configure --- a/configure Wed Mar 22 15:22:37 2023 +0200 +++ b/configure Mon Mar 27 22:41:27 2023 -0700 @@ -52,6 +52,7 @@ NJS_LIB_AUX_LIBS= . auto/readline . auto/openssl . auto/libxml2 +. auto/zlib . auto/libbfd . auto/link diff -r ec007866a53b -r 5e7fc8efebdc external/njs_zlib_module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/external/njs_zlib_module.c Mon Mar 27 22:41:27 2023 -0700 @@ -0,0 +1,566 @@ +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + + +#include +#include +#include + +#define NJS_ZLIB_CHUNK_SIZE 1024 + +static njs_int_t njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused); +static njs_int_t njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused); +njs_int_t njs_zlib_contant(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +static njs_int_t njs_zlib_init(njs_vm_t *vm); +static void *njs_zlib_alloc(void *opaque, u_int items, u_int size); +static void njs_zlib_free(void *opaque, void *address); + + +static njs_external_t njs_ext_zlib_constants[] = { + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_NO_COMPRESSION"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_NO_COMPRESSION, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_BEST_SPEED"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_BEST_SPEED, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_BEST_COMPRESSION"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_BEST_COMPRESSION, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_FILTERED"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_FILTERED, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_HUFFMAN_ONLY"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_HUFFMAN_ONLY, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_RLE"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_RLE, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_FIXED"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_FIXED, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("Z_DEFAULT_STRATEGY"), + .enumerable = 1, + .u.property = { + .handler = njs_zlib_contant, + .magic32 = Z_DEFAULT_STRATEGY, + } + }, + +}; + + +static njs_external_t njs_ext_zlib[] = { + + { + .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, + .name.symbol = NJS_SYMBOL_TO_STRING_TAG, + .u.property = { + .value = "zlib", + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("deflateRawSync"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_zlib_ext_deflate, + .magic8 = 1, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("deflateSync"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_zlib_ext_deflate, + .magic8 = 0, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("inflateRawSync"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_zlib_ext_inflate, + .magic8 = 1, + } + }, + + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("inflateSync"), + .writable = 1, + .configurable = 1, + .u.method = { + .native = njs_zlib_ext_inflate, + .magic8 = 0, + } + }, + + { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("constants"), + .writable = 1, + .configurable = 1, + .u.object = { + .properties = njs_ext_zlib_constants, + .nproperties = njs_nitems(njs_ext_zlib_constants), + } + }, + +}; + + +njs_module_t njs_zlib_module = { + .name = njs_str("zlib"), + .init = njs_zlib_init, +}; + + +static njs_int_t +njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t raw) +{ + int rc, level, mem_level, strategy, window_bits; + u_char *buffer; + size_t chunk_size; + ssize_t size; + njs_chb_t chain; + z_stream stream; + njs_int_t ret; + njs_str_t data, dictionary; + njs_value_t *options, *value; + njs_opaque_value_t lvalue; + + static const njs_str_t chunk_size_key = njs_str("chunkSize"); + static const njs_str_t dict_key = njs_str("dictionary"); + static const njs_str_t level_key = njs_str("level"); + static const njs_str_t mem_level_key = njs_str("memLevel"); + static const njs_str_t strategy_key = njs_str("strategy"); + static const njs_str_t window_bits_key = njs_str("windowBits"); + + ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1)); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + chunk_size = NJS_ZLIB_CHUNK_SIZE; + dictionary.start = NULL; + mem_level = 8; + level = Z_DEFAULT_COMPRESSION; + strategy = Z_DEFAULT_STRATEGY; + window_bits = raw ? -MAX_WBITS : MAX_WBITS; + + options = njs_arg(args, nargs, 2); + + if (njs_value_is_object(options)) { + value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue); + if (value != NULL) { + chunk_size = njs_value_number(value); + + if (njs_slow_path(chunk_size < 64)) { + njs_vm_error(vm, "chunkSize must be >= 64"); + return NJS_ERROR; + } + } + + value = njs_vm_object_prop(vm, options, &level_key, &lvalue); + if (value != NULL) { + level = njs_value_number(value); + + if (njs_slow_path(level < Z_DEFAULT_COMPRESSION + || level > Z_BEST_COMPRESSION)) + { + njs_vm_error(vm, "level must be in the range %d..%d", + Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); + return NJS_ERROR; + } + } + + value = njs_vm_object_prop(vm, options, &window_bits_key, &lvalue); + if (value != NULL) { + window_bits = njs_value_number(value); + + if (raw) { + if (njs_slow_path(window_bits < -15 || window_bits > -9)) { + njs_vm_error(vm, "windowBits must be in the range -15..-9"); + return NJS_ERROR; + } + + } else { + if (njs_slow_path(window_bits < 9 || window_bits > 15)) { + njs_vm_error(vm, "windowBits must be in the range 9..15"); + return NJS_ERROR; + } + } + } + + value = njs_vm_object_prop(vm, options, &mem_level_key, &lvalue); + if (value != NULL) { + mem_level = njs_value_number(value); + + if (njs_slow_path(mem_level < 1 || mem_level > 9)) { + njs_vm_error(vm, "memLevel must be in the range 0..9"); + return NJS_ERROR; + } + } + + value = njs_vm_object_prop(vm, options, &strategy_key, &lvalue); + if (value != NULL) { + strategy = njs_value_number(value); + + switch (strategy) { + case Z_FILTERED: + case Z_HUFFMAN_ONLY: + case Z_RLE: + case Z_FIXED: + case Z_DEFAULT_STRATEGY: + break; + + default: + njs_vm_error(vm, "unknown strategy: %d", strategy); + return NJS_ERROR; + } + } + + value = njs_vm_object_prop(vm, options, &dict_key, &lvalue); + if (value != NULL) { + ret = njs_vm_value_to_bytes(vm, &dictionary, value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + } + + stream.next_in = data.start; + stream.avail_in = data.length; + + stream.zalloc = njs_zlib_alloc; + stream.zfree = njs_zlib_free; + stream.opaque = njs_vm_memory_pool(vm); + + rc = deflateInit2(&stream, level, Z_DEFLATED, window_bits, mem_level, + strategy); + if (njs_slow_path(rc != Z_OK)) { + njs_vm_error(vm, "deflateInit2() failed"); + return NJS_ERROR; + } + + if (dictionary.start != NULL) { + rc = deflateSetDictionary(&stream, dictionary.start, dictionary.length); + if (njs_slow_path(rc != Z_OK)) { + njs_vm_error(vm, "deflateSetDictionary() failed"); + return NJS_ERROR; + } + } + + njs_chb_init(&chain, njs_vm_memory_pool(vm)); + + do { + stream.next_out = njs_chb_reserve(&chain, chunk_size); + if (njs_slow_path(stream.next_out == NULL)) { + njs_vm_memory_error(vm); + goto fail; + } + + stream.avail_out = chunk_size; + + rc = deflate(&stream, Z_FINISH); + if (njs_slow_path(rc < 0)) { + njs_vm_error(vm, "failed to deflate the data: %s", stream.msg); + goto fail; + } + + njs_chb_written(&chain, chunk_size - stream.avail_out); + + } while (stream.avail_out == 0); + + deflateEnd(&stream); + + size = njs_chb_size(&chain); + if (njs_slow_path(size < 0)) { + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + buffer = njs_mp_alloc(njs_vm_memory_pool(vm), size); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + njs_chb_join_to(&chain, buffer); + + njs_chb_destroy(&chain); + + return njs_vm_value_buffer_set(vm, njs_vm_retval(vm), buffer, size); + +fail: + + deflateEnd(&stream); + njs_chb_destroy(&chain); + + return NJS_ERROR; +} + + +static njs_int_t +njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t raw) +{ + int rc, window_bits; + u_char *buffer; + size_t chunk_size; + ssize_t size; + njs_chb_t chain; + z_stream stream; + njs_int_t ret; + njs_str_t data, dictionary; + njs_value_t *options, *value; + njs_opaque_value_t lvalue; + + static const njs_str_t chunk_size_key = njs_str("chunkSize"); + static const njs_str_t dict_key = njs_str("dictionary"); + static const njs_str_t window_bits_key = njs_str("windowBits"); + + ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1)); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + chunk_size = NJS_ZLIB_CHUNK_SIZE; + dictionary.start = NULL; + window_bits = raw ? -MAX_WBITS : MAX_WBITS; + + options = njs_arg(args, nargs, 2); + + if (njs_value_is_object(options)) { + value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue); + if (value != NULL) { + chunk_size = njs_value_number(value); + + if (njs_slow_path(chunk_size < 64)) { + njs_vm_error(vm, "chunkSize must be >= 64"); + return NJS_ERROR; + } + } + + value = njs_vm_object_prop(vm, options, &window_bits_key, &lvalue); + if (value != NULL) { + window_bits = njs_value_number(value); + + if (raw) { + if (njs_slow_path(window_bits < -15 || window_bits > -8)) { + njs_vm_error(vm, "windowBits must be in the range -15..-8"); + return NJS_ERROR; + } + + } else { + if (njs_slow_path(window_bits < 8 || window_bits > 15)) { + njs_vm_error(vm, "windowBits must be in the range 8..15"); + return NJS_ERROR; + } + } + } + + value = njs_vm_object_prop(vm, options, &dict_key, &lvalue); + if (value != NULL) { + ret = njs_vm_value_to_bytes(vm, &dictionary, value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + } + + stream.next_in = data.start; + stream.avail_in = data.length; + + stream.zalloc = njs_zlib_alloc; + stream.zfree = njs_zlib_free; + stream.opaque = njs_vm_memory_pool(vm); + + rc = inflateInit2(&stream, window_bits); + if (njs_slow_path(rc != Z_OK)) { + njs_vm_error(vm, "inflateInit2() failed"); + return NJS_ERROR; + } + + if (dictionary.start != NULL) { + rc = inflateSetDictionary(&stream, dictionary.start, dictionary.length); + if (njs_slow_path(rc != Z_OK)) { + njs_vm_error(vm, "deflateSetDictionary() failed"); + return NJS_ERROR; + } + } + + njs_chb_init(&chain, njs_vm_memory_pool(vm)); + + while (stream.avail_in > 0) { + stream.next_out = njs_chb_reserve(&chain, chunk_size); + if (njs_slow_path(stream.next_out == NULL)) { + njs_vm_memory_error(vm); + goto fail; + } + + stream.avail_out = chunk_size; + + rc = inflate(&stream, Z_NO_FLUSH); + if (njs_slow_path(rc < 0)) { + njs_vm_error(vm, "failed to inflate the compressed data: %s", + stream.msg); + goto fail; + } + + if (rc == Z_NEED_DICT) { + njs_vm_error(vm, "failed to inflate, dictionary is required"); + goto fail; + } + + njs_chb_written(&chain, chunk_size - stream.avail_out); + } + + rc = inflateEnd(&stream); + if (njs_slow_path(rc != Z_OK)) { + njs_vm_error(vm, "failed to end the inflate stream"); + return NJS_ERROR; + } + + size = njs_chb_size(&chain); + if (njs_slow_path(size < 0)) { + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + buffer = njs_mp_alloc(njs_vm_memory_pool(vm), size); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + njs_chb_join_to(&chain, buffer); + + njs_chb_destroy(&chain); + + return njs_vm_value_buffer_set(vm, njs_vm_retval(vm), buffer, size); + +fail: + + inflateEnd(&stream); + njs_chb_destroy(&chain); + + return NJS_ERROR; +} + + +njs_int_t +njs_zlib_contant(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_value_number_set(retval, njs_vm_prop_magic32(prop)); + + return NJS_OK; +} + + +static njs_int_t +njs_zlib_init(njs_vm_t *vm) +{ + njs_int_t ret, proto_id; + njs_mod_t *module; + njs_opaque_value_t value; + + proto_id = njs_vm_external_prototype(vm, njs_ext_zlib, + njs_nitems(njs_ext_zlib)); + if (njs_slow_path(proto_id < 0)) { + return NJS_ERROR; + } + + ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + module = njs_vm_add_module(vm, &njs_str_value("zlib"), + njs_value_arg(&value)); + if (njs_slow_path(module == NULL)) { + return NJS_ERROR; + } + + return NJS_OK; +} + + +static void * +njs_zlib_alloc(void *opaque, u_int items, u_int size) +{ + return njs_mp_alloc(opaque, items * size); +} + + +static void +njs_zlib_free(void *opaque, void *address) +{ + /* Do nothing. */ +} + diff -r ec007866a53b -r 5e7fc8efebdc nginx/config --- a/nginx/config Wed Mar 22 15:22:37 2023 +0200 +++ b/nginx/config Mon Mar 27 22:41:27 2023 -0700 @@ -6,6 +6,7 @@ NJS_SRCS="$ngx_addon_dir/ngx_js.c \ $ngx_addon_dir/ngx_js_fetch.c \ $ngx_addon_dir/ngx_js_regex.c \ $ngx_addon_dir/../external/njs_webcrypto_module.c + $ngx_addon_dir/../external/njs_zlib_module.c $ngx_addon_dir/../external/njs_xml_module.c" if [ $HTTP != NO ]; then @@ -14,7 +15,7 @@ if [ $HTTP != NO ]; then ngx_module_incs="$ngx_addon_dir/../src $ngx_addon_dir/../build" ngx_module_deps="$ngx_addon_dir/../build/libnjs.a $NJS_DEPS" ngx_module_srcs="$ngx_addon_dir/ngx_http_js_module.c $NJS_SRCS" - ngx_module_libs="PCRE OPENSSL LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm" + ngx_module_libs="PCRE OPENSSL ZLIB LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm" . auto/module @@ -29,7 +30,7 @@ if [ $STREAM != NO ]; then ngx_module_incs="$ngx_addon_dir/../src $ngx_addon_dir/../build" ngx_module_deps="$ngx_addon_dir/../build/libnjs.a $NJS_DEPS" ngx_module_srcs="$ngx_addon_dir/ngx_stream_js_module.c $NJS_SRCS" - ngx_module_libs="PCRE OPENSSL LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm" + ngx_module_libs="PCRE OPENSSL ZLIB LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm" . auto/module fi diff -r ec007866a53b -r 5e7fc8efebdc nginx/config.make --- a/nginx/config.make Wed Mar 22 15:22:37 2023 +0200 +++ b/nginx/config.make Mon Mar 27 22:41:27 2023 -0700 @@ -3,7 +3,7 @@ cat << END $ngx_addon_dir/../build/libnjs.a: $NGX_MAKEFILE cd $ngx_addon_dir/.. \\ && if [ -f build/Makefile ]; then \$(MAKE) clean; fi \\ - && CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-openssl --no-libxml2 --no-pcre \\ + && CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-openssl --no-libxml2 --no-zlib --no-pcre \\ && \$(MAKE) libnjs END diff -r ec007866a53b -r 5e7fc8efebdc nginx/ngx_js.c --- a/nginx/ngx_js.c Wed Mar 22 15:22:37 2023 +0200 +++ b/nginx/ngx_js.c Mon Mar 27 22:41:27 2023 -0700 @@ -19,6 +19,7 @@ static void ngx_js_cleanup_vm(void *data extern njs_module_t njs_webcrypto_module; extern njs_module_t njs_xml_module; +extern njs_module_t njs_zlib_module; static njs_external_t ngx_js_ext_core[] = { @@ -89,6 +90,7 @@ static njs_external_t ngx_js_ext_core[] njs_module_t *njs_js_addon_modules[] = { &njs_webcrypto_module, &njs_xml_module, + &njs_zlib_module, NULL, }; diff -r ec007866a53b -r 5e7fc8efebdc src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Mar 22 15:22:37 2023 +0200 +++ b/src/test/njs_unit_test.c Mon Mar 27 22:41:27 2023 -0700 @@ -22153,6 +22153,74 @@ static njs_unit_test_t njs_xml_test[] = }; +static njs_unit_test_t njs_zlib_test[] = +{ + { njs_str("const zlib = require('zlib');" + "['C3f0dgQA', 'O7fx3KZzmwE=']" + ".map(v => zlib.inflateRawSync(Buffer.from(v, 'base64')).toString())"), + njs_str("WAKA,αβγ") }, + + { njs_str("const zlib = require('zlib');" + "['eJwLd/R2BAAC+gEl', 'eJw7t/HcpnObAQ/sBIE=']" + ".map(v => zlib.inflateSync(Buffer.from(v, 'base64')).toString())"), + njs_str("WAKA,αβγ") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA', 'αβγ']" + ".map(v => zlib.deflateRawSync(v).toString('base64'))"), + njs_str("C3f0dgQA,O7fx3KZzmwE=") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA', 'αβγ']" + ".map(v => zlib.deflateRawSync(v, {dictionary: Buffer.from('WAKA')}).toString('base64'))"), + njs_str("CwdiAA==,O7fx3KZzmwE=") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA', 'αβγ']" + ".map(v => zlib.deflateRawSync(v, {level: zlib.constants.Z_NO_COMPRESSION}).toString('base64'))"), + njs_str("AQQA+/9XQUtB,AQYA+f/Osc6yzrM=") }, + + { njs_str("const zlib = require('zlib');" + "[zlib.constants.Z_FIXED, zlib.constants.Z_RLE]" + ".map(v => zlib.deflateRawSync('WAKA'.repeat(10), {strategy: v}).toString('base64'))"), + njs_str("C3f0dgwnAgMA,BcExAQAAAMKgbNwLYP8mwmQymUwmk8lkcg==") }, + + { njs_str("const zlib = require('zlib');" + "[1, 8]" + ".map(v => zlib.deflateRawSync('WAKA'.repeat(35)," + " {strategy: zlib.constants.Z_RLE, memLevel: v})" + " .toString('base64'))"), + njs_str("BMExAQAAAMKgbNwLYP8mwmQymUwmk8lkMplMJpPJZDKZTCaTyWQymUwmk+lzDHf0dgx39HYMd/R2BAA=," + "BcExAQAAAMKgbNwLYP8mwmQymUwmk8lkMplMJpPJZDKZTCaTyWQymUwmk8lkMjk=") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA', 'αβγ']" + ".map(v => zlib.deflateSync(v).toString('base64'))"), + njs_str("eJwLd/R2BAAC+gEl,eJw7t/HcpnObAQ/sBIE=") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA'.repeat(1024), 'αβγ'.repeat(1024)]" + ".map(v => [v, zlib.deflateRawSync(v).toString('base64')])" + ".every(pair => pair[0] == zlib.inflateRawSync(Buffer.from(pair[1], 'base64')).toString())"), + njs_str("true") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA'.repeat(1024), 'αβγ'.repeat(1024)]" + ".map(v => [v, zlib.deflateRawSync(v, {chunkSize:64}).toString('base64')])" + ".every(pair => pair[0] == zlib.inflateRawSync(Buffer.from(pair[1], 'base64')," + " {chunkSize:64}).toString())"), + njs_str("true") }, + + { njs_str("const zlib = require('zlib');" + "['WAKA', 'αβγ']" + ".map(v => [v, zlib.deflateRawSync(v, {dictionary: Buffer.from('WAKA')}).toString('base64')])" + ".every(pair => pair[0] == zlib.inflateRawSync(Buffer.from(pair[1], 'base64')," + " {dictionary: Buffer.from('WAKA')}).toString())"), + njs_str("true") }, + +}; + + static njs_unit_test_t njs_module_test[] = { { njs_str("function f(){return 2}; var f; f()"), @@ -24994,6 +25062,17 @@ static njs_test_suite_t njs_suites[] = njs_nitems(njs_xml_test), njs_unit_test }, + { +#if (NJS_HAVE_ZLIB && !NJS_HAVE_MEMORY_SANITIZER) + njs_str("zlib"), +#else + njs_str(""), +#endif + { .externals = 1, .repeat = 1, .unsafe = 1 }, + njs_zlib_test, + njs_nitems(njs_zlib_test), + njs_unit_test }, + { njs_str("module"), { .repeat = 1, .module = 1, .unsafe = 1 }, njs_module_test, From xeioex at nginx.com Thu Mar 30 03:48:04 2023 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 30 Mar 2023 03:48:04 +0000 Subject: [njs] Aligned Number constructor to the spec. Message-ID: details: https://hg.nginx.org/njs/rev/8dcea0ba0bf8 branches: changeset: 2078:8dcea0ba0bf8 user: Dmitry Volyntsev date: Wed Mar 29 20:28:33 2023 -0700 description: Aligned Number constructor to the spec. Previously, negative hexadecimal numbers were accepted as valid, whereas they are invalid input for the constructor. Also previously, the constructor did not accepted positive binary or octadecimal numbers as valid. This closes #630 issue on Github. diffstat: src/njs_lexer.c | 4 +- src/njs_number.c | 57 ++++++++++++++++++++++++++++++++++++++++----- src/njs_number.h | 6 +++- src/njs_string.c | 54 ++++++++++++++++++++++++------------------- src/njs_value.h | 2 +- src/njs_value_conversion.h | 2 +- src/njs_vmcode.c | 6 ++-- src/test/njs_unit_test.c | 38 +++++++++++++++++++++++++++++- 8 files changed, 128 insertions(+), 41 deletions(-) diffs (337 lines): diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_lexer.c --- a/src/njs_lexer.c Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_lexer.c Wed Mar 29 20:28:33 2023 -0700 @@ -933,7 +933,7 @@ njs_lexer_number(njs_lexer_t *lexer, njs goto illegal_token; } - token->number = njs_number_oct_parse(&p, lexer->end); + token->number = njs_number_oct_parse(&p, lexer->end, 1); if (p < lexer->end && (*p == '8' || *p == '9')) { goto illegal_trailer; @@ -951,7 +951,7 @@ njs_lexer_number(njs_lexer_t *lexer, njs goto illegal_token; } - token->number = njs_number_bin_parse(&p, lexer->end); + token->number = njs_number_bin_parse(&p, lexer->end, 1); if (p < lexer->end && (*p >= '2' && *p <= '9')) { goto illegal_trailer; diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_number.c --- a/src/njs_number.c Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_number.c Wed Mar 29 20:28:33 2023 -0700 @@ -62,7 +62,8 @@ njs_number_dec_parse(const u_char **star double -njs_number_oct_parse(const u_char **start, const u_char *end) +njs_number_oct_parse(const u_char **start, const u_char *end, + njs_bool_t literal) { u_char c; double num; @@ -78,7 +79,7 @@ njs_number_oct_parse(const u_char **star c = *p - '0'; if (njs_slow_path(c > 7)) { - if (*p == '_' && (p - _) > 1) { + if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } @@ -96,7 +97,8 @@ njs_number_oct_parse(const u_char **star double -njs_number_bin_parse(const u_char **start, const u_char *end) +njs_number_bin_parse(const u_char **start, const u_char *end, + njs_bool_t literal) { u_char c; double num; @@ -112,7 +114,7 @@ njs_number_bin_parse(const u_char **star c = *p - '0'; if (njs_slow_path(c > 1)) { - if (*p == '_' && (p - _) > 1) { + if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } @@ -1030,8 +1032,12 @@ njs_int_t njs_number_parse_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) { - njs_int_t ret; - njs_value_t *value, lvalue; + double num; + njs_int_t ret; + njs_value_t *value, lvalue; + njs_bool_t minus; + const u_char *p, *start, *end; + njs_string_prop_t string; value = njs_lvalue_arg(&lvalue, args, nargs, 1); @@ -1040,7 +1046,44 @@ njs_number_parse_float(njs_vm_t *vm, njs return ret; } - njs_set_number(&vm->retval, njs_string_to_number(value, 1)); + (void) njs_string_trim(value, &string, NJS_TRIM_START); + + p = string.start; + end = p + string.size; + + minus = 0; + + if (p == end) { + num = NAN; + goto done; + } + + if (*p == '+') { + p++; + + } else if (*p == '-') { + p++; + minus = 1; + } + + start = p; + num = njs_number_dec_parse(&p, end, 0); + + if (p == start) { + if (p + njs_length("Infinity") > end + || memcmp(p, "Infinity", njs_length("Infinity")) != 0) + { + num = NAN; + goto done; + } + + num = INFINITY; + p += njs_length("Infinity"); + } + +done: + + njs_set_number(&vm->retval, minus ? -num : num); return NJS_OK; } diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_number.h --- a/src/njs_number.h Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_number.h Wed Mar 29 20:28:33 2023 -0700 @@ -16,8 +16,10 @@ double njs_key_to_index(const njs_value_t *value); double njs_number_dec_parse(const u_char **start, const u_char *end, njs_bool_t literal); -double njs_number_oct_parse(const u_char **start, const u_char *end); -double njs_number_bin_parse(const u_char **start, const u_char *end); +double njs_number_oct_parse(const u_char **start, const u_char *end, + njs_bool_t literal); +double njs_number_bin_parse(const u_char **start, const u_char *end, + njs_bool_t literal); double njs_number_hex_parse(const u_char **start, const u_char *end, njs_bool_t literal); njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string, diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_string.c --- a/src/njs_string.c Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_string.c Wed Mar 29 20:28:33 2023 -0700 @@ -3874,7 +3874,7 @@ njs_string_prototype_iterator_obj(njs_vm double -njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float) +njs_string_to_number(const njs_value_t *value) { double num; njs_bool_t minus; @@ -3889,30 +3889,38 @@ njs_string_to_number(const njs_value_t * end = p + string.size; if (p == end) { - return parse_float ? NAN : 0.0; + return 0.0; } minus = 0; - if (*p == '+') { - p++; - - } else if (*p == '-') { - p++; - minus = 1; - } - - if (p == end) { - return NAN; - } - - if (!parse_float - && p + 2 < end && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) + if (p + 2 < end && p[0] == '0' + && (p[1] == 'x' || p[1] == 'X' + || p[1] == 'b' || p[1] == 'B' + || p[1] == 'o' || p[1] == 'O')) { p += 2; - num = njs_number_hex_parse(&p, end, 0); + + if (p[-1] == 'x' || p[-1] == 'X') { + num = njs_number_hex_parse(&p, end, 0); + + } else if (p[-1] == 'b' || p[-1] == 'B') { + num = njs_number_bin_parse(&p, end, 0); + + } else { + num = njs_number_oct_parse(&p, end, 0); + } } else { + + if (*p == '+') { + p++; + + } else if (*p == '-') { + p++; + minus = 1; + } + start = p; num = njs_number_dec_parse(&p, end, 0); @@ -3926,14 +3934,12 @@ njs_string_to_number(const njs_value_t * } } - if (!parse_float) { - while (p < end) { - if (!njs_is_whitespace(*p)) { - return NAN; - } - - p++; + while (p < end) { + if (!njs_is_whitespace(*p)) { + return NAN; } + + p++; } return minus ? -num : num; diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_value.h --- a/src/njs_value.h Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_value.h Wed Mar 29 20:28:33 2023 -0700 @@ -1073,7 +1073,7 @@ njs_int_t njs_primitive_value_to_string( const njs_value_t *src); njs_int_t njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain, const njs_value_t *src); -double njs_string_to_number(const njs_value_t *value, njs_bool_t parse_float); +double njs_string_to_number(const njs_value_t *value); njs_int_t njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64); njs_bool_t njs_string_eq(const njs_value_t *v1, const njs_value_t *v2); diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_value_conversion.h --- a/src/njs_value_conversion.h Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_value_conversion.h Wed Mar 29 20:28:33 2023 -0700 @@ -33,7 +33,7 @@ njs_value_to_number(njs_vm_t *vm, njs_va *dst = NAN; if (njs_is_string(value)) { - *dst = njs_string_to_number(value, 0); + *dst = njs_string_to_number(value); } return NJS_OK; diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/njs_vmcode.c --- a/src/njs_vmcode.c Mon Mar 27 22:41:27 2023 -0700 +++ b/src/njs_vmcode.c Wed Mar 29 20:28:33 2023 -0700 @@ -2512,7 +2512,7 @@ again: /* If "hv" is a string then "lv" can be a numeric or symbol. */ if (njs_is_string(hv)) { return !njs_is_symbol(lv) - && (njs_number(lv) == njs_string_to_number(hv, 0)); + && (njs_number(lv) == njs_string_to_number(hv)); } /* "hv" is an object and "lv" is either a string or a symbol or a numeric. */ @@ -2549,11 +2549,11 @@ njs_primitive_values_compare(njs_vm_t *v num2 = njs_number(val2); } else { - num2 = njs_string_to_number(val2, 0); + num2 = njs_string_to_number(val2); } } else if (njs_is_numeric(val2)) { - num1 = njs_string_to_number(val1, 0); + num1 = njs_string_to_number(val1); num2 = njs_number(val2); } else { diff -r 5e7fc8efebdc -r 8dcea0ba0bf8 src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Mon Mar 27 22:41:27 2023 -0700 +++ b/src/test/njs_unit_test.c Wed Mar 29 20:28:33 2023 -0700 @@ -994,7 +994,7 @@ static njs_unit_test_t njs_test[] = njs_str("3") }, { njs_str("5 - '-0x2'"), - njs_str("7") }, + njs_str("NaN") }, { njs_str("5 - '\t 0x2 \t'"), njs_str("3") }, @@ -13344,12 +13344,48 @@ static njs_unit_test_t njs_test[] = { njs_str("Number(false)"), njs_str("0") }, + { njs_str("Number('0b111')"), + njs_str("7") }, + + { njs_str("Number('0B111')"), + njs_str("7") }, + + { njs_str("Number('0b1_11')"), + njs_str("NaN") }, + + { njs_str("Number('-0b111')"), + njs_str("NaN") }, + { njs_str("Number(123)"), njs_str("123") }, { njs_str("Number('123')"), njs_str("123") }, + { njs_str("Number('0o123')"), + njs_str("83") }, + + { njs_str("Number('0O123')"), + njs_str("83") }, + + { njs_str("Number('0o1_23')"), + njs_str("NaN") }, + + { njs_str("Number('-0o123')"), + njs_str("NaN") }, + + { njs_str("Number('0x123')"), + njs_str("291") }, + + { njs_str("Number('0X123')"), + njs_str("291") }, + + { njs_str("Number('0x1_23')"), + njs_str("NaN") }, + + { njs_str("Number('-0x123')"), + njs_str("NaN") }, + { njs_str("['1', ' 1 ', '1\\t', '1\\n', '1\\r\\n'].reduce((a, x) => a + Number(x), 0)"), njs_str("5") }, From Michael.Kourlas at solace.com Thu Mar 30 17:19:08 2023 From: Michael.Kourlas at solace.com (Michael Kourlas) Date: Thu, 30 Mar 2023 17:19:08 +0000 Subject: [PATCH] HTTP: Add new uri_normalization_percent_decode option In-Reply-To: References: Message-ID: Hello, Thanks again for your comments. > This implies, basically, that there are 3 forms of the request > URI: 1) fully encoded, as in $request_uri, 2) fully decoded, as in > $uri now, and 3) "all-except-percent-and-reserved". To implement this > correctly, it needs clear definition when each form is used, and > it is going to be a non-trivial task to do this safely. I agree. A simple way to do this would be to make percent-decoding customizable on a per-directive basis. The core use case I was hoping to support is preserving encoded reserved characters in location matching (basically what was proposed in [1]), so that is what I would like to focus on in a reworked version of this patch. I propose the following: (1) The addition of a new variable called $uri_encoded_percent_and_reserved. As discussed, this variable is a special version of the normalized URI ($uri) that preserves any percent-encoded "%" or reserved characters. (2) Every transformation applied to $uri (e.g. from the "rewrite" directive, internal redirects, etc.) is automatically applied to $uri_encoded_percent_and_reserved as well. If this raises performance concerns, a new flag could be added to enable or disable the availability of $uri_encoded_percent_and_reserved. (3) The addition of a new optional parameter to the URI form of "location" blocks called "match-source": location [ = | ~ | ~* | ^~ ] uri [match-source=uri|uri-encoded-percent-and-reserved] { ... } For example: location ~ ^/api/objects/[^/]+/subobjects(/.*)?$ match-source=uri-encoded-percent-and-reserved { ... } "match-source=uri" is the default and the current behaviour. When "uri-encoded-percent-and-reserved" is used, the location matching for that block uses $uri_encoded_percent_and_reserved rather than $uri. Nested location blocks are not affected (unless they also use "uri-encoded-percent-and-reserved"). In future it would be possible to use a similar pattern with other directives that use $uri, such as "proxy_pass", but that can be done as part of a separate patch. If you think this is a sensible approach, I will submit a revised patch implementing it. Thanks, Michael Kourlas [1] https://trac.nginx.org/nginx/ticket/2225 ________________________________ Confidentiality notice This e-mail message and any attachment hereto contain confidential information which may be privileged and which is intended for the exclusive use of its addressee(s). If you receive this message in error, please inform sender immediately and destroy any copy thereof. Furthermore, any disclosure, distribution or copying of this message and/or any attachment hereto without the consent of the sender is strictly prohibited. Thank you. From pluknet at nginx.com Thu Mar 30 17:49:59 2023 From: pluknet at nginx.com (Sergey Kandaurov) Date: Thu, 30 Mar 2023 21:49:59 +0400 Subject: [PATCH] QUIC: optimized sending stream response In-Reply-To: <6d471753917c083b4044.1677152954@arut-laptop> References: <6d471753917c083b4044.1677152954@arut-laptop> Message-ID: > On 23 Feb 2023, at 15:49, Roman Arutyunyan wrote: > > # HG changeset patch > # User Roman Arutyunyan > # Date 1677152799 -14400 > # Thu Feb 23 15:46:39 2023 +0400 > # Branch quic > # Node ID 6d471753917c083b4044f73557f9af8d91c20d36 > # Parent 3c33d39a51d334d99fcc7d2b45e8d8190c431492 > QUIC: optimized sending stream response. > > When a stream is created by client, it's often the case that nginx will send > immediate response on that stream. An example is HTTP/3 request stream, which > in most cases quickly replies with at least HTTP headers. > > QUIC stream init handlers are called from a posted event. Output QUIC > frames are also sent to client from a posted event, called the push event. > If the push event is posted before the stream init event, then output produced > by stream may trigger sending an extra UDP datagram. To address this, push > event is now re-posted when a new stream init event is posted. > > An example is handling 0-RTT packets. Client typically sends an init packet > coalesced with a 0-RTT packet. Previously, nginx replied with a padded CRYPTO > datagram, followed by a 1-RTT stream reply datagram. Now CRYPTO and STREAM > packets are coalesced in one reply datagram, which saves bandwidth. > For the record, there are use-cases in connections without 0-RTT: - response to the 1st 1-RTT request in a new connection could be sent separately, because push event was already posted. The first 1-RTT request is usually coalesced with Init(ACK)+HS(ACK,CRYPTO) which are handled before 1-RTT and are the reason of posted push event. - additionally, handling of ACK+STREAM packets resulted in corresponding MAX_STREAMS and STREAM frames sent in separate packets/datagrams. Although it doesn't result in noticeable bandwidth waste unlike with 0-RTT, this is extra packets and cpu cycles spent on their serialization. > diff --git a/src/event/quic/ngx_event_quic_streams.c b/src/event/quic/ngx_event_quic_streams.c > --- a/src/event/quic/ngx_event_quic_streams.c > +++ b/src/event/quic/ngx_event_quic_streams.c > @@ -472,6 +472,17 @@ ngx_quic_get_stream(ngx_connection_t *c, > > if (qc->streams.initialized) { > ngx_post_event(rev, &ngx_posted_events); > + > + if (qc->push.posted) { > + /* > + * It's highly likely the posted stream will produce output > + * immediately. By postponing the push event, we coalesce > + * the stream output with queued frames in one UDP datagram. > + */ > + > + ngx_delete_posted_event(&qc->push); > + ngx_post_event(&qc->push, &ngx_posted_events); > + } > } > } Looks good. -- Sergey Kandaurov