1<?php
2namespace TYPO3\CMS\Install\Report;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17use TYPO3\CMS\Core\Core\Environment;
18use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
19use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
20use TYPO3\CMS\Core\Utility\GeneralUtility;
21use TYPO3\CMS\Install\Service\EnableFileService;
22use TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck;
23use TYPO3\CMS\Reports\Status;
24
25/**
26 * Provides an status report of the security of the install tool
27 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
28 */
29class SecurityStatusReport implements \TYPO3\CMS\Reports\StatusProviderInterface
30{
31    /**
32     * Compiles a collection of system status checks as a status report.
33     *
34     * @return Status[]
35     */
36    public function getStatus()
37    {
38        $this->executeAdminCommand();
39        return [
40            'installToolPassword' => $this->getInstallToolPasswordStatus(),
41            'installToolProtection' => $this->getInstallToolProtectionStatus(),
42            'serverResponseStatus' => GeneralUtility::makeInstance(ServerResponseCheck::class)->asStatus(),
43        ];
44    }
45
46    /**
47     * Checks whether the Install Tool password is set to its default value.
48     *
49     * @return Status An object representing the security of the install tool password
50     */
51    protected function getInstallToolPasswordStatus()
52    {
53        // @todo @deprecated: This should be removed in TYPO3 v10.0 when install tool allows proper hashes only
54        $value = $GLOBALS['LANG']->getLL('status_ok');
55        $message = '';
56        $severity = Status::OK;
57        $isDefaultPassword = false;
58        $installToolPassword = $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'];
59        $hashInstance = null;
60        $hashFactory = GeneralUtility::makeInstance(PasswordHashFactory::class);
61        try {
62            $hashInstance = $hashFactory->get($installToolPassword, 'BE');
63        } catch (InvalidPasswordHashException $e) {
64            // $hashInstance stays null
65            $value = $GLOBALS['LANG']->getLL('status_wrongValue');
66            $message = $e->getMessage();
67            $severity = Status::ERROR;
68        }
69        if ($installToolPassword !== '' && $hashInstance !== null) {
70            $isDefaultPassword = $hashInstance->checkPassword('joh316', $installToolPassword);
71        } elseif ($installToolPassword === 'bacb98acf97e0b6112b1d1b650b84971') {
72            // using MD5 of legacy default password 'joh316'
73            $isDefaultPassword = true;
74        }
75        if ($isDefaultPassword) {
76            $value = $GLOBALS['LANG']->getLL('status_insecure');
77            $severity = Status::ERROR;
78            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
79            $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
80            $changeInstallToolPasswordUrl = (string)$uriBuilder->buildUriFromRoute('tools_toolssettings');
81            $message = sprintf(
82                $GLOBALS['LANG']->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.installtool_default_password'),
83                '<a href="' . htmlspecialchars($changeInstallToolPasswordUrl) . '">',
84                '</a>'
85            );
86        }
87        return GeneralUtility::makeInstance(
88            Status::class,
89            $GLOBALS['LANG']->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_installToolPassword'),
90            $value,
91            $message,
92            $severity
93        );
94    }
95
96    /**
97     * Checks for the existence of the ENABLE_INSTALL_TOOL file.
98     *
99     * @return Status An object representing whether ENABLE_INSTALL_TOOL exists
100     */
101    protected function getInstallToolProtectionStatus()
102    {
103        $enableInstallToolFile = Environment::getPublicPath() . '/' . EnableFileService::INSTALL_TOOL_ENABLE_FILE_PATH;
104        $value = $GLOBALS['LANG']->getLL('status_disabled');
105        $message = '';
106        $severity = Status::OK;
107        if (EnableFileService::installToolEnableFileExists()) {
108            if (EnableFileService::isInstallToolEnableFilePermanent()) {
109                $severity = Status::WARNING;
110                $disableInstallToolUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '&adminCmd=remove_ENABLE_INSTALL_TOOL';
111                $value = $GLOBALS['LANG']->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_enabledPermanently');
112                $message = sprintf(
113                    $GLOBALS['LANG']->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.install_enabled'),
114                    '<code style="white-space: nowrap;">' . $enableInstallToolFile . '</code>'
115                );
116                $message .= ' <a href="' . htmlspecialchars($disableInstallToolUrl) . '">' .
117                    $GLOBALS['LANG']->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
118            } else {
119                if (EnableFileService::installToolEnableFileLifetimeExpired()) {
120                    EnableFileService::removeInstallToolEnableFile();
121                } else {
122                    $severity = Status::NOTICE;
123                    $disableInstallToolUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '&adminCmd=remove_ENABLE_INSTALL_TOOL';
124                    $value = $GLOBALS['LANG']->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_enabledTemporarily');
125                    $message = sprintf(
126                        $GLOBALS['LANG']->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_installEnabledTemporarily'),
127                        '<code style="white-space: nowrap;">' . $enableInstallToolFile . '</code>',
128                        floor((@filemtime($enableInstallToolFile) + EnableFileService::INSTALL_TOOL_ENABLE_FILE_LIFETIME - time()) / 60)
129                    );
130                    $message .= ' <a href="' . htmlspecialchars($disableInstallToolUrl) . '">' .
131                        $GLOBALS['LANG']->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.install_enabled_cmd') . '</a>';
132                }
133            }
134        }
135        return GeneralUtility::makeInstance(
136            Status::class,
137            $GLOBALS['LANG']->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_installTool'),
138            $value,
139            $message,
140            $severity
141        );
142    }
143
144    /**
145     * Executes commands like removing the Install Tool enable file.
146     */
147    protected function executeAdminCommand()
148    {
149        $command = GeneralUtility::_GET('adminCmd');
150        switch ($command) {
151            case 'remove_ENABLE_INSTALL_TOOL':
152                EnableFileService::removeInstallToolEnableFile();
153                break;
154            default:
155                // Do nothing
156        }
157    }
158}
159