From 33f4d279b82bca0a509227a17065244c6156e68f Mon Sep 17 00:00:00 2001
From: Benjamin Franzke <ben@bnf.dev>
Date: Tue, 13 Feb 2024 10:06:13 +0100
Subject: [PATCH] [SECURITY] Prevent arbitrary access to privileged resources
 via t3://

Resolves: #93571
Releases: main, 13.0, 12.4, 11.5
Change-Id: I9622bfa47ef9637cecaff4a790f742445f598682
Security-Bulletin: TYPO3-CORE-SA-2024-005
Security-References: CVE-2024-25120
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/82949
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
---
 .../Backend/Shortcut/ShortcutRepository.php   |  2 +-
 .../Classes/Controller/LinkController.php     |  2 +-
 .../Resource/ResourceController.php           |  2 +-
 .../Classes/Form/Element/LinkElement.php      | 13 ++++--
 .../Classes/LinkHandler/PageLinkHandler.php   | 13 +++++-
 .../Classes/LinkHandling/FileLinkHandler.php  |  8 ++++
 .../LegacyLinkNotationConverter.php           |  2 +-
 .../core/Classes/Resource/ResourceStorage.php | 35 ++++++++++++---
 .../Security/StoragePermissionsAspect.php     |  7 +--
 .../Utility/File/ExtendedFileUtility.php      |  2 +-
 .../TypoLinkSoftReferenceParserTest.php       |  3 ++
 .../TypoLinkTagSoftReferenceParserTest.php    |  2 +
 .../Unit/LinkHandling/FileLinkHandlerTest.php |  2 +-
 .../Controller/File/CreateFileController.php  |  2 +-
 .../Controller/File/EditFileController.php    |  2 +-
 .../Controller/File/FileUploadController.php  |  2 +-
 .../Controller/File/ReplaceFileController.php |  2 +-
 .../Classes/Controller/FileListController.php |  2 +-
 .../AbstractResourceLinkHandler.php           | 43 +++++++++++++++----
 19 files changed, 110 insertions(+), 36 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Backend/Shortcut/ShortcutRepository.php b/typo3/sysext/backend/Classes/Backend/Shortcut/ShortcutRepository.php
index 90d2d6786280..d3ac50d07d03 100644
--- a/typo3/sysext/backend/Classes/Backend/Shortcut/ShortcutRepository.php
+++ b/typo3/sysext/backend/Classes/Backend/Shortcut/ShortcutRepository.php
@@ -405,7 +405,7 @@ class ShortcutRepository
                 $combinedIdentifier = (string)($arguments['id'] ?? '');
                 if ($combinedIdentifier !== '') {
                     $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByCombinedIdentifier($combinedIdentifier);
-                    if ($storage === null || $storage->getUid() === 0) {
+                    if ($storage === null || $storage->isFallbackStorage()) {
                         // Continue, if invalid storage or disallowed fallback storage
                         continue;
                     }
diff --git a/typo3/sysext/backend/Classes/Controller/LinkController.php b/typo3/sysext/backend/Classes/Controller/LinkController.php
index d67d10960992..9254b20f51a1 100644
--- a/typo3/sysext/backend/Classes/Controller/LinkController.php
+++ b/typo3/sysext/backend/Classes/Controller/LinkController.php
@@ -56,7 +56,7 @@ final class LinkController
             if (!$resource instanceof File && !$resource instanceof Folder) {
                 throw new \InvalidArgumentException('Resource must be a file or a folder', 1679039649);
             }
-            if ($resource->getStorage()->getUid() === 0) {
+            if ($resource->getStorage()->isFallbackStorage()) {
                 throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1679039650);
             }
             if ($resource instanceof File) {
diff --git a/typo3/sysext/backend/Classes/Controller/Resource/ResourceController.php b/typo3/sysext/backend/Classes/Controller/Resource/ResourceController.php
index 9d86b618b39b..8528e4eb0a61 100644
--- a/typo3/sysext/backend/Classes/Controller/Resource/ResourceController.php
+++ b/typo3/sysext/backend/Classes/Controller/Resource/ResourceController.php
@@ -54,7 +54,7 @@ final class ResourceController
             if (!$origin instanceof File && !$origin instanceof Folder) {
                 throw new \InvalidArgumentException('Resource must be a file or a folder', 1676979120);
             }
-            if ($origin->getStorage()->getUid() === 0) {
+            if ($origin->getStorage()->isFallbackStorage()) {
                 throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1676299579);
             }
             if (!$origin->checkActionPermission('rename')) {
diff --git a/typo3/sysext/backend/Classes/Form/Element/LinkElement.php b/typo3/sysext/backend/Classes/Form/Element/LinkElement.php
index 8986b75bc647..621bcef6d560 100644
--- a/typo3/sysext/backend/Classes/Form/Element/LinkElement.php
+++ b/typo3/sysext/backend/Classes/Form/Element/LinkElement.php
@@ -31,6 +31,7 @@ use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
 use TYPO3\CMS\Core\Resource\Exception\InvalidPathException;
 use TYPO3\CMS\Core\Resource\File;
 use TYPO3\CMS\Core\Resource\Folder;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Core\Utility\StringUtility;
@@ -335,10 +336,12 @@ class LinkElement extends AbstractFormElement
             }
         }
 
+        $backendUser = $this->getBackendUser();
         // Resolve the actual link
         switch ($linkData['type']) {
             case LinkService::TYPE_PAGE:
-                $pageRecord = BackendUtility::readPageAccess($linkData['pageuid'] ?? null, '1=1');
+                $pagePermissionClause = $backendUser->getPagePermsClause(Permission::PAGE_SHOW);
+                $pageRecord = BackendUtility::readPageAccess($linkData['pageuid'] ?? null, $pagePermissionClause);
                 // Is this a real page
                 if ($pageRecord['uid'] ?? 0) {
                     $fragmentTitle = '';
@@ -372,7 +375,7 @@ class LinkElement extends AbstractFormElement
                 break;
             case LinkService::TYPE_FILE:
                 $file = $linkData['file'] ?? null;
-                if ($file instanceof File) {
+                if ($file instanceof File && $file->checkActionPermission('read') && !$file->getStorage()->isFallbackStorage()) {
                     $data = [
                         'text' => $file->getPublicUrl(),
                         'icon' => $this->iconFactory->getIconForFileExtension($file->getExtension(), Icon::SIZE_SMALL)->render(),
@@ -381,7 +384,7 @@ class LinkElement extends AbstractFormElement
                 break;
             case LinkService::TYPE_FOLDER:
                 $folder = $linkData['folder'] ?? null;
-                if ($folder instanceof Folder) {
+                if ($folder instanceof Folder && $folder->checkActionPermission('read') && !$folder->getStorage()->isFallbackStorage()) {
                     $data = [
                         'text' => $folder->getPublicUrl(),
                         'icon' => $this->iconFactory->getIcon('apps-filetree-folder-default', Icon::SIZE_SMALL)->render(),
@@ -391,7 +394,9 @@ class LinkElement extends AbstractFormElement
             case LinkService::TYPE_RECORD:
                 $table = $this->data['pageTsConfig']['TCEMAIN.']['linkHandler.'][$linkData['identifier'] . '.']['configuration.']['table'] ?? '';
                 $record = BackendUtility::getRecord($table, $linkData['uid']);
-                if ($record) {
+                $pagePermissionClause = $backendUser->getPagePermsClause(Permission::PAGE_SHOW);
+                $hasPageAccess = BackendUtility::readPageAccess($record['pid'] ?? null, $pagePermissionClause) !== false;
+                if ($record && $hasPageAccess && $backendUser->check('tables_select', $table)) {
                     $recordTitle = BackendUtility::getRecordTitle($table, $record);
                     $tableTitle = $this->getLanguageService()->sL($GLOBALS['TCA'][$table]['ctrl']['title']);
                     $data = [
diff --git a/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php b/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php
index c805e6658c2b..f7f48a73f4e5 100644
--- a/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php
+++ b/typo3/sysext/backend/Classes/LinkHandler/PageLinkHandler.php
@@ -25,6 +25,7 @@ use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
 use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\LinkHandling\LinkService;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 
@@ -87,11 +88,19 @@ class PageLinkHandler extends AbstractLinkHandler implements LinkHandlerInterfac
         $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
 
         $id = (int)$this->linkParts['url']['pageuid'];
-        $pageTitle = BackendUtility::getRecordWSOL('pages', $id, 'title')['title'] ?? '';
 
+        $idInfo = 'ID: ' . $id . (!empty($this->linkParts['url']['fragment']) ? ', #' . $this->linkParts['url']['fragment'] : '');
+
+        $permsClause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
+        $pageRecord = BackendUtility::readPageAccess($id, $permsClause);
+        if ($pageRecord === false) {
+            return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page') . ' ' . $idInfo;
+        }
+
+        $pageTitle = $pageRecord['title'] ?? '';
         return $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:page')
             . ($pageTitle ? ' \'' . GeneralUtility::fixed_lgd_cs($pageTitle, $titleLen) . '\'' : '')
-            . ' (ID: ' . $id . (!empty($this->linkParts['url']['fragment']) ? ', #' . $this->linkParts['url']['fragment'] : '') . ')';
+            . ' (' . $idInfo . ')';
     }
 
     /**
diff --git a/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php b/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php
index f6d7ce8dfbc1..845cceb9cfad 100644
--- a/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php
+++ b/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php
@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Core\LinkHandling;
 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
 use TYPO3\CMS\Core\Resource\FileInterface;
 use TYPO3\CMS\Core\Resource\ResourceFactory;
+use TYPO3\CMS\Core\Resource\Security\FileNameValidator;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -71,6 +72,13 @@ class FileLinkHandler implements LinkHandlingInterface
     {
         try {
             $file = $this->resolveFile($data);
+            $fileNameValidator = GeneralUtility::makeInstance(FileNameValidator::class);
+            if (
+                !$fileNameValidator->isValid(basename($file->getIdentifier())) ||
+                !$fileNameValidator->isValid($file->getName())
+            ) {
+                $file = null;
+            }
         } catch (FileDoesNotExistException $e) {
             $file = null;
         }
diff --git a/typo3/sysext/core/Classes/LinkHandling/LegacyLinkNotationConverter.php b/typo3/sysext/core/Classes/LinkHandling/LegacyLinkNotationConverter.php
index 889b484a297c..80555d3fa965 100644
--- a/typo3/sysext/core/Classes/LinkHandling/LegacyLinkNotationConverter.php
+++ b/typo3/sysext/core/Classes/LinkHandling/LegacyLinkNotationConverter.php
@@ -220,7 +220,7 @@ class LegacyLinkNotationConverter
             }
             $fileOrFolderObject = $this->getResourceFactory()->retrieveFileOrFolderObject($fileIdentifier);
             // Links to a file/folder in the main TYPO3 directory should not be considered as file links, but an external link
-            if ($fileOrFolderObject instanceof ResourceInterface && $fileOrFolderObject->getStorage()->getUid() === 0) {
+            if ($fileOrFolderObject instanceof ResourceInterface && $fileOrFolderObject->getStorage()->isFallbackStorage()) {
                 return [
                     'type' => LinkService::TYPE_URL,
                     'url' => $mixedIdentifier,
diff --git a/typo3/sysext/core/Classes/Resource/ResourceStorage.php b/typo3/sysext/core/Classes/Resource/ResourceStorage.php
index e0db2210ecc3..38f61e199731 100644
--- a/typo3/sysext/core/Classes/Resource/ResourceStorage.php
+++ b/typo3/sysext/core/Classes/Resource/ResourceStorage.php
@@ -354,6 +354,17 @@ class ResourceStorage implements ResourceStorageInterface
         return true;
     }
 
+    /**
+     * Returns true if this storage is a virtual storage that provides
+     * access to all files in the project root.
+     *
+     * @internal
+     */
+    public function isFallbackStorage(): bool
+    {
+        return $this->getUid() === 0;
+    }
+
     /*********************************
      * Capabilities
      ********************************/
@@ -718,7 +729,7 @@ class ResourceStorage implements ResourceStorageInterface
             return false;
         }
         // Check 3: No action allowed on files for denied file extensions
-        if (!$this->checkFileExtensionPermission($file->getName())) {
+        if (!$this->checkValidFileExtension($file)) {
             return false;
         }
         $isReadCheck = false;
@@ -830,6 +841,17 @@ class ResourceStorage implements ResourceStorageInterface
         return GeneralUtility::makeInstance(FileNameValidator::class)->isValid($fileName);
     }
 
+    /**
+     * Check file extension of an existing file against the
+     * current file deny pattern.
+     */
+    protected function checkValidFileExtension(FileInterface $file): bool
+    {
+        $fileNameValidator = GeneralUtility::makeInstance(FileNameValidator::class);
+        return $fileNameValidator->isValid($file->getName()) &&
+            $fileNameValidator->isValid(basename($file->getIdentifier()));
+    }
+
     /**
      * Assures read permission for given folder.
      *
@@ -896,7 +918,7 @@ class ResourceStorage implements ResourceStorageInterface
                 1375955429
             );
         }
-        if (!$this->checkFileExtensionPermission($file->getName())) {
+        if (!$this->checkValidFileExtension($file)) {
             throw new IllegalFileExtensionException(
                 'You are not allowed to use that file extension. File: "' . $file->getName() . '"',
                 1375955430
@@ -917,7 +939,7 @@ class ResourceStorage implements ResourceStorageInterface
         if (!$this->checkFileActionPermission('write', $file)) {
             throw new InsufficientFileWritePermissionsException('Writing to file "' . $file->getIdentifier() . '" is not allowed.', 1330121088);
         }
-        if (!$this->checkFileExtensionPermission($file->getName())) {
+        if (!$this->checkValidFileExtension($file)) {
             throw new IllegalFileExtensionException('You are not allowed to edit a file with extension "' . $file->getExtension() . '"', 1366711933);
         }
     }
@@ -950,7 +972,7 @@ class ResourceStorage implements ResourceStorageInterface
     protected function assureFileDeletePermissions(FileInterface $file)
     {
         // Check for disallowed file extensions
-        if (!$this->checkFileExtensionPermission($file->getName())) {
+        if (!$this->checkValidFileExtension($file)) {
             throw new IllegalFileExtensionException('You are not allowed to delete a file with extension "' . $file->getExtension() . '"', 1377778916);
         }
         // Check further permissions if file is not a processed file
@@ -1071,7 +1093,7 @@ class ResourceStorage implements ResourceStorageInterface
     protected function assureFileRenamePermissions(FileInterface $file, $targetFileName)
     {
         // Check if file extension is allowed
-        if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
+        if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkValidFileExtension($file)) {
             throw new IllegalFileExtensionException('You are not allowed to rename a file with this extension. File given: "' . $file->getName() . '"', 1371466663);
         }
         // Check if user is allowed to rename
@@ -1114,7 +1136,7 @@ class ResourceStorage implements ResourceStorageInterface
             throw new InsufficientFolderWritePermissionsException('You are not allowed to write to the target folder "' . $targetFolder->getIdentifier() . '"', 1319550435);
         }
         // Check for a valid file extension
-        if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkFileExtensionPermission($file->getName())) {
+        if (!$this->checkFileExtensionPermission($targetFileName) || !$this->checkValidFileExtension($file)) {
             throw new IllegalFileExtensionException('You are not allowed to copy a file of that type.', 1319553317);
         }
     }
@@ -1733,6 +1755,7 @@ class ResourceStorage implements ResourceStorageInterface
         string $alternativeFilename = null,
         string $overrideMimeType = null
     ): ResponseInterface {
+        $this->assureFileReadPermission($file);
         if (!$this->driver instanceof StreamableDriverInterface) {
             return $this->getPseudoStream($file, $asDownload, $alternativeFilename, $overrideMimeType);
         }
diff --git a/typo3/sysext/core/Classes/Resource/Security/StoragePermissionsAspect.php b/typo3/sysext/core/Classes/Resource/Security/StoragePermissionsAspect.php
index a01c7ea2f213..f5459b3b93cc 100644
--- a/typo3/sysext/core/Classes/Resource/Security/StoragePermissionsAspect.php
+++ b/typo3/sysext/core/Classes/Resource/Security/StoragePermissionsAspect.php
@@ -42,13 +42,10 @@ final class StoragePermissionsAspect
         if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
             && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()
             && !$GLOBALS['BE_USER']->isAdmin()
+            && !$storage->isFallbackStorage()
         ) {
             $storage->setEvaluatePermissions(true);
-            if ($storage->getUid() > 0) {
-                $storage->setUserPermissions($GLOBALS['BE_USER']->getFilePermissionsForStorage($storage));
-            } else {
-                $storage->setEvaluatePermissions(false);
-            }
+            $storage->setUserPermissions($GLOBALS['BE_USER']->getFilePermissionsForStorage($storage));
             $this->addFileMountsToStorage($storage);
         }
     }
diff --git a/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php b/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php
index 79167723aa62..31bd5612667c 100644
--- a/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php
+++ b/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php
@@ -590,7 +590,7 @@ class ExtendedFileUtility extends BasicFileUtility
         if ($object === null) {
             throw new InvalidFileException('The item ' . $identifier . ' was not a file or directory', 1320122453);
         }
-        if ($object->getStorage()->getUid() === 0) {
+        if ($object->getStorage()->isFallbackStorage()) {
             throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1375889830);
         }
         return $object;
diff --git a/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkSoftReferenceParserTest.php b/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkSoftReferenceParserTest.php
index 2e28e6c92f44..e1ca423840d9 100644
--- a/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkSoftReferenceParserTest.php
+++ b/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkSoftReferenceParserTest.php
@@ -247,6 +247,9 @@ final class TypoLinkSoftReferenceParserTest extends AbstractSoftReferenceParserT
         $storageObject->method('getUid')->willReturn(1);
         $fileObject = $this->createMock(File::class);
         $fileObject->expects(self::once())->method('getUid')->willReturn(42);
+        $fileObject->expects(self::any())->method('getName')->willReturn('download.jpg');
+        $fileObject->expects(self::any())->method('getIdentifier')->willReturn('fileadmin/download.jpg');
+
         $fileObject->expects(self::any())->method('getStorage')->willReturn($storageObject);
 
         $resourceFactory = $this->createMock(ResourceFactory::class);
diff --git a/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkTagSoftReferenceParserTest.php b/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkTagSoftReferenceParserTest.php
index 2c96f03814fc..eb8d7738b3dd 100644
--- a/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkTagSoftReferenceParserTest.php
+++ b/typo3/sysext/core/Tests/Unit/DataHandling/SoftReference/TypoLinkTagSoftReferenceParserTest.php
@@ -189,6 +189,8 @@ final class TypoLinkTagSoftReferenceParserTest extends AbstractSoftReferencePars
     {
         $fileObject = $this->createMock(File::class);
         $fileObject->expects(self::once())->method('getUid')->willReturn(42);
+        $fileObject->expects(self::any())->method('getName')->willReturn('download.jpg');
+        $fileObject->expects(self::any())->method('getIdentifier')->willReturn('fileadmin/download.jpg');
 
         $resourceFactory = $this->createMock(ResourceFactory::class);
         $resourceFactory->method('getFileObject')->with('42')->willReturn($fileObject);
diff --git a/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php b/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php
index 66a3e9f9e8e1..284efec7cc8f 100644
--- a/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php
+++ b/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php
@@ -98,7 +98,7 @@ final class FileLinkHandlerTest extends UnitTestCase
             ->getMock();
 
         // fake methods to return proper objects
-        $fileObject = new File(['identifier' => $expected['file'], 'name' => 'foobar.txt'], $storage);
+        $fileObject = new File(['identifier' => 'fileadmin/deep/down.jpg', 'name' => 'down.jpg'], $storage);
         $factory->method('getFileObject')->with($expected['file'])->willReturn($fileObject);
         $factory->method('getFileObjectFromCombinedIdentifier')->with($expected['file'])->willReturn($fileObject);
         $expected['file'] = $fileObject;
diff --git a/typo3/sysext/filelist/Classes/Controller/File/CreateFileController.php b/typo3/sysext/filelist/Classes/Controller/File/CreateFileController.php
index fcb8ff4bf886..d7c970ef3384 100644
--- a/typo3/sysext/filelist/Classes/Controller/File/CreateFileController.php
+++ b/typo3/sysext/filelist/Classes/Controller/File/CreateFileController.php
@@ -119,7 +119,7 @@ class CreateFileController
             $message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
             throw new \RuntimeException($title . ': ' . $message, 1667565756);
         }
-        if ($this->folderObject->getStorage()->getUid() === 0) {
+        if ($this->folderObject->getStorage()->isFallbackStorage()) {
             throw new InsufficientFolderAccessPermissionsException(
                 'You are not allowed to access folders outside your storages',
                 1667565757
diff --git a/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php b/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php
index 7ed140836342..0ad65be4a762 100644
--- a/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php
+++ b/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php
@@ -119,7 +119,7 @@ class EditFileController
         if (!$file instanceof FileInterface) {
             throw new InvalidFileException('Referenced target "' . $combinedIdentifier . '" could not be resolved to a valid file', 1294586841);
         }
-        if ($file->getStorage()->getUid() === 0) {
+        if ($file->getStorage()->isFallbackStorage()) {
             throw new InsufficientFileAccessPermissionsException('You are not allowed to access files outside your storages', 1375889832);
         }
 
diff --git a/typo3/sysext/filelist/Classes/Controller/File/FileUploadController.php b/typo3/sysext/filelist/Classes/Controller/File/FileUploadController.php
index f27b6d80d174..a9d9f623863e 100644
--- a/typo3/sysext/filelist/Classes/Controller/File/FileUploadController.php
+++ b/typo3/sysext/filelist/Classes/Controller/File/FileUploadController.php
@@ -58,7 +58,7 @@ class FileUploadController
         $folder = $this->resourceFactory->retrieveFileOrFolderObject($targetFolderCombinedIdentifier);
 
         if (!$folder instanceof FolderInterface
-            || $folder->getStorage()->getUid() === 0
+            || $folder->getStorage()->isFallbackStorage()
         ) {
             throw new InsufficientFolderAccessPermissionsException('You are not allowed to access folders outside your storages, or the folder couldn\'t be resolved', 1375889834);
         }
diff --git a/typo3/sysext/filelist/Classes/Controller/File/ReplaceFileController.php b/typo3/sysext/filelist/Classes/Controller/File/ReplaceFileController.php
index 10634c27e3b7..39f50c38af76 100644
--- a/typo3/sysext/filelist/Classes/Controller/File/ReplaceFileController.php
+++ b/typo3/sysext/filelist/Classes/Controller/File/ReplaceFileController.php
@@ -103,7 +103,7 @@ class ReplaceFileController
             $message = $lang->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
             throw new \RuntimeException($title . ': ' . $message, 1436895930);
         }
-        if ($this->fileOrFolderObject->getStorage()->getUid() === 0) {
+        if ($this->fileOrFolderObject->getStorage()->isFallbackStorage()) {
             throw new InsufficientFileAccessPermissionsException(
                 'You are not allowed to access files outside your storages',
                 1436895931
diff --git a/typo3/sysext/filelist/Classes/Controller/FileListController.php b/typo3/sysext/filelist/Classes/Controller/FileListController.php
index fd0bd0840082..dd09637fa2b7 100644
--- a/typo3/sysext/filelist/Classes/Controller/FileListController.php
+++ b/typo3/sysext/filelist/Classes/Controller/FileListController.php
@@ -124,7 +124,7 @@ class FileListController implements LoggerAwareInterface
                     }
                     $this->folderObject = $storage->getFolder($identifier);
                     // Disallow access to fallback storage 0
-                    if ($storage->getUid() === 0) {
+                    if ($storage->isFallbackStorage()) {
                         throw new InsufficientFolderAccessPermissionsException(
                             'You are not allowed to access files outside your storages',
                             1434539815
diff --git a/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php b/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php
index 06665c551658..07d282e01583 100644
--- a/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php
+++ b/typo3/sysext/filelist/Classes/LinkHandler/AbstractResourceLinkHandler.php
@@ -98,6 +98,13 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link
 
     public function formatCurrentUrl(): string
     {
+        $resource = $this->linkParts['url'][$this->type->value];
+        if (!$resource->checkActionPermission('read')) {
+            return '';
+        }
+        if ($resource->getStorage()->isFallbackStorage()) {
+            return '';
+        }
         return $this->linkParts['url'][$this->type->value]->getName();
     }
 
@@ -181,6 +188,12 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link
             } catch (FolderDoesNotExistException $e) {
             }
         }
+        if ($this->selectedFolder?->checkActionPermission('read') === false) {
+            $this->selectedFolder = null;
+        }
+        if ($this->selectedFolder?->getStorage()?->isFallbackStorage()) {
+            $this->selectedFolder = null;
+        }
         if (!$this->selectedFolder) {
             $this->selectedFolder = $this->resourceFactory->getDefaultStorage()?->getRootLevelFolder() ?? null;
         }
@@ -202,6 +215,13 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link
 
     public function isUpdateSupported(): bool
     {
+        $resource = $this->linkParts['url'][$this->type->value];
+        if (!$resource->checkActionPermission('read')) {
+            return false;
+        }
+        if ($resource->getStorage()->isFallbackStorage()) {
+            return false;
+        }
         return true;
     }
 
@@ -215,15 +235,22 @@ abstract class AbstractResourceLinkHandler implements LinkHandlerInterface, Link
      */
     public function getBodyTagAttributes(): array
     {
-        if (isset($this->linkParts['url'][$this->type->value]) && $this->linkParts['url'][$this->type->value] instanceof ($this->type->getResourceType())) {
-            return [
-                'data-linkbrowser-current-link' => GeneralUtility::makeInstance(LinkService::class)->asString([
-                    'type' => $this->type->getLinkServiceType(),
-                    $this->type->value => $this->linkParts['url'][$this->type->value],
-                ]),
-            ];
+        $resource = $this->linkParts['url'][$this->type->value] ?? null;
+        if (!$resource instanceof ($this->type->getResourceType())) {
+            return [];
+        }
+        if (!$resource->checkActionPermission('read')) {
+            return [];
+        }
+        if ($resource->getStorage()->isFallbackStorage()) {
+            return [];
         }
-        return [];
+        return [
+            'data-linkbrowser-current-link' => GeneralUtility::makeInstance(LinkService::class)->asString([
+                'type' => $this->type->getLinkServiceType(),
+                $this->type->value => $resource,
+            ]),
+        ];
     }
 
     protected function createUri(ServerRequestInterface $request, array $parameters = []): string
-- 
GitLab