From 1a165adbff9f3097cc3de0374ed5f424da9abc94 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Thu, 22 Feb 2018 15:21:56 +0100 Subject: [PATCH 01/13] Fixed issue: [security] Vulnerability in installer # Conflicts: # application/controllers/InstallerController.php --- .../controllers/InstallerController.php | 116 ++++++++++++------ 1 file changed, 81 insertions(+), 35 deletions(-) diff --git a/application/controllers/InstallerController.php b/application/controllers/InstallerController.php index 40e86a72dde..b706c58fc0a 100644 --- a/application/controllers/InstallerController.php +++ b/application/controllers/InstallerController.php @@ -125,6 +125,9 @@ protected function _sessioncontrol() */ private function stepWelcome() { + // Destroy the session. Good to have when running installer multiple times (for testing). + Yii::app()->session->clear(); + Yii::app()->session->destroy(); if (!is_null(Yii::app()->request->getPost('installerLang'))) { @@ -648,45 +651,57 @@ private function stepOptionalConfiguration() // Flush query cache because Yii does not handle properly the new DB prefix Yii::app()->cache->flush(); - //config file is written, and we've a db in place - $this->connection = Yii::app()->db; + + $aDbConfigArray = $this->_getDatabaseConfigArray(); + $aDbConfigArray['class'] = '\CDbConnection'; + \Yii::app()->setComponent('db', $aDbConfigArray, false); + $db = \Yii::app()->getDb(); + $db->setActive(true); + $this->connection = $db; //checking DB Connection if ($this->connection->getActive() == true) { - $sPasswordHash=hash('sha256', $sAdminPassword); try { - if (User::model()->count()>0){ - die(); + if (User::model()->count() > 0) { + //throw new Exception('Already admin in system'); + //safeDie('Fatal error: Already an admin user in the system.'); + $user = User::model()->findByPk(1); + if (empty($user)) { + throw new Exception('There is an admin user, but not with id 1'); + } + } else { + $user = new User; + + // Save permissions + $permission = new Permission; + $permission->entity_id = 0; + $permission->entity = 'global'; + $permission->uid = $user->uid; + $permission->permission = 'superadmin'; + $permission->read_p = 1; + $permission->save(); + + // Save global settings + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'SessionName', 'stg_value' => $this->_getRandomString())); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'sitename', 'stg_value' => $sSiteName)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminname', 'stg_value' => $sAdminRealName)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminemail', 'stg_value' => $sAdminEmail)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminbounce', 'stg_value' => $sAdminEmail)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'defaultlang', 'stg_value' => $sSiteLanguage)); } // Save user - $user=new User; // Fix UserID to 1 for MySQL even if installed in master-master configuration scenario if (in_array($this->connection->getDriverName(), array('mysql', 'mysqli'))) { - $user->uid=1; + $user->uid = 1; } - $user->users_name=$sAdminUserName; - $user->password=$sPasswordHash; - $user->full_name=$sAdminRealName; - $user->parent_id=0; - $user->lang=$sSiteLanguage; - $user->email=$sAdminEmail; - $user->save(); - // Save permissions - $permission=new Permission; - $permission->entity_id=0; - $permission->entity='global'; - $permission->uid=$user->uid; - $permission->permission='superadmin'; - $permission->read_p=1; - $permission->save(); - // Save global settings - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'SessionName', 'stg_value' => self::_getRandomString())); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'sitename', 'stg_value' => $sSiteName)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminname', 'stg_value' => $sAdminRealName)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminemail', 'stg_value' => $sAdminEmail)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminbounce', 'stg_value' => $sAdminEmail)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'defaultlang', 'stg_value' => $sSiteLanguage)); + $user->users_name = $sAdminUserName; + $user->setPassword($sAdminPassword); + $user->full_name = $sAdminRealName; + $user->parent_id = 0; + $user->lang = $sSiteLanguage; + $user->email = $sAdminEmail; + $result = $user->save(); // only continue if we're error free otherwise setup is broken. } catch (Exception $e) { throw new Exception(sprintf('Could not add optional settings: %s.', $e)); @@ -1278,12 +1293,43 @@ function _getDatabaseConfig() } /** - * Connect to the database - * @param array $aDbConfig : The config to be tested - * @param array $aData - * @return bool - */ - function _dbConnect($aDbConfig = array(), $aData = array()) + * Use with \Yii::app()->setComponent() to set connection at runtime. + * @return array + */ + private function _getDatabaseConfigArray() + { + $sDatabaseType = Yii::app()->session['dbtype']; + $sDatabasePort = Yii::app()->session['dbport']; + $sDatabaseName = Yii::app()->session['dbname']; + $sDatabaseUser = Yii::app()->session['dbuser']; + $sDatabasePwd = Yii::app()->session['dbpwd']; + $sDatabasePrefix = Yii::app()->session['dbprefix']; + $sDatabaseLocation = Yii::app()->session['dblocation']; + + $sCharset = 'utf8'; + if (in_array($sDatabaseType, array('mysql', 'mysqli'))) { + $sCharset = 'utf8mb4'; + } + + $db = array( + 'connectionString' => "$sDatabaseType:host=$sDatabaseLocation;port=$sDatabasePort;dbname=$sDatabaseName;", + 'emulatePrepare' => true, + 'username' => $sDatabaseUser, + 'password' => $sDatabasePwd, + 'charset' => $sCharset, + 'tablePrefix' => $sDatabasePrefix + ); + + return $db; + } + + /** + * Connect to the database + * @param array $aDbConfig : The config to be tested + * @param array $aData + * @return bool + */ + private function _dbConnect($aDbConfig = array(), $aData = array()) { $aDbConfig= empty($aDbConfig) ? self::_getDatabaseConfig() : $aDbConfig; extract($aDbConfig); From c3001ca814ba4a8a88a7cc3de5ca8b9f628c2858 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Thu, 22 Feb 2018 15:40:20 +0100 Subject: [PATCH 02/13] Dev: Replace 'optional settings' with 'administrator settings' (not optional anymore) --- application/controllers/InstallerController.php | 8 ++++---- application/views/installer/sidebar_view.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/controllers/InstallerController.php b/application/controllers/InstallerController.php index b706c58fc0a..3806443df82 100644 --- a/application/controllers/InstallerController.php +++ b/application/controllers/InstallerController.php @@ -616,11 +616,11 @@ function stepPopulateDb() */ private function stepOptionalConfiguration() { - + $aData = []; $aData['confirmation'] = Yii::app()->session['optconfig_message']; - $aData['title'] = gT("Optional settings"); - $aData['descp'] = gT("Optional settings to give you a head start"); - $aData['classesForStep'] = array('off','off','off','off','off','on'); + $aData['title'] = gT("Administrator settings"); + $aData['descp'] = gT("Further settings for application administrator"); + $aData['classesForStep'] = array('off', 'off', 'off', 'off', 'off', 'on'); $aData['progressValue'] = 80; $this->loadHelper('surveytranslator'); $aData['model'] = $model = new InstallerConfigForm('optional'); diff --git a/application/views/installer/sidebar_view.php b/application/views/installer/sidebar_view.php index e6fa51a6d88..59a94223198 100644 --- a/application/views/installer/sidebar_view.php +++ b/application/views/installer/sidebar_view.php @@ -25,6 +25,6 @@
  • - +
  • From c8e62d86d12361e694477003d69dddb57146a923 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Thu, 22 Feb 2018 14:26:10 +0100 Subject: [PATCH 03/13] Dev: Only write config file when installation is completely done Conflicts: application/controllers/InstallerController.php --- .../controllers/InstallerController.php | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/application/controllers/InstallerController.php b/application/controllers/InstallerController.php index 3806443df82..4266dd1b92c 100644 --- a/application/controllers/InstallerController.php +++ b/application/controllers/InstallerController.php @@ -100,10 +100,8 @@ public function run($action = 'index') */ function _checkInstallation() { - if (file_exists(APPPATH . 'config/config.php') && is_null(Yii::app()->request->getPost('InstallerConfigForm'))) - { + if (file_exists(APPPATH.'config/config.php')) { throw new CHttpException(500, 'Installation has been done already. Installer disabled.'); - exit(); } } @@ -669,7 +667,7 @@ private function stepOptionalConfiguration() $user = User::model()->findByPk(1); if (empty($user)) { throw new Exception('There is an admin user, but not with id 1'); - } + } } else { $user = new User; @@ -701,8 +699,28 @@ private function stepOptionalConfiguration() $user->parent_id = 0; $user->lang = $sSiteLanguage; $user->email = $sAdminEmail; - $result = $user->save(); + $user->save(); + // only continue if we're error free otherwise setup is broken. + Yii::app()->session['deletedirectories'] = true; + + $aData['title'] = gT("Success!"); + $aData['descp'] = gT("LimeSurvey has been installed successfully."); + $aData['classesForStep'] = array('off', 'off', 'off', 'off', 'off', 'off'); + $aData['progressValue'] = 100; + $aData['user'] = $sAdminUserName; + if ($sDefaultAdminPassword == $sAdminPassword) { + $aData['pwd'] = $sAdminPassword; + } else { + $aData['pwd'] = gT("The password you have chosen at the optional settings step."); + } + + $this->_writeConfigFile(); + + $this->render('/installer/success_view', $aData); + + return; + } catch (Exception $e) { throw new Exception(sprintf('Could not add optional settings: %s.', $e)); } @@ -720,16 +738,15 @@ private function stepOptionalConfiguration() $aData['pwd'] = gT("The password you have chosen at the optional settings step."); } + $this->_writeConfigFile(); + $this->render('/installer/success_view', $aData); return; } } else { unset($aData['confirmation']); } - } elseif(empty(Yii::app()->session['configFileWritten'])) { - $this->_writeConfigFile(); } - $this->render('/installer/optconfig_view', $aData); } From 2a8be511c7684bf407c1ae2eee051bfe97e4d2f2 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Thu, 22 Feb 2018 16:18:53 +0100 Subject: [PATCH 04/13] Dev: Remove session->destroy() in installer (problem with session) Conflicts: application/controllers/InstallerController.php --- application/controllers/InstallerController.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/application/controllers/InstallerController.php b/application/controllers/InstallerController.php index 4266dd1b92c..8b81e0a95f3 100644 --- a/application/controllers/InstallerController.php +++ b/application/controllers/InstallerController.php @@ -123,12 +123,7 @@ protected function _sessioncontrol() */ private function stepWelcome() { - // Destroy the session. Good to have when running installer multiple times (for testing). - Yii::app()->session->clear(); - Yii::app()->session->destroy(); - - if (!is_null(Yii::app()->request->getPost('installerLang'))) - { + if (!is_null(Yii::app()->request->getPost('installerLang'))) { Yii::app()->session['installerLang'] = Yii::app()->request->getPost('installerLang'); $this->redirect(array('installer/license')); } From ba9e57910a576fbe4849c5b50b6637a87918a776 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Fri, 23 Feb 2018 10:30:17 +0100 Subject: [PATCH 05/13] Dev: Some cleanup in installer --- .../controllers/InstallerController.php | 72 ++++++------------- application/models/User.php | 16 +++++ 2 files changed, 38 insertions(+), 50 deletions(-) diff --git a/application/controllers/InstallerController.php b/application/controllers/InstallerController.php index 8b81e0a95f3..f5fb350af66 100644 --- a/application/controllers/InstallerController.php +++ b/application/controllers/InstallerController.php @@ -618,12 +618,7 @@ private function stepOptionalConfiguration() $this->loadHelper('surveytranslator'); $aData['model'] = $model = new InstallerConfigForm('optional'); // Backup the default, needed only for $sDefaultAdminPassword - $sDefaultAdminUserName = $model->adminLoginName; $sDefaultAdminPassword = $model->adminLoginPwd; - $sDefaultAdminRealName = $model->adminName; - $sDefaultSiteName = $model->siteName; - $sDefaultSiteLanguage = $model->surveylang; - $sDefaultAdminEmail = $model->adminEmail; if(!is_null(Yii::app()->request->getPost('InstallerConfigForm'))) { $model->attributes = Yii::app()->request->getPost('InstallerConfigForm'); @@ -657,33 +652,11 @@ private function stepOptionalConfiguration() try { if (User::model()->count() > 0) { - //throw new Exception('Already admin in system'); - //safeDie('Fatal error: Already an admin user in the system.'); - $user = User::model()->findByPk(1); - if (empty($user)) { - throw new Exception('There is an admin user, but not with id 1'); - } - } else { - $user = new User; - - // Save permissions - $permission = new Permission; - $permission->entity_id = 0; - $permission->entity = 'global'; - $permission->uid = $user->uid; - $permission->permission = 'superadmin'; - $permission->read_p = 1; - $permission->save(); - - // Save global settings - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'SessionName', 'stg_value' => $this->_getRandomString())); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'sitename', 'stg_value' => $sSiteName)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminname', 'stg_value' => $sAdminRealName)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminemail', 'stg_value' => $sAdminEmail)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminbounce', 'stg_value' => $sAdminEmail)); - $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'defaultlang', 'stg_value' => $sSiteLanguage)); + throw new Exception('Already admin in system'); } + // Save user + $user = new User; // Fix UserID to 1 for MySQL even if installed in master-master configuration scenario if (in_array($this->connection->getDriverName(), array('mysql', 'mysqli'))) { $user->uid = 1; @@ -696,15 +669,31 @@ private function stepOptionalConfiguration() $user->email = $sAdminEmail; $user->save(); - // only continue if we're error free otherwise setup is broken. + // Save permissions + $permission = new Permission; + $permission->entity_id = 0; + $permission->entity = 'global'; + $permission->uid = $user->uid; + $permission->permission = 'superadmin'; + $permission->read_p = 1; + $permission->save(); + + // Save global settings + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'SessionName', 'stg_value' => $this->_getRandomString())); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'sitename', 'stg_value' => $sSiteName)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminname', 'stg_value' => $sAdminRealName)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminemail', 'stg_value' => $sAdminEmail)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'siteadminbounce', 'stg_value' => $sAdminEmail)); + $this->connection->createCommand()->insert("{{settings_global}}", array('stg_name' => 'defaultlang', 'stg_value' => $sSiteLanguage)); + Yii::app()->session['deletedirectories'] = true; $aData['title'] = gT("Success!"); $aData['descp'] = gT("LimeSurvey has been installed successfully."); - $aData['classesForStep'] = array('off', 'off', 'off', 'off', 'off', 'off'); + $aData['classesForStep'] = array('off','off','off','off','off','off'); $aData['progressValue'] = 100; $aData['user'] = $sAdminUserName; - if ($sDefaultAdminPassword == $sAdminPassword) { + if ($sDefaultAdminPassword==$sAdminPassword) { $aData['pwd'] = $sAdminPassword; } else { $aData['pwd'] = gT("The password you have chosen at the optional settings step."); @@ -720,23 +709,6 @@ private function stepOptionalConfiguration() throw new Exception(sprintf('Could not add optional settings: %s.', $e)); } - Yii::app()->session['deletedirectories'] = true; - - $aData['title'] = gT("Success!"); - $aData['descp'] = gT("LimeSurvey has been installed successfully."); - $aData['classesForStep'] = array('off','off','off','off','off','off'); - $aData['progressValue'] = 100; - $aData['user'] = $sAdminUserName; - if($sDefaultAdminPassword==$sAdminPassword){ - $aData['pwd'] = $sAdminPassword; - }else{ - $aData['pwd'] = gT("The password you have chosen at the optional settings step."); - } - - $this->_writeConfigFile(); - - $this->render('/installer/success_view', $aData); - return; } } else { unset($aData['confirmation']); diff --git a/application/models/User.php b/application/models/User.php index ac23954c62a..7c0273fbf85 100644 --- a/application/models/User.php +++ b/application/models/User.php @@ -527,4 +527,20 @@ public function search() )); } + /** + * Set user password with hash + * + * @param string $sPassword The clear text password + * @return \User + */ + public function setPassword($sPassword, $save = false) + { + // NB: Different method in 2.73 vs 3.0. + $sPasswordHash = hash('sha256', $sPassword); + $this->password = $sPasswordHash; + if ($save) { + $this->save(); + } + return $this; // Return current object + } } From 1944856e0ee02c562ae4ce1d5f6e3d2583d9d907 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Fri, 23 Feb 2018 10:42:19 +0100 Subject: [PATCH 06/13] Dev: Remove 'Previous' button on administrator page (installer) --- application/views/installer/optconfig_view.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/views/installer/optconfig_view.php b/application/views/installer/optconfig_view.php index c16f3a89f30..993fd4242e6 100644 --- a/application/views/installer/optconfig_view.php +++ b/application/views/installer/optconfig_view.php @@ -76,7 +76,6 @@ ?>