uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); } /** * Shows and handles backend user session confirmation ("sudo mode") for * accessing a particular Install Tool controller (as given in `$targetController`). * * @param ServerRequestInterface $request * @return ResponseInterface */ public function backendUserConfirmationAction(ServerRequestInterface $request): ResponseInterface { $flags = (int)($request->getQueryParams()['flags'] ?? 0); $targetController = (string)($request->getQueryParams()['targetController'] ?? ''); $targetHash = (string)($request->getQueryParams()['targetHash'] ?? ''); $expectedTargetHash = GeneralUtility::hmac($targetController, BackendModuleController::class); $flagInstallToolPassword = (bool)($flags & self::FLAG_INSTALL_TOOL_PASSWORD); $flagInvalidPassword = false; if (!in_array($targetController, self::ALLOWED_ACTIONS, true) || !hash_equals($expectedTargetHash, $targetHash)) { return new HtmlResponse('', 403); } if ($flags & self::FLAG_CONFIRMATION_REQUEST) { if ($flagInstallToolPassword && $this->verifyInstallToolPassword($request)) { return $this->setAuthorizedAndRedirect($targetController); } if (!$flagInstallToolPassword && $this->verifyBackendUserPassword($request)) { return $this->setAuthorizedAndRedirect($targetController); } $flagInvalidPassword = true; } $view = GeneralUtility::makeInstance(StandaloneView::class); $view->getTemplatePaths()->setTemplatePathAndFilename( ExtensionManagementUtility::extPath( 'install', 'Resources/Private/Templates/BackendModule/BackendUserConfirmation.html' ) ); $view->assignMultiple([ 'flagInvalidPassword' => $flagInvalidPassword, 'flagInstallToolPassword' => $flagInstallToolPassword, 'languageFileReference' => 'LLL:EXT:install/Resources/Private/Language/BackendModule.xlf', 'passwordModeUri' => $this->getBackendUserConfirmationUri([ 'targetController' => $targetController, 'targetHash' => $targetHash, // current flags, unset FLAG_CONFIRMATION_REQUEST, toggle FLAG_INSTALL_TOOL_PASSWORD 'flags' => $flags & ~self::FLAG_CONFIRMATION_REQUEST ^ self::FLAG_INSTALL_TOOL_PASSWORD, ]), 'verifyUri' => $this->getBackendUserConfirmationUri([ 'targetController' => $targetController, 'targetHash' => $targetHash, // current flags, add FLAG_CONFIRMATION_REQUEST 'flags' => $flags | self::FLAG_CONFIRMATION_REQUEST, ]), 'cancelUri' => '', ]); $this->applyRsaAuthModules(); return new HtmlResponse($view->render()); } /** * Initialize session and redirect to "maintenance" * * @return ResponseInterface */ public function maintenanceAction(): ResponseInterface { return $this->getBackendUserConfirmationRedirect('maintenance') ?? $this->setAuthorizedAndRedirect('maintenance'); } /** * Initialize session and redirect to "settings" * * @return ResponseInterface */ public function settingsAction(): ResponseInterface { return $this->getBackendUserConfirmationRedirect('settings') ?? $this->setAuthorizedAndRedirect('settings'); } /** * Initialize session and redirect to "upgrade" * * @return ResponseInterface */ public function upgradeAction(): ResponseInterface { return $this->getBackendUserConfirmationRedirect('upgrade') ?? $this->setAuthorizedAndRedirect('upgrade'); } /** * Initialize session and redirect to "environment" * * @return ResponseInterface */ public function environmentAction(): ResponseInterface { return $this->getBackendUserConfirmationRedirect('environment') ?? $this->setAuthorizedAndRedirect('environment'); } /** * Creates redirect response to backend user confirmation (if required). * * @param string $targetController * @return ResponseInterface|null */ protected function getBackendUserConfirmationRedirect(string $targetController): ?ResponseInterface { if ($this->getSessionService()->isAuthorizedBackendUserSession()) { return null; } if (Environment::getContext()->isDevelopment()) { return null; } $redirectUri = $this->getBackendUserConfirmationUri([ 'targetController' => $targetController, 'targetHash' => GeneralUtility::hmac($targetController, BackendModuleController::class), ]); return new RedirectResponse((string)$redirectUri, 403); } protected function getBackendUserConfirmationUri(array $parameters): Uri { return $this->uriBuilder->buildUriFromRoute( 'install.backend-user-confirmation', $parameters ); } /** * Starts / updates the session and redirects to the Install Tool * with given action. * * @param $controller * @return ResponseInterface */ protected function setAuthorizedAndRedirect(string $controller): ResponseInterface { $this->getSessionService()->setAuthorizedBackendSession(); $redirectLocation = 'install.php?install[controller]=' . $controller . '&install[context]=backend'; return new RedirectResponse($redirectLocation, 303); } /** * Verifies that provided password matches Install Tool password. * * @param ServerRequestInterface $request * @return bool */ protected function verifyInstallToolPassword(ServerRequestInterface $request): bool { $parsedBody = $request->getParsedBody(); $password = $parsedBody['confirmationPassword'] ?? null; $installToolPassword = $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'] ?? null; if (!is_string($password) || empty($installToolPassword)) { return false; } try { $hashFactory = GeneralUtility::makeInstance(PasswordHashFactory::class); $hashInstance = $hashFactory->get($installToolPassword, 'BE'); return $hashInstance->checkPassword($password, $installToolPassword); } catch (InvalidPasswordHashException $exception) { return false; } } /** * Verifies that provided password is actually correct for current backend user * by stepping through authentication chain in `$GLOBALS['BE_USER]`. * * @param ServerRequestInterface $request * @return bool */ protected function verifyBackendUserPassword(ServerRequestInterface $request): bool { $parsedBody = $request->getParsedBody(); $password = empty($parsedBody['confirmationPasswordInternal']) ? ($parsedBody['confirmationPassword'] ?? null) // default field : $parsedBody['confirmationPasswordInternal']; // filled e.g. by `ext:rsaauth` if (!is_string($password)) { return false; } // clone current backend user object to avoid // possible side effects for the real instance $backendUser = clone $this->getBackendUser(); $loginData = [ 'status' => 'sudo-mode', 'origin' => BackendModuleController::class, 'uname' => $backendUser->user['username'], 'uident' => $password, ]; // currently there is no dedicated API to perform authentication // that's why this process partially has to be simulated here $loginData = $backendUser->processLoginData($loginData); $authInfo = $backendUser->getAuthInfoArray(); $authenticated = false; /** @var AbstractAuthenticationService $service or any other service (sic!) */ foreach ($this->getAuthServices($backendUser, $loginData, $authInfo) as $service) { $ret = (int)$service->authUser($backendUser->user); if ($ret <= 0) { return false; } if ($ret >= 200) { return true; } if ($ret >= 100) { continue; } $authenticated = true; continue; } return $authenticated; } /** * Initializes authentication services to be used in a foreach loop * * @param BackendUserAuthentication $backendUser * @param array $loginData * @param array $authInfo * @return \Generator */ protected function getAuthServices(BackendUserAuthentication $backendUser, array $loginData, array $authInfo): \Generator { $serviceChain = []; $subType = 'authUserBE'; while ($service = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain)) { $serviceChain[] = $service->getServiceKey(); if (!is_object($service)) { break; } $service->initAuth($subType, $loginData, $authInfo, $backendUser); yield $service; } } protected function getBackendUser(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } /** * Install Tool modified sessions meta-data (handler, storage, name) which * conflicts with existing session that for instance is used for ext:rsaauth. * * @return SessionService */ protected function getSessionService(): SessionService { if ($this->sessionService === null) { $this->sessionService = new SessionService(); $this->sessionService->startSession(); } return $this->sessionService; } protected function applyRsaAuthModules() { if (!ExtensionManagementUtility::isLoaded('rsaauth')) { return; } $rsaEncryptionEncoder = GeneralUtility::makeInstance(RsaEncryptionEncoder::class); $rsaEncryptionEncoder->enableRsaEncryption(true); } }