summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS4
-rw-r--r--ext/openssl/tests/stream_crypto_flags_003.phpt6
-rw-r--r--ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt63
-rw-r--r--ext/openssl/xp_ssl.c178
-rw-r--r--ext/standard/file.c5
5 files changed, 190 insertions, 66 deletions
diff --git a/NEWS b/NEWS
index f751b42136..73c8c1ad6e 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,10 @@ PHP NEWS
. Fixed bug #76532 (Integer overflow and excessive memory usage
in mb_strimwidth). (MarcusSchwarz)
+- OpenSSL:
+ . Add min_proto_version and max_proto_version ssl stream options as well as
+ related constants for possible TLS protocol values. (Jakub Zelenka)
+
- PCRE:
. Fixed bug #76512 (\w no longer includes unicode characters). (cmb)
. Fixed bug #76514 (Regression in preg_match makes it fail with
diff --git a/ext/openssl/tests/stream_crypto_flags_003.phpt b/ext/openssl/tests/stream_crypto_flags_003.phpt
index 28cb640cfe..fd6746daa0 100644
--- a/ext/openssl/tests/stream_crypto_flags_003.phpt
+++ b/ext/openssl/tests/stream_crypto_flags_003.phpt
@@ -14,7 +14,7 @@ $serverCode = <<<'CODE'
'local_cert' => __DIR__ . '/bug54992.pem',
// Only accept TLSv1.2 connections
- 'crypto_method' => STREAM_CRYPTO_METHOD_SSLv3_SERVER | STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_0_SERVER | STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
]]);
$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
@@ -51,6 +51,6 @@ include 'ServerClientTestCase.inc';
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
--EXPECTF--
resource(%d) of type (stream)
-bool(false)
-bool(false)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
diff --git a/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt b/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt
new file mode 100644
index 0000000000..467ecf0d7a
--- /dev/null
+++ b/ext/openssl/tests/tls_min_v1.0_max_v1.1_wrapper.phpt
@@ -0,0 +1,63 @@
+--TEST--
+tls stream wrapper with min version 1.0 and max version 1.1
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip openssl not loaded");
+if (!function_exists("proc_open")) die("skip no proc_open");
+?>
+--FILE--
+<?php
+$serverCode = <<<'CODE'
+ $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
+ $ctx = stream_context_create(['ssl' => [
+ 'local_cert' => __DIR__ . '/streams_crypto_method.pem',
+ 'min_proto_version' => STREAM_CRYPTO_PROTO_TLSv1_0,
+ 'max_proto_version' => STREAM_CRYPTO_PROTO_TLSv1_1,
+ ]]);
+
+ $server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
+ phpt_notify();
+
+ for ($i=0; $i < 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("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)
+bool(false)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 0d5027f41e..6ab47d6952 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -15,6 +15,7 @@
| Authors: Wez Furlong <wez@thebrainroom.com> |
| Daniel Lowrey <rdlowrey@php.net> |
| Chris Wright <daverandom@php.net> |
+ | Jakub Zelenka <bukka@php.net> |
+----------------------------------------------------------------------+
*/
@@ -52,9 +53,22 @@
#undef X509_EXTENSIONS
#endif
+/* Flags for determining allowed stream crypto methods */
+#define STREAM_CRYPTO_IS_CLIENT (1<<0)
+#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
+#define STREAM_CRYPTO_METHOD_SSLv3 (1<<2)
+#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 HAVE_TLS11 1
#define HAVE_TLS12 1
@@ -74,14 +88,6 @@
#define HAVE_SEC_LEVEL 1
#endif
-/* Flags for determining allowed stream crypto methods */
-#define STREAM_CRYPTO_IS_CLIENT (1<<0)
-#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
-#define STREAM_CRYPTO_METHOD_SSLv3 (1<<2)
-#define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3)
-#define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4)
-#define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5)
-
/* 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)
@@ -945,46 +951,6 @@ static int php_openssl_set_local_cert(SSL_CTX *ctx, php_stream *stream) /* {{{ *
}
/* }}} */
-static const SSL_METHOD *php_openssl_select_crypto_method(zend_long method_value, int is_client) /* {{{ */
-{
- if (method_value == STREAM_CRYPTO_METHOD_SSLv2) {
- php_error_docref(NULL, E_WARNING,
- "SSLv2 unavailable in this PHP version");
- return NULL;
- } else if (method_value == STREAM_CRYPTO_METHOD_SSLv3) {
-#ifdef HAVE_SSL3
- return is_client ? SSLv3_client_method() : SSLv3_server_method();
-#else
- php_error_docref(NULL, E_WARNING,
- "SSLv3 unavailable in the OpenSSL library against which PHP is linked");
- return NULL;
-#endif
- } else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_0) {
- return is_client ? TLSv1_client_method() : TLSv1_server_method();
- } else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_1) {
-#ifdef HAVE_TLS11
- return is_client ? TLSv1_1_client_method() : TLSv1_1_server_method();
-#else
- php_error_docref(NULL, E_WARNING,
- "TLSv1.1 unavailable in the OpenSSL library against which PHP is linked");
- return NULL;
-#endif
- } else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_2) {
-#ifdef HAVE_TLS12
- return is_client ? TLSv1_2_client_method() : TLSv1_2_server_method();
-#else
- php_error_docref(NULL, E_WARNING,
- "TLSv1.2 unavailable in the OpenSSL library against which PHP is linked");
- return NULL;
-#endif
- } else {
- php_error_docref(NULL, E_WARNING,
- "Invalid crypto method");
- return NULL;
- }
-}
-/* }}} */
-
#define PHP_SSL_MAX_VERSION_LEN 32
static char *php_openssl_cipher_get_version(const SSL_CIPHER *c, char *buffer, size_t max_len) /* {{{ */
@@ -1000,6 +966,7 @@ static char *php_openssl_cipher_get_version(const SSL_CIPHER *c, char *buffer, s
}
/* }}} */
+#if PHP_OPENSSL_API_VERSION < 0x10100
static int php_openssl_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
{
int ssl_ctx_options = SSL_OP_ALL;
@@ -1029,6 +996,89 @@ static int php_openssl_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
return ssl_ctx_options;
}
/* }}} */
+#endif
+
+static inline int php_openssl_get_min_proto_version_flag(int flags) /* {{{ */
+{
+ int ver;
+ for (ver = PHP_OPENSSL_MIN_PROTO_VERSION; ver <= PHP_OPENSSL_MAX_PROTO_VERSION; ver <<= 1) {
+ if (flags & ver) {
+ return ver;
+ }
+ }
+ return STREAM_CRYPTO_METHOD_TLSv1_2;
+}
+/* }}} */
+
+static inline int php_openssl_get_max_proto_version_flag(int flags) /* {{{ */
+{
+ int ver;
+ for (ver = PHP_OPENSSL_MAX_PROTO_VERSION; ver >= PHP_OPENSSL_MIN_PROTO_VERSION; ver >>= 1) {
+ if (flags & ver) {
+ return ver;
+ }
+ }
+ return STREAM_CRYPTO_METHOD_TLSv1_2;
+}
+/* }}} */
+
+#if PHP_OPENSSL_API_VERSION >= 0x10100
+static inline int php_openssl_map_proto_version(int flag) /* {{{ */
+{
+ switch (flag) {
+#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;
+
+ }
+}
+/* }}} */
+
+static int php_openssl_get_min_proto_version(int flags) /* {{{ */
+{
+ return php_openssl_map_proto_version(php_openssl_get_min_proto_version_flag(flags));
+}
+/* }}} */
+
+static int php_openssl_get_max_proto_version(int flags) /* {{{ */
+{
+ return php_openssl_map_proto_version(php_openssl_get_max_proto_version_flag(flags));
+}
+/* }}} */
+#endif
+
+static int php_openssl_get_proto_version_flags(int flags, int min, int max) /* {{{ */
+{
+ int ver;
+
+ if (!min) {
+ min = php_openssl_get_min_proto_version_flag(flags);
+ }
+ if (!max) {
+ max = php_openssl_get_max_proto_version_flag(flags);
+ }
+
+ for (ver = PHP_OPENSSL_MIN_PROTO_VERSION; ver <= PHP_OPENSSL_MAX_PROTO_VERSION; ver <<= 1) {
+ if (ver >= min && ver <= max) {
+ if (!(flags & ver)) {
+ flags |= ver;
+ }
+ } else if (flags & ver) {
+ flags &= ~ver;
+ }
+ }
+
+ return flags;
+}
+/* }}} */
static void php_openssl_limit_handshake_reneg(const SSL *ssl) /* {{{ */
{
@@ -1538,6 +1588,8 @@ int php_openssl_setup_crypto(php_stream *stream,
const SSL_METHOD *method;
int ssl_ctx_options;
int method_flags;
+ zend_long min_version = 0;
+ zend_long max_version = 0;
char *cipherlist = NULL;
char *alpn_protocols = NULL;
zval *val;
@@ -1558,23 +1610,18 @@ int php_openssl_setup_crypto(php_stream *stream,
sslsock->is_client = cparam->inputs.method & STREAM_CRYPTO_IS_CLIENT;
method_flags = ((cparam->inputs.method >> 1) << 1);
- /* Should we use a specific crypto method or is generic SSLv23 okay? */
- if ((method_flags & (method_flags-1)) == 0) {
- ssl_ctx_options = SSL_OP_ALL;
- method = php_openssl_select_crypto_method(method_flags, sslsock->is_client);
- if (method == NULL) {
- return FAILURE;
- }
- } else {
- method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method();
- ssl_ctx_options = php_openssl_get_crypto_method_ctx_flags(method_flags);
- if (ssl_ctx_options == -1) {
- return FAILURE;
- }
- }
-
+ method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method();
sslsock->ctx = SSL_CTX_new(method);
+ GET_VER_OPT_LONG("min_proto_version", min_version);
+ GET_VER_OPT_LONG("max_proto_version", max_version);
+ method_flags = php_openssl_get_proto_version_flags(method_flags, min_version, max_version);
+#if PHP_OPENSSL_API_VERSION < 0x10100
+ ssl_ctx_options = php_openssl_get_crypto_method_ctx_flags(method_flags);
+#else
+ ssl_ctx_options = SSL_OP_ALL;
+#endif
+
if (sslsock->ctx == NULL) {
php_error_docref(NULL, E_WARNING, "SSL context creation failure");
return FAILURE;
@@ -1663,6 +1710,11 @@ int php_openssl_setup_crypto(php_stream *stream,
SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options);
+#if PHP_OPENSSL_API_VERSION >= 0x10100
+ SSL_CTX_set_min_proto_version(sslsock->ctx, php_openssl_get_min_proto_version(method_flags));
+ SSL_CTX_set_max_proto_version(sslsock->ctx, php_openssl_get_max_proto_version(method_flags));
+#endif
+
if (sslsock->is_client == 0 &&
PHP_STREAM_CONTEXT(stream) &&
FAILURE == php_openssl_set_server_specific_opts(stream, sslsock->ctx)
diff --git a/ext/standard/file.c b/ext/standard/file.c
index da2503056a..643876acb9 100644
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@ -228,6 +228,11 @@ PHP_MINIT_FUNCTION(file)
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_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_SHUT_RD", STREAM_SHUT_RD, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_SHUT_WR", STREAM_SHUT_WR, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_SHUT_RDWR", STREAM_SHUT_RDWR, CONST_CS|CONST_PERSISTENT);