Skip to content

Commit 505e8d2

Browse files
committed
Fix phpGH-9310: SSL local_cert and local_pk do not respect open_basedir restriction
1 parent b4ec3e9 commit 505e8d2

File tree

7 files changed

+278
-48
lines changed

7 files changed

+278
-48
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ PHP NEWS
1414
. Fixed bug GH-9371 (Crash with JIT on mac arm64)
1515
(jdp1024/David Carlier)
1616

17+
- OpenSSL:
18+
. Fixed bug GH-9310 (SSL local_cert and local_pk do not respect
19+
open_basedir). (Jakub Zelenka)
20+
1721
- Random:
1822
. Fixed bug GH-9415 (Randomizer::getInt(0, 2**32 - 1) with Mt19937
1923
always returns 1). (timwolla)

Zend/zend_execute_API.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ ZEND_API const char *get_active_function_arg_name(uint32_t arg_num) /* {{{ */
588588

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

ext/openssl/openssl.c

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ static void php_openssl_check_path_error(uint32_t arg_num, int type, const char
489489
}
490490

491491
/* openssl file path check extended */
492-
static bool php_openssl_check_path_ex(
492+
bool php_openssl_check_path_ex(
493493
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
494494
bool contains_file_protocol, bool is_from_array, const char *option_name)
495495
{
@@ -546,31 +546,6 @@ static bool php_openssl_check_path_ex(
546546
return false;
547547
}
548548

549-
/* openssl file path check */
550-
static inline bool php_openssl_check_path(
551-
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num)
552-
{
553-
return php_openssl_check_path_ex(
554-
file_path, file_path_len, real_path, arg_num, false, false, NULL);
555-
}
556-
557-
/* openssl file path extra check with zend string */
558-
static inline bool php_openssl_check_path_str_ex(
559-
zend_string *file_path, char *real_path, uint32_t arg_num,
560-
bool contains_file_protocol, bool is_from_array, const char *option_name)
561-
{
562-
return php_openssl_check_path_ex(
563-
ZSTR_VAL(file_path), ZSTR_LEN(file_path), real_path, arg_num, contains_file_protocol,
564-
is_from_array, option_name);
565-
}
566-
567-
/* openssl file path check with zend string */
568-
static inline bool php_openssl_check_path_str(
569-
zend_string *file_path, char *real_path, uint32_t arg_num)
570-
{
571-
return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL);
572-
}
573-
574549
static int ssl_stream_data_index;
575550

576551
php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl)

ext/openssl/php_openssl.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,36 @@ php_stream_transport_factory_func php_openssl_ssl_socket_factory;
9292

9393
void php_openssl_store_errors(void);
9494

95+
/* openssl file path extra */
96+
bool php_openssl_check_path_ex(
97+
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num,
98+
bool contains_file_protocol, bool is_from_array, const char *option_name);
99+
100+
/* openssl file path check */
101+
static inline bool php_openssl_check_path(
102+
const char *file_path, size_t file_path_len, char *real_path, uint32_t arg_num)
103+
{
104+
return php_openssl_check_path_ex(
105+
file_path, file_path_len, real_path, arg_num, false, false, NULL);
106+
}
107+
108+
/* openssl file path extra check with zend string */
109+
static inline bool php_openssl_check_path_str_ex(
110+
zend_string *file_path, char *real_path, uint32_t arg_num,
111+
bool contains_file_protocol, bool is_from_array, const char *option_name)
112+
{
113+
return php_openssl_check_path_ex(
114+
ZSTR_VAL(file_path), ZSTR_LEN(file_path), real_path, arg_num, contains_file_protocol,
115+
is_from_array, option_name);
116+
}
117+
118+
/* openssl file path check with zend string */
119+
static inline bool php_openssl_check_path_str(
120+
zend_string *file_path, char *real_path, uint32_t arg_num)
121+
{
122+
return php_openssl_check_path_str_ex(file_path, real_path, arg_num, true, false, NULL);
123+
}
124+
95125
PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method);
96126
PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long length);
97127
PHP_OPENSSL_API zend_string* php_openssl_encrypt(

ext/openssl/tests/CertificateGenerator.inc

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ class CertificateGenerator
8585
openssl_x509_export_to_file($this->ca, $file);
8686
}
8787

88-
public function saveNewCertAsFileWithKey(
89-
$commonNameForCert, $file, $keyLength = null, $subjectAltName = null
88+
public function saveNewCertAndKey(
89+
$commonNameForCert, $certFile, $keyFile, $keyLength = null, $subjectAltName = null
9090
) {
9191
$dn = [
9292
'countryName' => 'BY',
@@ -117,7 +117,7 @@ $subjectAltNameConfig
117117
basicConstraints = CA:FALSE
118118
$subjectAltNameConfig
119119
CONFIG;
120-
$configFile = $file . '.cnf';
120+
$configFile = $certFile . '.cnf';
121121
file_put_contents($configFile, $configCode);
122122

123123
try {
@@ -146,12 +146,24 @@ CONFIG;
146146
$keyText = '';
147147
openssl_pkey_export($this->lastKey, $keyText, null, $config);
148148

149-
file_put_contents($file, $certText . PHP_EOL . $keyText);
149+
if ($certFile === $keyFile) {
150+
file_put_contents($certFile, $certText . PHP_EOL . $keyText);
151+
} else {
152+
file_put_contents($certFile, $certText);
153+
file_put_contents($keyFile, $keyText);
154+
}
150155
} finally {
151156
unlink($configFile);
152157
}
153158
}
154159

160+
161+
public function saveNewCertAsFileWithKey(
162+
$commonNameForCert, $file, $keyLength = null, $subjectAltName = null
163+
) {
164+
$this->saveNewCertAndKey($commonNameForCert, $file, $file, $keyLength, $subjectAltName);
165+
}
166+
155167
public function getCertDigest($algo)
156168
{
157169
return openssl_x509_fingerprint($this->lastCert, $algo);

ext/openssl/tests/gh9310.phpt

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
--TEST--
2+
GH-9310: local_cert and local_pk do not respect open_basedir restriction
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
?>
9+
--FILE--
10+
<?php
11+
include 'ServerClientTestCase.inc';
12+
13+
$baseDir = __DIR__ . '/gh9310';
14+
@mkdir($baseDir);
15+
$baseDirCertFile = $baseDir . '/cert.crt';
16+
$baseDirPkFile = $baseDir . '/private.key';
17+
$certFile = __DIR__ . '/gh9310.crt';
18+
$pkFile = __DIR__ . '/gh9310.key';
19+
20+
include 'CertificateGenerator.inc';
21+
$certificateGenerator = new CertificateGenerator();
22+
$certificateGenerator->saveNewCertAndKey('gh9310', $certFile, $pkFile);
23+
24+
copy($certFile, $baseDirCertFile);
25+
copy($pkFile, $baseDirPkFile);
26+
copy(__DIR__ . '/sni_server_uk_cert.pem', $baseDir . '/sni_server_uk_cert.pem');
27+
28+
29+
$serverCodeTemplate = <<<'CODE'
30+
ini_set('log_errors', 'On');
31+
ini_set('open_basedir', __DIR__ . '/gh9310');
32+
$serverUri = "ssl://127.0.0.1:64321";
33+
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
34+
$serverCtx = stream_context_create(['ssl' => [
35+
'local_cert' => '%s',
36+
'local_pk' => '%s',
37+
]]);
38+
39+
$sock = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
40+
phpt_notify();
41+
42+
$link = stream_socket_accept($sock);
43+
CODE;
44+
45+
$clientCode = <<<'CODE'
46+
$serverUri = "ssl://127.0.0.1:64321";
47+
$clientFlags = STREAM_CLIENT_CONNECT;
48+
49+
$clientCtx = stream_context_create(['ssl' => [
50+
'verify_peer' => false,
51+
'verify_peer_name' => false
52+
]]);
53+
54+
phpt_wait();
55+
@stream_socket_client($serverUri, $errno, $errstr, 2, $clientFlags, $clientCtx);
56+
CODE;
57+
58+
$sniServerCodeV1 = <<<'CODE'
59+
ini_set('log_errors', 'On');
60+
ini_set('open_basedir', __DIR__ . '/gh9310');
61+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
62+
$ctx = stream_context_create(['ssl' => [
63+
'SNI_server_certs' => [
64+
"cs.php.net" => __DIR__ . "/sni_server_cs.pem",
65+
]
66+
]]);
67+
68+
$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
69+
phpt_notify();
70+
71+
stream_socket_accept($server);
72+
CODE;
73+
74+
$sniServerCodeV2 = <<<'CODE'
75+
ini_set('log_errors', 'On');
76+
ini_set('open_basedir', __DIR__ . '/gh9310');
77+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
78+
$ctx = stream_context_create(['ssl' => [
79+
'SNI_server_certs' => [
80+
"uk.php.net" => [
81+
'local_cert' => __DIR__ . '/gh9310/sni_server_uk_cert.pem',
82+
'local_pk' => __DIR__ . '/sni_server_uk_key.pem',
83+
]
84+
]
85+
]]);
86+
87+
$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
88+
phpt_notify();
89+
90+
stream_socket_accept($server);
91+
CODE;
92+
93+
$sniServerCodeV3 = <<<'CODE'
94+
ini_set('log_errors', 'On');
95+
ini_set('open_basedir', __DIR__ . '/gh9310');
96+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
97+
$ctx = stream_context_create(['ssl' => [
98+
'SNI_server_certs' => [
99+
"us.php.net" => [
100+
'local_cert' => __DIR__ . '/sni_server_us_cert.pem',
101+
'local_pk' => __DIR__ . '/sni_server_us_key.pem',
102+
]
103+
]
104+
]]);
105+
106+
$server = stream_socket_server('tls://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
107+
phpt_notify();
108+
109+
stream_socket_accept($server);
110+
CODE;
111+
112+
$sniClientCodeTemplate = <<<'CODE'
113+
$flags = STREAM_CLIENT_CONNECT;
114+
$ctxArr = [
115+
'cafile' => __DIR__ . '/sni_server_ca.pem',
116+
];
117+
118+
phpt_wait();
119+
120+
$ctxArr['peer_name'] = '%s';
121+
$ctx = stream_context_create(['ssl' => $ctxArr]);
122+
@stream_socket_client("tls://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
123+
CODE;
124+
125+
$serverCode = sprintf($serverCodeTemplate, $baseDirCertFile . "\0test", $baseDirPkFile);
126+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
127+
128+
$serverCode = sprintf($serverCodeTemplate, $baseDirCertFile, $baseDirPkFile . "\0test");
129+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
130+
131+
$serverCode = sprintf($serverCodeTemplate, $certFile, $pkFile);
132+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
133+
134+
$serverCode = sprintf($serverCodeTemplate, $baseDirCertFile, $pkFile);
135+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
136+
137+
$sniClientCode = sprintf($sniClientCodeTemplate, 'cs.php.net');
138+
ServerClientTestCase::getInstance()->run($sniClientCode, $sniServerCodeV1);
139+
140+
$sniClientCode = sprintf($sniClientCodeTemplate, 'uk.php.net');
141+
ServerClientTestCase::getInstance()->run($sniClientCode, $sniServerCodeV2);
142+
143+
$sniClientCode = sprintf($sniClientCodeTemplate, 'us.php.net');
144+
ServerClientTestCase::getInstance()->run($sniClientCode, $sniServerCodeV3);
145+
146+
?>
147+
--CLEAN--
148+
<?php
149+
$baseDir = __DIR__ . '/gh9310';
150+
151+
@unlink(__DIR__ . '/gh9310.crt');
152+
@unlink(__DIR__ . '/gh9310.key');
153+
@unlink($baseDir . '/cert.crt');
154+
@unlink($baseDir . '/private.key');
155+
@unlink($baseDir . '/sni_server_uk_cert.pem');
156+
@rmdir($baseDir);
157+
?>
158+
--EXPECTF--
159+
PHP Warning: stream_socket_accept(): Path for local_cert in ssl stream context option must not contain any null bytes in %s
160+
PHP Warning: stream_socket_accept(): Unable to get real path of certificate file `%scert.crt' in %s
161+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
162+
PHP Warning: stream_socket_accept(): Accept failed: %s
163+
PHP Warning: stream_socket_accept(): Path for local_pk in ssl stream context option must not contain any null bytes in %s
164+
PHP Warning: stream_socket_accept(): Unable to get real path of private key file `%sprivate.key' in %s
165+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
166+
PHP Warning: stream_socket_accept(): Accept failed: %s
167+
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%sgh9310.crt) is not within the allowed path(s): (%sgh9310) in %s
168+
PHP Warning: stream_socket_accept(): Unable to get real path of certificate file `%sgh9310.crt' in %s
169+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
170+
PHP Warning: stream_socket_accept(): Accept failed: %s
171+
PHP Warning: stream_socket_accept(): open_basedir restriction in effect. File(%sgh9310.key) is not within the allowed path(s): (%sgh9310) in %s
172+
PHP Warning: stream_socket_accept(): Unable to get real path of private key file `%sgh9310.key' in %s
173+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
174+
PHP Warning: stream_socket_accept(): Accept failed: %s
175+
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
176+
PHP Warning: stream_socket_accept(): Failed setting local cert chain file `%ssni_server_cs.pem'; file not found in %s
177+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
178+
PHP Warning: stream_socket_accept(): Accept failed: %s
179+
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
180+
PHP Warning: stream_socket_accept(): Failed setting local private key file `%ssni_server_uk_key.pem'; could not open file in %s
181+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
182+
PHP Warning: stream_socket_accept(): Accept failed: %s
183+
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
184+
PHP Warning: stream_socket_accept(): Failed setting local cert chain file `%ssni_server_us_cert.pem'; could not open file in %s
185+
PHP Warning: stream_socket_accept(): Failed to enable crypto in %s
186+
PHP Warning: stream_socket_accept(): Accept failed: %s

0 commit comments

Comments
 (0)