]> granicus.if.org Git - php/commitdiff
Verify peers by default in client socket operations
authorDaniel Lowrey <rdlowrey@php.net>
Tue, 28 Jan 2014 17:05:56 +0000 (10:05 -0700)
committerDaniel Lowrey <rdlowrey@php.net>
Tue, 28 Jan 2014 17:05:56 +0000 (10:05 -0700)
ext/openssl/openssl.c
ext/openssl/php_openssl_structs.h [new file with mode: 0644]
ext/openssl/tests/bug46127.phpt
ext/openssl/tests/bug48182.phpt
ext/openssl/tests/openssl_peer_fingerprint.phpt
ext/openssl/tests/peer_verification.phpt [new file with mode: 0644]
ext/openssl/tests/sni_001.phpt
ext/openssl/tests/streams_crypto_method.phpt
ext/openssl/xp_ssl.c

index b38dd6d206c89e7883dd1d6ecbc736e436881762..d57b3eafde590e80c83fff853f7aea701f99b4f5 100644 (file)
@@ -27,7 +27,9 @@
 #endif
 
 #include "php.h"
+#include "php_ini.h"
 #include "php_openssl.h"
+#include "php_openssl_structs.h"
 
 /* PHP Includes */
 #include "ext/standard/file.h"
@@ -1071,6 +1073,13 @@ static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {
 }
 /* }}} */
 
+/* {{{ INI Settings */
+PHP_INI_BEGIN()
+       PHP_INI_ENTRY("openssl.cafile", NULL, PHP_INI_ALL, NULL)
+       PHP_INI_ENTRY("openssl.capath", NULL, PHP_INI_ALL, NULL)
+PHP_INI_END()
+/* }}} */
 /* {{{ PHP_MINIT_FUNCTION
  */
 PHP_MINIT_FUNCTION(openssl)
@@ -1203,7 +1212,9 @@ PHP_MINIT_FUNCTION(openssl)
 
        php_register_url_stream_wrapper("https", &php_stream_http_wrapper TSRMLS_CC);
        php_register_url_stream_wrapper("ftps", &php_stream_ftp_wrapper TSRMLS_CC);
-       
+
+       REGISTER_INI_ENTRIES();
+
        return SUCCESS;
 }
 /* }}} */
@@ -1217,6 +1228,7 @@ PHP_MINFO_FUNCTION(openssl)
        php_info_print_table_row(2, "OpenSSL Library Version", SSLeay_version(SSLEAY_VERSION));
        php_info_print_table_row(2, "OpenSSL Header Version", OPENSSL_VERSION_TEXT);
        php_info_print_table_end();
+       DISPLAY_INI_ENTRIES();
 }
 /* }}} */
 
@@ -1243,6 +1255,8 @@ PHP_MSHUTDOWN_FUNCTION(openssl)
        /* reinstate the default tcp handler */
        php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC);
 
+       UNREGISTER_INI_ENTRIES();
+
        return SUCCESS;
 }
 /* }}} */
@@ -5063,9 +5077,13 @@ 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;
 
-       /* verification is turned off */
-       if (!(GET_VER_OPT("verify_peer") && zval_is_true(*val))) {
+       if (!(GET_VER_OPT("verify_peer") || sslsock->is_client)
+               || (GET_VER_OPT("verify_peer") && !zval_is_true(*val))
+       ) {
                return SUCCESS;
        }
 
@@ -5105,6 +5123,11 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
 
        GET_VER_OPT_STRING("CN_match", cnmatch);
 
+       /* 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 (cnmatch) {
                if (matches_san_list(peer, cnmatch TSRMLS_CC)) {
                        return SUCCESS;
@@ -5150,7 +5173,9 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{
        ERR_clear_error();
 
        /* look at context options in the stream and set appropriate verification flags */
-       if (GET_VER_OPT("verify_peer") && zval_is_true(*val)) {
+       if (GET_VER_OPT("verify_peer") && !zval_is_true(*val)) {
+               SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
+       } else {
 
                /* turn on verification callback */
                SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
@@ -5159,19 +5184,35 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{
                GET_VER_OPT_STRING("cafile", cafile);
                GET_VER_OPT_STRING("capath", capath);
 
+               if (!cafile) {
+                       zend_bool exists = 1;
+                       cafile = zend_ini_string_ex("openssl.cafile", sizeof("openssl.cafile"), 0, &exists);
+               }
+
+               if (!capath) {
+                       zend_bool exists = 1;
+                       capath = zend_ini_string_ex("openssl.capath", sizeof("openssl.capath"), 0, &exists);
+               }
+
                if (cafile || capath) {
                        if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set verify locations `%s' `%s'", cafile, capath);
                                return NULL;
                        }
+               } else {
+                       php_openssl_netstream_data_t *sslsock;
+                       sslsock = (php_openssl_netstream_data_t*)stream->abstract;
+                       if (sslsock->is_client && !SSL_CTX_set_default_verify_paths(ctx)) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                                       "Unable to set default verify locations and no CA settings specified");
+                               return NULL;
+                       }
                }
 
                if (GET_VER_OPT("verify_depth")) {
                        convert_to_long_ex(val);
                        SSL_CTX_set_verify_depth(ctx, Z_LVAL_PP(val));
                }
-       } else {
-               SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
        }
 
        /* callback for the passphrase (for localcert) */
@@ -5237,6 +5278,7 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{
                        }
                }
        }
+
        if (ok) {
                SSL *ssl = SSL_new(ctx);
 
diff --git a/ext/openssl/php_openssl_structs.h b/ext/openssl/php_openssl_structs.h
new file mode 100644 (file)
index 0000000..13f8f32
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 5                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 1997-2013 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author: Wez Furlong <wez@thebrainroom.com>                           |
+  |         Daniel Lowrey <rdlowrey@gmail.com>                           |
+  +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php_network.h"
+#include <openssl/ssl.h>
+
+/* This implementation is very closely tied to the that of the native
+ * sockets implemented in the core.
+ * Don't try this technique in other extensions!
+ * */
+
+typedef struct _php_openssl_netstream_data_t {
+       php_netstream_data_t s;
+       SSL *ssl_handle;
+       SSL_CTX *ctx;
+       struct timeval connect_timeout;
+       int enable_on_connect;
+       int is_client;
+       int ssl_active;
+       php_stream_xport_crypt_method_t method;
+       char *url_name;
+       unsigned state_set:1;
+       unsigned _spare:31;
+} php_openssl_netstream_data_t;
index a3bfd3a012998c71df8eede97c968f04d8cb3201..1de4eacd016d20f1e62bb792ed029590184b02eb 100644 (file)
@@ -45,7 +45,10 @@ if ($pid == 0) { // child
 
 // client or failed
 sleep(1);
-$sock = fsockopen('ssl://127.0.0.1', $port, $errno, $errstr);
+$ctx = stream_context_create(['ssl' => [
+    'verify_peer' => false
+]]);
+$sock = stream_socket_client("ssl://127.0.0.1:{$port}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx);
 if (!$sock) exit;
 
 echo fgets($sock);
index 146c4c9226df962b3a03ea27c2e7d97f0fdc1541..b78ce57074ff34ac7de0f4d5ad2bd0d769dd85ba 100644 (file)
@@ -13,8 +13,7 @@ function ssl_server($port) {
        $host = 'ssl://127.0.0.1'.':'.$port;
        $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
        $data = "Sending bug48182\n";
-
-       $pem = dirname(__FILE__) . '/bug46127.pem';
+       $pem = dirname(__FILE__) . '/bug54992.pem';
        $ssl_params = array( 'verify_peer' => false, 'allow_self_signed' => true, 'local_cert' => $pem);
        $ssl = array('ssl' => $ssl_params);
 
@@ -47,8 +46,11 @@ function ssl_async_client($port) {
        $host = 'ssl://127.0.0.1'.':'.$port;
        $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
        $data = "Sending data over to SSL server in async mode with contents like Hello World\n";
-
-       $socket = stream_socket_client($host, $errno, $errstr, 10, $flags);
+       $context = stream_context_create(array('ssl' => array(
+               'cafile' => dirname(__FILE__) . '/bug54992-ca.pem',
+               'CN_match' => 'bug54992.local'
+       )));
+       $socket = stream_socket_client($host, $errno, $errstr, 10, $flags, $context);
        stream_set_blocking($socket, 0);
 
        while ($socket && $data) {
index 2960dffae506ed06f57d9d373ab0cab3a0675f3a..2e4c192c03b825d422869093ab7dd8ed317fe3f8 100644 (file)
@@ -24,6 +24,7 @@ if ($pid == -1) {
                                'verify_peer'           => true,
                                'cafile'                => __DIR__ . '/bug54992-ca.pem',
                                'capture_peer_cert'     => true,
+                               'CN_match'              => 'bug54992.local',
                                'peer_fingerprint'      => '81cafc260aa8d82956ebc6212a362ece',
                        )
                )
@@ -38,6 +39,7 @@ if ($pid == -1) {
                                'verify_peer'           => true,
                                'cafile'                => __DIR__ . '/bug54992-ca.pem',
                                'capture_peer_cert'     => true,
+                               'CN_match'              => 'bug54992.local',
                                'peer_fingerprint'      => array(
                                        'sha256' => '78ea579f2c3b439359dec5dac9d445108772927427c4780037e87df3799a0aa0',
                                ),
@@ -59,4 +61,4 @@ 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)
-resource(9) of type (stream)
+resource(%d) of type (stream)
diff --git a/ext/openssl/tests/peer_verification.phpt b/ext/openssl/tests/peer_verification.phpt
new file mode 100644 (file)
index 0000000..7c3347f
--- /dev/null
@@ -0,0 +1,56 @@
+--TEST--
+Peer verification enabled for client streams
+--SKIPIF--
+<?php 
+if (!extension_loaded("openssl")) die("skip");
+if (!function_exists('pcntl_fork')) die("skip no fork");
+--FILE--
+<?php
+$flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
+$ctx = stream_context_create(['ssl' => [
+       'local_cert' => __DIR__ . '/bug54992.pem',
+       'allow_self_signed' => true
+]]);
+$server = stream_socket_server('ssl://127.0.0.1:64321', $errno, $errstr, $flags, $ctx);
+
+$pid = pcntl_fork();
+if ($pid == -1) {
+       die('could not fork');
+} else if ($pid) {
+       // Expected to fail -- no CA File present
+       var_dump(@stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, STREAM_CLIENT_CONNECT));
+       
+       // Expected to fail -- no CA File present
+       $ctx = stream_context_create(['ssl' => ['verify_peer' => true]]);
+       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]]);
+       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
+       $ctx = stream_context_create(['ssl' => [
+               'cafile' => __DIR__ . '/bug54992-ca.pem',
+               'CN_match' => 'bug54992.local',
+       ]]);
+       var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, STREAM_CLIENT_CONNECT, $ctx));
+
+       // Should succeed with globally available CA file specified via php.ini
+       $cafile = __DIR__ . '/bug54992-ca.pem';
+       ini_set('openssl.cafile', $cafile);
+       var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, STREAM_CLIENT_CONNECT, $ctx));
+
+} else {       
+       @pcntl_wait($status);
+       @stream_socket_accept($server, 3);
+       @stream_socket_accept($server, 3);
+       @stream_socket_accept($server, 3);
+       @stream_socket_accept($server, 3);
+       @stream_socket_accept($server, 3);
+}
+--EXPECTF--
+bool(false)
+bool(false)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
index 3d7798cf8572e5838b8a27299f433fa4f489c788..2f76a9f9187cd423db76d12c3e461eb44114e552 100644 (file)
@@ -24,6 +24,7 @@ function context() {
        return stream_context_create(array(
                'ssl' => array(
                        'capture_peer_cert' => true,
+                       'verify_peer' => false
                ),
        ));
 }
index 97a6e9ee8ba853827bed817a8a92b38862202ea6..981f56b399fb6526519381da8ac558b8016231fe 100644 (file)
@@ -10,6 +10,7 @@ if (!extension_loaded('pcntl')) die('skip, pcntl required');
 function client($port, $method) {
        $ctx = stream_context_create();
        stream_context_set_option($ctx, 'ssl', 'crypto_method', $method);
+       stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
 
        $fp = @fopen('https://127.0.0.1:' . $port . '/', 'r', false, $ctx);
        if ($fp) {
index 631d4756e47defe91691a5b4f4eaf6e54346628a..cc8ea8a033e5baedb94b98a56886c93d19cee005 100644 (file)
@@ -23,8 +23,8 @@
 #include "ext/standard/url.h"
 #include "streams/php_streams_int.h"
 #include "ext/standard/php_smart_str.h"
-#include "php_network.h"
 #include "php_openssl.h"
+#include "php_openssl_structs.h"
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
 #include <openssl/err.h>
@@ -41,25 +41,6 @@ int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stre
 SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC);
 int php_openssl_get_x509_list_id(void);
 
-/* This implementation is very closely tied to the that of the native
- * sockets implemented in the core.
- * Don't try this technique in other extensions!
- * */
-
-typedef struct _php_openssl_netstream_data_t {
-       php_netstream_data_t s;
-       SSL *ssl_handle;
-       SSL_CTX *ctx;
-       struct timeval connect_timeout;
-       int enable_on_connect;
-       int is_client;
-       int ssl_active;
-       php_stream_xport_crypt_method_t method;
-       char *sni;
-       unsigned state_set:1;
-       unsigned _spare:31;
-} php_openssl_netstream_data_t;
-
 php_stream_ops php_openssl_socket_ops;
 
 /* it doesn't matter that we do some hash traversal here, since it is done only
@@ -285,11 +266,12 @@ static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_
                }
        }
 
-       if (sslsock->sni) {
-               pefree(sslsock->sni, php_stream_is_persistent(stream));
+       if (sslsock->url_name) {
+               pefree(sslsock->url_name, php_stream_is_persistent(stream));
        }
+
        pefree(sslsock, php_stream_is_persistent(stream));
-       
+
        return 0;
 }
 
@@ -467,12 +449,25 @@ static inline int php_openssl_setup_crypto(php_stream *stream,
        return 0;
 }
 
+static void enable_server_name_indication(php_stream_context *ctx, php_openssl_netstream_data_t *sslsock)
+{
+       zval **val = NULL;
+
+       if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
+               convert_to_string_ex(val);
+               SSL_set_tlsext_host_name(sslsock->ssl_handle, &val);
+       } else if (sslsock->url_name) {
+               SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->url_name);
+       }
+}
+
 static inline int php_openssl_enable_crypto(php_stream *stream,
                php_openssl_netstream_data_t *sslsock,
                php_stream_xport_crypto_param *cparam
                TSRMLS_DC)
 {
        int n, retry = 1;
+       zval **val = NULL;
 
        if (cparam->inputs.activate && !sslsock->ssl_active) {
                struct timeval  start_time,
@@ -481,9 +476,14 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
                                                has_timeout = 0;
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
-               if (sslsock->is_client && sslsock->sni) {
-                       SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
+
+               if (sslsock->is_client
+                       && (php_stream_context_get_option(stream->context, "ssl", "SNI_enabled", &val) == FAILURE
+                       || zend_is_true(*val TSRMLS_CC))
+               ) {
+                       enable_server_name_indication(stream->context, sslsock);
                }
+
 #endif
 
                if (!sslsock->state_set) {
@@ -920,21 +920,9 @@ static int get_crypto_method(php_stream_context *ctx) {
         return STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
 }
 
-static char * get_sni(php_stream_context *ctx, const char *resourcename, size_t resourcenamelen, int is_persistent TSRMLS_DC) {
+static char * get_url_name(const char *resourcename, size_t resourcenamelen, int is_persistent TSRMLS_DC) {
        php_url *url;
 
-       if (ctx) {
-               zval **val = NULL;
-
-               if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
-                       return NULL;
-               }
-               if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
-                       convert_to_string_ex(val);
-                       return pestrdup(Z_STRVAL_PP(val), is_persistent);
-               }
-       }
-
        if (!resourcename) {
                return NULL;
        }
@@ -946,7 +934,7 @@ static char * get_sni(php_stream_context *ctx, const char *resourcename, size_t
 
        if (url->host) {
                const char * host = url->host;
-               char * sni = NULL;
+               char * url_name = NULL;
                size_t len = strlen(host);
 
                /* skip trailing dots */
@@ -955,11 +943,11 @@ static char * get_sni(php_stream_context *ctx, const char *resourcename, size_t
                }
 
                if (len) {
-                       sni = pestrndup(host, len, is_persistent);
+                       url_name = pestrndup(host, len, is_persistent);
                }
 
                php_url_free(url);
-               return sni;
+               return url_name;
        }
 
        php_url_free(url);
@@ -1001,8 +989,6 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
                return NULL;
        }
 
-       sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
-       
        if (strncmp(proto, "ssl", protolen) == 0) {
                sslsock->enable_on_connect = 1;
 
@@ -1042,7 +1028,9 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, size_t protolen,
                return NULL;
 #endif
        }
-       
+
+       sslsock->url_name = get_url_name(resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
+
        return stream;
 }