Skip to content

Commit d404b5e

Browse files
committed
Add ssl/tls streams options for min and max proto version
1 parent ce0721b commit d404b5e

File tree

5 files changed

+190
-66
lines changed

5 files changed

+190
-66
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ PHP NEWS
3131
. Fixed bug #76532 (Integer overflow and excessive memory usage
3232
in mb_strimwidth). (MarcusSchwarz)
3333

34+
- OpenSSL:
35+
. Add min_proto_version and max_proto_version ssl stream options as well as
36+
related constants for possible TLS protocol values. (Jakub Zelenka)
37+
3438
- PCRE:
3539
. Fixed bug #76512 (\w no longer includes unicode characters). (cmb)
3640
. Fixed bug #76514 (Regression in preg_match makes it fail with

ext/openssl/tests/stream_crypto_flags_003.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ $serverCode = <<<'CODE'
1414
'local_cert' => __DIR__ . '/bug54992.pem',
1515

1616
// Only accept TLSv1.2 connections
17-
'crypto_method' => STREAM_CRYPTO_METHOD_SSLv3_SERVER | STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
17+
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_0_SERVER | STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
1818
]]);
1919

2020
$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
@@ -51,6 +51,6 @@ include 'ServerClientTestCase.inc';
5151
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
5252
--EXPECTF--
5353
resource(%d) of type (stream)
54-
bool(false)
55-
bool(false)
54+
resource(%d) of type (stream)
55+
resource(%d) of type (stream)
5656

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
--TEST--
2+
tls stream wrapper with min version 1.0 and max version 1.1
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl")) die("skip openssl not loaded");
6+
if (!function_exists("proc_open")) die("skip no proc_open");
7+
?>
8+
--FILE--
9+
<?php
10+
$serverCode = <<<'CODE'
11+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
12+
$ctx = stream_context_create(['ssl' => [
13+
'local_cert' => __DIR__ . '/streams_crypto_method.pem',
14+
'min_proto_version' => STREAM_CRYPTO_PROTO_TLSv1_0,
15+
'max_proto_version' => STREAM_CRYPTO_PROTO_TLSv1_1,
16+
]]);
17+
18+
$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
19+
phpt_notify();
20+
21+
for ($i=0; $i < 6; $i++) {
22+
@stream_socket_accept($server, 3);
23+
}
24+
CODE;
25+
26+
$clientCode = <<<'CODE'
27+
$flags = STREAM_CLIENT_CONNECT;
28+
$ctx = stream_context_create(['ssl' => [
29+
'verify_peer' => false,
30+
'verify_peer_name' => false,
31+
]]);
32+
33+
phpt_wait();
34+
35+
$client = stream_socket_client("tlsv1.0://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx);
36+
var_dump($client);
37+
38+
$client = @stream_socket_client("sslv3://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx);
39+
var_dump($client);
40+
41+
$client = @stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx);
42+
var_dump($client);
43+
44+
$client = @stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx);
45+
var_dump($client);
46+
47+
$client = @stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx);
48+
var_dump($client);
49+
50+
$client = @stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 3, $flags, $ctx);
51+
var_dump($client);
52+
CODE;
53+
54+
include 'ServerClientTestCase.inc';
55+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
56+
?>
57+
--EXPECTF--
58+
resource(%d) of type (stream)
59+
bool(false)
60+
resource(%d) of type (stream)
61+
bool(false)
62+
resource(%d) of type (stream)
63+
resource(%d) of type (stream)

ext/openssl/xp_ssl.c

Lines changed: 115 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
| Authors: Wez Furlong <wez@thebrainroom.com> |
1616
| Daniel Lowrey <rdlowrey@php.net> |
1717
| Chris Wright <daverandom@php.net> |
18+
| Jakub Zelenka <bukka@php.net> |
1819
+----------------------------------------------------------------------+
1920
*/
2021

@@ -52,9 +53,22 @@
5253
#undef X509_EXTENSIONS
5354
#endif
5455

56+
/* Flags for determining allowed stream crypto methods */
57+
#define STREAM_CRYPTO_IS_CLIENT (1<<0)
58+
#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
59+
#define STREAM_CRYPTO_METHOD_SSLv3 (1<<2)
60+
#define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3)
61+
#define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4)
62+
#define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5)
63+
5564
#ifndef OPENSSL_NO_SSL3
5665
#define HAVE_SSL3 1
66+
#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_SSLv3
67+
#else
68+
#define PHP_OPENSSL_MIN_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_0
5769
#endif
70+
#define PHP_OPENSSL_MAX_PROTO_VERSION STREAM_CRYPTO_METHOD_TLSv1_2
71+
5872

5973
#define HAVE_TLS11 1
6074
#define HAVE_TLS12 1
@@ -74,14 +88,6 @@
7488
#define HAVE_SEC_LEVEL 1
7589
#endif
7690

77-
/* Flags for determining allowed stream crypto methods */
78-
#define STREAM_CRYPTO_IS_CLIENT (1<<0)
79-
#define STREAM_CRYPTO_METHOD_SSLv2 (1<<1)
80-
#define STREAM_CRYPTO_METHOD_SSLv3 (1<<2)
81-
#define STREAM_CRYPTO_METHOD_TLSv1_0 (1<<3)
82-
#define STREAM_CRYPTO_METHOD_TLSv1_1 (1<<4)
83-
#define STREAM_CRYPTO_METHOD_TLSv1_2 (1<<5)
84-
8591
/* Simplify ssl context option retrieval */
8692
#define GET_VER_OPT(name) \
8793
(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) /* {{{ *
945951
}
946952
/* }}} */
947953

948-
static const SSL_METHOD *php_openssl_select_crypto_method(zend_long method_value, int is_client) /* {{{ */
949-
{
950-
if (method_value == STREAM_CRYPTO_METHOD_SSLv2) {
951-
php_error_docref(NULL, E_WARNING,
952-
"SSLv2 unavailable in this PHP version");
953-
return NULL;
954-
} else if (method_value == STREAM_CRYPTO_METHOD_SSLv3) {
955-
#ifdef HAVE_SSL3
956-
return is_client ? SSLv3_client_method() : SSLv3_server_method();
957-
#else
958-
php_error_docref(NULL, E_WARNING,
959-
"SSLv3 unavailable in the OpenSSL library against which PHP is linked");
960-
return NULL;
961-
#endif
962-
} else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_0) {
963-
return is_client ? TLSv1_client_method() : TLSv1_server_method();
964-
} else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_1) {
965-
#ifdef HAVE_TLS11
966-
return is_client ? TLSv1_1_client_method() : TLSv1_1_server_method();
967-
#else
968-
php_error_docref(NULL, E_WARNING,
969-
"TLSv1.1 unavailable in the OpenSSL library against which PHP is linked");
970-
return NULL;
971-
#endif
972-
} else if (method_value == STREAM_CRYPTO_METHOD_TLSv1_2) {
973-
#ifdef HAVE_TLS12
974-
return is_client ? TLSv1_2_client_method() : TLSv1_2_server_method();
975-
#else
976-
php_error_docref(NULL, E_WARNING,
977-
"TLSv1.2 unavailable in the OpenSSL library against which PHP is linked");
978-
return NULL;
979-
#endif
980-
} else {
981-
php_error_docref(NULL, E_WARNING,
982-
"Invalid crypto method");
983-
return NULL;
984-
}
985-
}
986-
/* }}} */
987-
988954
#define PHP_SSL_MAX_VERSION_LEN 32
989955

990956
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
1000966
}
1001967
/* }}} */
1002968

969+
#if PHP_OPENSSL_API_VERSION < 0x10100
1003970
static int php_openssl_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
1004971
{
1005972
int ssl_ctx_options = SSL_OP_ALL;
@@ -1029,6 +996,89 @@ static int php_openssl_get_crypto_method_ctx_flags(int method_flags) /* {{{ */
1029996
return ssl_ctx_options;
1030997
}
1031998
/* }}} */
999+
#endif
1000+
1001+
static inline int php_openssl_get_min_proto_version_flag(int flags) /* {{{ */
1002+
{
1003+
int ver;
1004+
for (ver = PHP_OPENSSL_MIN_PROTO_VERSION; ver <= PHP_OPENSSL_MAX_PROTO_VERSION; ver <<= 1) {
1005+
if (flags & ver) {
1006+
return ver;
1007+
}
1008+
}
1009+
return STREAM_CRYPTO_METHOD_TLSv1_2;
1010+
}
1011+
/* }}} */
1012+
1013+
static inline int php_openssl_get_max_proto_version_flag(int flags) /* {{{ */
1014+
{
1015+
int ver;
1016+
for (ver = PHP_OPENSSL_MAX_PROTO_VERSION; ver >= PHP_OPENSSL_MIN_PROTO_VERSION; ver >>= 1) {
1017+
if (flags & ver) {
1018+
return ver;
1019+
}
1020+
}
1021+
return STREAM_CRYPTO_METHOD_TLSv1_2;
1022+
}
1023+
/* }}} */
1024+
1025+
#if PHP_OPENSSL_API_VERSION >= 0x10100
1026+
static inline int php_openssl_map_proto_version(int flag) /* {{{ */
1027+
{
1028+
switch (flag) {
1029+
#ifdef HAVE_SSL3
1030+
case STREAM_CRYPTO_METHOD_SSLv3:
1031+
return SSL3_VERSION;
1032+
#endif
1033+
case STREAM_CRYPTO_METHOD_TLSv1_0:
1034+
return TLS1_VERSION;
1035+
case STREAM_CRYPTO_METHOD_TLSv1_1:
1036+
return TLS1_1_VERSION;
1037+
/* case STREAM_CRYPTO_METHOD_TLSv1_2: */
1038+
default:
1039+
return TLS1_2_VERSION;
1040+
1041+
}
1042+
}
1043+
/* }}} */
1044+
1045+
static int php_openssl_get_min_proto_version(int flags) /* {{{ */
1046+
{
1047+
return php_openssl_map_proto_version(php_openssl_get_min_proto_version_flag(flags));
1048+
}
1049+
/* }}} */
1050+
1051+
static int php_openssl_get_max_proto_version(int flags) /* {{{ */
1052+
{
1053+
return php_openssl_map_proto_version(php_openssl_get_max_proto_version_flag(flags));
1054+
}
1055+
/* }}} */
1056+
#endif
1057+
1058+
static int php_openssl_get_proto_version_flags(int flags, int min, int max) /* {{{ */
1059+
{
1060+
int ver;
1061+
1062+
if (!min) {
1063+
min = php_openssl_get_min_proto_version_flag(flags);
1064+
}
1065+
if (!max) {
1066+
max = php_openssl_get_max_proto_version_flag(flags);
1067+
}
1068+
1069+
for (ver = PHP_OPENSSL_MIN_PROTO_VERSION; ver <= PHP_OPENSSL_MAX_PROTO_VERSION; ver <<= 1) {
1070+
if (ver >= min && ver <= max) {
1071+
if (!(flags & ver)) {
1072+
flags |= ver;
1073+
}
1074+
} else if (flags & ver) {
1075+
flags &= ~ver;
1076+
}
1077+
}
1078+
1079+
return flags;
1080+
}
1081+
/* }}} */
10321082

10331083
static void php_openssl_limit_handshake_reneg(const SSL *ssl) /* {{{ */
10341084
{
@@ -1538,6 +1588,8 @@ int php_openssl_setup_crypto(php_stream *stream,
15381588
const SSL_METHOD *method;
15391589
int ssl_ctx_options;
15401590
int method_flags;
1591+
zend_long min_version = 0;
1592+
zend_long max_version = 0;
15411593
char *cipherlist = NULL;
15421594
char *alpn_protocols = NULL;
15431595
zval *val;
@@ -1558,23 +1610,18 @@ int php_openssl_setup_crypto(php_stream *stream,
15581610
sslsock->is_client = cparam->inputs.method & STREAM_CRYPTO_IS_CLIENT;
15591611
method_flags = ((cparam->inputs.method >> 1) << 1);
15601612

1561-
/* Should we use a specific crypto method or is generic SSLv23 okay? */
1562-
if ((method_flags & (method_flags-1)) == 0) {
1563-
ssl_ctx_options = SSL_OP_ALL;
1564-
method = php_openssl_select_crypto_method(method_flags, sslsock->is_client);
1565-
if (method == NULL) {
1566-
return FAILURE;
1567-
}
1568-
} else {
1569-
method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method();
1570-
ssl_ctx_options = php_openssl_get_crypto_method_ctx_flags(method_flags);
1571-
if (ssl_ctx_options == -1) {
1572-
return FAILURE;
1573-
}
1574-
}
1575-
1613+
method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method();
15761614
sslsock->ctx = SSL_CTX_new(method);
15771615

1616+
GET_VER_OPT_LONG("min_proto_version", min_version);
1617+
GET_VER_OPT_LONG("max_proto_version", max_version);
1618+
method_flags = php_openssl_get_proto_version_flags(method_flags, min_version, max_version);
1619+
#if PHP_OPENSSL_API_VERSION < 0x10100
1620+
ssl_ctx_options = php_openssl_get_crypto_method_ctx_flags(method_flags);
1621+
#else
1622+
ssl_ctx_options = SSL_OP_ALL;
1623+
#endif
1624+
15781625
if (sslsock->ctx == NULL) {
15791626
php_error_docref(NULL, E_WARNING, "SSL context creation failure");
15801627
return FAILURE;
@@ -1663,6 +1710,11 @@ int php_openssl_setup_crypto(php_stream *stream,
16631710

16641711
SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options);
16651712

1713+
#if PHP_OPENSSL_API_VERSION >= 0x10100
1714+
SSL_CTX_set_min_proto_version(sslsock->ctx, php_openssl_get_min_proto_version(method_flags));
1715+
SSL_CTX_set_max_proto_version(sslsock->ctx, php_openssl_get_max_proto_version(method_flags));
1716+
#endif
1717+
16661718
if (sslsock->is_client == 0 &&
16671719
PHP_STREAM_CONTEXT(stream) &&
16681720
FAILURE == php_openssl_set_server_specific_opts(stream, sslsock->ctx)

ext/standard/file.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ PHP_MINIT_FUNCTION(file)
228228
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_1_SERVER", STREAM_CRYPTO_METHOD_TLSv1_1_SERVER, CONST_CS|CONST_PERSISTENT);
229229
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_METHOD_TLSv1_2_SERVER", STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, CONST_CS|CONST_PERSISTENT);
230230

231+
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_SSLv3", STREAM_CRYPTO_METHOD_SSLv3_SERVER, CONST_CS|CONST_PERSISTENT);
232+
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_0", STREAM_CRYPTO_METHOD_TLSv1_0_SERVER, CONST_CS|CONST_PERSISTENT);
233+
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_1", STREAM_CRYPTO_METHOD_TLSv1_1_SERVER, CONST_CS|CONST_PERSISTENT);
234+
REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_2", STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, CONST_CS|CONST_PERSISTENT);
235+
231236
REGISTER_LONG_CONSTANT("STREAM_SHUT_RD", STREAM_SHUT_RD, CONST_CS|CONST_PERSISTENT);
232237
REGISTER_LONG_CONSTANT("STREAM_SHUT_WR", STREAM_SHUT_WR, CONST_CS|CONST_PERSISTENT);
233238
REGISTER_LONG_CONSTANT("STREAM_SHUT_RDWR", STREAM_SHUT_RDWR, CONST_CS|CONST_PERSISTENT);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy