]> granicus.if.org Git - php/commitdiff
Bug #47030 (separate host and peer verification)
authorDaniel Lowrey <rdlowrey@php.net>
Fri, 14 Feb 2014 22:17:30 +0000 (15:17 -0700)
committerDaniel Lowrey <rdlowrey@php.net>
Fri, 14 Feb 2014 22:17:30 +0000 (15:17 -0700)
ext/openssl/openssl.c
ext/openssl/tests/bug46127.phpt
ext/openssl/tests/peer_verification.phpt
ext/openssl/tests/sni_001.phpt
ext/openssl/tests/stream_verify_host_001.phpt [new file with mode: 0644]
ext/openssl/tests/stream_verify_host_002.phpt [new file with mode: 0644]
ext/openssl/tests/stream_verify_host_003.phpt [new file with mode: 0644]
ext/openssl/tests/streams_crypto_method.phpt
ext/openssl/tests/tlsv1.1_wrapper_001.phpt
ext/openssl/tests/tlsv1.2_wrapper_002.phpt

index d57b3eafde590e80c83fff853f7aea701f99b4f5..f7acda157945d1b17bd58977bb1b02b44e6289f0 100644 (file)
@@ -5077,62 +5077,82 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
        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;
                }
index 1de4eacd016d20f1e62bb792ed029590184b02eb..ef4a9be03157417bcb58816e489a99b758a38083 100644 (file)
@@ -13,6 +13,7 @@ function ssl_server($port) {
        $pem = dirname(__FILE__) . '/bug46127.pem';
        $ssl = array(
                        'verify_peer' => false,
+                       'verify_host' => false,
                        'allow_self_signed' => true,
                        'local_cert' => $pem,
                        //              'passphrase' => '',
@@ -46,7 +47,8 @@ if ($pid == 0) { // child
 // 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;
index 7c3347fd652e14ea84e8ca46cd1af42fc8bcfe88..b19012a9b8b0f7366cb245b9efbf5a2b8a36ecd1 100644 (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));
        
        // 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
index 2f76a9f9187cd423db76d12c3e461eb44114e552..0dbd18d381520bb30a94adf937d5a1999b74462c 100644 (file)
@@ -20,13 +20,18 @@ SNI 001
  * 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) {
@@ -73,13 +78,13 @@ function do_enable_crypto_test($url, $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";
 
@@ -97,14 +102,14 @@ do_http_test('https://bob.sni.velox.ch/', $context);
 /* 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);
diff --git a/ext/openssl/tests/stream_verify_host_001.phpt b/ext/openssl/tests/stream_verify_host_001.phpt
new file mode 100644 (file)
index 0000000..aa85ad5
--- /dev/null
@@ -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)
diff --git a/ext/openssl/tests/stream_verify_host_002.phpt b/ext/openssl/tests/stream_verify_host_002.phpt
new file mode 100644 (file)
index 0000000..1ac81e2
--- /dev/null
@@ -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)
diff --git a/ext/openssl/tests/stream_verify_host_003.phpt b/ext/openssl/tests/stream_verify_host_003.phpt
new file mode 100644 (file)
index 0000000..ce6430a
--- /dev/null
@@ -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)
index b7b8e257b44a82ef5dae89643a7a30824d3c23d8..6f6bedd6336367ec0b404ae269fa9430b371db2a 100644 (file)
@@ -8,9 +8,7 @@ if (!extension_loaded('pcntl')) die('skip, pcntl required');
 --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);
@@ -18,31 +16,29 @@ $server = stream_socket_server('sslv3://127.0.0.1:12345', $errno, $errstr, $serv
 $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
@@ -51,11 +47,11 @@ Connection: close
 Hello World!
 
 EOS;
-            fwrite($client, $response);
-            fclose($client);
+               fwrite($client, $response);
+               fclose($client);
 
-            exit();
-    }
+               exit();
+       }
 }
 ?>
 --EXPECTF--
index 56211f0b965e06e0e3a7eb42caddfdf93daa140f..82048e525de7544d4fe5a5a3a1983fbd6766be47 100644 (file)
@@ -21,7 +21,8 @@ if ($pid == -1) {
 } 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);
index cb3f4106c727e739e930b4371981de4654d37938..d58d1a12624a41bf72fd7c96878f14e95a1d3343 100644 (file)
@@ -21,7 +21,8 @@ if ($pid == -1) {
 } 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);