Skip to content

Commit 16155f1

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.
1 parent c6b058b commit 16155f1

File tree

2 files changed

+98
-5
lines changed

2 files changed

+98
-5
lines changed

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)