From 65d81833bbd1de8c38abc591525ebce56bdbd95c Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Wed, 6 Feb 2019 10:39:55 +0000 Subject: [PATCH 1/2] Use pkg-config for ICU, as the old icu-config has been deprecated --- acinclude.m4 | 96 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 5039f74d9ac..9e08cb200d4 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -2212,45 +2212,79 @@ AC_DEFUN([PHP_SETUP_ICU],[ PHP_ICU_DIR=DEFAULT fi - if test "$PHP_ICU_DIR" = "DEFAULT"; then - dnl Try to find icu-config - AC_PATH_PROG(ICU_CONFIG, icu-config, no, [$PATH:/usr/local/bin]) - else - ICU_CONFIG="$PHP_ICU_DIR/bin/icu-config" + AC_MSG_CHECKING([for location of ICU headers and libraries]) + found_icu=no + + dnl First try to find pkg-config + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) fi - AC_MSG_CHECKING([for location of ICU headers and libraries]) + dnl If pkg-config is found try using it + if test "$PHP_ICU_DIR" = "DEFAULT" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists icu-uc icu-io icu-i18n; then + if $PKG_CONFIG --atleast-version=40 icu-uc; then + found_icu=yes + icu_version_full=`$PKG_CONFIG --modversion icu-uc` + ac_IFS=$IFS + IFS="." + set $icu_version_full + IFS=$ac_IFS + icu_version=`expr [$]1 \* 1000 + [$]2` + AC_MSG_RESULT([found $icu_version_full]) - dnl Trust icu-config to know better what the install prefix is.. - icu_install_prefix=`$ICU_CONFIG --prefix 2> /dev/null` - if test "$?" != "0" || test -z "$icu_install_prefix"; then - AC_MSG_RESULT([not found]) - AC_MSG_ERROR([Unable to detect ICU prefix or $ICU_CONFIG failed. Please verify ICU install prefix and make sure icu-config works.]) - else - AC_MSG_RESULT([$icu_install_prefix]) + ICU_LIBS=`$PKG_CONFIG --libs icu-uc icu-io icu-i18n` + ICU_INCS=`$PKG_CONFIG --cflags-only-I icu-uc icu-io icu-i18n` + ICU_CXXFLAGS="-DU_USING_ICU_NAMESPACE=1" - dnl Check ICU version - AC_MSG_CHECKING([for ICU 4.0 or greater]) - icu_version_full=`$ICU_CONFIG --version` - ac_IFS=$IFS - IFS="." - set $icu_version_full - IFS=$ac_IFS - icu_version=`expr [$]1 \* 1000 + [$]2` - AC_MSG_RESULT([found $icu_version_full]) + AC_MSG_RESULT([found $ICU_VERSION]) - if test "$icu_version" -lt "4000"; then - AC_MSG_ERROR([ICU version 4.0 or later is required]) + PHP_EVAL_LIBLINE($ICU_LIBS, $1) + PHP_EVAL_INCLINE($ICU_INCS) + else + AC_MSG_ERROR([ICU version 4.0 or later required.]) + fi + fi + + dnl If pkg-config fails for some reason, revert to the old method + if test "$found_icu" = "no"; then + if test "$PHP_ICU_DIR" = "DEFAULT"; then + dnl Try to find icu-config + AC_PATH_PROG(ICU_CONFIG, icu-config, no, [$PATH:/usr/local/bin]) + else + ICU_CONFIG="$PHP_ICU_DIR/bin/icu-config" fi - ICU_VERSION=$icu_version - ICU_INCS=`$ICU_CONFIG --cppflags-searchpath` - ICU_LIBS=`$ICU_CONFIG --ldflags --ldflags-icuio` - PHP_EVAL_INCLINE($ICU_INCS) - PHP_EVAL_LIBLINE($ICU_LIBS, $1) + dnl Trust icu-config to know better what the install prefix is.. + icu_install_prefix=`$ICU_CONFIG --prefix 2> /dev/null` + if test "$?" != "0" || test -z "$icu_install_prefix"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Unable to detect ICU prefix or $ICU_CONFIG failed. Please verify ICU install prefix and make sure icu-config works.]) + else + AC_MSG_RESULT([$icu_install_prefix]) - ICU_CXXFLAGS=`$ICU_CONFIG --cxxflags` - ICU_CXXFLAGS="$ICU_CXXFLAGS -DU_USING_ICU_NAMESPACE=1" + dnl Check ICU version + AC_MSG_CHECKING([for ICU 4.0 or greater]) + icu_version_full=`$ICU_CONFIG --version` + ac_IFS=$IFS + IFS="." + set $icu_version_full + IFS=$ac_IFS + icu_version=`expr [$]1 \* 1000 + [$]2` + AC_MSG_RESULT([found $icu_version_full]) + + if test "$icu_version" -lt "4000"; then + AC_MSG_ERROR([ICU version 4.0 or later is required]) + fi + + ICU_VERSION=$icu_version + ICU_INCS=`$ICU_CONFIG --cppflags-searchpath` + ICU_LIBS=`$ICU_CONFIG --ldflags --ldflags-icuio` + PHP_EVAL_INCLINE($ICU_INCS) + PHP_EVAL_LIBLINE($ICU_LIBS, $1) + + ICU_CXXFLAGS=`$ICU_CONFIG --cxxflags` + ICU_CXXFLAGS="$ICU_CXXFLAGS -DU_USING_ICU_NAMESPACE=1" + fi fi ]) From 2eaabf06fc5a62104ecb597830b2852d71b0a111 Mon Sep 17 00:00:00 2001 From: Darek Slusarczyk Date: Mon, 11 Feb 2019 17:16:49 +0100 Subject: [PATCH 2/2] security fix - by default 'local infile' is disabled: - set default for mysqli.allow_local_infile=0 - explicitly disable PDO::MYSQL_ATTR_LOCAL_INFILE in case of lack of driver options - add getAttribute support for PDO::MYSQL_ATTR_LOCAL_INFILE - update existing tests where needed - add new tests [checking default value and setting on] the 'local infile' in ext/mysqli and ext/pdo_mysql --- NEWS | 4 +++ ext/mysqli/mysqli.c | 4 +-- ext/mysqli/tests/061.phpt | 2 ++ ext/mysqli/tests/bug36745.phpt | 2 ++ ext/mysqli/tests/bug53503.phpt | 2 ++ ext/mysqli/tests/bug68077.phpt | 3 ++ ext/mysqli/tests/mysqli_constants.phpt | 2 ++ ext/mysqli/tests/mysqli_get_client_stats.phpt | 1 + ext/mysqli/tests/mysqli_info.phpt | 2 ++ .../mysqli_local_infile_default_off.phpt | 26 +++++++++++++++++ .../tests/mysqli_local_infile_set_on.phpt | 28 +++++++++++++++++++ ext/mysqli/tests/mysqli_real_connect.phpt | 2 ++ .../tests/mysqli_real_connect_pconn.phpt | 1 + ext/mysqlnd/mysqlnd_connection.c | 3 +- ext/pdo_mysql/mysql_driver.c | 15 ++++++++++ .../tests/pdo_mysql___construct_options.phpt | 5 ++-- .../pdo_mysql_local_infile_default_off.phpt | 26 +++++++++++++++++ .../tests/pdo_mysql_local_infile_set_on.phpt | 26 +++++++++++++++++ 18 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 ext/mysqli/tests/mysqli_local_infile_default_off.phpt create mode 100644 ext/mysqli/tests/mysqli_local_infile_set_on.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt diff --git a/NEWS b/NEWS index 587ed12ef11..abee4e78e1e 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2019, PHP 7.1.27 +- MySQL + . Disabled LOCAL INFILE by default, can be enabled using php.ini directive + mysqli.allow_local_infile for mysqli, or PDO::MYSQL_ATTR_LOCAL_INFILE + attribute for pdo_mysql. (Darek Slusarczyk) 10 Jan 2019, PHP 7.1.26 diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 8f3446952e3..fff095f081f 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -524,7 +524,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("mysqli.default_socket", NULL, PHP_INI_ALL, OnUpdateStringUnempty, default_socket, zend_mysqli_globals, mysqli_globals) #endif STD_PHP_INI_BOOLEAN("mysqli.reconnect", "0", PHP_INI_SYSTEM, OnUpdateLong, reconnect, zend_mysqli_globals, mysqli_globals) - STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysqli_globals, mysqli_globals) + STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile", "0", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysqli_globals, mysqli_globals) PHP_INI_END() /* }}} */ @@ -549,7 +549,7 @@ static PHP_GINIT_FUNCTION(mysqli) mysqli_globals->reconnect = 0; mysqli_globals->report_mode = 0; mysqli_globals->report_ht = 0; - mysqli_globals->allow_local_infile = 1; + mysqli_globals->allow_local_infile = 0; #ifdef HAVE_EMBEDDED_MYSQLI mysqli_globals->embedded = 1; #else diff --git a/ext/mysqli/tests/061.phpt b/ext/mysqli/tests/061.phpt index 5817d8230df..be2028b66ea 100644 --- a/ext/mysqli/tests/061.phpt +++ b/ext/mysqli/tests/061.phpt @@ -17,6 +17,8 @@ if ($msg = check_local_infile_support($link, $engine)) mysqli_close($link); ?> +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- --INI-- +mysqli.allow_local_infile=1 +mysqli.allow_persistent=1 +mysqli.max_persistent=1 open_basedir= --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--FILE-- + +--EXPECTF-- +server: %s +connector: 0 +done! diff --git a/ext/mysqli/tests/mysqli_local_infile_set_on.phpt b/ext/mysqli/tests/mysqli_local_infile_set_on.phpt new file mode 100644 index 00000000000..172d6dcb9b3 --- /dev/null +++ b/ext/mysqli/tests/mysqli_local_infile_set_on.phpt @@ -0,0 +1,28 @@ +--TEST-- +enable local infile +--SKIPIF-- + +--INI-- +mysqli.allow_local_infile=1 +--FILE-- + +--EXPECTF-- +server: %s +connector: 1 +done! diff --git a/ext/mysqli/tests/mysqli_real_connect.phpt b/ext/mysqli/tests/mysqli_real_connect.phpt index 5477ea1745d..be0a10b0bf8 100644 --- a/ext/mysqli/tests/mysqli_real_connect.phpt +++ b/ext/mysqli/tests/mysqli_real_connect.phpt @@ -6,6 +6,8 @@ require_once('skipif.inc'); require_once('skipifemb.inc'); require_once('skipifconnectfailure.inc'); ?> +--INI-- +mysqli.allow_local_infile=1 --FILE-- --INI-- +mysqli.allow_local_infile=1 mysqli.allow_persistent=1 mysqli.max_persistent=10 --FILE-- diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index 654673f500c..ee63e07efaf 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -489,7 +489,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * MYSQLND_VIO * vio = conn->vio; DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags"); - /* we allow load data local infile by default */ + /* allow CLIENT_LOCAL_FILES capability, although extensions basing on mysqlnd + shouldn't allow 'load data local infile' by default due to security issues */ mysql_flags |= MYSQLND_CAPABILITIES; mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */ diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index 85b35b5b705..101510e3856 100644 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -467,6 +467,12 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_ case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE: ZVAL_LONG(return_value, H->max_buffer_size); break; +#else + case PDO_MYSQL_ATTR_LOCAL_INFILE: + ZVAL_BOOL( + return_value, + (H->server->data->options->flags & CLIENT_LOCAL_FILES) == CLIENT_LOCAL_FILES); + break; #endif default: @@ -746,6 +752,15 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) CLIENT_SSL_DONT_VERIFY_SERVER_CERT; } } +#endif + } else { +#if defined(MYSQL_OPT_LOCAL_INFILE) || defined(PDO_USE_MYSQLND) + // in case there are no driver options disable 'local infile' explicitly + zend_long local_infile = 0; + if (mysql_options(H->server, MYSQL_OPT_LOCAL_INFILE, (const char *)&local_infile)) { + pdo_mysql_error(dbh); + goto cleanup; + } #endif } diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt index 62051d7ae2c..08cd8eb9420 100644 --- a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt @@ -19,7 +19,7 @@ MySQLPDOTest::skip(); try { $db = new PDO($dsn, $user, $pass, array($option => $value)); if (!is_object($db) || ($value !== ($tmp = @$db->getAttribute($option)))) - printf("[%03d] Execting '%s'/%s got '%s'/%s' for options '%s'\n", + printf("[%03d] Expecting '%s'/%s got '%s'/%s' for options '%s'\n", $offset, $value, gettype($value), $tmp, gettype($tmp), @@ -172,8 +172,7 @@ MySQLPDOTest::skip(); [016] PDO::MYSQL_ATTR_DIRECT_QUERY should be on [017] PDO::ATTR_EMULATE_PREPARES should be off [018] PDO::MYSQL_ATTR_DIRECT_QUERY should be off -[021] Execting '1'/boolean got ''/boolean' for options 'PDO::MYSQL_ATTR_LOCAL_INFILE' -[023] Execting 'SET @a=1'/string got ''/boolean' for options 'PDO::MYSQL_ATTR_INIT_COMMAND' +[023] Expecting 'SET @a=1'/string got ''/boolean' for options 'PDO::MYSQL_ATTR_INIT_COMMAND' [024] SQLSTATE[42000] [1065] Query was empty [025] SQLSTATE[42S02] [1146] Table '%s.nonexistent' doesn't exist done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt b/ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt new file mode 100644 index 00000000000..5a0b5f67e05 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt @@ -0,0 +1,26 @@ +--TEST-- +ensure default for local infile is off +--SKIPIF-- + +--FILE-- +getAttribute(PDO::MYSQL_ATTR_LOCAL_INFILE)), "\n"; +echo "done!\n"; +?> +--EXPECTF-- +false +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt b/ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt new file mode 100644 index 00000000000..4394bc0576a --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt @@ -0,0 +1,26 @@ +--TEST-- +enable local infile +--SKIPIF-- + +--FILE-- + true)); +echo var_export($db->getAttribute(PDO::MYSQL_ATTR_LOCAL_INFILE)), "\n"; +echo "done!\n"; +?> +--EXPECTF-- +true +done!