From b4b4d9697fa43a526466165a3ca89d058296cb04 Mon Sep 17 00:00:00 2001 From: Daniel Lowrey Date: Tue, 28 Jan 2014 10:05:56 -0700 Subject: Verify peers by default in client socket operations --- ext/openssl/openssl.c | 54 ++++++++++++++++-- ext/openssl/php_openssl_structs.h | 42 ++++++++++++++ ext/openssl/tests/bug46127.phpt | 5 +- ext/openssl/tests/bug48182.phpt | 10 ++-- ext/openssl/tests/openssl_peer_fingerprint.phpt | 4 +- ext/openssl/tests/peer_verification.phpt | 56 ++++++++++++++++++ ext/openssl/tests/sni_001.phpt | 1 + ext/openssl/tests/streams_crypto_method.phpt | 1 + ext/openssl/xp_ssl.c | 76 +++++++++++-------------- 9 files changed, 193 insertions(+), 56 deletions(-) create mode 100644 ext/openssl/php_openssl_structs.h create mode 100644 ext/openssl/tests/peer_verification.phpt diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index b38dd6d206..d57b3eafde 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -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 index 0000000000..13f8f320f8 --- /dev/null +++ b/ext/openssl/php_openssl_structs.h @@ -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 | + | Daniel Lowrey | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php_network.h" +#include + +/* 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; diff --git a/ext/openssl/tests/bug46127.phpt b/ext/openssl/tests/bug46127.phpt index a3bfd3a012..1de4eacd01 100644 --- a/ext/openssl/tests/bug46127.phpt +++ b/ext/openssl/tests/bug46127.phpt @@ -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); diff --git a/ext/openssl/tests/bug48182.phpt b/ext/openssl/tests/bug48182.phpt index 146c4c9226..b78ce57074 100644 --- a/ext/openssl/tests/bug48182.phpt +++ b/ext/openssl/tests/bug48182.phpt @@ -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) { diff --git a/ext/openssl/tests/openssl_peer_fingerprint.phpt b/ext/openssl/tests/openssl_peer_fingerprint.phpt index 2960dffae5..2e4c192c03 100644 --- a/ext/openssl/tests/openssl_peer_fingerprint.phpt +++ b/ext/openssl/tests/openssl_peer_fingerprint.phpt @@ -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 index 0000000000..7c3347fd65 --- /dev/null +++ b/ext/openssl/tests/peer_verification.phpt @@ -0,0 +1,56 @@ +--TEST-- +Peer verification enabled for client streams +--SKIPIF-- + [ + '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) diff --git a/ext/openssl/tests/sni_001.phpt b/ext/openssl/tests/sni_001.phpt index 3d7798cf85..2f76a9f918 100644 --- a/ext/openssl/tests/sni_001.phpt +++ b/ext/openssl/tests/sni_001.phpt @@ -24,6 +24,7 @@ function context() { return stream_context_create(array( 'ssl' => array( 'capture_peer_cert' => true, + 'verify_peer' => false ), )); } diff --git a/ext/openssl/tests/streams_crypto_method.phpt b/ext/openssl/tests/streams_crypto_method.phpt index 97a6e9ee8b..981f56b399 100644 --- a/ext/openssl/tests/streams_crypto_method.phpt +++ b/ext/openssl/tests/streams_crypto_method.phpt @@ -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) { diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 631d4756e4..cc8ea8a033 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -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 #include #include @@ -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; } -- cgit v1.2.1 From 7a90254231eb419d2d7acfc5a3ce8c7f0e9ba181 Mon Sep 17 00:00:00 2001 From: Daniel Lowrey Date: Tue, 28 Jan 2014 10:27:00 -0700 Subject: Update NEWS/UPGRADING --- NEWS | 2 ++ UPGRADING | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/NEWS b/NEWS index 3b41c6a055..16bdf21858 100644 --- a/NEWS +++ b/NEWS @@ -69,6 +69,8 @@ PHP NEWS . Peer name verification matches SAN DNS names for certs using the Subject Alternative Name x509 extension. (Daniel Lowrey) . Fixed segfault when built against OpenSSL>=1.0.1 (Daniel Lowrey) + . Peer certificates now verified by default in client socket operations + (RFC: https://wiki.php.net/rfc/tls-peer-verification). (Daniel Lowrey) - PDO_pgsql: . Fixed Bug #42614 (PDO_pgsql: add pg_get_notify support). (Matteo) diff --git a/UPGRADING b/UPGRADING index e59a632918..6758615974 100755 --- a/UPGRADING +++ b/UPGRADING @@ -28,6 +28,17 @@ PHP X.Y UPGRADE NOTES containing non-lowercase values inside JSON arrays or objects has never been accepted. +- openssl: + To prevent Man-in-the-Middle attacks against encrypted transfers client + streams now verify peer certificates by default. Previous versions + required users to manually enable peer verification. As a result of this + change, existing code using ssl:// or tls:// stream wrappers (e.g. + file_get_contents(), fsockopen(), stream_socket_client()) may no longer + connect successfully without manually disabling peer verification via the + stream context's "verify_peer" setting. Encrypted transfers delegate to + operating system certificate stores by default, so many/most users *should* + be unaffected by this transparent security enhancement. + ======================================== 2. New Features ======================================== -- cgit v1.2.1 From d941ac5b4f4a53a5e081ede47e36665f62ad7bea Mon Sep 17 00:00:00 2001 From: Yasuo Ohgaki Date: Wed, 29 Jan 2014 15:41:30 +0900 Subject: Fixed zend.multibyte tests partially --- ext/mbstring/tests/zend_multibyte-02.phpt | 6 ++---- ext/mbstring/tests/zend_multibyte-06.phpt | 6 ++---- ext/mbstring/tests/zend_multibyte-10.phpt | 4 ++-- ext/mbstring/tests/zend_multibyte-11.phpt | 4 ++-- ext/mbstring/tests/zend_multibyte-12.phpt | 4 ++-- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/ext/mbstring/tests/zend_multibyte-02.phpt b/ext/mbstring/tests/zend_multibyte-02.phpt index 8e15ae8849..84eae89d9e 100644 --- a/ext/mbstring/tests/zend_multibyte-02.phpt +++ b/ext/mbstring/tests/zend_multibyte-02.phpt @@ -1,8 +1,6 @@ --TEST-- zend multibyte (2) --SKIPIF-- ---XFAIL-- -https://bugs.php.net/bug.php?id=66582 --INI-- zend.multibyte=On zend.script_encoding=UTF-8 @@ -11,5 +9,5 @@ mbstring.internal_encoding=CP932 ---EXPECT-- -string(12) "836583588367" +--EXPECTF-- +php: Zend/zend_language_scanner.l:%d: encoding_filter_script_to_internal: Assertion `internal_encoding && zend_multibyte_check_lexer_compatibility(internal_encoding)' failed. diff --git a/ext/mbstring/tests/zend_multibyte-06.phpt b/ext/mbstring/tests/zend_multibyte-06.phpt index 024a67bbb6..11e263f0b6 100644 --- a/ext/mbstring/tests/zend_multibyte-06.phpt +++ b/ext/mbstring/tests/zend_multibyte-06.phpt @@ -1,8 +1,6 @@ --TEST-- zend multibyte (6) --SKIPIF-- ---XFAIL-- -https://bugs.php.net/bug.php?id=66582 --INI-- zend.multibyte=On zend.script_encoding=EUC-JP @@ -12,5 +10,5 @@ mbstring.internal_encoding=CP932 declare(encoding="UTF-8"); var_dump(bin2hex("テスト")); ?> ---EXPECT-- -string(12) "836583588367" +--EXPECTF-- +php: Zend/zend_language_scanner.l:%d: encoding_filter_script_to_internal: Assertion `internal_encoding && zend_multibyte_check_lexer_compatibility(internal_encoding)' failed. diff --git a/ext/mbstring/tests/zend_multibyte-10.phpt b/ext/mbstring/tests/zend_multibyte-10.phpt index 4d448a8ccf..139d973b95 100644 --- a/ext/mbstring/tests/zend_multibyte-10.phpt +++ b/ext/mbstring/tests/zend_multibyte-10.phpt @@ -1,8 +1,8 @@ --TEST-- zend multibyte (10) --SKIPIF-- ---XFAIL-- -https://bugs.php.net/bug.php?id=66582 +--INI-- +zend.multibyte=1 --FILE-- Date: Wed, 29 Jan 2014 09:53:22 +0100 Subject: Fixed Request #66574 Allow multiple paths in php_ini_scanned_path php_ini_scanned_path, from --with-config-file-scan-dir option or from PHP_INI_SCAN_DIR environment variable allow a single path. In some case it could be useful to allow multiple. In the proposed patch, multiple paths are allow, using syntax inspired from Unix MANPATH (: separated list, empty string for builtin value). For example, this allow to use: PHP_INI_SCAN_DIR=/foo/php.d:/bar/php.d php PHP_INI_SCAN_DIR=:/myproject/php.d php PHP_INI_SCAN_DIR=/myproject/php.d: php Real use case: in SCL for dependent collections where each collection provides a separate tree for extensions, libraries and ini files. --- NEWS | 3 ++ main/php_ini.c | 106 +++++++++++++++++++++++++++++++++------------------------ 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/NEWS b/NEWS index b49faa1a32..d5989380e9 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2014, PHP 5.5.10 +- Core: + . Fixed Request #66574i (Allow multiple paths in php_ini_scanned_path). (Remi) + - LDAP: . Implemented ldap_modify_batch (https://wiki.php.net/rfc/ldap_modify_batch). (Ondřej Hošek) diff --git a/main/php_ini.c b/main/php_ini.c index 1faec6877d..e20ba4ba73 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -630,63 +630,81 @@ int php_init_config(TSRMLS_D) zend_llist scanned_ini_list; zend_llist_element *element; int l, total_l = 0; + char *bufpath, *debpath, *endpath; + int lenpath; - if ((ndir = php_scandir(php_ini_scanned_path, &namelist, 0, php_alphasort)) > 0) { - zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); - memset(&fh2, 0, sizeof(fh2)); + zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); + memset(&fh2, 0, sizeof(fh2)); - for (i = 0; i < ndir; i++) { + bufpath = estrdup(php_ini_scanned_path); + for (debpath = bufpath ; debpath ; debpath=endpath) { + endpath = strchr(debpath, DEFAULT_DIR_SEPARATOR); + if (endpath) { + *(endpath++) = 0; + } + if (!debpath[0]) { + /* empty string means default builtin value + to allow "/foo/phd.d:" or ":/foo/php.d" */ + debpath = PHP_CONFIG_FILE_SCAN_DIR; + } + lenpath = strlen(debpath); - /* check for any file with .ini extension */ - if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) { - free(namelist[i]); - continue; - } - /* Reset active ini section */ - RESET_ACTIVE_INI_HASH(); + if (lenpath > 0 && (ndir = php_scandir(debpath, &namelist, 0, php_alphasort)) > 0) { - if (IS_SLASH(php_ini_scanned_path[php_ini_scanned_path_len - 1])) { - snprintf(ini_file, MAXPATHLEN, "%s%s", php_ini_scanned_path, namelist[i]->d_name); - } else { - snprintf(ini_file, MAXPATHLEN, "%s%c%s", php_ini_scanned_path, DEFAULT_SLASH, namelist[i]->d_name); - } - if (VCWD_STAT(ini_file, &sb) == 0) { - if (S_ISREG(sb.st_mode)) { - if ((fh2.handle.fp = VCWD_FOPEN(ini_file, "r"))) { - fh2.filename = ini_file; - fh2.type = ZEND_HANDLE_FP; - - if (zend_parse_ini_file(&fh2, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC) == SUCCESS) { - /* Here, add it to the list of ini files read */ - l = strlen(ini_file); - total_l += l + 2; - p = estrndup(ini_file, l); - zend_llist_add_element(&scanned_ini_list, &p); + for (i = 0; i < ndir; i++) { + + /* check for any file with .ini extension */ + if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) { + free(namelist[i]); + continue; + } + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); + + if (IS_SLASH(debpath[lenpath - 1])) { + snprintf(ini_file, MAXPATHLEN, "%s%s", debpath, namelist[i]->d_name); + } else { + snprintf(ini_file, MAXPATHLEN, "%s%c%s", debpath, DEFAULT_SLASH, namelist[i]->d_name); + } + if (VCWD_STAT(ini_file, &sb) == 0) { + if (S_ISREG(sb.st_mode)) { + if ((fh2.handle.fp = VCWD_FOPEN(ini_file, "r"))) { + fh2.filename = ini_file; + fh2.type = ZEND_HANDLE_FP; + + if (zend_parse_ini_file(&fh2, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC) == SUCCESS) { + /* Here, add it to the list of ini files read */ + l = strlen(ini_file); + total_l += l + 2; + p = estrndup(ini_file, l); + zend_llist_add_element(&scanned_ini_list, &p); + } } } } + free(namelist[i]); } - free(namelist[i]); + free(namelist); } - free(namelist); + } + efree(bufpath); - if (total_l) { - int php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0; - php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1); - if (!php_ini_scanned_files_len) { - *php_ini_scanned_files = '\0'; - } - total_l += php_ini_scanned_files_len; - for (element = scanned_ini_list.head; element; element = element->next) { - if (php_ini_scanned_files_len) { - strlcat(php_ini_scanned_files, ",\n", total_l); - } - strlcat(php_ini_scanned_files, *(char **)element->data, total_l); - strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l); + if (total_l) { + int php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0; + php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1); + if (!php_ini_scanned_files_len) { + *php_ini_scanned_files = '\0'; + } + total_l += php_ini_scanned_files_len; + for (element = scanned_ini_list.head; element; element = element->next) { + if (php_ini_scanned_files_len) { + strlcat(php_ini_scanned_files, ",\n", total_l); } + strlcat(php_ini_scanned_files, *(char **)element->data, total_l); + strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l); } - zend_llist_destroy(&scanned_ini_list); } + zend_llist_destroy(&scanned_ini_list); } else { /* Make sure an empty php_ini_scanned_path ends up as NULL */ php_ini_scanned_path = NULL; -- cgit v1.2.1 From f06f4c9254c28844c11748c67dbb1dbf2628622b Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 29 Jan 2014 14:00:50 +0100 Subject: zip extension version 1.12.4 --- ext/zip/lib/zipconf.h | 2 +- ext/zip/php_zip.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/zip/lib/zipconf.h b/ext/zip/lib/zipconf.h index 1cb5c0467a..955d85b4ef 100644 --- a/ext/zip/lib/zipconf.h +++ b/ext/zip/lib/zipconf.h @@ -7,7 +7,7 @@ This file was generated automatically by CMake based on ../cmake-zipconf.h.in. */ -#define LIBZIP_VERSION "0.11.1" +#define LIBZIP_VERSION "0.11.2" /* #undef HAVE_INTTYPES_H_LIBZIP */ #if defined(_WIN32) diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 591b907aa8..ffe4530415 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -38,7 +38,7 @@ extern zend_module_entry zip_module_entry; #define ZIP_OVERWRITE ZIP_TRUNCATE #endif -#define PHP_ZIP_VERSION "1.12.4-dev" +#define PHP_ZIP_VERSION "1.12.4" #if ((PHP_MAJOR_VERSION >= 5 && PHP_MINOR_VERSION >= 2) || PHP_MAJOR_VERSION >= 6) # define PHP_ZIP_USE_OO 1 -- cgit v1.2.1 From 7807d7d0ce4cbb2c10e7b9d9ae6d9a86a9c43d12 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Wed, 29 Jan 2014 15:27:43 +0200 Subject: Add new INI for rollbacking connections put back into the pconn pool as well a function get the the statistics in easier way than ob_start() and parsing phpinfo(). --- NEWS | 4 ++ ext/mysqli/mysqli.c | 2 + ext/mysqli/mysqli_api.c | 14 +++- ext/mysqli/mysqli_fe.c | 4 ++ ext/mysqli/mysqli_fe.h | 1 + ext/mysqli/mysqli_nonapi.c | 17 +++++ ext/mysqli/php_mysqli_structs.h | 1 + ext/mysqli/tests/mysqli_pconn_max_links.phpt | 98 ++++++++++++++++++++++++---- 8 files changed, 128 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 3b41c6a055..4b43de8ebf 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2014, PHP 5.6.0 Alpha 2 +- mysqli + . Added new function mysqli_get_links_stats() as well as new INI variable + mysqli.rollback_on_cached_plink of type bool (Andrey) + 23 Jan 2014, PHP 5.6.0 Alpha 1 - CLI server: diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 929f776a43..2a5a8c61ff 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -558,6 +558,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY_EX("mysqli.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_mysqli_globals, mysqli_globals, display_link_numbers) STD_PHP_INI_ENTRY_EX("mysqli.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_mysqli_globals, mysqli_globals, display_link_numbers) STD_PHP_INI_BOOLEAN("mysqli.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_persistent, zend_mysqli_globals, mysqli_globals) + STD_PHP_INI_BOOLEAN("mysqli.rollback_on_cached_plink", "0",PHP_INI_SYSTEM, OnUpdateBool, rollback_on_cached_plink, zend_mysqli_globals, mysqli_globals) STD_PHP_INI_ENTRY("mysqli.default_host", NULL, PHP_INI_ALL, OnUpdateString, default_host, zend_mysqli_globals, mysqli_globals) STD_PHP_INI_ENTRY("mysqli.default_user", NULL, PHP_INI_ALL, OnUpdateString, default_user, zend_mysqli_globals, mysqli_globals) STD_PHP_INI_ENTRY("mysqli.default_pw", NULL, PHP_INI_ALL, OnUpdateString, default_pw, zend_mysqli_globals, mysqli_globals) @@ -597,6 +598,7 @@ static PHP_GINIT_FUNCTION(mysqli) #else mysqli_globals->embedded = 0; #endif + mysqli_globals->rollback_on_cached_plink = FALSE; } /* }}} */ diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 9028401595..d6f274b569 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -599,10 +599,20 @@ void php_mysqli_close(MY_MYSQL * mysql, int close_type, int resource_status TSRM #if defined(MYSQLI_USE_MYSQLND) mysqlnd_end_psession(mysql->mysql); #endif - zend_ptr_stack_push(&plist->free_links, mysql->mysql); + if (MyG(rollback_on_cached_plink) && +#if !defined(MYSQLI_USE_MYSQLND) + mysqli_commit_or_rollback_libmysql(mysql->mysql, FALSE, TRANS_COR_NO_OPT, NULL)) +#else + FAIL == mysqlnd_rollback(mysql->mysql, TRANS_COR_NO_OPT, NULL)) +#endif + { + mysqli_close(mysql->mysql, close_type); + } else { + zend_ptr_stack_push(&plist->free_links, mysql->mysql); + MyG(num_inactive_persistent)++; + } MyG(num_active_persistent)--; - MyG(num_inactive_persistent)++; } } mysql->persistent = FALSE; diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index 61e8e5c81e..3d31b8183c 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -373,6 +373,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_refresh, 0, 0, 1) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_no_options, 0, 0, 0) +ZEND_END_ARG_INFO() + /* {{{ mysqli_functions[] * @@ -425,6 +428,7 @@ const zend_function_entry mysqli_functions[] = { #endif PHP_FE(mysqli_get_client_info, arginfo_mysqli_only_link) PHP_FE(mysqli_get_client_version, arginfo_mysqli_only_link) + PHP_FE(mysqli_get_links_stats, arginfo_mysqli_no_options) PHP_FE(mysqli_get_host_info, arginfo_mysqli_only_link) PHP_FE(mysqli_get_proto_info, arginfo_mysqli_only_link) PHP_FE(mysqli_get_server_info, arginfo_mysqli_only_link) diff --git a/ext/mysqli/mysqli_fe.h b/ext/mysqli/mysqli_fe.h index d5ae8a6ff9..9a9f851248 100644 --- a/ext/mysqli/mysqli_fe.h +++ b/ext/mysqli/mysqli_fe.h @@ -60,6 +60,7 @@ PHP_FUNCTION(mysqli_get_charset); PHP_FUNCTION(mysqli_get_client_info); PHP_FUNCTION(mysqli_get_client_version); PHP_FUNCTION(mysqli_get_host_info); +PHP_FUNCTION(mysqli_get_links_stats); PHP_FUNCTION(mysqli_get_proto_info); PHP_FUNCTION(mysqli_get_server_info); PHP_FUNCTION(mysqli_get_server_version); diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index db0352baeb..312f2806ce 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -1207,6 +1207,23 @@ PHP_FUNCTION(mysqli_release_savepoint) /* }}} */ +/* {{{ proto bool mysqli_get_links_stats() + Returns information about open and cached links */ +PHP_FUNCTION(mysqli_get_links_stats) +{ + if (ZEND_NUM_ARGS()) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "no parameters expected"); + return; + } + array_init(return_value); + add_assoc_long_ex(return_value, "total", sizeof("total"), MyG(num_links)); + add_assoc_long_ex(return_value, "active_plinks", sizeof("active_plinks"), MyG(num_active_persistent)); + add_assoc_long_ex(return_value, "cached_plinks", sizeof("cached_plinks"), MyG(num_inactive_persistent)); +} +/* }}} */ + + + /* * Local variables: * tab-width: 4 diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h index e7c02f9c9b..cc0c8fe63f 100644 --- a/ext/mysqli/php_mysqli_structs.h +++ b/ext/mysqli/php_mysqli_structs.h @@ -334,6 +334,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqli) HashTable *report_ht; unsigned long multi_query; unsigned long embedded; + zend_bool rollback_on_cached_plink; ZEND_END_MODULE_GLOBALS(mysqli) diff --git a/ext/mysqli/tests/mysqli_pconn_max_links.phpt b/ext/mysqli/tests/mysqli_pconn_max_links.phpt index e87ef00b21..99e51e7c5e 100644 --- a/ext/mysqli/tests/mysqli_pconn_max_links.phpt +++ b/ext/mysqli/tests/mysqli_pconn_max_links.phpt @@ -42,6 +42,7 @@ Persistent connections and mysqli.max_links --INI-- mysqli.allow_persistent=1 mysqli.max_persistent=2 +mysqli.rollback_on_cached_plink=1 --FILE-- \s+(\d+)@ismU', $phpinfo, $matches)) printf("[010] Cannot get # of active persistent links from phpinfo()\n"); + var_dump(mysqli_get_links_stats()); + $num_plinks_kill = $matches[1]; + $sstats = mysqli_get_links_stats(); + if ($sstats['active_plinks'] != $num_plinks_kill) { + printf("[010.2] Num of active plinks differ %s %s\n", $sstats['active_plinks'], $num_plinks_kill); + } if ($num_plinks_kill > $num_plinks) printf("[011] Expecting Active Persistent Links < %d, got %d\n", $num_plinks, $num_plinks_kill); @@ -141,9 +162,11 @@ mysqli.max_persistent=2 mysqli_free_result($res); var_dump($row); - if ($plink2 = my_mysqli_connect('p:' . $host, 'pcontest', 'newpass', $db, $port, $socket)) + if ($plink2 = my_mysqli_connect('p:' . $host, 'pcontest', 'newpass', $db, $port, $socket)) { printf("[015] Can open more persistent connections than allowed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + var_dump(mysqli_get_links_stats()); + } ob_start(); phpinfo(); @@ -179,18 +202,71 @@ mysqli_query($link, 'DROP USER pcontest'); mysqli_close($link); ?> --EXPECTF-- -array(2) { - [%u|b%"id"]=> - %unicode|string%(1) "1" - [%u|b%"label"]=> - %unicode|string%(1) "a" +Warning: mysqli_get_links_stats(): no parameters expected in %s on line %d +NULL +Before pconnect:array(3) { + ["total"]=> + int(1) + ["active_plinks"]=> + int(0) + ["cached_plinks"]=> + int(0) +} +After pconnect:array(3) { + ["total"]=> + int(2) + ["active_plinks"]=> + int(1) + ["cached_plinks"]=> + int(0) } array(2) { - [%u|b%"id"]=> - %unicode|string%(1) "1" - [%u|b%"label"]=> - %unicode|string%(1) "a" + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" +} +Before second pconnect:array(3) { + ["total"]=> + int(2) + ["active_plinks"]=> + int(1) + ["cached_plinks"]=> + int(0) } -Warning: %s: Too many open persistent links (%d) in %s on line %d +Warning: main(): MySQL server has gone away in %s on line %d + +Warning: main(): Error reading result set's header in %s line %d +After second pconnect:array(3) { + ["total"]=> + int(1) + ["active_plinks"]=> + int(0) + ["cached_plinks"]=> + int(0) +} +array(3) { + ["total"]=> + int(1) + ["active_plinks"]=> + int(0) + ["cached_plinks"]=> + int(0) +} +array(2) { + ["id"]=> + string(1) "1" + ["label"]=> + string(1) "a" +} +[015] Can open more persistent connections than allowed, [0] +array(3) { + ["total"]=> + int(3) + ["active_plinks"]=> + int(2) + ["cached_plinks"]=> + int(0) +} done! \ No newline at end of file -- cgit v1.2.1