Skip to content

Commit 6b2b60f

Browse files
committed
Fix bug #80770: openssl cafile not used in SNI SSL_CTX
The issue is about not being able to connect as cafile for SNI is not used in its SSL context. This sets it up so it is possible to capture the client certificate which is only possible when verify_peer is true. Closes GH-18893
1 parent 7147226 commit 6b2b60f

File tree

3 files changed

+102
-5
lines changed

3 files changed

+102
-5
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ PHP NEWS
3434
. Fixed bug GH-14082 (Segmentation fault on unknown address 0x600000000018
3535
in ext/opcache/jit/zend_jit.c). (nielsdos)
3636

37+
- OpenSSL:
38+
. Fixed bug #80770 (It is not possible to get client peer certificate with
39+
stream_socket_server). (Jakub Zelenka)
40+
3741
- PCNTL:
3842
. Fixed bug GH-18958 (Fatal error during shutdown after pcntl_rfork() or
3943
pcntl_forkx() with zend-max-execution-timers). (Arnaud)

ext/openssl/tests/bug80770.phpt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
--TEST--
2+
Bug #80770: SNI_server_certs does not inherit peer verification options
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 v1.1.1 required");
9+
?>
10+
--FILE--
11+
<?php
12+
$clientCertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug80770_client.pem.tmp';
13+
$caCertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug80770_ca.pem.tmp';
14+
15+
$serverCode = <<<'CODE'
16+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
17+
$ctx = stream_context_create(['ssl' => [
18+
'SNI_server_certs' => [
19+
"cs.php.net" => __DIR__ . "/sni_server_cs.pem",
20+
"uk.php.net" => __DIR__ . "/sni_server_uk.pem",
21+
"us.php.net" => __DIR__ . "/sni_server_us.pem"
22+
],
23+
'verify_peer' => true,
24+
'cafile' => '%s',
25+
'capture_peer_cert' => true,
26+
'verify_peer_name' => false,
27+
'security_level' => 0,
28+
]]);
29+
$server = stream_socket_server('tcp://127.0.0.1:0', $errno, $errstr, $flags, $ctx);
30+
phpt_notify_server_start($server);
31+
32+
$client = stream_socket_accept($server, 30);
33+
if ($client) {
34+
$success = stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER);
35+
if ($success) {
36+
$options = stream_context_get_options($client);
37+
$hasCert = isset($options['ssl']['peer_certificate']);
38+
phpt_notify(message: $hasCert ? "CLIENT_CERT_CAPTURED" : "NO_CLIENT_CERT");
39+
} else {
40+
phpt_notify(message: "TLS_HANDSHAKE_FAILED");
41+
}
42+
} else {
43+
phpt_notify(message: "ACCEPT_FAILED");
44+
}
45+
CODE;
46+
$serverCode = sprintf($serverCode, $caCertFile);
47+
48+
$clientCode = <<<'CODE'
49+
$flags = STREAM_CLIENT_CONNECT;
50+
$ctx = stream_context_create(['ssl' => [
51+
'verify_peer' => false,
52+
'verify_peer_name' => false,
53+
'local_cert' => '%s',
54+
'peer_name' => 'cs.php.net',
55+
'security_level' => 0,
56+
]]);
57+
$client = stream_socket_client("tcp://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx);
58+
if ($client) {
59+
stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
60+
}
61+
62+
$result = phpt_wait();
63+
echo trim($result);
64+
CODE;
65+
$clientCode = sprintf($clientCode, $clientCertFile);
66+
67+
include 'CertificateGenerator.inc';
68+
69+
// Generate CA and client certificate signed by that CA
70+
$certificateGenerator = new CertificateGenerator();
71+
$certificateGenerator->saveCaCert($caCertFile);
72+
$certificateGenerator->saveNewCertAsFileWithKey('Bug80770 Test Client', $clientCertFile);
73+
74+
include 'ServerClientTestCase.inc';
75+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
76+
?>
77+
--CLEAN--
78+
<?php
79+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'bug80770_client.pem.tmp');
80+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'bug80770_ca.pem.tmp');
81+
?>
82+
--EXPECTF--
83+
CLIENT_CERT_CAPTURED

ext/openssl/xp_ssl.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,8 @@ static SSL_CTX *php_openssl_create_sni_server_ctx(char *cert_path, char *key_pat
14431443
}
14441444
/* }}} */
14451445

1446-
static zend_result php_openssl_enable_server_sni(php_stream *stream, php_openssl_netstream_data_t *sslsock) /* {{{ */
1446+
static zend_result php_openssl_enable_server_sni(
1447+
php_stream *stream, php_openssl_netstream_data_t *sslsock, bool verify_peer)
14471448
{
14481449
zval *val;
14491450
zval *current;
@@ -1564,6 +1565,12 @@ static zend_result php_openssl_enable_server_sni(php_stream *stream, php_openssl
15641565
return FAILURE;
15651566
}
15661567

1568+
if (!verify_peer) {
1569+
php_openssl_disable_peer_verification(ctx, stream);
1570+
} else if (FAILURE == php_openssl_enable_peer_verification(ctx, stream)) {
1571+
return FAILURE;
1572+
}
1573+
15671574
sslsock->sni_certs[i].name = pestrdup(ZSTR_VAL(key), php_stream_is_persistent(stream));
15681575
sslsock->sni_certs[i].ctx = ctx;
15691576
++i;
@@ -1574,7 +1581,6 @@ static zend_result php_openssl_enable_server_sni(php_stream *stream, php_openssl
15741581

15751582
return SUCCESS;
15761583
}
1577-
/* }}} */
15781584

15791585
static void php_openssl_enable_client_sni(php_stream *stream, php_openssl_netstream_data_t *sslsock) /* {{{ */
15801586
{
@@ -1666,6 +1672,7 @@ zend_result php_openssl_setup_crypto(php_stream *stream,
16661672
char *cipherlist = NULL;
16671673
char *alpn_protocols = NULL;
16681674
zval *val;
1675+
bool verify_peer = false;
16691676

16701677
if (sslsock->ssl_handle) {
16711678
if (sslsock->s.is_blocked) {
@@ -1717,8 +1724,11 @@ zend_result php_openssl_setup_crypto(php_stream *stream,
17171724

17181725
if (GET_VER_OPT("verify_peer") && !zend_is_true(val)) {
17191726
php_openssl_disable_peer_verification(sslsock->ctx, stream);
1720-
} else if (FAILURE == php_openssl_enable_peer_verification(sslsock->ctx, stream)) {
1721-
return FAILURE;
1727+
} else {
1728+
verify_peer = true;
1729+
if (FAILURE == php_openssl_enable_peer_verification(sslsock->ctx, stream)) {
1730+
return FAILURE;
1731+
}
17221732
}
17231733

17241734
/* callback for the passphrase (for localcert) */
@@ -1819,7 +1829,7 @@ zend_result php_openssl_setup_crypto(php_stream *stream,
18191829

18201830
#ifdef HAVE_TLS_SNI
18211831
/* Enable server-side SNI */
1822-
if (!sslsock->is_client && php_openssl_enable_server_sni(stream, sslsock) == FAILURE) {
1832+
if (!sslsock->is_client && php_openssl_enable_server_sni(stream, sslsock, verify_peer) == FAILURE) {
18231833
return FAILURE;
18241834
}
18251835
#endif

0 commit comments

Comments
 (0)