Merge branch 'PHP-5.6'

* PHP-5.6:
  Bug #47030 (separate host and peer verification)
This commit is contained in:
Daniel Lowrey 2014-02-14 15:40:36 -07:00
commit 8562b8c163
10 changed files with 227 additions and 88 deletions

View file

@ -5077,62 +5077,82 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
zval **val = NULL; zval **val = NULL;
char *cnmatch = NULL; char *cnmatch = NULL;
int err; int err;
php_openssl_netstream_data_t *sslsock; 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;
sslsock = (php_openssl_netstream_data_t*)stream->abstract; must_verify_peer = GET_VER_OPT("verify_peer")
? zend_is_true(*val)
: sslsock->is_client;
if (!(GET_VER_OPT("verify_peer") || sslsock->is_client) must_verify_host = GET_VER_OPT("verify_host")
|| (GET_VER_OPT("verify_peer") && !zval_is_true(*val)) ? zend_is_true(*val)
) { : sslsock->is_client;
return SUCCESS;
}
if (peer == NULL) { 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"); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not get peer certificate");
return FAILURE; return FAILURE;
} }
err = SSL_get_verify_result(ssl); /* Verify the peer against using CA file/path settings */
switch (err) { if (must_verify_peer) {
case X509_V_OK: err = SSL_get_verify_result(ssl);
/* fine */ switch (err) {
break; case X509_V_OK:
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: /* fine */
if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
/* allowed */
break; break;
} case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
/* not allowed, so fall through */ if (GET_VER_OPT("allow_self_signed") && zval_is_true(*val)) {
default: /* allowed */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not verify peer: code:%d %s", err, X509_verify_cert_error_string(err)); break;
return FAILURE; }
} /* not allowed, so fall through */
default:
/* if the cert passed the usual checks, apply our own local policies now */ php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Could not verify peer: code:%d %s",
if (GET_VER_OPT("peer_fingerprint")) { err,
if (Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_ARRAY) { X509_verify_cert_error_string(err)
if (!php_x509_fingerprint_match(peer, *val TSRMLS_CC)) { );
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Peer fingerprint doesn't match");
return FAILURE; return FAILURE;
}
} else {
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); /* If a peer_fingerprint match is required this trumps host verification */
if (must_verify_fingerprint) {
/* If no CN_match was specified assign the autodetected name when connecting as a client */ if (Z_TYPE_PP(val) == IS_STRING || Z_TYPE_PP(val) == IS_ARRAY) {
if (cnmatch == NULL && sslsock->is_client) { if (!php_x509_fingerprint_match(peer, *val TSRMLS_CC)) {
cnmatch = sslsock->url_name; 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"
);
}
} }
if (cnmatch) { /* verify the host name presented in the peer certificate */
if (matches_san_list(peer, cnmatch TSRMLS_CC)) {
return SUCCESS; if (must_verify_host) {
} else if (matches_common_name(peer, cnmatch TSRMLS_CC)) { GET_VER_OPT_STRING("CN_match", cnmatch);
return SUCCESS; /* 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;
} else {
return FAILURE;
}
} else { } else {
return FAILURE; return FAILURE;
} }

View file

@ -13,6 +13,7 @@ function ssl_server($port) {
$pem = dirname(__FILE__) . '/bug46127.pem'; $pem = dirname(__FILE__) . '/bug46127.pem';
$ssl = array( $ssl = array(
'verify_peer' => false, 'verify_peer' => false,
'verify_host' => false,
'allow_self_signed' => true, 'allow_self_signed' => true,
'local_cert' => $pem, 'local_cert' => $pem,
// 'passphrase' => '', // 'passphrase' => '',
@ -46,7 +47,8 @@ if ($pid == 0) { // child
// client or failed // client or failed
sleep(1); sleep(1);
$ctx = stream_context_create(['ssl' => [ $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); $sock = stream_socket_client("ssl://127.0.0.1:{$port}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx);
if (!$sock) exit; if (!$sock) exit;

View file

@ -25,7 +25,10 @@ if ($pid == -1) {
var_dump(@stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, STREAM_CLIENT_CONNECT, $ctx)); 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 // 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)); 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 // Should succeed with CA file specified in context

View file

@ -20,13 +20,18 @@ SNI 001
* the server returned. * the server returned.
*/ */
function context() { function context($host = NULL) {
return stream_context_create(array(
'ssl' => array( $ctx = stream_context_create();
'capture_peer_cert' => true, stream_context_set_option($ctx, 'ssl', 'capture_peer_cert', true);
'verify_peer' => false 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) { function get_CN($context) {
@ -73,13 +78,13 @@ function do_enable_crypto_test($url, $context) {
/* Test https:// streams */ /* Test https:// streams */
echo "-- auto host name (1) --\n"; 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"; 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"; 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"; echo "-- user supplied server name --\n";
@ -97,14 +102,14 @@ do_http_test('https://bob.sni.velox.ch/', $context);
/* Test ssl:// socket streams */ /* Test ssl:// socket streams */
echo "-- raw SSL stream (1) --\n"; 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"; 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"; 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'); stream_context_set_option($context, 'ssl', 'SNI_server_name', 'bob.sni.velox.ch');
do_ssl_test('ssl://mallory.sni.velox.ch:443', $context); do_ssl_test('ssl://mallory.sni.velox.ch:443', $context);

View file

@ -0,0 +1,35 @@
--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)

View file

@ -0,0 +1,36 @@
--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)

View file

@ -0,0 +1,40 @@
--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)

View file

@ -8,9 +8,7 @@ if (!extension_loaded('pcntl')) die('skip, pcntl required');
--FILE-- --FILE--
<?php <?php
$serverCtx = stream_context_create(['ssl' => [ $serverCtx = stream_context_create(['ssl' => [
'local_cert' => dirname(__FILE__) . '/streams_crypto_method.pem', 'local_cert' => dirname(__FILE__) . '/streams_crypto_method.pem',
'allow_self_signed' => true,
'verify_peer' => false
]]); ]]);
$serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$server = stream_socket_server('sslv3://127.0.0.1:12345', $errno, $errstr, $serverFlags, $serverCtx); $server = stream_socket_server('sslv3://127.0.0.1:12345', $errno, $errstr, $serverFlags, $serverCtx);
@ -18,31 +16,29 @@ $server = stream_socket_server('sslv3://127.0.0.1:12345', $errno, $errstr, $serv
$pid = pcntl_fork(); $pid = pcntl_fork();
if ($pid == -1) { if ($pid == -1) {
die('could not fork'); die('could not fork');
} else if ($pid) { } else if ($pid) {
$clientCtx = stream_context_create(['ssl' => [ $clientCtx = stream_context_create(['ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT, 'crypto_method' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
'verify_peer' => false 'verify_peer' => false,
]]); 'verify_host' => false
]]);
$fp = fopen('https://127.0.0.1:12345/', 'r', false, $clientCtx); $fp = fopen('https://127.0.0.1:12345/', 'r', false, $clientCtx);
if ($fp) { if ($fp) {
fpassthru($fp); fpassthru($fp);
fclose($fp); fclose($fp);
} }
} else { } else {
@pcntl_wait($status); @pcntl_wait($status);
$client = @stream_socket_accept($server);
$client = @stream_socket_accept($server); if ($client) {
$in = '';
if ($client) { while (!preg_match('/\r?\n\r?\n/', $in)) {
$in = ''; $in .= fread($client, 2048);
while (!preg_match('/\r?\n\r?\n/', $in)) { }
$in .= fread($client, 2048); $response = <<<EOS
}
$response = <<<EOS
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Type: text/plain Content-Type: text/plain
Content-Length: 13 Content-Length: 13
@ -51,11 +47,11 @@ Connection: close
Hello World! Hello World!
EOS; EOS;
fwrite($client, $response); fwrite($client, $response);
fclose($client); fclose($client);
exit(); exit();
} }
} }
?> ?>
--EXPECTF-- --EXPECTF--

View file

@ -21,7 +21,8 @@ if ($pid == -1) {
} elseif ($pid) { } elseif ($pid) {
$flags = STREAM_CLIENT_CONNECT; $flags = STREAM_CLIENT_CONNECT;
$ctx = stream_context_create(array('ssl' => array( $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); $client = stream_socket_client("tlsv1.1://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);

View file

@ -21,7 +21,8 @@ if ($pid == -1) {
} elseif ($pid) { } elseif ($pid) {
$flags = STREAM_CLIENT_CONNECT; $flags = STREAM_CLIENT_CONNECT;
$ctx = stream_context_create(array('ssl' => array( $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); $client = stream_socket_client("tlsv1.2://127.0.0.1:64321", $errno, $errstr, 1, $flags, $ctx);