diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index bdee55e638cca..23ce1c9242ffa 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1574,6 +1574,9 @@ PHP_MINIT_FUNCTION(openssl) php_stream_xport_register("tlsv1.0", php_openssl_ssl_socket_factory); php_stream_xport_register("tlsv1.1", php_openssl_ssl_socket_factory); php_stream_xport_register("tlsv1.2", php_openssl_ssl_socket_factory); +#if OPENSSL_VERSION_NUMBER >= 0x10101000 + php_stream_xport_register("tlsv1.3", php_openssl_ssl_socket_factory); +#endif /* override the default tcp socket provider */ php_stream_xport_register("tcp", php_openssl_ssl_socket_factory); @@ -1647,6 +1650,9 @@ PHP_MSHUTDOWN_FUNCTION(openssl) php_stream_xport_unregister("tlsv1.0"); php_stream_xport_unregister("tlsv1.1"); php_stream_xport_unregister("tlsv1.2"); +#if OPENSSL_VERSION_NUMBER >= 0x10101000 + php_stream_xport_unregister("tlsv1.3"); +#endif /* reinstate the default tcp handler */ php_stream_xport_register("tcp", php_stream_generic_socket_factory); diff --git a/ext/openssl/tests/session_meta_capture_tlsv13.phpt b/ext/openssl/tests/session_meta_capture_tlsv13.phpt new file mode 100644 index 0000000000000..0f92463faef50 --- /dev/null +++ b/ext/openssl/tests/session_meta_capture_tlsv13.phpt @@ -0,0 +1,58 @@ +--TEST-- +Capture SSL session meta array in stream context for TLSv1.3 +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, + ]]); + + $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); + phpt_notify(); + + @stream_socket_accept($server, 1); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$peerName = 'session_meta_capture_tlsv13'; +$clientCode = <<<'CODE' + $serverUri = "ssl://127.0.0.1:64321"; + $clientFlags = STREAM_CLIENT_CONNECT; + $clientCtx = stream_context_create(['ssl' => [ + 'verify_peer' => true, + 'cafile' => '%s', + 'peer_name' => '%s', + 'capture_session_meta' => true, + ]]); + + phpt_wait(); + + stream_context_set_option($clientCtx, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT); + @stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx); + $meta = stream_context_get_options($clientCtx)['ssl']['session_meta']; + var_dump($meta['protocol']); +CODE; +$clientCode = sprintf($clientCode, $cacertFile, $peerName); + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveCaCert($cacertFile); +$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +string(7) "TLSv1.3" diff --git a/ext/openssl/tests/tls_wrapper.phpt b/ext/openssl/tests/tls_wrapper.phpt index 1a0ac3abb7d2a..53f8a972ab32d 100644 --- a/ext/openssl/tests/tls_wrapper.phpt +++ b/ext/openssl/tests/tls_wrapper.phpt @@ -4,6 +4,7 @@ tls stream wrapper --FILE-- +--FILE-- + [ + 'local_cert' => __DIR__ . '/streams_crypto_method.pem', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); + phpt_notify(); + + for ($i = 0; $i < (phpt_has_sslv3() ? 7 : 6); $i++) { + @stream_socket_accept($server, 3); + } +CODE; + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ]]); + + phpt_wait(); + + $client = stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("tlsv1.3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECTF-- +resource(%d) of type (stream) +bool(false) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) +resource(%d) of type (stream) diff --git a/ext/openssl/tests/tlsv1.3_wrapper.phpt b/ext/openssl/tests/tlsv1.3_wrapper.phpt new file mode 100644 index 0000000000000..c0e3cf4d72a10 --- /dev/null +++ b/ext/openssl/tests/tlsv1.3_wrapper.phpt @@ -0,0 +1,50 @@ +--TEST-- +tlsv1.3 stream wrapper +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => __DIR__ . '/streams_crypto_method.pem', + ]]); + + $server = stream_socket_server('tlsv1.3://127.0.0.1:64321', $errno, $errstr, $flags, $ctx); + phpt_notify(); + + for ($i=0; $i < 3; $i++) { + @stream_socket_accept($server, 3); + } +CODE; + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ]]); + + phpt_wait(); + + $client = stream_socket_client("tlsv1.3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); + + $client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx); + var_dump($client); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECTF-- +resource(%d) of type (stream) +bool(false) +bool(false) diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 9ada33a3568b0..961bfb7031837 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -58,18 +58,13 @@ #define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3) #define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4) #define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5) - -#ifndef OPENSSL_NO_SSL3 -#define HAVE_SSL3 1 -#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3 -#else -#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_0 -#endif -#define PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_2 - +#define STREAM_CRYPTO_METHOD_TLSv1_3 (1<<6) #define HAVE_TLS11 1 #define HAVE_TLS12 1 +#if OPENSSL_VERSION_NUMBER >= 0x10101000 +#define HAVE_TLS13 1 +#endif #ifndef OPENSSL_NO_ECDH #define HAVE_ECDH 1 @@ -86,6 +81,18 @@ #define HAVE_SEC_LEVEL 1 #endif +#ifndef OPENSSL_NO_SSL3 +#define HAVE_SSL3 1 +#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3 +#else +#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_0 +#endif +#ifdef HAVE_TLS13 +#define PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_3 +#else +#define PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_2 +#endif + /* Simplify ssl context option retrieval */ #define GET_VER_OPT(name) \ (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != NULL) @@ -998,6 +1005,11 @@ static int php_openssl_get_crypto_method_ctx_flags(int method_flags) /* {{{ */ ssl_ctx_options |= SSL_OP_NO_TLSv1_2; } #endif +#ifdef HAVE_TLS13 + if (!(method_flags & STREAM_CRYPTO_METHOD_TLSv1_3)) { + ssl_ctx_options |= SSL_OP_NO_TLSv1_3; + } +#endif return ssl_ctx_options; } @@ -1012,7 +1024,7 @@ static inline int php_openssl_get_min_proto_version_flag(int flags) /* {{{ */ return ver; } } - return STREAM_CRYPTO_METHOD_TLSv1_2; + return PHP_OPENSSL_MAX_PROTO_VERSION; } /* }}} */ @@ -1024,7 +1036,7 @@ static inline int php_openssl_get_max_proto_version_flag(int flags) /* {{{ */ return ver; } } - return STREAM_CRYPTO_METHOD_TLSv1_2; + return STREAM_CRYPTO_METHOD_TLSv1_3; } /* }}} */ @@ -1032,18 +1044,22 @@ static inline int php_openssl_get_max_proto_version_flag(int flags) /* {{{ */ static inline int php_openssl_map_proto_version(int flag) /* {{{ */ { switch (flag) { +#ifdef HAVE_TLS13 + case STREAM_CRYPTO_METHOD_TLSv1_3: + return TLS1_3_VERSION; +#endif + case STREAM_CRYPTO_METHOD_TLSv1_2: + return TLS1_2_VERSION; + case STREAM_CRYPTO_METHOD_TLSv1_1: + return TLS1_1_VERSION; + case STREAM_CRYPTO_METHOD_TLSv1_0: + return TLS1_VERSION; #ifdef HAVE_SSL3 case STREAM_CRYPTO_METHOD_SSLv3: return SSL3_VERSION; #endif - case STREAM_CRYPTO_METHOD_TLSv1_0: - return TLS1_VERSION; - case STREAM_CRYPTO_METHOD_TLSv1_1: - return TLS1_1_VERSION; - /* case STREAM_CRYPTO_METHOD_TLSv1_2: */ default: return TLS1_2_VERSION; - } } /* }}} */ @@ -1788,6 +1804,11 @@ static zend_array *php_openssl_capture_session_meta(SSL *ssl_handle) /* {{{ */ char version_str[PHP_SSL_MAX_VERSION_LEN]; switch (proto) { +#ifdef HAVE_TLS13 + case TLS1_3_VERSION: + proto_str = "TLSv1.3"; + break; +#endif #ifdef HAVE_TLS12 case TLS1_2_VERSION: proto_str = "TLSv1.2"; @@ -2392,6 +2413,9 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val array_init(&tmp); switch (SSL_version(sslsock->ssl_handle)) { +#ifdef HAVE_TLS13 + case TLS1_3_VERSION: proto_str = "TLSv1.3"; break; +#endif #ifdef HAVE_TLS12 case TLS1_2_VERSION: proto_str = "TLSv1.2"; break; #endif @@ -2731,6 +2755,16 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen, "TLSv1.2 support is not compiled into the OpenSSL library against which PHP is linked"); php_stream_close(stream); return NULL; +#endif + } else if (strncmp(proto, "tlsv1.3", protolen) == 0) { +#ifdef HAVE_TLS13 + sslsock->enable_on_connect = 1; + sslsock->method = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT; +#else + php_error_docref(NULL, E_WARNING, + "TLSv1.3 support is not compiled into the OpenSSL library against which PHP is linked"); + php_stream_close(stream); + return NULL; #endif } diff --git a/ext/standard/file.c b/ext/standard/file.c index 572bf769bfc49..32d35ce432c94 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -217,6 +217,7 @@ PHP_MINIT_FUNCTION(file) REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT", STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT", STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT", STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT", STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_ANY_SERVER", STREAM_CRYPTO_METHOD_ANY_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_SSLv2_SERVER", STREAM_CRYPTO_METHOD_SSLv2_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_SSLv3_SERVER", STREAM_CRYPTO_METHOD_SSLv3_SERVER, CONST_CS|CONST_PERSISTENT); @@ -225,11 +226,13 @@ PHP_MINIT_FUNCTION(file) REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_0_SERVER", STREAM_CRYPTO_METHOD_TLSv1_0_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_1_SERVER", STREAM_CRYPTO_METHOD_TLSv1_1_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_2_SERVER", STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_3_SERVER", STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_SSLv3", STREAM_CRYPTO_METHOD_SSLv3_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_0", STREAM_CRYPTO_METHOD_TLSv1_0_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_1", STREAM_CRYPTO_METHOD_TLSv1_1_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_2", STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_3", STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_SHUT_RD", STREAM_SHUT_RD, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_SHUT_WR", STREAM_SHUT_WR, CONST_CS|CONST_PERSISTENT); diff --git a/main/streams/php_stream_transport.h b/main/streams/php_stream_transport.h index a73966ec332c1..f49d9c787da3d 100644 --- a/main/streams/php_stream_transport.h +++ b/main/streams/php_stream_transport.h @@ -171,21 +171,23 @@ typedef enum { STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT = (1 << 3 | 1), STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT = (1 << 4 | 1), STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT = (1 << 5 | 1), + STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT = (1 << 6 | 1), /* TLS equates to TLS_ANY as of PHP 7.2 */ - STREAM_CRYPTO_METHOD_TLS_CLIENT = ((1 << 3) | (1 << 4) | (1 << 5) | 1), - STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT = ((1 << 3) | (1 << 4) | (1 << 5) | 1), - STREAM_CRYPTO_METHOD_ANY_CLIENT = ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | 1), + STREAM_CRYPTO_METHOD_TLS_CLIENT = ((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | 1), + STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT = ((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | 1), + STREAM_CRYPTO_METHOD_ANY_CLIENT = ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | 1), STREAM_CRYPTO_METHOD_SSLv2_SERVER = (1 << 1), STREAM_CRYPTO_METHOD_SSLv3_SERVER = (1 << 2), /* v23 no longer negotiates SSL2 or SSL3 */ - STREAM_CRYPTO_METHOD_SSLv23_SERVER = ((1 << 3) | (1 << 4) | (1 << 5)), + STREAM_CRYPTO_METHOD_SSLv23_SERVER = ((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)), STREAM_CRYPTO_METHOD_TLSv1_0_SERVER = (1 << 3), STREAM_CRYPTO_METHOD_TLSv1_1_SERVER = (1 << 4), STREAM_CRYPTO_METHOD_TLSv1_2_SERVER = (1 << 5), + STREAM_CRYPTO_METHOD_TLSv1_3_SERVER = (1 << 6), /* TLS equates to TLS_ANY as of PHP 7.2 */ - STREAM_CRYPTO_METHOD_TLS_SERVER = ((1 << 3) | (1 << 4) | (1 << 5)), - STREAM_CRYPTO_METHOD_TLS_ANY_SERVER = ((1 << 3) | (1 << 4) | (1 << 5)), - STREAM_CRYPTO_METHOD_ANY_SERVER = ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5)) + STREAM_CRYPTO_METHOD_TLS_SERVER = ((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)), + STREAM_CRYPTO_METHOD_TLS_ANY_SERVER = ((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)), + STREAM_CRYPTO_METHOD_ANY_SERVER = ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)) } php_stream_xport_crypt_method_t; /* These functions provide crypto support on the underlying transport */