Skip to content

TLS 1.3 support - patch v3 #3909

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

Merged
merged 3 commits into from
Apr 28, 2019
Merged
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 @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
58 changes: 58 additions & 0 deletions ext/openssl/tests/session_meta_capture_tlsv13.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
--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
$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_meta_capture_tlsv13.pem.tmp';
$cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_meta_capture_tlsv13-ca.pem.tmp';

$serverCode = <<<'CODE'
$serverUri = "ssl://127.0.0.1:64321";
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$serverCtx = stream_context_create(['ssl' => [
'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"
1 change: 1 addition & 0 deletions ext/openssl/tests/tls_wrapper.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ tls stream wrapper
<?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
Expand Down
66 changes: 66 additions & 0 deletions ext/openssl/tests/tls_wrapper_with_tls_v1.3.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
--TEST--
tls stream wrapper when TLS 1.3 available
--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('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)
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("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)
68 changes: 51 additions & 17 deletions ext/openssl/xp_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
/* }}} */

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

#if PHP_OPENSSL_API_VERSION >= 0x10100
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;

}
}
/* }}} */
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

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
16 changes: 9 additions & 7 deletions main/streams/php_stream_transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down