From b56f2c9ed01332bbeaee73599a0ea166c62d01e8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 1 Aug 2016 18:27:07 +0200 Subject: [PATCH] basic lockdown logic Signed-off-by: Robin Appelman --- apps/dav/lib/Connector/Sabre/Auth.php | 1 + lib/private/App/AppManager.php | 5 + lib/private/Files/Filesystem.php | 42 +++-- lib/private/Lockdown/Filesystem/NullCache.php | 122 ++++++++++++ .../Lockdown/Filesystem/NullStorage.php | 177 ++++++++++++++++++ lib/private/Lockdown/LockdownManager.php | 46 +++++ lib/private/Server.php | 11 ++ lib/private/User/Session.php | 2 + lib/private/legacy/app.php | 2 +- lib/public/Lockdown/ILockdownManager.php | 32 ++++ 10 files changed, 424 insertions(+), 16 deletions(-) create mode 100644 lib/private/Lockdown/Filesystem/NullCache.php create mode 100644 lib/private/Lockdown/Filesystem/NullStorage.php create mode 100644 lib/private/Lockdown/LockdownManager.php create mode 100644 lib/public/Lockdown/ILockdownManager.php diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php index a35eed88073..95222dafec9 100644 --- a/apps/dav/lib/Connector/Sabre/Auth.php +++ b/apps/dav/lib/Connector/Sabre/Auth.php @@ -159,6 +159,7 @@ class Auth extends AbstractBasic { } catch (Exception $e) { $class = get_class($e); $msg = $e->getMessage(); + \OC::$server->getLogger()->logException($e); throw new ServiceUnavailable("$class: $msg"); } } diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 55fd575e129..354f891828b 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -112,6 +112,11 @@ class AppManager implements IAppManager { return $value !== 'no'; }); ksort($this->installedAppsCache); + foreach ($this->installedAppsCache as $appId => $value) { + if (!\OC::$server->getLockdownManager()->canAccessApp($appId)) { + unset($this->installedAppsCache[$appId]); + } + } } return $this->installedAppsCache; } diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 55cf38bbdc9..ac0e66973d4 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -62,6 +62,7 @@ use OC\Cache\CappedMemoryCache; use OC\Files\Config\MountProviderCollection; use OC\Files\Mount\MountPoint; use OC\Files\Storage\StorageFactory; +use OC\Lockdown\Filesystem\NullStorage; use OCP\Files\Config\IMountProvider; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; @@ -216,7 +217,7 @@ class Filesystem { * @internal */ public static function logWarningWhenAddingStorageWrapper($shouldLog) { - self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog; + self::$logWarningWhenAddingStorageWrapper = (bool)$shouldLog; } /** @@ -426,25 +427,36 @@ class Filesystem { self::$usersSetup[$user] = true; } - /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ - $mountConfigManager = \OC::$server->getMountProviderCollection(); + if (\OC::$server->getLockdownManager()->canAccessFilesystem()) { + /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ + $mountConfigManager = \OC::$server->getMountProviderCollection(); - // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers - $homeMount = $mountConfigManager->getHomeMountForUser($userObject); + // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers + $homeMount = $mountConfigManager->getHomeMountForUser($userObject); - self::getMountManager()->addMount($homeMount); + self::getMountManager()->addMount($homeMount); - \OC\Files\Filesystem::getStorage($user); + \OC\Files\Filesystem::getStorage($user); - // Chance to mount for other storages - if ($userObject) { - $mounts = $mountConfigManager->getMountsForUser($userObject); - array_walk($mounts, array(self::$mounts, 'addMount')); - $mounts[] = $homeMount; - $mountConfigManager->registerMounts($userObject, $mounts); - } + // Chance to mount for other storages + if ($userObject) { + $mounts = $mountConfigManager->getMountsForUser($userObject); + array_walk($mounts, array(self::$mounts, 'addMount')); + $mounts[] = $homeMount; + $mountConfigManager->registerMounts($userObject, $mounts); + } - self::listenForNewMountProviders($mountConfigManager, $userManager); + self::listenForNewMountProviders($mountConfigManager, $userManager); + } else { + self::$mounts->addMount(new MountPoint( + new NullStorage([]), + '/' . $user + )); + self::$mounts->addMount(new MountPoint( + new NullStorage([]), + '/' . $user . '/files' + )); + } \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user)); } diff --git a/lib/private/Lockdown/Filesystem/NullCache.php b/lib/private/Lockdown/Filesystem/NullCache.php new file mode 100644 index 00000000000..8c6b5258aa8 --- /dev/null +++ b/lib/private/Lockdown/Filesystem/NullCache.php @@ -0,0 +1,122 @@ + + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Lockdown\Filesystem; + +use OC\Files\Cache\CacheEntry; +use OCP\Constants; +use OCP\Files\Cache\ICache; +use OCP\Files\Cache\ICacheEntry; +use OCP\Files\FileInfo; + +class NullCache implements ICache { + public function getNumericStorageId() { + return -1; + } + + public function get($file) { + return $file !== '' ? null : + new CacheEntry([ + 'fileid' => -1, + 'parent' => -1, + 'name' => '', + 'path' => '', + 'size' => '0', + 'mtime' => time(), + 'storage_mtime' => time(), + 'etag' => '', + 'mimetype' => FileInfo::MIMETYPE_FOLDER, + 'mimepart' => 'httpd', + 'permissions' => Constants::PERMISSION_READ + ]); + } + + public function getFolderContents($folder) { + return []; + } + + public function getFolderContentsById($fileId) { + return []; + } + + public function put($file, array $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function insert($file, array $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function update($id, array $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getId($file) { + return -1; + } + + public function getParentId($file) { + return -1; + } + + public function inCache($file) { + return $file === ''; + } + + public function remove($file) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function move($source, $target) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getStatus($file) { + return ICache::COMPLETE; + } + + public function search($pattern) { + return []; + } + + public function searchByMime($mimetype) { + return []; + } + + public function searchByTag($tag, $userId) { + return []; + } + + public function getIncomplete() { + return []; + } + + public function getPathById($id) { + return ''; + } + + public function normalize($path) { + return $path; + } + +} diff --git a/lib/private/Lockdown/Filesystem/NullStorage.php b/lib/private/Lockdown/Filesystem/NullStorage.php new file mode 100644 index 00000000000..967b6d2c6e7 --- /dev/null +++ b/lib/private/Lockdown/Filesystem/NullStorage.php @@ -0,0 +1,177 @@ + + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Lockdown\Filesystem; + +use Icewind\Streams\IteratorDirectory; +use OC\Files\Storage\Common; + +class NullStorage extends Common { + public function __construct($parameters) { + parent::__construct($parameters); + } + + public function getId() { + return 'null'; + } + + public function mkdir($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function rmdir($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function opendir($path) { + return new IteratorDirectory([]); + } + + public function is_dir($path) { + return $path === ''; + } + + public function is_file($path) { + return false; + } + + public function stat($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function filetype($path) { + return ($path === '') ? 'dir' : false; + } + + public function filesize($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function isCreatable($path) { + return false; + } + + public function isReadable($path) { + return $path === ''; + } + + public function isUpdatable($path) { + return false; + } + + public function isDeletable($path) { + return false; + } + + public function isSharable($path) { + return false; + } + + public function getPermissions($path) { + return null; + } + + public function file_exists($path) { + return $path === ''; + } + + public function filemtime($path) { + return ($path === '') ? time() : false; + } + + public function file_get_contents($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function file_put_contents($path, $data) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function unlink($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function rename($path1, $path2) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function copy($path1, $path2) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function fopen($path, $mode) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getMimeType($path) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function hash($type, $path, $raw = false) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function free_space($path) { + return 0; + } + + public function touch($path, $mtime = null) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function getLocalFile($path) { + return false; + } + + public function hasUpdated($path, $time) { + return false; + } + + public function getETag($path) { + return ''; + } + + public function isLocal() { + return false; + } + + public function getDirectDownload($path) { + return false; + } + + public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + throw new \OC\ForbiddenException('This request is not allowed to access the filesystem'); + } + + public function test() { + return true; + } + + public function getOwner($path) { + return null; + } + + public function getCache($path = '', $storage = null) { + return new NullCache(); + } +} diff --git a/lib/private/Lockdown/LockdownManager.php b/lib/private/Lockdown/LockdownManager.php new file mode 100644 index 00000000000..9f10646a9dd --- /dev/null +++ b/lib/private/Lockdown/LockdownManager.php @@ -0,0 +1,46 @@ + + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Lockdown; + +use OC\Authentication\Token\IToken; +use OCP\Lockdown\ILockdownManager; + +class LockdownManager implements ILockdownManager { + /** @var IToken|null */ + private $token; + + private $enabled = false; + + public function enable() { + $this->enabled = true; + } + + public function setToken(IToken $token) { + $this->token = $token; + } + + public function canAccessFilesystem() { + return true; + } + + public function canAccessApp($app) { + return $app === 'logreader' || $app === 'files' || $app === 'dav'; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index abedf8230ed..c6755357a1d 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -69,6 +69,7 @@ use OC\IntegrityCheck\Helpers\FileAccessHelper; use OC\Lock\DBLockingProvider; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; +use OC\Lockdown\LockdownManager; use OC\Mail\Mailer; use OC\Memcache\ArrayCache; use OC\Notification\Manager; @@ -795,6 +796,9 @@ class Server extends ServerContainer implements IServerContainer { $c->getSystemConfig() ); }); + $this->registerService('LockdownManager', function (Server $c) { + return new LockdownManager(); + }); } /** @@ -1534,4 +1538,11 @@ class Server extends ServerContainer implements IServerContainer { $factory = $this->query(\OC\Files\AppData\Factory::class); return $factory->get($app); } + + /** + * @return \OCP\Lockdown\ILockdownManager + */ + public function getLockdownManager() { + return $this->query('LockdownManager'); + } } diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index ef408aa4077..82af9281a4c 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -341,10 +341,12 @@ class Session implements IUserSession, Emitter { if ($isTokenPassword) { $this->session->set('app_password', $password); + \OC::$server->getLockdownManager()->setToken($this->tokenProvider->getToken($password)); } else if($this->supportsCookies($request)) { // Password login, but cookies supported -> create (browser) session token $this->createSessionToken($request, $this->getUser()->getUID(), $user, $password); } + \OC::$server->getLockdownManager()->enable(); return true; } diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index c2ff9a5be3c..5503b8b525c 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -140,7 +140,7 @@ class OC_App { public static function loadApp($app, $checkUpgrade = true) { self::$loadedApps[] = $app; $appPath = self::getAppPath($app); - if($appPath === false) { + if($appPath === false || !\OC::$server->getLockdownManager()->canAccessApp($app)) { return; } diff --git a/lib/public/Lockdown/ILockdownManager.php b/lib/public/Lockdown/ILockdownManager.php new file mode 100644 index 00000000000..0b0c525501e --- /dev/null +++ b/lib/public/Lockdown/ILockdownManager.php @@ -0,0 +1,32 @@ + + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCP\Lockdown; + +use OC\Authentication\Token\IToken; + +interface ILockdownManager { + public function enable(); + + public function setToken(IToken $token); + + public function canAccessFilesystem(); + + public function canAccessApp($app); +} -- 2.39.5