diff --git a/.gitignore b/.gitignore index 47a71b8c1..bc4aef17f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,11 @@ vendor/* test/vendor/* +# all conf but listing prevention +/conf/** +!/conf/.htaccess +!/conf/web.config + # all datas but listing prevention /data/** !/data/.htaccess diff --git a/application/utils.inc.php b/application/utils.inc.php index 12751f5b4..529dbe21b 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -2194,7 +2194,7 @@ class utils * * not contained in base path * Otherwise return the real path (see realpath()) * - * @since 2.7.0 N°2538 + * @since 2.6.5 2.7.0 N°2538 */ final public static function RealPath($sPath, $sBasePath) { diff --git a/conf/web.config b/conf/web.config index 599a5f260..58c9c3ac3 100644 --- a/conf/web.config +++ b/conf/web.config @@ -1,8 +1,13 @@ - + - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/data/web.config b/data/web.config index 599a5f260..58c9c3ac3 100644 --- a/data/web.config +++ b/data/web.config @@ -1,8 +1,13 @@ - + - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/datamodels/2.x/itop-backup/ajax.backup.php b/datamodels/2.x/itop-backup/ajax.backup.php index 2d026d20c..7196242c3 100644 --- a/datamodels/2.x/itop-backup/ajax.backup.php +++ b/datamodels/2.x/itop-backup/ajax.backup.php @@ -1,6 +1,6 @@ add($sHtmlErrorMessage); + $oPage->output(); + + die($exitCode); +} + + + + + + +$sOperation = utils::ReadParam('operation', ''); + +$oPage = new ajax_page(''); +$oPage->no_cache(); +$oPage->SetContentType('text/html'); + + + +/** + * Check security + */ +switch ($sOperation) +{ + /** + * Can't use normal check methods (DoLogin for ex) as the datamodel can't be loaded here + * So we're only using a token generated in the restore_token operation + */ + case 'restore_exec': + IssueLog::Enable(APPROOT.'log/error.log'); + if (utils::GetConfig()->Get('demo_mode')) + { + DisplayErrorAndDie($oPage, '
Sorry, '.ITOP_APPLICATION_SHORT.' is in demonstration mode: the feature is disabled.
'); + } + + $sToken = utils::ReadParam('token', '', false, 'raw_data'); + $sBasePath = APPROOT.'/data/'; + $sTokenFile = $sBasePath.'restore.'.$sToken.'.tok'; + $tokenRealPath = utils::RealPath($sTokenFile, $sBasePath); + if (($tokenRealPath === false) || (!is_file($tokenRealPath))) + { + IssueLog::Error("ajax.backup.php operation=$sOperation ERROR = inexisting token $sToken"); + $sEscapedToken = utils::HtmlEntities($sToken); + DisplayErrorAndDie($oPage, "

Error: missing token file: '$sEscapedToken'

"); + } + + break; + + default: + require_once(APPROOT.'/application/startup.inc.php'); + require_once(APPROOT.'/application/loginwebpage.class.inc.php'); + + LoginWebPage::DoLogin(); + + $sTransactionId = utils::ReadParam('transaction_id', '', true, 'transaction_id'); + // the consumer page is not reloaded after download, we need to keep the transaction_id + $bRemoveTransactionId = ($sOperation !== 'download'); + if (!utils::IsTransactionValid($sTransactionId, $bRemoveTransactionId)) + { + $sEscapedOperation = utils::HtmlEntities($sOperation); + DisplayErrorAndDie($oPage, "
Error: invalid Transaction ID. The operation '$sEscapedOperation' was NOT performed!
"); + } + + ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); + + if (utils::GetConfig()->Get('demo_mode')) + { + DisplayErrorAndDie($oPage, '
Sorry, '.ITOP_APPLICATION_SHORT.' is in demonstration mode: the feature is disabled.
'); + } + break; +} + + +/** + * Backup from an interactive session + */ try { - $sOperation = utils::ReadParam('operation', ''); - switch ($sOperation) { case 'backup': - require_once(APPROOT.'/application/startup.inc.php'); - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed - ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); - $oPage = new ajax_page(""); - $oPage->no_cache(); - $oPage->SetContentType('text/html'); + try + { + set_time_limit(0); + $oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/); + $sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient! + } + catch (Exception $e) + { + $oPage->p('Error: '.$e->getMessage()); + IssueLog::Error($sOperation.' - '.$e->getMessage()); + } - if (utils::GetConfig()->Get('demo_mode')) - { - $oPage->add("
Sorry, iTop is in demonstration mode: the feature is disabled.
"); - } - else - { - try - { - set_time_limit(0); - $oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/); - $sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient! - } - catch (Exception $e) - { - $oPage->p('Error: '.$e->getMessage()); - IssueLog::Error($sOperation.' - '.$e->getMessage()); - } - } $oPage->output(); break; /* - * Fix a token : + * Fix a specific token : * We can't load the MetaModel because in DBRestore, after restore is done we're launching a compile ! - * So as \LoginWebPage::DoLogin needs a loaded DataModel, we can't use it + * So as LoginWebPage::DoLogin needs a loaded DataModel, we can't use it + * Also, we can't use \utils::IsTransactionValid as it uses \MetaModel::GetConfig * As a result we're setting a token file to make sure the restore is called by an authenticated user with the correct rights ! */ case 'restore_get_token': - require_once(APPROOT.'/application/startup.inc.php'); - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed - ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); - - $oPage = new ajax_page(""); - $oPage->no_cache(); - $oPage->SetContentType('text/html'); - $sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data'); $oRestoreMutex = new iTopMutex('restore.'.$sEnvironment); - if (!$oRestoreMutex->IsLocked()) + if ($oRestoreMutex->IsLocked()) { - $sFile = utils::ReadParam('file', '', false, 'raw_data'); - $sToken = str_replace(' ', '', (string)microtime()); - $sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok'; - file_put_contents($sTokenFile, $sFile); + DisplayErrorAndDie($oPage, '

'.Dict::S('bkp-restore-running').'

'); + } + + $sFile = utils::ReadParam('file', '', false, 'raw_data'); + $sToken = str_replace(' ', '', (string)microtime()).$sTransactionId; + $sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok'; + file_put_contents($sTokenFile, $sFile); + + $oPage->add_ready_script( + <<add_ready_script( - <<p(Dict::S('bkp-restore-running')); - } $oPage->output(); break; @@ -109,72 +172,47 @@ EOF require_once(APPROOT.'/setup/backup.class.inc.php'); require_once(dirname(__FILE__).'/dbrestore.class.inc.php'); - IssueLog::Enable(APPROOT.'log/error.log'); - - $oPage = new ajax_page(""); - $oPage->no_cache(); - $oPage->SetContentType('text/html'); - - if (utils::GetConfig()->Get('demo_mode')) + $sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data'); + $oRestoreMutex = new iTopMutex('restore.'.$sEnvironment); + IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'"); + $oRestoreMutex->Lock(); + IssueLog::Info('Backup Restore - LOCK acquired, executing...'); + try { - $oPage->add("
Sorry, iTop is in demonstration mode: the feature is disabled.
"); + set_time_limit(0); + + // Get the file and destroy the token (single usage) + $sFile = file_get_contents($tokenRealPath); + + // Loading config file : we don't have the MetaModel but we have the current env ! + $sConfigFilePath = utils::GetConfigFilePath($sEnvironment); + $oItopConfig = new Config($sConfigFilePath, true); + $sMySQLBinDir = $oItopConfig->GetModuleSetting('itop-backup', 'mysql_bindir', ''); + + $oDBRS = new DBRestore($oItopConfig); + $oDBRS->SetMySQLBinDir($sMySQLBinDir); + + $sBackupDir = APPROOT.'data/backups/'; + $sBackupFile = $sBackupDir.$sFile; + $sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment); + + IssueLog::Info('Backup Restore - Done, releasing the LOCK'); } - else + catch (Exception $e) { - $sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data'); - $oRestoreMutex = new iTopMutex('restore.'.$sEnvironment); - IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'"); - $oRestoreMutex->Lock(); - IssueLog::Info('Backup Restore - LOCK acquired, executing...'); - try - { - set_time_limit(0); - - // Get the file and destroy the token (single usage) - $sToken = utils::ReadParam('token', '', false, 'raw_data'); - $sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok'; - if (!is_file($sTokenFile)) - { - throw new Exception("Error: missing token file: '$sTokenFile'"); - } - $sFile = file_get_contents($sTokenFile); - unlink($sTokenFile); - - // Loading config file : we don't have the MetaModel but we have the current env ! - $sConfigFilePath = utils::GetConfigFilePath($sEnvironment); - $oItopConfig = new Config($sConfigFilePath, true); - $sMySQLBinDir = $oItopConfig->GetModuleSetting('itop-backup', 'mysql_bindir', ''); - - $oDBRS = new DBRestore($oItopConfig); - $oDBRS->SetMySQLBinDir($sMySQLBinDir); - - $sBackupDir = APPROOT.'data/backups/'; - $sBackupFile = $sBackupDir.$sFile; - $sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment); - - IssueLog::Info('Backup Restore - Done, releasing the LOCK'); - $oRestoreMutex->Unlock(); - } - catch (Exception $e) - { - $oRestoreMutex->Unlock(); - $oPage->p('Error: '.$e->getMessage()); - IssueLog::Error($sOperation.' - '.$e->getMessage()); - } + $oPage->p('Error: '.$e->getMessage()); + IssueLog::Error($sOperation.' - '.$e->getMessage()); } + finally + { + unlink($tokenRealPath); + $oRestoreMutex->Unlock(); + } + $oPage->output(); break; case 'download': - require_once(APPROOT.'/application/startup.inc.php'); - require_once(APPROOT.'/application/loginwebpage.class.inc.php'); - LoginWebPage::DoLogin(); // Check user rights and prompt if needed - ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); - - if (utils::GetConfig()->Get('demo_mode')) - { - throw new Exception('iTop is in demonstration mode: the feature is disabled'); - } $sFile = utils::ReadParam('file', '', false, 'raw_data'); $oBackup = new DBBackupScheduled(); $sBackupDir = APPROOT.'data/backups/'; diff --git a/datamodels/2.x/itop-backup/status.php b/datamodels/2.x/itop-backup/status.php index b6ce74f76..feeeb9b1b 100644 --- a/datamodels/2.x/itop-backup/status.php +++ b/datamodels/2.x/itop-backup/status.php @@ -1,6 +1,6 @@ set_base(utils::GetAbsoluteUrlAppRoot().'pages/'); @@ -186,7 +182,13 @@ try } else { - $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath)); + $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', + array( + 'operation' => 'download', + 'file' => $sFilePath, + 'transaction_id' => $sTransactionId, + ) + ); $sName = "".$sFileName.''; } $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile)); @@ -234,7 +236,13 @@ try } else { - $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', array('operation' => 'download', 'file' => $sFilePath)); + $sAjax = utils::GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php', + array( + 'operation' => 'download', + 'file' => $sFilePath, + 'transaction_id' => $sTransactionId, + ) + ); $sName = "".$sFileName.''; } $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile)); @@ -298,9 +306,9 @@ try $sDBSubName = addslashes(MetaModel::GetConfig()->Get('db_subname')); $sEnvironment = addslashes(utils::GetCurrentEnvironment()); - + $oP->add_script( -<< 0) { $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){ @@ -373,7 +384,7 @@ function LaunchRestoreNow(sBackupFile, sConfirmationMessage) }); } } -EOF +JS ); if (MetaModel::GetConfig()->Get('demo_mode')) diff --git a/log/web.config b/log/web.config index 599a5f260..58c9c3ac3 100644 --- a/log/web.config +++ b/log/web.config @@ -1,8 +1,13 @@ - + - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/pages/audit.php b/pages/audit.php index 22d5a1695..7a055b479 100644 --- a/pages/audit.php +++ b/pages/audit.php @@ -282,6 +282,7 @@ try { try { + $iCount = 0; $oDefinitionFilter = DBObjectSearch::FromOQL($oAuditCategory->Get('definition_set')); $oDefinitionFilter->UpdateContextFromUser(); FilterByContext($oDefinitionFilter, $oAppContext); @@ -348,7 +349,7 @@ try $aRow['nb_errors'] = "n/a"; $aRow['percent_ok'] = ''; $aRow['class'] = 'red'; - $sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), $e->getMessage()); + $sMessage = Dict::Format('UI:Audit:ErrorIn_Category_Reason', $oAuditCategory->GetHyperlink(), utils::HtmlEntities($e->getMessage())); $oP->p(" ".$sMessage); $aResults[] = $aRow;