zval **val = NULL;
char *cnmatch = NULL;
int err;
- php_openssl_netstream_data_t *sslsock;
-
- sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+ zend_bool must_verify_peer;
+ zend_bool must_verify_host;
+ zend_bool must_verify_fingerprint;
+ php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
- if (!(GET_VER_OPT("verify_peer") || sslsock->is_client)
- || (GET_VER_OPT("verify_peer") && !zval_is_true(*val))
- ) {
- return SUCCESS;
- }
+ must_verify_peer = GET_VER_OPT("verify_peer")
+ ? zend_is_true(*val)
+ : sslsock->is_client;
- if (peer == NULL) {
+ must_verify_host = GET_VER_OPT("verify_host")
+ ? zend_is_true(*val)
+ : sslsock->is_client;
+
+ must_verify_fingerprint = (GET_VER_OPT("peer_fingerprint") && zend_is_true(*val));
+
+ if ((must_verify_peer || must_verify_host || must_verify_fingerprint) && peer == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not get peer certificate");
return FAILURE;
}
- err = SSL_get_verify_result(ssl);
- switch (err) {
- case X509_V_OK:
- /* fine */
- break;
- case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
- /* allowed */
+ /* Verify the peer against using CA file/path settings */
+ if (must_verify_peer) {
+ err = SSL_get_verify_result(ssl);
+ switch (err) {
+ case X509_V_OK:
+ /* fine */
break;
- }
- /* not allowed, so fall through */
- default:
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err));
- return FAILURE;
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
+ /* allowed */
+ break;
+ }
+ /* not allowed, so fall through */
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Could not verify peer: code:%d %s",
+ err,
+ X509_verify_cert_error_string(err)
+ );
+ return FAILURE;
+ }
}
- /* if the cert passed the usual checks, apply our own local policies now */
-
- if (GET_VER_OPT("peer_fingerprint")) {
+ /* If a peer_fingerprint match is required this trumps host verification */
+ if (must_verify_fingerprint) {
if (Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_ARRAY) {
if (!php_x509_fingerprint_match(peer, *val TSRMLS_CC)) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer fingerprint doesn't match");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Peer fingerprint doesn't match"
+ );
return FAILURE;
}
} else {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected peer fingerprint must be a string or an array");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Expected peer fingerprint must be a string or an array"
+ );
}
}
- GET_VER_OPT_STRING("CN_match", cnmatch);
+ /* verify the host name presented in the peer certificate */
- /* If no CN_match was specified assign the autodetected name when connecting as a client */
- if (cnmatch == NULL && sslsock->is_client) {
- cnmatch = sslsock->url_name;
- }
+ if (must_verify_host) {
+ GET_VER_OPT_STRING("CN_match", cnmatch);
+ /* If no CN_match was specified assign the autodetected url name in client environments */
+ if (cnmatch == NULL && sslsock->is_client) {
+ cnmatch = sslsock->url_name;
+ }
- if (cnmatch) {
- if (matches_san_list(peer, cnmatch TSRMLS_CC)) {
- return SUCCESS;
- } else if (matches_common_name(peer, cnmatch TSRMLS_CC)) {
- return SUCCESS;
+ if (cnmatch) {
+ if (matches_san_list(peer, cnmatch TSRMLS_CC)) {
+ return SUCCESS;
+ } else if (matches_common_name(peer, cnmatch TSRMLS_CC)) {
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
} else {
return FAILURE;
}
$pem = dirname(__FILE__) . '/bug46127.pem';
$ssl = array(
'verify_peer' => false,
+ 'verify_host' => false,
'allow_self_signed' => true,
'local_cert' => $pem,
// 'passphrase' => '',
// client or failed
sleep(1);
$ctx = stream_context_create(['ssl' => [
- 'verify_peer' => false
+ 'verify_peer' => false,
+ 'verify_host' => false
]]);
$sock = stream_socket_client("ssl://127.0.0.1:{$port}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx);
if (!$sock) exit;
var_dump(@stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, STREAM_CLIENT_CONNECT, $ctx));
// Should succeed with peer verification disabled in context
- $ctx = stream_context_create(['ssl' => ['verify_peer' => false]]);
+ $ctx = stream_context_create(['ssl' => [
+ 'verify_peer' => false,
+ 'verify_host' => false
+ ]]);
var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, STREAM_CLIENT_CONNECT, $ctx));
// Should succeed with CA file specified in context
* the server returned.
*/
-function context() {
- return stream_context_create(array(
- 'ssl' => array(
- 'capture_peer_cert' => true,
- 'verify_peer' => false
- ),
- ));
+function context($host = NULL) {
+
+ $ctx = stream_context_create();
+ stream_context_set_option($ctx, 'ssl', 'capture_peer_cert', true);
+ stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
+ if ($host) {
+ stream_context_set_option($ctx, 'ssl', 'CN_match', $host);
+ } else {
+ stream_context_set_option($ctx, 'ssl', 'verify_host', false);
+ }
+
+ return $ctx;
}
function get_CN($context) {
/* Test https:// streams */
echo "-- auto host name (1) --\n";
-do_http_test('https://alice.sni.velox.ch/', context());
+do_http_test('https://alice.sni.velox.ch/', context('alice.sni.velox.ch'));
echo "-- auto host name (2) --\n";
-do_http_test('https://bob.sni.velox.ch/', context());
+do_http_test('https://bob.sni.velox.ch/', context('bob.sni.velox.ch'));
echo "-- auto host name (3) --\n";
-do_http_test('https://bob.sni.velox.ch./', context());
+do_http_test('https://bob.sni.velox.ch./', context('bob.sni.velox.ch'));
echo "-- user supplied server name --\n";
/* Test ssl:// socket streams */
echo "-- raw SSL stream (1) --\n";
-do_ssl_test('ssl://bob.sni.velox.ch:443', context());
+do_ssl_test('ssl://bob.sni.velox.ch:443', context('bob.sni.velox.ch'));
echo "-- raw SSL stream (2) --\n";
-do_ssl_test('ssl://mallory.sni.velox.ch:443', context());
+do_ssl_test('ssl://mallory.sni.velox.ch:443', context('mallory.sni.velox.ch'));
echo "-- raw SSL stream with user supplied sni --\n";
-$context = context();
+$context = context('bob.sni.velox.ch');
stream_context_set_option($context, 'ssl', 'SNI_server_name', 'bob.sni.velox.ch');
do_ssl_test('ssl://mallory.sni.velox.ch:443', $context);
--- /dev/null
+--TEST--
+Verify host name by default in client transfers
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$serverUri = "ssl://127.0.0.1:64321";
+$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+$serverCtx = stream_context_create(['ssl' => [
+ 'local_cert' => __DIR__ . '/bug54992.pem'
+]]);
+$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} else if ($pid) {
+
+ $clientFlags = STREAM_CLIENT_CONNECT;
+ $clientCtx = stream_context_create(['ssl' => [
+ 'verify_peer' => false,
+ 'CN_match' => 'bug54992.local'
+ ]]);
+
+ $client = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx);
+ var_dump($client);
+
+} else {
+ @pcntl_wait($status);
+ @stream_socket_accept($server, 1);
+}
+--EXPECTF--
+resource(%d) of type (stream)
--- /dev/null
+--TEST--
+Allow host name mismatch when "verify_host" disabled
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$serverUri = "ssl://127.0.0.1:64321";
+$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+$serverCtx = stream_context_create(['ssl' => [
+ 'local_cert' => __DIR__ . '/bug54992.pem'
+]]);
+$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} else if ($pid) {
+
+ $clientFlags = STREAM_CLIENT_CONNECT;
+ $clientCtx = stream_context_create(['ssl' => [
+ 'verify_peer' => true,
+ 'cafile' => __DIR__ . '/bug54992-ca.pem',
+ 'verify_host' => false
+ ]]);
+
+ $client = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx);
+ var_dump($client);
+
+} else {
+ @pcntl_wait($status);
+ @stream_socket_accept($server, 1);
+}
+--EXPECTF--
+resource(%d) of type (stream)
--- /dev/null
+--TEST--
+Host name mismatch triggers error
+--SKIPIF--
+<?php
+if (!extension_loaded("openssl")) die("skip");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$serverUri = "ssl://127.0.0.1:64321";
+$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+$serverCtx = stream_context_create(['ssl' => [
+ 'local_cert' => __DIR__ . '/bug54992.pem'
+]]);
+$server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx);
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die('could not fork');
+} else if ($pid) {
+
+ $clientFlags = STREAM_CLIENT_CONNECT;
+ $clientCtx = stream_context_create(['ssl' => [
+ 'verify_peer' => true,
+ 'cafile' => __DIR__ . '/bug54992-ca.pem'
+ ]]);
+
+ $client = stream_socket_client($serverUri, $errno, $errstr, 1, $clientFlags, $clientCtx);
+ var_dump($client);
+
+} else {
+ @pcntl_wait($status);
+ @stream_socket_accept($server, 1);
+}
+--EXPECTF--
+Warning: stream_socket_client(): Peer certificate CN=`bug54992.local' did not match expected CN=`127.0.0.1' in %s on line %d
+
+Warning: stream_socket_client(): Failed to enable crypto in %s on line %d
+
+Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d
+bool(false)
--FILE--
<?php
$serverCtx = stream_context_create(['ssl' => [
- 'local_cert' => dirname(__FILE__) . '/streams_crypto_method.pem',
- 'allow_self_signed' => true,
- 'verify_peer' => false
+ 'local_cert' => dirname(__FILE__) . '/streams_crypto_method.pem',
]]);
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$server = stream_socket_server('sslv3://127.0.0.1:12345', $errno, $errstr, $serverFlags, $serverCtx);
$pid = pcntl_fork();
if ($pid == -1) {
- die('could not fork');
+ die('could not fork');
} else if ($pid) {
- $clientCtx = stream_context_create(['ssl' => [
- 'crypto_method' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
- 'verify_peer' => false
- ]]);
-
- $fp = fopen('https://127.0.0.1:12345/', 'r', false, $clientCtx);
-
- if ($fp) {
- fpassthru($fp);
- fclose($fp);
- }
+ $clientCtx = stream_context_create(['ssl' => [
+ 'crypto_method' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
+ 'verify_peer' => false,
+ 'verify_host' => false
+ ]]);
+
+ $fp = fopen('https://127.0.0.1:12345/', 'r', false, $clientCtx);
+
+ if ($fp) {
+ fpassthru($fp);
+ fclose($fp);
+ }
} else {
- @pcntl_wait($status);
-
- $client = @stream_socket_accept($server);
-
- if ($client) {
- $in = '';
- while (!preg_match('/\r?\n\r?\n/', $in)) {
- $in .= fread($client, 2048);
- }
-
- $response = <<<EOS
+ @pcntl_wait($status);
+ $client = @stream_socket_accept($server);
+ if ($client) {
+ $in = '';
+ while (!preg_match('/\r?\n\r?\n/', $in)) {
+ $in .= fread($client, 2048);
+ }
+ $response = <<<EOS
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 13
Hello World!
EOS;
- fwrite($client, $response);
- fclose($client);
+ fwrite($client, $response);
+ fclose($client);
- exit();
- }
+ exit();
+ }
}
?>
--EXPECTF--
} elseif ($pid) {
$flags = STREAM_CLIENT_CONNECT;
$ctx = stream_context_create(array('ssl' => array(
- 'verify_peer' => false
+ 'verify_peer' => false,
+ 'verify_host' => false
)));
$client = stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);
} elseif ($pid) {
$flags = STREAM_CLIENT_CONNECT;
$ctx = stream_context_create(array('ssl' => array(
- 'verify_peer' => false
+ 'verify_peer' => false,
+ 'verify_host' => false
)));
$client = stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);