Skip to content

[Patch v2] Added tls 1.3 support for PHP #3700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,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);
Expand Down Expand Up @@ -1632,6 +1635,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);
Expand Down
50 changes: 50 additions & 0 deletions ext/openssl/tests/session_meta_capture_tlsv13.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
Capture SSL session meta array in stream context for TLSv1.3
--SKIPIF--
<?php
if (!extension_loaded("openssl")) die("skip openssl not loaded");
if (!function_exists("proc_open")) die("skip no proc_open");
if (OPENSSL_VERSION_NUMBER < 0x10101000) die("skip OpenSSL v1.1.1 required");
?>
--FILE--
<?php
$serverCode = <<<'CODE'
$serverUri = "ssl://127.0.0.1:64321";
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$serverCtx = stream_context_create(['ssl' => [
'local_cert' => __DIR__ . '/bug54992.pem',
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER,
]]);

$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
phpt_notify();

@stream_socket_accept($server, 1);
@stream_socket_accept($server, 1);
@stream_socket_accept($server, 1);
@stream_socket_accept($server, 1);
CODE;

$clientCode = <<<'CODE'
$serverUri = "ssl://127.0.0.1:64321";
$clientFlags = STREAM_CLIENT_CONNECT;
$clientCtx = stream_context_create(['ssl' => [
'verify_peer' => true,
'cafile' => __DIR__ . '/bug54992-ca.pem',
'peer_name' => 'bug54992.local',
'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;

include 'ServerClientTestCase.inc';
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
?>
--EXPECT--
string(7) "TLSv1.3"
50 changes: 50 additions & 0 deletions ext/openssl/tests/tlsv1.3_wrapper.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
tlsv1.3 stream wrapper
--SKIPIF--
<?php
if (!extension_loaded("openssl")) die("skip openssl not loaded");
if (!function_exists("proc_open")) die("skip no proc_open");
if (OPENSSL_VERSION_NUMBER < 0x10101000) die("skip OpenSSL v1.1.1 required");
?>
--FILE--
<?php
$serverCode = <<<'CODE'
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
'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("sslv3://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)
41 changes: 36 additions & 5 deletions ext/openssl/xp_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,22 @@
#define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3)
#define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4)
#define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5)
#define STREAM_CRYPTO_METHOD_TLSv1_3 (1<<6)

#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 PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_3


#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
Expand Down Expand Up @@ -998,6 +1002,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;
}
Expand All @@ -1012,7 +1021,7 @@ static inline int php_openssl_get_min_proto_version_flag(int flags) /* {{{ */
return ver;
}
}
return STREAM_CRYPTO_METHOD_TLSv1_2;
return STREAM_CRYPTO_METHOD_TLSv1_3;
}
/* }}} */

Expand All @@ -1024,7 +1033,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;
}
/* }}} */

Expand All @@ -1040,9 +1049,13 @@ static inline int php_openssl_map_proto_version(int flag) /* {{{ */
return TLS1_VERSION;
case STREAM_CRYPTO_METHOD_TLSv1_1:
return TLS1_1_VERSION;
/* case STREAM_CRYPTO_METHOD_TLSv1_2: */
case STREAM_CRYPTO_METHOD_TLSv1_2:
return TLS1_2_VERSION;
/* case STREAM_CRYPTO_METHOD_TLSv1_3: */
#ifdef HAVE_TLS13
default:
return TLS1_2_VERSION;
return TLS1_3_VERSION;
#endif

}
}
Expand Down Expand Up @@ -1788,6 +1801,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";
Expand Down Expand Up @@ -2392,6 +2410,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
Expand Down Expand Up @@ -2739,6 +2760,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
}

Expand Down
3 changes: 3 additions & 0 deletions ext/standard/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions main/streams/php_stream_transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ 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),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These generic constants for any version need to include TLS v1.3, too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have requested not to touch those as I don't want to negotiate 1.3 by default. It could break the existing clients so I would like to not do it at least for PHP 7.4. I think we could change that in PHP 8 though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? Significant work has been put into TLS 1.3 to avoid breaking with broken TLS 1.2 implementations.

If you want to avoid TLS 1.3 by default, please update the default wrapper, but these constants specifically are for ANY TLS version, so the patch should reflect that.

Expand All @@ -182,6 +183,7 @@ typedef enum {
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)),
Expand Down