Skip to content

Commit 81098e1

Browse files
committed
Throw exception when channel binding is not supported
Instead of returning null.
1 parent 7d1e8b5 commit 81098e1

10 files changed

Lines changed: 149 additions & 25 deletions

‎Zend/Optimizer/zend_func_infos.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ static const func_info_t func_infos[] = {
297297
F1("openssl_get_curve_names", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE),
298298
#endif
299299
F1("openssl_get_cert_locations", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING),
300-
F1("openssl_get_channel_binding", MAY_BE_STRING|MAY_BE_NULL),
300+
F1("openssl_get_channel_binding", MAY_BE_STRING),
301301
FN("pcntl_signal_get_handler", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_OBJECT|MAY_BE_LONG),
302302
FN("preg_replace", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_NULL),
303303
FN("preg_filter", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_NULL),

‎ext/openssl/openssl.c‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ ZEND_DECLARE_MODULE_GLOBALS(openssl)
5757

5858
zend_class_entry *php_openssl_exception_ce;
5959

60+
/* Openssl\UnsupportedChannelBindingException class */
61+
62+
static zend_class_entry *php_openssl_unsupported_channel_binding_exception_ce;
63+
6064
/* Openssl\ChannelBindingType enum */
6165

6266
static zend_class_entry *php_openssl_channel_binding_type_ce;
@@ -761,6 +765,8 @@ PHP_INI_END()
761765
PHP_MINIT_FUNCTION(openssl)
762766
{
763767
php_openssl_exception_ce = register_class_Openssl_OpensslException(zend_ce_exception);
768+
php_openssl_unsupported_channel_binding_exception_ce =
769+
register_class_Openssl_UnsupportedChannelBindingException(php_openssl_exception_ce);
764770
php_openssl_channel_binding_type_ce = register_class_Openssl_ChannelBindingType();
765771

766772
php_openssl_certificate_ce = register_class_OpenSSLCertificate();
@@ -5127,10 +5133,24 @@ PHP_FUNCTION(openssl_get_channel_binding)
51275133
case PHP_OPENSSL_CHANNEL_BINDING_RESULT_OK:
51285134
RETURN_STR(result);
51295135
case PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_APPLICABLE:
5130-
RETURN_NULL();
5136+
switch (type) {
5137+
case ZEND_ENUM_Openssl_ChannelBindingType_TlsUnique:
5138+
zend_throw_exception(php_openssl_unsupported_channel_binding_exception_ce,
5139+
"Channel binding type \"tls-unique\" is not supported for TLS 1.3", 0);
5140+
break;
5141+
case ZEND_ENUM_Openssl_ChannelBindingType_TlsServerEndPoint:
5142+
zend_throw_exception(php_openssl_unsupported_channel_binding_exception_ce,
5143+
"Channel binding type \"tls-server-end-point\" requires a certificate", 0);
5144+
break;
5145+
default:
5146+
zend_throw_exception(php_openssl_unsupported_channel_binding_exception_ce,
5147+
"Channel binding type is not applicable to this connection", 0);
5148+
break;
5149+
}
5150+
RETURN_THROWS();
51315151
case PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_TLS:
5132-
zend_throw_exception_ex(php_openssl_exception_ce, 0,
5133-
"Stream does not have transport encryption enabled");
5152+
zend_throw_exception(zend_ce_value_error,
5153+
"Stream does not have transport encryption enabled", 0);
51345154
RETURN_THROWS();
51355155
case PHP_OPENSSL_CHANNEL_BINDING_RESULT_ERROR:
51365156
{

‎ext/openssl/openssl.stub.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ class OpensslException extends Exception
1111
{
1212
}
1313

14+
class UnsupportedChannelBindingException extends OpensslException
15+
{
16+
}
17+
1418
/**
1519
* Channel binding type for openssl_get_channel_binding().
1620
* The backing values are the IANA "Channel Binding" registry names.
@@ -793,6 +797,6 @@ function openssl_password_verify(string $algo, #[\SensitiveParameter] string $pa
793797
* @param resource $stream
794798
* @refcount 1
795799
*/
796-
function openssl_get_channel_binding($stream, Openssl\ChannelBindingType $channel_binding_type): ?string {}
800+
function openssl_get_channel_binding($stream, Openssl\ChannelBindingType $channel_binding_type): string {}
797801

798802
}

‎ext/openssl/openssl_arginfo.h‎

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ext/openssl/openssl_decl.h‎

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ext/openssl/php_openssl.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ typedef struct _php_openssl_session_object {
229229
/* Result of php_openssl_netstream_get_channel_binding(). */
230230
typedef enum php_openssl_channel_binding_result {
231231
PHP_OPENSSL_CHANNEL_BINDING_RESULT_OK = 0, /* *out holds a zend_string with the data */
232-
PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_APPLICABLE, /* *out = NULL (e.g. tls-unique over TLS 1.3) */
232+
PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_APPLICABLE, /* *out = NULL (e.g. tls-unique over TLS 1.3, or tls-server-end-point without a certificate) */
233233
PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_TLS, /* stream is not an active TLS stream */
234234
PHP_OPENSSL_CHANNEL_BINDING_RESULT_ERROR, /* an OpenSSL-level failure occurred */
235235
} php_openssl_channel_binding_result;

‎ext/openssl/tests/openssl_get_channel_binding_errors.phpt‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ try {
3333
echo $e::class, ': ', $e->getMessage(), "\n";
3434
}
3535

36-
/* A stream without transport encryption -> Openssl\OpensslException. */
36+
/* The unsupported-type exception extends the general OpenSSL exception. */
37+
var_dump(is_subclass_of(
38+
Openssl\UnsupportedChannelBindingException::class,
39+
Openssl\OpensslException::class,
40+
));
41+
42+
/* A stream without transport encryption -> ValueError. */
3743
$plain = fopen("php://memory", "r");
3844
foreach (Openssl\ChannelBindingType::cases() as $type) {
3945
try {
@@ -52,6 +58,7 @@ TlsExporter=tls-exporter
5258
TypeError: openssl_get_channel_binding(): Argument #2 ($channel_binding_type) must be of type Openssl\ChannelBindingType, string given
5359
TypeError: openssl_get_channel_binding(): Argument #2 ($channel_binding_type) must be of type Openssl\ChannelBindingType, string given
5460
TypeError: openssl_get_channel_binding(): Argument #1 ($stream) must be of type resource, int given
55-
tls-unique: Openssl\OpensslException: Stream does not have transport encryption enabled
56-
tls-server-end-point: Openssl\OpensslException: Stream does not have transport encryption enabled
57-
tls-exporter: Openssl\OpensslException: Stream does not have transport encryption enabled
61+
bool(true)
62+
tls-unique: ValueError: Stream does not have transport encryption enabled
63+
tls-server-end-point: ValueError: Stream does not have transport encryption enabled
64+
tls-exporter: ValueError: Stream does not have transport encryption enabled
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
--TEST--
2+
openssl_get_channel_binding(): TLS 1.2 PSK, tls-server-end-point is not applicable
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
if (OPENSSL_VERSION_NUMBER < 0x10101000) die("skip OpenSSL >= 1.1.1 required");
9+
?>
10+
--FILE--
11+
<?php
12+
$serverCode = <<<'CODE'
13+
$serverCtx = stream_context_create(['ssl' => [
14+
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
15+
'ciphers' => 'PSK',
16+
'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk {
17+
if ($identity === 'client_id') {
18+
return new Openssl\Psk("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff");
19+
}
20+
return null;
21+
},
22+
]]);
23+
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr,
24+
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx);
25+
phpt_notify_server_start($server);
26+
$conn = stream_socket_accept($server, 30);
27+
fwrite($conn, "ready");
28+
phpt_wait();
29+
fclose($conn);
30+
CODE;
31+
32+
$clientCode = <<<'CODE'
33+
$clientCtx = stream_context_create(['ssl' => [
34+
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
35+
'ciphers' => 'PSK',
36+
'verify_peer' => false,
37+
'verify_peer_name' => false,
38+
'psk_client_cb' => function ($stream): ?Openssl\Psk {
39+
return new Openssl\Psk(
40+
"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff",
41+
'client_id'
42+
);
43+
},
44+
]]);
45+
$client = stream_socket_client('tls://{{ ADDR }}', $errno, $errstr,
46+
30, STREAM_CLIENT_CONNECT, $clientCtx);
47+
var_dump($client !== false);
48+
var_dump(fread($client, 1024));
49+
50+
foreach (Openssl\ChannelBindingType::cases() as $type) {
51+
try {
52+
$v = openssl_get_channel_binding($client, $type);
53+
printf("%s len=%d\n", $type->value, strlen($v));
54+
} catch (Openssl\UnsupportedChannelBindingException $e) {
55+
echo $e::class, ': ', $e->getMessage(), "\n";
56+
}
57+
}
58+
59+
phpt_notify('server');
60+
fclose($client);
61+
CODE;
62+
63+
include __DIR__ . '/ServerClientTestCase.inc';
64+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
65+
?>
66+
--EXPECTF--
67+
bool(true)
68+
string(5) "ready"
69+
tls-unique len=%d
70+
Openssl\UnsupportedChannelBindingException: Channel binding type "tls-server-end-point" requires a certificate
71+
tls-exporter len=32

‎ext/openssl/tests/openssl_get_channel_binding_tls13.phpt‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ $serverCode = <<<'CODE'
2828
exit(1);
2929
}
3030
foreach (Openssl\ChannelBindingType::cases() as $type) {
31-
$v = openssl_get_channel_binding($conn, $type);
32-
fwrite($conn, $type->value . "=" . (is_string($v) ? bin2hex($v) : "") . "\n");
31+
try {
32+
$v = openssl_get_channel_binding($conn, $type);
33+
fwrite($conn, $type->value . "=" . bin2hex($v) . "\n");
34+
} catch (Openssl\UnsupportedChannelBindingException) {
35+
fwrite($conn, $type->value . "=unsupported\n");
36+
}
3337
}
3438
phpt_wait();
3539
fclose($conn);
@@ -50,8 +54,14 @@ $clientCode = <<<'CODE'
5054
}
5155
5256
$my = [];
57+
$myException = [];
5358
foreach (Openssl\ChannelBindingType::cases() as $type) {
54-
$my[$type->value] = openssl_get_channel_binding($client, $type);
59+
try {
60+
$my[$type->value] = openssl_get_channel_binding($client, $type);
61+
} catch (Openssl\UnsupportedChannelBindingException $e) {
62+
$my[$type->value] = null;
63+
$myException[$type->value] = $e::class . ': ' . $e->getMessage();
64+
}
5565
}
5666
5767
$peer = [];
@@ -62,7 +72,7 @@ $clientCode = <<<'CODE'
6272
$eq = strpos($line, "=");
6373
$t = substr($line, 0, $eq);
6474
$enc = substr($line, $eq + 1);
65-
$peer[$t] = ($enc === "") ? null : hex2bin($enc);
75+
$peer[$t] = ($enc === "unsupported") ? null : hex2bin($enc);
6676
}
6777
6878
foreach (Openssl\ChannelBindingType::cases() as $type) {
@@ -75,7 +85,8 @@ $clientCode = <<<'CODE'
7585
$t, var_export($c === $s, true), $lc, $ls);
7686
}
7787
78-
echo "tls_unique_is_null=" . var_export($my["tls-unique"] === null, true) . "\n";
88+
echo "tls_unique_unsupported=" . var_export($my["tls-unique"] === null, true) . "\n";
89+
echo "tls_unique_message=" . ($myException["tls-unique"] ?? "none") . "\n";
7990
8091
$opts = stream_context_get_options($client);
8192
$cert = $opts['ssl']['peer_certificate'] ?? null;
@@ -102,5 +113,6 @@ ServerClientTestCase::getInstance()->run($clientCode, ['server' => $serverCode])
102113
tls-unique equal=true len_client=null len_server=null
103114
tls-server-end-point equal=true len_client=32 len_server=32
104115
tls-exporter equal=true len_client=32 len_server=32
105-
tls_unique_is_null=true
116+
tls_unique_unsupported=true
117+
tls_unique_message=Openssl\UnsupportedChannelBindingException: Channel binding type "tls-unique" is not supported for TLS 1.3
106118
tse_matches_sha256_fingerprint=true

‎ext/openssl/xp_ssl.c‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3970,8 +3970,8 @@ static int php_openssl_get_cert_signature_md_nid(const X509 *cert) /* {{{ */
39703970
*
39713971
* On success *out is set to a newly-allocated zend_string holding the data
39723972
* and PHP_OPENSSL_CHANNEL_BINDING_RESULT_OK is returned. For a type that is not applicable to the
3973-
* connection (currently: tls-unique over TLS 1.3) *out is set to NULL and
3974-
* PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_APPLICABLE is returned. If the stream is not an active TLS
3973+
* connection (tls-unique over TLS 1.3, or tls-server-end-point without a certificate) *out is set
3974+
* to NULL and PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_APPLICABLE is returned. If the stream is not an active TLS
39753975
* stream PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_TLS is returned, and if an OpenSSL call fails
39763976
* PHP_OPENSSL_CHANNEL_BINDING_RESULT_ERROR is returned; in both of these cases *out is set to NULL.
39773977
*/
@@ -4029,7 +4029,7 @@ php_openssl_channel_binding_result php_openssl_netstream_get_channel_binding( /*
40294029
unsigned int digest_len = 0;
40304030

40314031
if (cert == NULL) {
4032-
return PHP_OPENSSL_CHANNEL_BINDING_RESULT_ERROR;
4032+
return PHP_OPENSSL_CHANNEL_BINDING_RESULT_NOT_APPLICABLE;
40334033
}
40344034

40354035
md_nid = php_openssl_get_cert_signature_md_nid(cert);

0 commit comments

Comments
 (0)