c3cba1dec35bcac1abb9b190621c144a4fab37fd
[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 use TYPO3\CMS\Install\Service\EnableFileService;
30
31 /**
32 * Performs several checks about the system's health
33 *
34 * @author Ingo Renner <ingo@typo3.org>
35 */
36 class SecurityStatus implements \TYPO3\CMS\Reports\StatusProviderInterface {
37
38 /**
39 * Determines the Install Tool's status, mainly concerning its protection.
40 *
41 * @return array List of statuses
42 */
43 public function getStatus() {
44 $this->executeAdminCommand();
45 $statuses = array(
46 'adminUserAccount' => $this->getAdminAccountStatus(),
47 'encryptionKeyEmpty' => $this->getEncryptionKeyStatus(),
48 'fileDenyPattern' => $this->getFileDenyPatternStatus(),
49 'htaccessUpload' => $this->getHtaccessUploadStatus(),
50 'installToolEnabled' => $this->getInstallToolProtectionStatus(),
51 'installToolPassword' => $this->getInstallToolPasswordStatus(),
52 'saltedpasswords' => $this->getSaltedPasswordsStatus()
53 );
54 return $statuses;
55 }
56
57 /**
58 * Checks whether a an BE user account named admin with default password exists.
59 *
60 * @return \TYPO3\CMS\Reports\Status An object representing whether a default admin account exists
61 */
62 protected function getAdminAccountStatus() {
63 $value = $GLOBALS['LANG']->getLL('status_ok');
64 $message = '';
65 $severity = \TYPO3\CMS\Reports\Status::OK;
66 $whereClause = 'username = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('admin', 'be_users') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('be_users');
67 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid, username, password', 'be_users', $whereClause);
68 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
69 $secure = TRUE;
70 /** @var $saltingObject \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface */
71 $saltingObject = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($row['password']);
72 if (is_object($saltingObject)) {
73 if ($saltingObject->checkPassword('password', $row['password'])) {
74 $secure = FALSE;
75 }
76 }
77 // Check against plain MD5
78 if ($row['password'] === '5f4dcc3b5aa765d61d8327deb882cf99') {
79 $secure = FALSE;
80 }
81 if (!$secure) {
82 $value = $GLOBALS['LANG']->getLL('status_insecure');
83 $severity = \TYPO3\CMS\Reports\Status::ERROR;
84 $editUserAccountUrl = 'alt_doc.php?returnUrl=' . rawurlencode(BackendUtility::getModuleUrl('system_ReportsTxreportsm1')) . '&edit[be_users][' . $row['uid'] . ']=edit';
85 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.backend_admin'), '<a href="' . $editUserAccountUrl . '">', '</a>');
86 }
87 }
88 $GLOBALS['TYPO3_DB']->sql_free_result($res);
89 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_adminUserAccount'), $value, $message, $severity);
90 }
91
92 /**
93 * Checks whether the encryption key is empty.
94 *
95 * @return \TYPO3\CMS\Reports\Status An object representing whether the encryption key is empty or not
96 */
97 protected function getEncryptionKeyStatus() {
98 $value = $GLOBALS['LANG']->getLL('status_ok');
99 $message = '';
100 $severity = \TYPO3\CMS\Reports\Status::OK;
101 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
102 $value = $GLOBALS['LANG']->getLL('status_insecure');
103 $severity = \TYPO3\CMS\Reports\Status::ERROR;
104 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
105 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_encryption'), '<a href="' . $url . '">', '</a>');
106 }
107 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_encryptionKey'), $value, $message, $severity);
108 }
109
110 /**
111 * Checks if fileDenyPattern was changed which is dangerous on Apache
112 *
113 * @return \TYPO3\CMS\Reports\Status An object representing whether the file deny pattern has changed
114 */
115 protected function getFileDenyPatternStatus() {
116 $value = $GLOBALS['LANG']->getLL('status_ok');
117 $message = '';
118 $severity = \TYPO3\CMS\Reports\Status::OK;
119 $defaultParts = GeneralUtility::trimExplode('|', FILE_DENY_PATTERN_DEFAULT, TRUE);
120 $givenParts = GeneralUtility::trimExplode('|', $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], TRUE);
121 $result = array_intersect($defaultParts, $givenParts);
122 if ($defaultParts !== $result) {
123 $value = $GLOBALS['LANG']->getLL('status_insecure');
124 $severity = \TYPO3\CMS\Reports\Status::ERROR;
125 $url = 'install/index.php?redirect_url=index.php' . urlencode('?TYPO3_INSTALL[type]=config#set_encryptionKey');
126 $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 />');
127 }
128 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_fileDenyPattern'), $value, $message, $severity);
129 }
130
131 /**
132 * Checks if fileDenyPattern allows to upload .htaccess files which is
133 * dangerous on Apache.
134 *
135 * @return \TYPO3\CMS\Reports\Status An object representing whether it's possible to upload .htaccess files
136 */
137 protected function getHtaccessUploadStatus() {
138 $value = $GLOBALS['LANG']->getLL('status_ok');
139 $message = '';
140 $severity = \TYPO3\CMS\Reports\Status::OK;
141 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] != FILE_DENY_PATTERN_DEFAULT && GeneralUtility::verifyFilenameAgainstDenyPattern('.htaccess')) {
142 $value = $GLOBALS['LANG']->getLL('status_insecure');
143 $severity = \TYPO3\CMS\Reports\Status::ERROR;
144 $message = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_deny_htaccess');
145 }
146 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_htaccessUploadProtection'), $value, $message, $severity);
147 }
148
149 /**
150 * Checks whether memcached is configured, if that's the case we assume it's also used.
151 *
152 * @return boolean TRUE if memcached is used, FALSE otherwise.
153 */
154 protected function isMemcachedUsed() {
155 $memcachedUsed = FALSE;
156 $memcachedServers = $this->getConfiguredMemcachedServers();
157 if (count($memcachedServers)) {
158 $memcachedUsed = TRUE;
159 }
160 return $memcachedUsed;
161 }
162
163 /**
164 * Executes commands like removing the Install Tool enable file.
165 *
166 * @return void
167 */
168 protected function executeAdminCommand() {
169 $command = GeneralUtility::_GET('adminCmd');
170 switch ($command) {
171 case 'remove_ENABLE_INSTALL_TOOL':
172 EnableFileService::removeInstallToolEnableFile();
173 break;
174 default:
175 // Do nothing
176 }
177 }
178
179 /**
180 * Checks whether the Install Tool password is set to its default value.
181 *
182 * @return \TYPO3\CMS\Reports\Status An object representing the security of the install tool password
183 */
184 protected function getInstallToolPasswordStatus() {
185 $value = $GLOBALS['LANG']->getLL('status_ok');
186 $message = '';
187 $severity = \TYPO3\CMS\Reports\Status::OK;
188 $validPassword = TRUE;
189 $installToolPassword = $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'];
190 $saltFactory = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($installToolPassword);
191 if (is_object($saltFactory)) {
192 $validPassword = !$saltFactory->checkPassword('joh316', $installToolPassword);
193 } elseif ($installToolPassword === md5('joh316')) {
194 $validPassword = FALSE;
195 }
196 if (!$validPassword) {
197 $value = $GLOBALS['LANG']->getLL('status_insecure');
198 $severity = \TYPO3\CMS\Reports\Status::ERROR;
199 $changeInstallToolPasswordUrl = BackendUtility::getModuleUrl('system_InstallInstall');
200 $message = sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.installtool_default_password'), '<a href="' . $changeInstallToolPasswordUrl . '">', '</a>');
201 }
202 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_installToolPassword'), $value, $message, $severity);
203 }
204
205 /**
206 * Checks whether the Install Tool password is set to its default value.
207 *
208 * @return \TYPO3\CMS\Reports\Status An object representing the security of the saltedpassswords extension
209 */
210 protected function getSaltedPasswordsStatus() {
211 $value = $GLOBALS['LANG']->getLL('status_ok');
212 $message = '';
213 $severity = \TYPO3\CMS\Reports\Status::OK;
214 /** @var \TYPO3\CMS\Saltedpasswords\Utility\ExtensionManagerConfigurationUtility $configCheck */
215 $configCheck = GeneralUtility::makeInstance('TYPO3\\CMS\\Saltedpasswords\\Utility\\ExtensionManagerConfigurationUtility');
216 $message = '<p>' . $GLOBALS['LANG']->getLL('status_saltedPasswords_infoText') . '</p>';
217 $messageDetail = '';
218 $flashMessage = $configCheck->checkConfigurationBackend(array(), new \TYPO3\CMS\Core\TypoScript\ConfigurationForm());
219 if (strpos($flashMessage, 'message-error') !== FALSE) {
220 $value = $GLOBALS['LANG']->getLL('status_insecure');
221 $severity = \TYPO3\CMS\Reports\Status::ERROR;
222 $messageDetail .= $flashMessage;
223 }
224 if (strpos($flashMessage, 'message-warning') !== FALSE) {
225 $severity = \TYPO3\CMS\Reports\Status::WARNING;
226 $messageDetail .= $flashMessage;
227 }
228 if (strpos($flashMessage, 'message-information') !== FALSE) {
229 $messageDetail .= $flashMessage;
230 }
231 $unsecureUserCount = \TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility::getNumberOfBackendUsersWithInsecurePassword();
232 if ($unsecureUserCount > 0) {
233 $value = $GLOBALS['LANG']->getLL('status_insecure');
234 $severity = \TYPO3\CMS\Reports\Status::ERROR;
235 $messageDetail .= '<div class="typo3-message message-warning">' . $GLOBALS['LANG']->getLL('status_saltedPasswords_notAllPasswordsHashed') . '</div>';
236 }
237 $message .= $messageDetail;
238 if (empty($messageDetail)) {
239 $message = '';
240 }
241 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_saltedPasswords'), $value, $message, $severity);
242 }
243
244 /**
245 * Checks for the existence of the ENABLE_INSTALL_TOOL file.
246 *
247 * @return \TYPO3\CMS\Reports\Status An object representing whether ENABLE_INSTALL_TOOL exists
248 */
249 protected function getInstallToolProtectionStatus() {
250 $enableInstallToolFile = PATH_site . 'typo3conf/ENABLE_INSTALL_TOOL';
251 $value = $GLOBALS['LANG']->getLL('status_disabled');
252 $message = '';
253 $severity = \TYPO3\CMS\Reports\Status::OK;
254 if (EnableFileService::installToolEnableFileExists()) {
255 if (EnableFileService::isInstallToolEnableFilePermanent()) {
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 if (EnableFileService::installToolEnableFileLifetimeExpired()) {
263 EnableFileService::removeInstallToolEnableFile();
264 } else {
265 $severity = \TYPO3\CMS\Reports\Status::NOTICE;
266 $disableInstallToolUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '&amp;adminCmd=remove_ENABLE_INSTALL_TOOL';
267 $value = $GLOBALS['LANG']->getLL('status_enabledTemporarily');
268 $message = sprintf($GLOBALS['LANG']->getLL('status_installEnabledTemporarily'), '<span style="white-space: nowrap;">' . $enableInstallToolFile . '</span>', floor($enableInstallToolFileTtl / 60));
269 $message .= ' <a href="' . $disableInstallToolUrl . '">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
270 }
271 }
272 }
273 return GeneralUtility::makeInstance('TYPO3\\CMS\\Reports\\Status', $GLOBALS['LANG']->getLL('status_installTool'), $value, $message, $severity);
274 }
275
276 }