[SECURITY] Limit user access in workspace previews 98/47598/2
authorNicole Cordes <typo3@cordes.co>
Tue, 12 Apr 2016 09:09:51 +0000 (11:09 +0200)
committerOliver Hader <oliver.hader@typo3.org>
Tue, 12 Apr 2016 09:09:53 +0000 (11:09 +0200)
To view a preview of a workspace page a backend user is simulated.
Currently the user who created the preview link is taken into account.
This patch creates a limited backend user to be able to process the
web request.

Resolves: #28175
Releases: master, 7.6, 6.2
Security-Commit: 4d89b8acd5af116444402b0fc2527fd8217e0fca
Security-Bulletins: TYPO3-CORE-SA-2016-009, 010, 011, 012
Change-Id: I4ae4b46fa3345d179cd2748ae0caa48ed3a8e4d9
Reviewed-on: https://review.typo3.org/47598
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
typo3/sysext/version/Classes/Hook/PreviewHook.php
typo3/sysext/version/ext_localconf.php

index fe0808d..b415873 100644 (file)
@@ -14,6 +14,9 @@ namespace TYPO3\CMS\Version\Hook;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -46,6 +49,14 @@ class PreviewHook implements \TYPO3\CMS\Core\SingletonInterface {
        protected $previewConfiguration = FALSE;
 
        /**
+        * Defines whether to force read permissions on pages.
+        *
+        * @var bool
+        * @see \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::getPagePermsClause
+        */
+       protected $forceReadPermissions = FALSE;
+
+       /**
         * hook to check if the preview is activated
         * right now, this hook is called at the end of "$TSFE->connectToDB"
         *
@@ -92,33 +103,59 @@ class PreviewHook implements \TYPO3\CMS\Core\SingletonInterface {
         * @return void
         */
        public function initializePreviewUser(&$params, &$pObj) {
+               // if there is a valid BE user, and the full workspace should be previewed, the workspacePreview option should be set
+               $workspaceUid = $this->previewConfiguration['fullWorkspace'];
+               $workspaceRecord = null;
                if ((is_null($params['BE_USER']) || $params['BE_USER'] === FALSE) && $this->previewConfiguration !== FALSE && $this->previewConfiguration['BEUSER_uid'] > 0) {
-                       // New backend user object
-                       $BE_USER = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\FrontendBackendUserAuthentication');
-                       $BE_USER->userTS_dontGetCached = 1;
-                       $BE_USER->OS = TYPO3_OS;
-                       $BE_USER->setBeUserByUid($this->previewConfiguration['BEUSER_uid']);
-                       $BE_USER->unpack_uc('');
-                       if ($BE_USER->user['uid']) {
-                               $BE_USER->fetchGroupData();
+                       // First initialize a temp user object and resolve usergroup information
+                       /** @var FrontendBackendUserAuthentication $tempBackendUser */
+                       $tempBackendUser = $this->createFrontendBackendUser();
+                       $tempBackendUser->userTS_dontGetCached = 1;
+                       $tempBackendUser->setBeUserByUid($this->previewConfiguration['BEUSER_uid']);
+                       if ($tempBackendUser->user['uid']) {
+                               $tempBackendUser->unpack_uc('');
+                               $tempBackendUser->fetchGroupData();
+                               // Handle degradation of admin users
+                               if ($tempBackendUser->isAdmin() && ExtensionManagementUtility::isLoaded('workspaces')) {
+                                       $workspaceRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
+                                               'uid, adminusers, reviewers, members, db_mountpoints',
+                                               'sys_workspace',
+                                               'pid=0 AND uid=' . (int)$workspaceUid . BackendUtility::deleteClause('sys_workspace')
+                                       );
+                                       // Either use configured workspace mount or current page id, if admin user does not have any page mounts
+                                       if (empty($tempBackendUser->groupData['webmounts'])) {
+                                               $tempBackendUser->groupData['webmounts'] = !empty($workspaceRecord['db_mountpoints']) ? $workspaceRecord['db_mountpoints'] : $pObj->id;
+                                       }
+                                       // Force add degraded admin user as member of this workspace
+                                       $workspaceRecord['members'] = 'be_users_' . $this->previewConfiguration['BEUSER_uid'];
+                                       // Force read permission for degraded admin user
+                                       $this->forceReadPermissions = TRUE;
+                               }
+                               // Store only needed information in the real simulate backend
+                               $BE_USER = $this->createFrontendBackendUser();
+                               $BE_USER->userTS_dontGetCached = 1;
+                               $BE_USER->user = $tempBackendUser->user;
+                               $BE_USER->user['admin'] = 0;
+                               $BE_USER->groupData['webmounts'] = $tempBackendUser->groupData['webmounts'];
+                               $BE_USER->groupList = $tempBackendUser->groupList;
+                               $BE_USER->userGroups = $tempBackendUser->userGroups;
+                               $BE_USER->userGroupsUID = $tempBackendUser->userGroupsUID;
                                $pObj->beUserLogin = TRUE;
                        } else {
                                $BE_USER = NULL;
                                $pObj->beUserLogin = FALSE;
                                $_SESSION['TYPO3-TT-start'] = FALSE;
                        }
+                       unset($tempBackendUser);
                        $params['BE_USER'] = $BE_USER;
                }
-               // if there is a valid BE user, and the full workspace should be
-               // previewed, the workspacePreview option shouldbe set
-               $workspaceUid = $this->previewConfiguration['fullWorkspace'];
                if (
                        $pObj->beUserLogin
                        && is_object($params['BE_USER'])
                        && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($workspaceUid)
                        && $params['BE_USER']->isInWebMount($pObj->id)
                ) {
-                       if ($workspaceUid == 0 || $workspaceUid >= -1 && $params['BE_USER']->checkWorkspace($workspaceUid)) {
+                       if ($workspaceUid == 0 || $workspaceUid >= -1 && $params['BE_USER']->checkWorkspace($workspaceRecord ?: $workspaceUid)) {
                                // Check Access to workspace. Live (0) is OK to preview for all.
                                $pObj->workspacePreview = (int)$workspaceUid;
                        } else {
@@ -129,6 +166,40 @@ class PreviewHook implements \TYPO3\CMS\Core\SingletonInterface {
        }
 
        /**
+        * Overrides the page permission clause in case an admin
+        * user has been degraded to a regular user without any user
+        * group assignments. This method is used as hook callback.
+        *
+        * @param array $parameters
+        * @return string
+        * @see \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::getPagePermsClause
+        */
+       public function overridePagePermissionClause(array $parameters) {
+               $clause = $parameters['currentClause'];
+               if ($parameters['perms'] & 1 && $this->forceReadPermissions) {
+                       $clause = ' 1=1';
+               }
+               return $clause;
+       }
+
+       /**
+        * Overrides the row permission value in case an admin
+        * user has been degraded to a regular user without any user
+        * group assignments. This method is used as hook callback.
+        *
+        * @param array $parameters
+        * @return int
+        * @see \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::calcPerms
+        */
+       public function overridePermissionCalculation(array $parameters) {
+               $permissions = $parameters['outputPermissions'];
+               if (!($permissions & 1) && $this->forceReadPermissions) {
+                       $permissions |= 1;
+               }
+               return $permissions;
+       }
+
+       /**
         * Looking for a ADMCMD_prev code, looks it up if found and returns configuration data.
         * Background: From the backend a request to the frontend to show a page, possibly with
         * workspace preview can be "recorded" and associated with a keyword.
@@ -273,4 +344,13 @@ class PreviewHook implements \TYPO3\CMS\Core\SingletonInterface {
                return $ttlHours ? $ttlHours : 24 * 2;
        }
 
+       /**
+        * @return FrontendBackendUserAuthentication
+        */
+       protected function createFrontendBackendUser() {
+               return GeneralUtility::makeInstance(
+                       'TYPO3\\CMS\\Backend\\FrontendBackendUserAuthentication'
+               );
+       }
+
 }
index 90e43d6..8d77650 100644 (file)
@@ -13,6 +13,8 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_iconworks.php']['ov
 // Register hook to check for the preview mode in the FE
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB']['version_preview'] = 'TYPO3\\CMS\\Version\\Hook\\PreviewHook->checkForPreview';
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser']['version_preview'] = 'TYPO3\\CMS\\Version\\Hook\\PreviewHook->initializePreviewUser';
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause']['version_preview'] = 'TYPO3\\CMS\\Version\\Hook\\PreviewHook->overridePagePermissionClause';
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms']['version_preview'] = 'TYPO3\\CMS\\Version\\Hook\\PreviewHook->overridePermissionCalculation';
 
 if (TYPO3_MODE === 'BE') {
        // add default notification options to every page