Skip to content
Open
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
76 changes: 75 additions & 1 deletion ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,76 @@ static void php_openssl_pkey_free_obj(zend_object *object)
zend_object_std_dtor(&key_object->std);
}

/* OpenSSLSession class */
/* Openssl\Psk class */

zend_class_entry *php_openssl_psk_ce;

static zend_object_handlers php_openssl_psk_object_handlers;

bool php_openssl_is_psk_ce(zval *val)
{
return Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_psk_ce;
}

zend_string *php_openssl_psk_get_psk(zval *psk_zv)
{
zval rv;
zval *prop = zend_read_property(php_openssl_psk_ce, Z_OBJ_P(psk_zv), ZEND_STRL("psk"), 0, &rv);
if (UNEXPECTED(Z_TYPE_P(prop) != IS_STRING)) {
return NULL;
}
return Z_STR_P(prop);
}

zend_string *php_openssl_psk_get_identity(zval *psk_zv)
{
zval rv;
zval *prop = zend_read_property(php_openssl_psk_ce, Z_OBJ_P(psk_zv),
ZEND_STRL("identity"), 0, &rv);
if (Z_TYPE_P(prop) == IS_NULL) {
return NULL;
}
if (UNEXPECTED(Z_TYPE_P(prop) != IS_STRING)) {
return NULL;
}
return Z_STR_P(prop);
}

PHP_METHOD(Openssl_Psk, __construct)
{
zend_string *psk;
zend_string *identity = NULL;

ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(psk)
Z_PARAM_OPTIONAL
Z_PARAM_STR_OR_NULL(identity)
ZEND_PARSE_PARAMETERS_END();

if (ZSTR_LEN(psk) == 0) {
zend_argument_value_error(1, "must not be empty");
RETURN_THROWS();
}
if (ZSTR_LEN(psk) > PHP_OPENSSL_PSK_MAX_PSK_LEN) {
zend_argument_value_error(1, "must not exceed %d bytes", PHP_OPENSSL_PSK_MAX_PSK_LEN);
RETURN_THROWS();
}
if (identity != NULL && ZSTR_LEN(identity) > PHP_OPENSSL_PSK_MAX_IDENTITY_LEN) {
zend_argument_value_error(2, "must not exceed %d bytes", PHP_OPENSSL_PSK_MAX_IDENTITY_LEN);
RETURN_THROWS();
}

zend_update_property_str(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("psk"), psk);

if (identity != NULL) {
zend_update_property_str(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS),
ZEND_STRL("identity"), identity);
} else {
zend_update_property_null(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("identity"));
}
}

/* Openssl\Session class */

zend_class_entry *php_openssl_session_ce;

Expand Down Expand Up @@ -716,6 +785,11 @@ PHP_MINIT_FUNCTION(openssl)
php_openssl_pkey_object_handlers.clone_obj = NULL;
php_openssl_pkey_object_handlers.compare = zend_objects_not_comparable;

php_openssl_psk_ce = register_class_Openssl_Psk();
php_openssl_psk_ce->default_object_handlers = &php_openssl_psk_object_handlers;

memcpy(&php_openssl_psk_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));

php_openssl_session_ce = register_class_Openssl_Session();
php_openssl_session_ce->create_object = php_openssl_session_create_object;
php_openssl_session_ce->default_object_handlers = &php_openssl_session_object_handlers;
Expand Down
21 changes: 21 additions & 0 deletions ext/openssl/openssl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@ class OpensslException extends Exception
{
}

/**
* @strict-properties
*/
final class Psk
{
/**
* @cvalue PHP_OPENSSL_PSK_MAX_PSK_LEN
*/
public const int MAX_PSK_LEN = UNKNOWN;

/**
* @cvalue PHP_OPENSSL_PSK_MAX_IDENTITY_LEN
*/
public const int MAX_IDENTITY_LEN = UNKNOWN;

public readonly string $psk;
public readonly ?string $identity;

public function __construct(string $psk, ?string $identity = null) {}
}

/**
* @strict-properties
*/
Expand Down
47 changes: 46 additions & 1 deletion ext/openssl/openssl_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion ext/openssl/php_openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,19 @@ static inline php_openssl_pkey_object *php_openssl_pkey_from_obj(zend_object *ob
bool php_openssl_is_pkey_ce(zval *val);
void php_openssl_pkey_object_init(zval *zv, EVP_PKEY *pkey, bool is_private);

/* OpenSSLSession class */
/* Openssl\Psk class */

/* Matches OpenSSL's PSK_MAX_PSK_LEN and PSK_MAX_IDENTITY_LEN */
#define PHP_OPENSSL_PSK_MAX_PSK_LEN 256
#define PHP_OPENSSL_PSK_MAX_IDENTITY_LEN 128

extern zend_class_entry *php_openssl_psk_ce;

bool php_openssl_is_psk_ce(zval *val);
zend_string *php_openssl_psk_get_psk(zval *psk_zv);
zend_string *php_openssl_psk_get_identity(zval *psk_zv);

/* Openssl\Session class */

#include <openssl/ssl.h>

Expand Down
46 changes: 46 additions & 0 deletions ext/openssl/tests/tls_psk_callback_not_callable.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
TLS PSK callback option must be a valid callable
--EXTENSIONS--
openssl
--SKIPIF--
<?php
if (!function_exists("proc_open")) die("skip no proc_open");
?>
--FILE--
<?php
$serverCode = <<<'CODE'
$serverCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
'ciphers' => 'PSK',
'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk {
return new Openssl\Psk("k", "id");
},
]]);
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx);
phpt_notify_server_start($server);
@stream_socket_accept($server, 3);
CODE;

$clientCode = <<<'CODE'
$clientCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
'ciphers' => 'PSK',
'verify_peer' => false,
'psk_client_cb' => 'php_openssl_no_such_function',
]]);
try {
@stream_socket_client('tls://{{ ADDR }}', $errno, $errstr,
5, STREAM_CLIENT_CONNECT, $clientCtx);
echo "no exception\n";
} catch (TypeError $e) {
echo "caught: ", $e->getMessage(), "\n";
}
CODE;

include __DIR__ . '/ServerClientTestCase.inc';
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
?>
--EXPECTF--
caught: psk_client_cb must be a valid callback, %s

48 changes: 48 additions & 0 deletions ext/openssl/tests/tls_psk_callback_wrong_type.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--TEST--
TLS PSK client callback returning wrong type raises TypeError
--EXTENSIONS--
openssl
--SKIPIF--
<?php
if (!function_exists("proc_open")) die("skip no proc_open");
?>
--FILE--
<?php
$serverCode = <<<'CODE'
$serverCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
'ciphers' => 'PSK',
'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk {
return new Openssl\Psk("k", "id");
},
]]);
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx);
phpt_notify_server_start($server);
@stream_socket_accept($server, 3);
CODE;

$clientCode = <<<'CODE'
$clientCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
'ciphers' => 'PSK',
'verify_peer' => false,
'psk_client_cb' => function ($stream) {
/* Returning a string instead of Openssl\Psk|null. */
return "not a Psk object";
},
]]);
try {
@stream_socket_client('tls://{{ ADDR }}', $errno, $errstr,
5, STREAM_CLIENT_CONNECT, $clientCtx);
echo "no exception\n";
} catch (TypeError $e) {
echo "caught: ", $e->getMessage(), "\n";
}
CODE;

include __DIR__ . '/ServerClientTestCase.inc';
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
?>
--EXPECT--
caught: PSK callback must return Openssl\Psk or null
45 changes: 45 additions & 0 deletions ext/openssl/tests/tls_psk_client_callback_null.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
TLS 1.2 PSK: client callback returning null aborts handshake (no shared cipher)
--EXTENSIONS--
openssl
--SKIPIF--
<?php
if (!function_exists("proc_open")) die("skip no proc_open");
?>
--FILE--
<?php
$serverCode = <<<'CODE'
$serverCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
'ciphers' => 'PSK',
'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk {
return new Openssl\Psk("doesnotmatter", null);
},
]]);
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx);
phpt_notify_server_start($server);
@stream_socket_accept($server, 3);
CODE;

$clientCode = <<<'CODE'
$clientCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
'ciphers' => 'PSK',
'verify_peer' => false,
'psk_client_cb' => function ($stream): ?Openssl\Psk {
/* Reject: no PSK to use, no other ciphers offered. */
return null;
},
]]);
$client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr,
5, STREAM_CLIENT_CONNECT, $clientCtx);
var_dump($client);
CODE;

include __DIR__ . '/ServerClientTestCase.inc';
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
?>
--EXPECT--
bool(false)

Loading
Loading