diff --git a/ChangeLog b/ChangeLog index cf3ab5a81b..644ed47e40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ phpMyAdmin - ChangeLog ====================== -4.6.6 (not yet released) +4.6.6 (2017-01-23) - issue #12759 Fix Notice regarding 'Undefined index: old_usergroup' - issue #12760 Fix Notice regarding 'Undefined index: users' - issue #12762 Fixed parsing of SQL with BINARY function @@ -39,6 +39,14 @@ phpMyAdmin - ChangeLog - issue #12881 Fix database search with newer php-gettext - issue #12894 Fix linter error on unterminated variable name - issue #12732 Fixed filtering for active processes +- issue [security] Multiple vulnerabilities in setup script, see PMASA-2016-44. +- issue [security] Open redirect, see PMASA-2017-1. +- issue [security] php-gettext code execution, see PMASA-2017-2. +- issue [security] DOS vulnerabiltiy in table editing, see PMASA-2017-3. +- issue [security] CSS injection in themes, see PMASA-2017-4. +- issue [security] Cookie attribute injection attack, see PMASA-2017-5. +- issue [security] SSRF in replication, see PMASA-2017-6. +- issue [security] DOS in replication status, see PMASA-2017-7. 4.6.5.2 (2016-12-05) - issue #12765 Fixed SQL export with newlines diff --git a/README b/README index 0f8671254a..d547484ee6 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ phpMyAdmin - Readme =================== -Version 4.6.6-dev +Version 4.6.6 A web interface for MySQL and MariaDB. diff --git a/doc/conf.py b/doc/conf.py index 1b34b9babc..daa0c446e2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -51,7 +51,7 @@ copyright = u'2012 - 2016, The phpMyAdmin devel team' # built documents. # # The short X.Y version. -version = '4.6.6-dev' +version = '4.6.6' # The full version, including alpha/beta/rc tags. release = version diff --git a/doc/setup.rst b/doc/setup.rst index 5f8e146a71..b6be504c51 100644 --- a/doc/setup.rst +++ b/doc/setup.rst @@ -305,60 +305,13 @@ Using Setup script ------------------ Instead of manually editing :file:`config.inc.php`, you can use phpMyAdmin's -setup feature. First you must manually create a folder ``config`` -in the phpMyAdmin directory. This is a security measure. On a -Linux/Unix system you can use the following commands: +setup feature. The file can be generated using the setup and you can download it +for upload to the server. -.. code-block:: sh - - - cd phpMyAdmin - mkdir config # create directory for saving - chmod o+rw config # give it world writable permissions - -And to edit an existing configuration, copy it over first: - -.. code-block:: sh - - - cp config.inc.php config/ # copy current configuration for editing - chmod o+w config/config.inc.php # give it world writable permissions - -.. note:: - - Debian and Ubuntu have simplified this setup and all you need to do is to - execute :program:`/usr/sbin/pma-configure`. - -On other platforms, simply create the folder and ensure that your web -server has read and write access to it. :ref:`faq1_26` can help with -this. - -Next, open your browser and visit the location where you installed phpMyAdmin, with the ``/setup`` suffix. If you have an existing configuration, -use the ``Load`` button to bring its content inside the setup panel. -Note that **changes are not saved to disk until you explicitly choose ``Save``** -from the *Configuration* area of the screen. Normally the script saves the new -:file:`config.inc.php` to the ``config/`` directory, but if the webserver does -not have the proper permissions you may see the error "Cannot load or -save configuration." Ensure that the ``config/`` directory exists and -has the proper permissions - or use the ``Download`` link to save the -config file locally and upload it (via FTP or some similar means) to the -proper location. - -Once the file has been saved, it must be moved out of the ``config/`` -directory and the permissions must be reset, again as a security -measure: - -.. code-block:: sh - - - mv config/config.inc.php . # move file to current directory - chmod o-rw config.inc.php # remove world read and write permissions - rm -rf config # remove not needed directory - -.. note:: - - Debian and Ubuntu have simplified this setup and all you need to do is to - execute :program:`/usr/sbin/pma-secure`. +Next, open your browser and visit the location where you installed phpMyAdmin, +with the ``/setup`` suffix. The changes are not saved to the server, you need to +use the :guilabel:`Download` button to save them to your computer and then upload +to the server. Now the file is ready to be used. You can choose to review or edit the file with your favorite editor, if you prefer to set some advanced diff --git a/libraries/Config.php b/libraries/Config.php index f1191ce01c..254d9533d4 100644 --- a/libraries/Config.php +++ b/libraries/Config.php @@ -101,7 +101,7 @@ class Config */ public function checkSystem() { - $this->set('PMA_VERSION', '4.6.6-dev'); + $this->set('PMA_VERSION', '4.6.6'); /** * @deprecated */ diff --git a/libraries/Theme.php b/libraries/Theme.php index 0762cce155..18685c010a 100644 --- a/libraries/Theme.php +++ b/libraries/Theme.php @@ -431,9 +431,6 @@ class Theme if (!is_null($fs)) { return $fs; } - if (isset($_COOKIE['pma_fontsize'])) { - return htmlspecialchars($_COOKIE['pma_fontsize']); - } return '82%'; } diff --git a/libraries/config/ConfigFile.php b/libraries/config/ConfigFile.php index b74c1cc838..a28888ea38 100644 --- a/libraries/config/ConfigFile.php +++ b/libraries/config/ConfigFile.php @@ -482,16 +482,6 @@ class ConfigFile } } - /** - * Returns config file path, relative to phpMyAdmin's root path - * - * @return string - */ - public function getFilePath() - { - return SETUP_CONFIG_FILE; - } - /** * Returns configuration array (full, multidimensional format) * diff --git a/libraries/config/PageSettings.php b/libraries/config/PageSettings.php index 5aeddab481..ef43390b28 100644 --- a/libraries/config/PageSettings.php +++ b/libraries/config/PageSettings.php @@ -112,7 +112,10 @@ class PageSettings $result = PMA_saveUserprefs($cf->getConfigArray()); if ($result === true) { // reload page - header('Location: ' . $_SERVER['REQUEST_URI']); + $response = Response::getInstance(); + PMA_sendHeaderLocation( + $response->getFooter()->getSelfUrl('unencoded') + ); exit(); } else { $error = $result; diff --git a/libraries/controllers/database/DatabaseStructureController.php b/libraries/controllers/database/DatabaseStructureController.php index cd22393f35..925ccd1244 100644 --- a/libraries/controllers/database/DatabaseStructureController.php +++ b/libraries/controllers/database/DatabaseStructureController.php @@ -887,7 +887,7 @@ class DatabaseStructureController extends DatabaseController if ($this->db == PMA_extractDbOrTable($db_table) && preg_match( "@^" . - mb_substr(PMA_extractDbOrTable($db_table, 'table'), 0, -1) . "@", + preg_quote(mb_substr(PMA_extractDbOrTable($db_table, 'table'), 0, -1)) . "@", $truename ) ) { diff --git a/libraries/core.lib.php b/libraries/core.lib.php index 02abd02dee..4c640ba005 100644 --- a/libraries/core.lib.php +++ b/libraries/core.lib.php @@ -935,6 +935,10 @@ function PMA_cleanupPathInfo() } $_PATH_INFO = PMA_getenv('PATH_INFO'); if (! empty($_PATH_INFO) && ! empty($PMA_PHP_SELF)) { + $question_pos = mb_strpos($PMA_PHP_SELF, '?'); + if ($question_pos != false) { + $PMA_PHP_SELF = mb_substr($PMA_PHP_SELF, 0, $question_pos); + } $path_info_pos = mb_strrpos($PMA_PHP_SELF, $_PATH_INFO); if ($path_info_pos !== false) { $path_info_part = mb_substr($PMA_PHP_SELF, $path_info_pos, mb_strlen($_PATH_INFO)); @@ -943,7 +947,24 @@ function PMA_cleanupPathInfo() } } } - $PMA_PHP_SELF = htmlspecialchars($PMA_PHP_SELF); + + $path = []; + foreach(explode('/', $PMA_PHP_SELF) as $part) { + // ignore parts that have no value + if (empty($part) || $part === '.') continue; + + if ($part !== '..') { + // cool, we found a new part + array_push($path, $part); + } else if (count($path) > 0) { + // going back up? sure + array_pop($path); + } + // Here we intentionall ignore case where we go too up + // as there is nothing sane to do + } + + $PMA_PHP_SELF = htmlspecialchars('/' . join('/', $path)); } /** diff --git a/libraries/navigation/NavigationTree.php b/libraries/navigation/NavigationTree.php index 4ee837b36d..e3f6b6a825 100644 --- a/libraries/navigation/NavigationTree.php +++ b/libraries/navigation/NavigationTree.php @@ -173,10 +173,13 @@ class NavigationTree * @todo describe a scenario where this code is executed */ if (!$GLOBALS['cfg']['Server']['DisableIS']) { + $dbSeparator = $GLOBALS['dbi']->escapeString( + $GLOBALS['cfg']['NavigationTreeDbSeparator'] + ); $query = "SELECT (COUNT(DB_first_level) DIV %d) * %d "; $query .= "from ( "; $query .= " SELECT distinct SUBSTRING_INDEX(SCHEMA_NAME, "; - $query .= " '" . $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['NavigationTreeDbSeparator']) . "', 1) "; + $query .= " '%s', 1) "; $query .= " DB_first_level "; $query .= " FROM INFORMATION_SCHEMA.SCHEMATA "; $query .= " WHERE `SCHEMA_NAME` < '%s' "; @@ -187,6 +190,7 @@ class NavigationTree $query, (int)$GLOBALS['cfg']['FirstLevelNavigationItems'], (int)$GLOBALS['cfg']['FirstLevelNavigationItems'], + $dbSeparator, $GLOBALS['dbi']->escapeString($GLOBALS['db']) ) ); diff --git a/libraries/navigation/nodes/Node.php b/libraries/navigation/nodes/Node.php index f265fca288..c5ee89d9d8 100644 --- a/libraries/navigation/nodes/Node.php +++ b/libraries/navigation/nodes/Node.php @@ -424,7 +424,9 @@ class Node return $retval; } - $dbSeparator = $GLOBALS['cfg']['NavigationTreeDbSeparator']; + $dbSeparator = $GLOBALS['dbi']->escapeString( + $GLOBALS['cfg']['NavigationTreeDbSeparator'] + ); if (isset($GLOBALS['cfg']['Server']['DisableIS']) && !$GLOBALS['cfg']['Server']['DisableIS'] ) { @@ -434,7 +436,7 @@ class Node $query .= "SELECT DB_first_level "; $query .= "FROM ( "; $query .= "SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, "; - $query .= "'" . $GLOBALS['dbi']->escapeString($dbSeparator) . "', 1) "; + $query .= "'%s', 1) "; $query .= "DB_first_level "; $query .= "FROM INFORMATION_SCHEMA.SCHEMATA "; $query .= $this->_getWhereClause('SCHEMA_NAME', $searchClause); @@ -444,11 +446,19 @@ class Node $query .= ") t2 "; $query .= $this->_getWhereClause('SCHEMA_NAME', $searchClause); $query .= "AND 1 = LOCATE(CONCAT(DB_first_level, "; - $query .= "'" . $GLOBALS['dbi']->escapeString($dbSeparator) . "'), "; + $query .= "'%s'), "; $query .= "CONCAT(SCHEMA_NAME, "; - $query .= "'" . $GLOBALS['dbi']->escapeString($dbSeparator) . "')) "; + $query .= "'%s')) "; $query .= "ORDER BY SCHEMA_NAME ASC"; - $retval = $GLOBALS['dbi']->fetchResult($query); + + $retval = $GLOBALS['dbi']->fetchResult( + sprintf( + $query, + $dbSeparator, + $dbSeparator, + $dbSeparator + ) + ); return $retval; } diff --git a/libraries/php-gettext/gettext.php b/libraries/php-gettext/gettext.php index d712deac1c..9f1c16a214 100644 --- a/libraries/php-gettext/gettext.php +++ b/libraries/php-gettext/gettext.php @@ -350,6 +350,10 @@ class gettext_reader { * @return int array index of the right plural form */ function select_string($n) { + if (!is_int($n)) { + throw new InvalidArgumentException( + "Select_string only accepts integers: " . $n); + } $string = $this->get_plural_forms(); $string = str_replace('nplurals',"\$total",$string); $string = str_replace("n",$n,$string); diff --git a/libraries/replication_gui.lib.php b/libraries/replication_gui.lib.php index c1b047dd6e..dcb9e5851c 100644 --- a/libraries/replication_gui.lib.php +++ b/libraries/replication_gui.lib.php @@ -906,7 +906,10 @@ function PMA_handleControlRequest() $messageSuccess = null; $messageError = null; - if (isset($_REQUEST['slave_changemaster'])) { + if (isset($_REQUEST['slave_changemaster']) && ! $GLOBALS['cfg']['AllowArbitraryServer']) { + $_SESSION['replication']['sr_action_status'] = 'error'; + $_SESSION['replication']['sr_action_info'] = __('Connection to server is disabled, please enable $cfg[\'AllowArbitraryServer\'] in phpMyAdmin configuration.'); + } elseif (isset($_REQUEST['slave_changemaster'])) { $result = PMA_handleRequestForSlaveChangeMaster(); } elseif (isset($_REQUEST['sr_slave_server_control'])) { $result = PMA_handleRequestForSlaveServerControl(); diff --git a/libraries/vendor_config.php b/libraries/vendor_config.php index 025eba39cb..56e71ab0c9 100644 --- a/libraries/vendor_config.php +++ b/libraries/vendor_config.php @@ -25,17 +25,6 @@ define('CHANGELOG_FILE', './ChangeLog'); */ define('LICENSE_FILE', './LICENSE'); -/** - * Path to config file generated using setup script. - */ -define('SETUP_CONFIG_FILE', './config/config.inc.php'); - -/** - * Whether setup requires writable directory where config - * file will be generated. - */ -define('SETUP_DIR_WRITABLE', true); - /** * Directory where SQL scripts to create/upgrade configuration storage reside. */ diff --git a/setup/config.php b/setup/config.php index db9e7c905e..372cd9f916 100644 --- a/setup/config.php +++ b/setup/config.php @@ -15,28 +15,9 @@ require './lib/common.inc.php'; require './libraries/config/setup.forms.php'; -/** - * Loads configuration file path - * - * Do this in a function to avoid messing up with global $cfg - * - * @param string $config_file_path - * - * @return array - */ -function loadConfig($config_file_path) -{ - $cfg = array(); - if (file_exists($config_file_path)) { - include $config_file_path; - } - return $cfg; -} - $form_display = new FormDisplay($GLOBALS['ConfigFile']); $form_display->registerForm('_config.php', $forms['_config.php']); $form_display->save('_config.php'); -$config_file_path = $GLOBALS['ConfigFile']->getFilePath(); if (isset($_POST['eol'])) { $_SESSION['eol'] = ($_POST['eol'] == 'unix') ? 'unix' : 'win'; @@ -58,40 +39,6 @@ if (PMA_ifSetOr($_POST['submit_clear'], '')) { PMA_downloadHeader('config.inc.php', 'text/plain'); echo ConfigGenerator::getConfigFile($GLOBALS['ConfigFile']); exit; -} elseif (PMA_ifSetOr($_POST['submit_save'], '')) { - // - // Save generated config file on the server - // - $result = @file_put_contents( - $config_file_path, - ConfigGenerator::getConfigFile($GLOBALS['ConfigFile']) - ); - if ($result === false) { - $state = 'config_not_saved'; - } else { - $state = 'config_saved'; - } - header('HTTP/1.1 303 See Other'); - header('Location: index.php' . PMA_URL_getCommon() . '&action_done=' . $state); - exit; -} elseif (PMA_ifSetOr($_POST['submit_load'], '')) { - // - // Load config file from the server - // - $GLOBALS['ConfigFile']->setConfigData( - loadConfig($config_file_path) - ); - header('HTTP/1.1 303 See Other'); - header('Location: index.php' . PMA_URL_getCommon()); - exit; -} elseif (PMA_ifSetOr($_POST['submit_delete'], '')) { - // - // Delete config file on the server - // - @unlink($config_file_path); - header('HTTP/1.1 303 See Other'); - header('Location: index.php' . PMA_URL_getCommon()); - exit; } else { // // Show generated config file in a