Skip to content

Fix GH-9310: SSL local_cert and local_pk do not respect open_basedir #9363

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

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ ZEND_API const char *get_active_function_arg_name(uint32_t arg_num) /* {{{ */

ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t arg_num) /* {{{ */
{
if (!func || func->common.num_args < arg_num) {
if (!func || arg_num == 0 || func->common.num_args < arg_num) {
return NULL;
}

Expand Down
27 changes: 1 addition & 26 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ static void php_openssl_check_path_error(uint32_t arg_num, int type, const char
}

/* openssl file path check extended */
static bool php_openssl_check_path_ex(
bool php_openssl_check_path_ex(
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
bool contains_file_protocol, bool is_from_array, const char *option_name)
{
Expand Down Expand Up @@ -546,31 +546,6 @@ static bool php_openssl_check_path_ex(
return false;
}

/* openssl file path check */
static inline bool php_openssl_check_path(
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num)
{
return php_openssl_check_path_ex(
file_path, file_path_len, real_path, arg_num, false, false, NULL);
}

/* openssl file path extra check with zend string */
static inline bool php_openssl_check_path_str_ex(
zend_string *file_path, char *real_path, uint32_t arg_num,
bool contains_file_protocol, bool is_from_array, const char *option_name)
{
return php_openssl_check_path_ex(
ZSTR_VAL(file_path), ZSTR_LEN(file_path), real_path, arg_num, contains_file_protocol,
is_from_array, option_name);
}

/* openssl file path check with zend string */
static inline bool php_openssl_check_path_str(
zend_string *file_path, char *real_path, uint32_t arg_num)
{
return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL);
}

static int ssl_stream_data_index;

php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl)
Expand Down
30 changes: 30 additions & 0 deletions ext/openssl/php_openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,36 @@ php_stream_transport_factory_func php_openssl_ssl_socket_factory;

void php_openssl_store_errors(void);

/* openssl file path extra */
bool php_openssl_check_path_ex(
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
bool contains_file_protocol, bool is_from_array, const char *option_name);

/* openssl file path check */
static inline bool php_openssl_check_path(
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num)
{
return php_openssl_check_path_ex(
file_path, file_path_len, real_path, arg_num, false, false, NULL);
}

/* openssl file path extra check with zend string */
static inline bool php_openssl_check_path_str_ex(
zend_string *file_path, char *real_path, uint32_t arg_num,
bool contains_file_protocol, bool is_from_array, const char *option_name)
{
return php_openssl_check_path_ex(
ZSTR_VAL(file_path), ZSTR_LEN(file_path), real_path, arg_num, contains_file_protocol,
is_from_array, option_name);
}

/* openssl file path check with zend string */
static inline bool php_openssl_check_path_str(
zend_string *file_path, char *real_path, uint32_t arg_num)
{
return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL);
}

PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method);
PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long length);
PHP_OPENSSL_API zend_string* php_openssl_encrypt(
Expand Down
20 changes: 16 additions & 4 deletions ext/openssl/tests/CertificateGenerator.inc
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ class CertificateGenerator
openssl_x509_export_to_file($this->ca, $file);
}

public function saveNewCertAsFileWithKey(
$commonNameForCert, $file, $keyLength = null, $subjectAltName = null
public function saveNewCertAndKey(
$commonNameForCert, $certFile, $keyFile, $keyLength = null, $subjectAltName = null
) {
$dn = [
'countryName' => 'BY',
Expand Down Expand Up @@ -117,7 +117,7 @@ $subjectAltNameConfig
basicConstraints = CA:FALSE
$subjectAltNameConfig
CONFIG;
$configFile = $file . '.cnf';
$configFile = $certFile . '.cnf';
file_put_contents($configFile, $configCode);

try {
Expand Down Expand Up @@ -146,12 +146,24 @@ CONFIG;
$keyText = '';
openssl_pkey_export($this->lastKey, $keyText, null, $config);

file_put_contents($file, $certText . PHP_EOL . $keyText);
if ($certFile === $keyFile) {
file_put_contents($certFile, $certText . PHP_EOL . $keyText);
} else {
file_put_contents($certFile, $certText);
file_put_contents($keyFile, $keyText);
}
} finally {
unlink($configFile);
}
}


public function saveNewCertAsFileWithKey(
$commonNameForCert, $file, $keyLength = null, $subjectAltName = null
) {
$this->saveNewCertAndKey($commonNameForCert, $file, $file, $keyLength, $subjectAltName);
}

public function getCertDigest($algo)
{
return openssl_x509_fingerprint($this->lastCert, $algo);
Expand Down
186 changes: 186 additions & 0 deletions ext/openssl/tests/gh9310.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
--TEST--
GH-9310: local_cert and local_pk do not respect open_basedir restriction
--EXTENSIONS--
openssl
--SKIPIF--
<?php
if (!function_exists("proc_open")) die("skip no proc_open");
?>
--FILE--
<?php
include 'ServerClientTestCase.inc';

$baseDir = __DIR__ . '/gh9310';
@mkdir($baseDir);
$baseDirCertFile = $baseDir . '/cert.crt';
$baseDirPkFile = $baseDir . '/private.key';
$certFile = __DIR__ . '/gh9310.crt';
$pkFile = __DIR__ . '/gh9310.key';

include 'CertificateGenerator.inc';
$certificateGenerator = new CertificateGenerator();
$certificateGenerator->saveNewCertAndKey('gh9310', $certFile, $pkFile);

copy($certFile, $baseDirCertFile);
copy($pkFile, $baseDirPkFile);
copy(__DIR__ . '/sni_server_uk_cert.pem', $baseDir . '/sni_server_uk_cert.pem');


$serverCodeTemplate = <<<'CODE'
ini_set('log_errors', 'On');
ini_set('open_basedir', __DIR__ . '/gh9310');
$serverUri = "ssl://127.0.0.1:64321";
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$serverCtx = stream_context_create(['ssl' => [
'local_cert' => '%s',
'local_pk' => '%s',
]]);

$sock = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
phpt_notify();

$link = stream_socket_accept($sock);
CODE;

$clientCode = <<<'CODE'
$serverUri = "ssl://127.0.0.1:64321";
$clientFlags = STREAM_CLIENT_CONNECT;

$clientCtx = stream_context_create(['ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]]);

phpt_wait();
@stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx);
CODE;

$sniServerCodeV1 = <<<'CODE'
ini_set('log_errors', 'On');
ini_set('open_basedir', __DIR__ . '/gh9310');
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
'SNI_server_certs' => [
"cs.php.net" => __DIR__ . "/sni_server_cs.pem",
]
]]);

$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
phpt_notify();

stream_socket_accept($server);
CODE;

$sniServerCodeV2 = <<<'CODE'
ini_set('log_errors', 'On');
ini_set('open_basedir', __DIR__ . '/gh9310');
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
'SNI_server_certs' => [
"uk.php.net" => [
'local_cert' => __DIR__ . '/gh9310/sni_server_uk_cert.pem',
'local_pk' => __DIR__ . '/sni_server_uk_key.pem',
]
]
]]);

$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
phpt_notify();

stream_socket_accept($server);
CODE;

$sniServerCodeV3 = <<<'CODE'
ini_set('log_errors', 'On');
ini_set('open_basedir', __DIR__ . '/gh9310');
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
'SNI_server_certs' => [
"us.php.net" => [
'local_cert' => __DIR__ . '/sni_server_us_cert.pem',
'local_pk' => __DIR__ . '/sni_server_us_key.pem',
]
]
]]);

$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
phpt_notify();

stream_socket_accept($server);
CODE;

$sniClientCodeTemplate = <<<'CODE'
$flags = STREAM_CLIENT_CONNECT;
$ctxArr = [
'cafile' => __DIR__ . '/sni_server_ca.pem',
];

phpt_wait();

$ctxArr['peer_name'] = '%s';
$ctx = stream_context_create(['ssl' => $ctxArr]);
@stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
CODE;

$serverCode = sprintf($serverCodeTemplate, $baseDirCertFile . "\0test", $baseDirPkFile);
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);

$serverCode = sprintf($serverCodeTemplate, $baseDirCertFile, $baseDirPkFile . "\0test");
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);

$serverCode = sprintf($serverCodeTemplate, $certFile, $pkFile);
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);

$serverCode = sprintf($serverCodeTemplate, $baseDirCertFile, $pkFile);
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);

$sniClientCode = sprintf($sniClientCodeTemplate, 'cs.php.net');
ServerClientTestCase::getInstance()->run($sniClientCode, $sniServerCodeV1);

$sniClientCode = sprintf($sniClientCodeTemplate, 'uk.php.net');
ServerClientTestCase::getInstance()->run($sniClientCode, $sniServerCodeV2);

$sniClientCode = sprintf($sniClientCodeTemplate, 'us.php.net');
ServerClientTestCase::getInstance()->run($sniClientCode, $sniServerCodeV3);

?>
--CLEAN--
<?php
$baseDir = __DIR__ . '/gh9310';

@unlink(__DIR__ . '/gh9310.crt');
@unlink(__DIR__ . '/gh9310.key');
@unlink($baseDir . '/cert.crt');
@unlink($baseDir . '/private.key');
@unlink($baseDir . '/sni_server_uk_cert.pem');
@rmdir($baseDir);
?>
--EXPECTF--
PHP Warning: stream_socket_accept(): Path for local_cert in ssl stream context option must not contain any null bytes in %s
PHP Warning: stream_socket_accept(): Unable to get real path of certificate file `%scert.crt' in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
PHP Warning: stream_socket_accept(): Path for local_pk in ssl stream context option must not contain any null bytes in %s
PHP Warning: stream_socket_accept(): Unable to get real path of private key file `%sprivate.key' in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%sgh9310.crt) is not within the allowed path(s): (%sgh9310) in %s
PHP Warning: stream_socket_accept(): Unable to get real path of certificate file `%sgh9310.crt' in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%sgh9310.key) is not within the allowed path(s): (%sgh9310) in %s
PHP Warning: stream_socket_accept(): Unable to get real path of private key file `%sgh9310.key' in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%ssni_server_cs.pem) is not within the allowed path(s): (%sgh9310) in %s
PHP Warning: stream_socket_accept(): Failed setting local cert chain file `%ssni_server_cs.pem'; file not found in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%ssni_server_uk_key.pem) is not within the allowed path(s): (%sgh9310) in %s
PHP Warning: stream_socket_accept(): Failed setting local private key file `%ssni_server_uk_key.pem'; could not open file in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%ssni_server_us_cert.pem) is not within the allowed path(s): (%sgh9310) in %s
PHP Warning: stream_socket_accept(): Failed setting local cert chain file `%ssni_server_us_cert.pem'; could not open file in %s
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
PHP Warning: stream_socket_accept(): Accept failed: %s
Loading