0794c9c4cc6363d804dd7754f20a561571f98db2
[Packages/TYPO3.CMS.git] / typo3 / sysext / reports / Classes / Report / Status / SecurityStatus.php
1 <?php
2 namespace TYPO3\CMS\Reports\Report\Status;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2009-2013 Ingo Renner <ingo@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Backend\Utility\BackendUtility;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29
30 /**
31 * Performs several checks about the system's health
32 *
33 * @author Ingo Renner <ingo@typo3.org>
34 */
35 class SecurityStatus implements \TYPO3\CMS\Reports\StatusProviderInterface {
36
37 /**
38 * Determines the Install Tool's status, mainly concerning its protection.
39 *
40 * @return array List of statuses
41 */
42 public function getStatus() {
43 $this->executeAdminCommand();
44 $statuses = array(
45 'adminUserAccount' => $this->getAdminAccountStatus(),
46 'encryptionKeyEmpty' => $this->getEncryptionKeyStatus(),
47 'fileDenyPattern' => $this->getFileDenyPatternStatus(),
48 'htaccessUpload' => $this->getHtaccessUploadStatus(),
49 'installToolEnabled' => $this->getInstallToolProtectionStatus(),
50 'installToolPassword' => $this->getInstallToolPasswordStatus(),
51 'saltedpasswords' => $this->getSaltedPasswordsStatus()
52 );
53 return $statuses;
54 }
55
56 /**
57 * Checks whether a an BE user account named admin with default password exists.
58 *
59 * @return \TYPO3\CMS\Reports\Status An object representing whether a default admin account exists
60 */
61 protected function getAdminAccountStatus() {
62 $value = $GLOBALS['LANG']->getLL('status_ok');
63 $message = '';
64 $severity = \TYPO3\CMS\Reports\Status::OK;
65 $whereClause = 'username = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('admin', 'be_users') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('be_users');
66 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid, username, password', 'be_users', $whereClause);
67 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
68 $secure = TRUE;
69 /** @var $saltingObject \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface */
70 $saltingObject = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($row['password']);
71 if (is_object($saltingObject)) {
72 if ($saltingObject->checkPassword('password', $row['password'])) {
73 $secure = FALSE;
74 }
75 }
76 // Check against plain MD5
77 if ($row['password'] === '5f4dcc3b5aa765d61d8327deb882cf99') {
78 $secure = FALSE;
79 }
80 if (!$secure) {
81 $value = $GLOBALS['LANG']->getLL('status_insecure');
82 $severity = \TYPO3\CMS\Reports\Status::ERROR;
83 $editUserAccountUrl = 'alt_doc.php?returnUrl=' . rawurlencode(BackendUtility::getModuleUrl('system_ReportsTxreportsm1')) . '&edit[be_users][' . $row['uid'] . ']=edit';
84 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.backend_admin'), '<a href="' . $editUserAccountUrl . '">', '</a>');
85 }
86 }
87 $GLOBALS['TYPO3_DB']->sql_free_result($res);
88 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_adminUserAccount'), $value, $message, $severity);
89 }
90
91 /**
92 * Checks whether the encryption key is empty.
93 *
94 * @return \TYPO3\CMS\Reports\Status An object representing whether the encryption key is empty or not
95 */
96 protected function getEncryptionKeyStatus() {
97 $value = $GLOBALS['LANG']->getLL('status_ok');
98 $message = '';
99 $severity = \TYPO3\CMS\Reports\Status::OK;
100 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
101 $value = $GLOBALS['LANG']->getLL('status_insecure');
102 $severity = \TYPO3\CMS\Reports\Status::ERROR;
103 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
104 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_encryption'), '<a href="' . $url . '">', '</a>');
105 }
106 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_encryptionKey'), $value, $message, $severity);
107 }
108
109 /**
110 * Checks if fileDenyPattern was changed which is dangerous on Apache
111 *
112 * @return \TYPO3\CMS\Reports\Status An object representing whether the file deny pattern has changed
113 */
114 protected function getFileDenyPatternStatus() {
115 $value = $GLOBALS['LANG']->getLL('status_ok');
116 $message = '';
117 $severity = \TYPO3\CMS\Reports\Status::OK;
118 $defaultParts = GeneralUtility::trimExplode('|', FILE_DENY_PATTERN_DEFAULT, TRUE);
119 $givenParts = GeneralUtility::trimExplode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], TRUE);
120 $result = array_intersect($defaultParts, $givenParts);
121 if ($defaultParts !== $result) {
122 $value = $GLOBALS['LANG']->getLL('status_insecure');
123 $severity = \TYPO3\CMS\Reports\Status::ERROR;
124 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
125 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_pattern_partsNotPresent'), '<br /><pre>' . htmlspecialchars(FILE_DENY_PATTERN_DEFAULT) . '</pre><br />');
126 }
127 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_fileDenyPattern'), $value, $message, $severity);
128 }
129
130 /**
131 * Checks if fileDenyPattern allows to upload .htaccess files which is
132 * dangerous on Apache.
133 *
134 * @return \TYPO3\CMS\Reports\Status An object representing whether it's possible to upload .htaccess files
135 */
136 protected function getHtaccessUploadStatus() {
137 $value = $GLOBALS['LANG']->getLL('status_ok');
138 $message = '';
139 $severity = \TYPO3\CMS\Reports\Status::OK;
140 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] != FILE_DENY_PATTERN_DEFAULT && GeneralUtility::verifyFilenameAgainstDenyPattern('.htaccess')) {
141 $value = $GLOBALS['LANG']->getLL('status_insecure');
142 $severity = \TYPO3\CMS\Reports\Status::ERROR;
143 $message = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_htaccess');
144 }
145 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_htaccessUploadProtection'), $value, $message, $severity);
146 }
147
148 /**
149 * Checks whether memcached is configured, if that's the case we assume it's also used.
150 *
151 * @return boolean TRUE if memcached is used, FALSE otherwise.
152 */
153 protected function isMemcachedUsed() {
154 $memcachedUsed = FALSE;
155 $memcachedServers = $this->getConfiguredMemcachedServers();
156 if (count($memcachedServers)) {
157 $memcachedUsed = TRUE;
158 }
159 return $memcachedUsed;
160 }
161
162 /**
163 * Executes commands like removing the Install Tool enable file.
164 *
165 * @return void
166 */
167 protected function executeAdminCommand() {
168 $command = GeneralUtility::_GET('adminCmd');
169 switch ($command) {
170 case 'remove_ENABLE_INSTALL_TOOL':
171 unlink(PATH_site . 'typo3conf/ENABLE_INSTALL_TOOL');
172 break;
173 default:
174 // Do nothing
175 }
176 }
177
178 /**
179 * Checks whether the Install Tool password is set to its default value.
180 *
181 * @return \TYPO3\CMS\Reports\Status An object representing the security of the install tool password
182 */
183 protected function getInstallToolPasswordStatus() {
184 $value = $GLOBALS['LANG']->getLL('status_ok');
185 $message = '';
186 $severity = \TYPO3\CMS\Reports\Status::OK;
187 $validPassword = TRUE;
188 $installToolPassword = $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'];
189 $saltFactory = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($installToolPassword);
190 if (is_object($saltFactory)) {
191 $validPassword = !$saltFactory->checkPassword('joh316', $installToolPassword);
192 } elseif ($installToolPassword === md5('joh316')) {
193 $validPassword = FALSE;
194 }
195 if (!$validPassword) {
196 $value = $GLOBALS['LANG']->getLL('status_insecure');
197 $severity = \TYPO3\CMS\Reports\Status::ERROR;
198 $changeInstallToolPasswordUrl = BackendUtility::getModuleUrl('system_InstallInstall');
199 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.installtool_default_password'), '<a href="' . $changeInstallToolPasswordUrl . '">', '</a>');
200 }
201 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_installToolPassword'), $value, $message, $severity);
202 }
203
204 /**
205 * Checks whether the Install Tool password is set to its default value.
206 *
207 * @return \TYPO3\CMS\Reports\Status An object representing the security of the saltedpassswords extension
208 */
209 protected function getSaltedPasswordsStatus() {
210 $value = $GLOBALS['LANG']->getLL('status_ok');
211 $message = '';
212 $severity = \TYPO3\CMS\Reports\Status::OK;
213 /** @var \TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility $configCheck */
214 $configCheck = GeneralUtility::makeInstance('TYPO3\\CMS\\Saltedpasswords\\Utility\\ExtensionManagerConfigurationUtility');
215 $message = '<p>' . $GLOBALS['LANG']->getLL('status_saltedPasswords_infoText') . '</p>';
216 $messageDetail = '';
217 $flashMessage = $configCheck->checkConfigurationBackend(array(), new \TYPO3\CMS\Core\TypoScript\ConfigurationForm());
218 if (strpos($flashMessage, 'message-error') !== FALSE) {
219 $value = $GLOBALS['LANG']->getLL('status_insecure');
220 $severity = \TYPO3\CMS\Reports\Status::ERROR;
221 $messageDetail .= $flashMessage;
222 }
223 if (strpos($flashMessage, 'message-warning') !== FALSE) {
224 $severity = \TYPO3\CMS\Reports\Status::WARNING;
225 $messageDetail .= $flashMessage;
226 }
227 if (strpos($flashMessage, 'message-information') !== FALSE) {
228 $messageDetail .= $flashMessage;
229 }
230 $unsecureUserCount = \TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility::getNumberOfBackendUsersWithInsecurePassword();
231 if ($unsecureUserCount > 0) {
232 $value = $GLOBALS['LANG']->getLL('status_insecure');
233 $severity = \TYPO3\CMS\Reports\Status::ERROR;
234 $messageDetail .= '<div class="typo3-message message-warning">' . $GLOBALS['LANG']->getLL('status_saltedPasswords_notAllPasswordsHashed') . '</div>';
235 }
236 $message .= $messageDetail;
237 if (empty($messageDetail)) {
238 $message = '';
239 }
240 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_saltedPasswords'), $value, $message, $severity);
241 }
242
243 /**
244 * Checks for the existence of the ENABLE_INSTALL_TOOL file.
245 *
246 * @return \TYPO3\CMS\Reports\Status An object representing whether ENABLE_INSTALL_TOOL exists
247 */
248 protected function getInstallToolProtectionStatus() {
249 $enableInstallToolFile = PATH_site . 'typo3conf/ENABLE_INSTALL_TOOL';
250 $value = $GLOBALS['LANG']->getLL('status_disabled');
251 $message = '';
252 $severity = \TYPO3\CMS\Reports\Status::OK;
253 $enableInstallToolFileExists = is_file($enableInstallToolFile);
254 if ($enableInstallToolFileExists) {
255 if (trim(file_get_contents($enableInstallToolFile)) === 'KEEP_FILE') {
256 $severity = \TYPO3\CMS\Reports\Status::WARNING;
257 $disableInstallToolUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '&amp;adminCmd=remove_ENABLE_INSTALL_TOOL';
258 $value = $GLOBALS['LANG']->getLL('status_enabledPermanently');
259 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled'), '<span style="white-space: nowrap;">' . $enableInstallToolFile . '</span>');
260 $message .= ' <a href="' . $disableInstallToolUrl . '">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
261 } else {
262 $enableInstallToolFileTtl = filemtime($enableInstallToolFile) + 3600 - time();
263 if ($enableInstallToolFileTtl <= 0) {
264 unlink($enableInstallToolFile);
265 } else {
266 $severity = \TYPO3\CMS\Reports\Status::NOTICE;
267 $disableInstallToolUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '&amp;adminCmd=remove_ENABLE_INSTALL_TOOL';
268 $value = $GLOBALS['LANG']->getLL('status_enabledTemporarily');
269 $message = sprintf($GLOBALS['LANG']->getLL('status_installEnabledTemporarily'), '<span style="white-space: nowrap;">' . $enableInstallToolFile . '</span>', floor($enableInstallToolFileTtl / 60));
270 $message .= ' <a href="' . $disableInstallToolUrl . '">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
271 }
272 }
273 }
274 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_installTool'), $value, $message, $severity);
275 }
276
277 }