diff --git a/ext/openssl/tests/gh22081.phpt b/ext/openssl/tests/gh22081.phpt new file mode 100644 index 000000000000..17f74be7584c --- /dev/null +++ b/ext/openssl/tests/gh22081.phpt @@ -0,0 +1,76 @@ +--TEST-- +GH-22081: server reneg limit not reallocated across non-blocking retries +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + /* Plain TCP listener so the TLS handshake is driven manually below. */ + $server = stream_socket_server('tcp://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Complete each handshake in non-blocking mode so that php_openssl_enable_crypto() is + * re-entered across multiple WANT_READ/WANT_WRITE rounds per connection. */ + for ($i = 0; $i < 5; $i++) { + $conn = @stream_socket_accept($server, 30); + if (!$conn) { + continue; + } + stream_set_blocking($conn, false); + do { + $r = stream_socket_enable_crypto($conn, true, STREAM_CRYPTO_METHOD_TLS_SERVER); + } while ($r === 0); + + if ($r === true) { + fwrite($conn, "ok $i\n"); + } + fclose($conn); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ]]); + + for ($i = 0; $i < 5; $i++) { + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client) { + echo trim(fgets($client)) . "\n"; + fclose($client); + } + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('gh22081-server-reneg-nonblocking', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +ok 0 +ok 1 +ok 2 +ok 3 +ok 4 + diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 4d0dad20a439..307cc3489c3c 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -2688,28 +2688,23 @@ static int php_openssl_enable_crypto(php_stream *stream, struct timeval start_time, *timeout; bool blocked = sslsock->s.is_blocked, has_timeout = false; - if (sslsock->is_client) { - /* Set session data for client */ - if ( php_openssl_apply_client_session_data(stream, sslsock)) { - return FAILURE; - } -#ifdef HAVE_TLS_SNI - php_openssl_enable_client_sni(stream, sslsock); -#endif - } else { - php_openssl_init_server_reneg_limit(stream, sslsock); - } - + if (!sslsock->state_set) { #ifdef PHP_OPENSSL_TLS_DEBUG - BIO *b_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); - SSL_set_msg_callback(sslsock->ssl_handle, SSL_trace); - SSL_set_msg_callback_arg(sslsock->ssl_handle, b_out); + BIO *b_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); + SSL_set_msg_callback(sslsock->ssl_handle, SSL_trace); + SSL_set_msg_callback_arg(sslsock->ssl_handle, b_out); #endif - - if (!sslsock->state_set) { if (sslsock->is_client) { + /* Set session data for client */ + if (php_openssl_apply_client_session_data(stream, sslsock) == FAILURE) { + return -1; + } +#ifdef HAVE_TLS_SNI + php_openssl_enable_client_sni(stream, sslsock); +#endif SSL_set_connect_state(sslsock->ssl_handle); } else { + php_openssl_init_server_reneg_limit(stream, sslsock); SSL_set_accept_state(sslsock->ssl_handle); } sslsock->state_set = 1;