diff options
Diffstat (limited to 'lib')
18 files changed, 303 insertions, 77 deletions
diff --git a/lib/base.php b/lib/base.php index 1a3dbbaa56a..f3076a1181a 100644 --- a/lib/base.php +++ b/lib/base.php @@ -722,7 +722,8 @@ class OC { } private static function registerEncryptionWrapper() { - \OCP\Util::connectHook('OC_Filesystem', 'preSetup', 'OC\Encryption\Manager', 'setupStorage'); + $manager = self::$server->getEncryptionManager(); + \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage'); } private static function registerEncryptionHooks() { diff --git a/lib/private/Server.php b/lib/private/Server.php index 392b2b17b9c..581a2b44cea 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -59,6 +59,7 @@ use OC\Lock\DBLockingProvider; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; use OC\Mail\Mailer; +use OC\Memcache\ArrayCache; use OC\Notification\Manager; use OC\Security\CertificateManager; use OC\Security\CSP\ContentSecurityPolicyManager; @@ -117,7 +118,8 @@ class Server extends ServerContainer implements IServerContainer { $c->getLogger(), $c->getL10N('core'), new View(), - $util + $util, + new ArrayCache() ); }); diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index 6edd0e6886a..c361f01216f 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -26,6 +26,7 @@ use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IUser; use OCP\IGroup; +use OCP\Share\Exceptions\IllegalIDChangeException; class Share implements \OCP\Share\IShare { @@ -75,7 +76,19 @@ class Share implements \OCP\Share\IShare { * @inheritdoc */ public function setId($id) { - $this->id = $id; + if (is_int($id)) { + $id = (string)$id; + } + + if(!is_string($id)) { + throw new \InvalidArgumentException('String expected.'); + } + + if ($this->id !== null) { + throw new IllegalIDChangeException('Not allowed to assign a new internal id to a share'); + } + + $this->id = trim($id); return $this; } @@ -100,7 +113,15 @@ class Share implements \OCP\Share\IShare { * @inheritdoc */ public function setProviderId($id) { - $this->providerId = $id; + if(!is_string($id)) { + throw new \InvalidArgumentException('String expected.'); + } + + if ($this->providerId !== null) { + throw new IllegalIDChangeException('Not allowed to assign a new provider id to a share'); + } + + $this->providerId = trim($id); return $this; } diff --git a/lib/private/app.php b/lib/private/app.php index 3b8cbba3898..05d220f7d38 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -207,6 +207,9 @@ class OC_App { */ public static function setAppTypes($app) { $appData = self::getAppInfo($app); + if(!is_array($appData)) { + return; + } if (isset($appData['types'])) { $appTypes = implode(',', $appData['types']); @@ -783,6 +786,10 @@ class OC_App { if (array_search($app, $blacklist) === false) { $info = OC_App::getAppInfo($app); + if (!is_array($info)) { + \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR); + continue; + } if (!isset($info['name'])) { \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR); @@ -1081,6 +1088,14 @@ class OC_App { if ($app !== false) { // check if the app is compatible with this version of ownCloud $info = self::getAppInfo($app); + if(!is_array($info)) { + throw new \Exception( + $l->t('App "%s" cannot be installed because appinfo file cannot be read.', + [$info['name']] + ) + ); + } + $version = \OCP\Util::getVersion(); if (!self::isAppCompatible($version, $info)) { throw new \Exception( diff --git a/lib/private/encryption/encryptionwrapper.php b/lib/private/encryption/encryptionwrapper.php new file mode 100644 index 00000000000..11beb0cd6b1 --- /dev/null +++ b/lib/private/encryption/encryptionwrapper.php @@ -0,0 +1,124 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * 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 <http://www.gnu.org/licenses/> + * + */ + + +namespace OC\Encryption; + + +use OC\Memcache\ArrayCache; +use OC\Files\Filesystem; +use OC\Files\Storage\Wrapper\Encryption; +use OCP\Files\Mount\IMountPoint; +use OC\Files\View; +use OCP\Files\Storage; +use OCP\ILogger; + +/** + * Class EncryptionWrapper + * + * applies the encryption storage wrapper + * + * @package OC\Encryption + */ +class EncryptionWrapper { + + /** @var ArrayCache */ + private $arrayCache; + + /** @var Manager */ + private $manager; + + /** @var ILogger */ + private $logger; + + /** + * EncryptionWrapper constructor. + * + * @param ArrayCache $arrayCache + * @param Manager $manager + * @param ILogger $logger + */ + public function __construct(ArrayCache $arrayCache, + Manager $manager, + ILogger $logger + ) { + $this->arrayCache = $arrayCache; + $this->manager = $manager; + $this->logger = $logger; + } + + /** + * Wraps the given storage when it is not a shared storage + * + * @param string $mountPoint + * @param Storage $storage + * @param IMountPoint $mount + * @return Encryption|Storage + */ + public function wrapStorage($mountPoint, Storage $storage, IMountPoint $mount) { + $parameters = [ + 'storage' => $storage, + 'mountPoint' => $mountPoint, + 'mount' => $mount + ]; + + if (!$storage->instanceOfStorage('OC\Files\Storage\Shared') + && !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') + && !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud')) { + + $user = \OC::$server->getUserSession()->getUser(); + $mountManager = Filesystem::getMountManager(); + $uid = $user ? $user->getUID() : null; + $fileHelper = \OC::$server->getEncryptionFilesHelper(); + $keyStorage = \OC::$server->getEncryptionKeyStorage(); + + $util = new Util( + new View(), + \OC::$server->getUserManager(), + \OC::$server->getGroupManager(), + \OC::$server->getConfig() + ); + $update = new Update( + new View(), + $util, + Filesystem::getMountManager(), + $this->manager, + $fileHelper, + $uid + ); + return new Encryption( + $parameters, + $this->manager, + $util, + $this->logger, + $fileHelper, + $uid, + $keyStorage, + $update, + $mountManager, + $this->arrayCache + ); + } else { + return $storage; + } + } + +} diff --git a/lib/private/encryption/manager.php b/lib/private/encryption/manager.php index d1d17a92887..d45bbf07ee9 100644 --- a/lib/private/encryption/manager.php +++ b/lib/private/encryption/manager.php @@ -27,6 +27,7 @@ namespace OC\Encryption; use OC\Encryption\Keys\Storage; use OC\Files\Filesystem; use OC\Files\View; +use OC\Memcache\ArrayCache; use OC\ServiceUnavailableException; use OCP\Encryption\IEncryptionModule; use OCP\Encryption\IManager; @@ -54,20 +55,25 @@ class Manager implements IManager { /** @var Util */ protected $util; + /** @var ArrayCache */ + protected $arrayCache; + /** * @param IConfig $config * @param ILogger $logger * @param IL10N $l10n * @param View $rootView * @param Util $util + * @param ArrayCache $arrayCache */ - public function __construct(IConfig $config, ILogger $logger, IL10N $l10n, View $rootView, Util $util) { + public function __construct(IConfig $config, ILogger $logger, IL10N $l10n, View $rootView, Util $util, ArrayCache $arrayCache) { $this->encryptionModules = array(); $this->config = $config; $this->logger = $logger; $this->l = $l10n; $this->rootView = $rootView; $this->util = $util; + $this->arrayCache = $arrayCache; } /** @@ -227,14 +233,9 @@ class Manager implements IManager { /** * Add storage wrapper */ - public static function setupStorage() { - $util = new Util( - new View(), - \OC::$server->getUserManager(), - \OC::$server->getGroupManager(), - \OC::$server->getConfig() - ); - Filesystem::addStorageWrapper('oc_encryption', array($util, 'wrapStorage'), 2); + public function setupStorage() { + $encryptionWrapper = new EncryptionWrapper($this->arrayCache, $this, $this->logger); + Filesystem::addStorageWrapper('oc_encryption', array($encryptionWrapper, 'wrapStorage'), 2); } diff --git a/lib/private/encryption/util.php b/lib/private/encryption/util.php index 860c541934a..9e0cfca830d 100644 --- a/lib/private/encryption/util.php +++ b/lib/private/encryption/util.php @@ -28,10 +28,8 @@ use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException; use OC\Encryption\Exceptions\EncryptionHeaderToLargeException; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; use OC\Files\Filesystem; -use OC\Files\Storage\Wrapper\Encryption; use OC\Files\View; use OCP\Encryption\IEncryptionModule; -use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage; use OCP\IConfig; @@ -392,52 +390,4 @@ class Util { return $this->config->getAppValue('core', 'encryption_key_storage_root', ''); } - /** - * Wraps the given storage when it is not a shared storage - * - * @param string $mountPoint - * @param Storage $storage - * @param IMountPoint $mount - * @return Encryption|Storage - */ - public function wrapStorage($mountPoint, Storage $storage, IMountPoint $mount) { - $parameters = [ - 'storage' => $storage, - 'mountPoint' => $mountPoint, - 'mount' => $mount]; - - if (!$storage->instanceOfStorage('OC\Files\Storage\Shared') - && !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') - && !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud')) { - - $manager = \OC::$server->getEncryptionManager(); - $user = \OC::$server->getUserSession()->getUser(); - $logger = \OC::$server->getLogger(); - $mountManager = Filesystem::getMountManager(); - $uid = $user ? $user->getUID() : null; - $fileHelper = \OC::$server->getEncryptionFilesHelper(); - $keyStorage = \OC::$server->getEncryptionKeyStorage(); - $update = new Update( - new View(), - $this, - Filesystem::getMountManager(), - $manager, - $fileHelper, - $uid - ); - return new Encryption( - $parameters, - $manager, - $this, - $logger, - $fileHelper, - $uid, - $keyStorage, - $update, - $mountManager - ); - } else { - return $storage; - } - } } diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php index 81eea9944f8..1add4d7fd0a 100644 --- a/lib/private/files/storage/wrapper/encryption.php +++ b/lib/private/files/storage/wrapper/encryption.php @@ -33,6 +33,7 @@ use OC\Files\Cache\CacheEntry; use OC\Files\Filesystem; use OC\Files\Mount\Manager; use OC\Files\Storage\LocalTempFileTrait; +use OC\Memcache\ArrayCache; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Encryption\IFile; use OCP\Encryption\IManager; @@ -82,6 +83,9 @@ class Encryption extends Wrapper { /** @var array remember for which path we execute the repair step to avoid recursions */ private $fixUnencryptedSizeOf = array(); + /** @var ArrayCache */ + private $arrayCache; + /** * @param array $parameters * @param IManager $encryptionManager @@ -92,6 +96,7 @@ class Encryption extends Wrapper { * @param IStorage $keyStorage * @param Update $update * @param Manager $mountManager + * @param ArrayCache $arrayCache */ public function __construct( $parameters, @@ -102,9 +107,10 @@ class Encryption extends Wrapper { $uid = null, IStorage $keyStorage = null, Update $update = null, - Manager $mountManager = null + Manager $mountManager = null, + ArrayCache $arrayCache = null ) { - + $this->mountPoint = $parameters['mountPoint']; $this->mount = $parameters['mount']; $this->encryptionManager = $encryptionManager; @@ -116,6 +122,7 @@ class Encryption extends Wrapper { $this->unencryptedSize = array(); $this->update = $update; $this->mountManager = $mountManager; + $this->arrayCache = $arrayCache; parent::__construct($parameters); } @@ -167,20 +174,25 @@ class Encryption extends Wrapper { return null; } $fullPath = $this->getFullPath($path); + $info = $this->getCache()->get($path); if (isset($this->unencryptedSize[$fullPath])) { $data['encrypted'] = true; $data['size'] = $this->unencryptedSize[$fullPath]; } else { - $info = $this->getCache()->get($path); if (isset($info['fileid']) && $info['encrypted']) { $data['size'] = $this->verifyUnencryptedSize($path, $info['size']); $data['encrypted'] = true; } } + if (isset($info['encryptedVersion']) && $info['encryptedVersion'] > 1) { + $data['encryptedVersion'] = $info['encryptedVersion']; + } + return $data; } + /** * see http://php.net/manual/en/function.file_get_contents.php * @@ -352,6 +364,14 @@ class Encryption extends Wrapper { */ public function fopen($path, $mode) { + // check if the file is stored in the array cache, this means that we + // copy a file over to the versions folder, in this case we don't want to + // decrypt it + if ($this->arrayCache->hasKey('encryption_copy_version_' . $path)) { + $this->arrayCache->remove('encryption_copy_version_' . $path); + return $this->storage->fopen($path, $mode); + } + $encryptionEnabled = $this->encryptionManager->isEnabled(); $shouldEncrypt = false; $encryptionModule = null; @@ -674,7 +694,12 @@ class Encryption extends Wrapper { // key from the original file. Just create a 1:1 copy and done if ($this->isVersion($targetInternalPath) || $this->isVersion($sourceInternalPath)) { + // remember that we try to create a version so that we can detect it during + // fopen($sourceInternalPath) and by-pass the encryption in order to + // create a 1:1 copy of the file + $this->arrayCache->set('encryption_copy_version_' . $sourceInternalPath, true); $result = $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + $this->arrayCache->remove('encryption_copy_version_' . $sourceInternalPath); if ($result) { $info = $this->getCache('', $sourceStorage)->get($sourceInternalPath); // make sure that we update the unencrypted size for the version @@ -841,7 +866,7 @@ class Encryption extends Wrapper { $firstBlock = $this->readFirstBlock($path); if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) { - $headerSize = strlen($firstBlock); + $headerSize = $this->util->getHeaderSize(); } return $headerSize; diff --git a/lib/private/installer.php b/lib/private/installer.php index c026383e26e..421e281e56b 100644 --- a/lib/private/installer.php +++ b/lib/private/installer.php @@ -342,6 +342,9 @@ class OC_Installer{ } $info = OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true); + if(!is_array($info)) { + throw new \Exception($l->t('App cannot be installed because appinfo file cannot be read.')); + } // We can't trust the parsed info.xml file as it may have been tampered // with by an attacker and thus we need to use the local data to check diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index 5afbd4495c4..a1b75a34213 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -160,7 +160,11 @@ class TemplateLayout extends \OC_Template { $web = $info[1]; $file = $info[2]; - $this->append( 'cssfiles', $web.'/'.$file . '?v=' . self::$versionHash); + if (substr($file, -strlen('print.css')) === 'print.css') { + $this->append( 'printcssfiles', $web.'/'.$file . '?v=' . self::$versionHash); + } else { + $this->append( 'cssfiles', $web.'/'.$file . '?v=' . self::$versionHash); + } } } } @@ -227,10 +231,35 @@ class TemplateLayout extends \OC_Template { } $cssFiles = self::findStylesheetFiles(\OC_Util::$styles); - $cssHash = self::hashFileNames($cssFiles); - if (!file_exists("$assetDir/assets/$cssHash.css")) { - $cssFiles = array_map(function ($item) { + // differentiate between screen stylesheets and printer stylesheets + $screenCssFiles = array_filter($cssFiles, function($cssFile) { + return substr_compare($cssFile[2], 'print.css', -strlen('print.css')) !== 0; + }); + $screenCssAsset = $this->generateCssAsset($screenCssFiles); + + $printCssFiles = array_filter($cssFiles, function($cssFile) { + return substr_compare($cssFile[2], 'print.css', -strlen('print.css')) === 0; + }); + $printCssAsset = $this->generateCssAsset($printCssFiles); + + $this->append('jsfiles', \OC::$server->getURLGenerator()->linkTo('assets', "$jsHash.js")); + $this->append('cssfiles', $screenCssAsset); + $this->append('printcssfiles', $printCssAsset); + } + + /** + * generates a single css asset file from an array of css files if at least one of them has changed + * otherwise it just returns the path to the old asset file + * @param $files + * @return string + */ + private function generateCssAsset($files) { + $assetDir = \OC::$server->getConfig()->getSystemValue('assetdirectory', \OC::$SERVERROOT); + $hash = self::hashFileNames($files); + + if (!file_exists("$assetDir/assets/$hash.css")) { + $files = array_map(function ($item) { $root = $item[0]; $file = $item[2]; $assetPath = $root . '/' . $file; @@ -246,16 +275,17 @@ class TemplateLayout extends \OC_Template { $sourceRoot, $sourcePath ); - }, $cssFiles); - $cssCollection = new AssetCollection($cssFiles); - $cssCollection->setTargetPath("assets/$cssHash.css"); + }, $files); + + $cssCollection = new AssetCollection($files); + $cssCollection->setTargetPath("assets/$hash.css"); $writer = new AssetWriter($assetDir); $writer->writeAsset($cssCollection); + } - $this->append('jsfiles', \OC::$server->getURLGenerator()->linkTo('assets', "$jsHash.js")); - $this->append('cssfiles', \OC::$server->getURLGenerator()->linkTo('assets', "$cssHash.css")); + return \OC::$server->getURLGenerator()->linkTo('assets', "$hash.css"); } /** diff --git a/lib/public/share/exceptions/genericshareexception.php b/lib/public/Share/Exceptions/GenericShareException.php index b32c2f26574..b32c2f26574 100644 --- a/lib/public/share/exceptions/genericshareexception.php +++ b/lib/public/Share/Exceptions/GenericShareException.php diff --git a/lib/public/Share/Exceptions/IllegalIDChangeException.php b/lib/public/Share/Exceptions/IllegalIDChangeException.php new file mode 100644 index 00000000000..6cd887c386b --- /dev/null +++ b/lib/public/Share/Exceptions/IllegalIDChangeException.php @@ -0,0 +1,27 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * 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 <http://www.gnu.org/licenses/> + * + */ +namespace OCP\Share\Exceptions; + +/** + * Exception for illegal attempts to modify an id of a share + * @since 9.1.0 + */ +class IllegalIDChangeException extends GenericShareException {} diff --git a/lib/public/share/exceptions/sharenotfound.php b/lib/public/Share/Exceptions/ShareNotFound.php index 96e7c096492..96e7c096492 100644 --- a/lib/public/share/exceptions/sharenotfound.php +++ b/lib/public/Share/Exceptions/ShareNotFound.php diff --git a/lib/public/share/imanager.php b/lib/public/Share/IManager.php index 64e5b554de9..64e5b554de9 100644 --- a/lib/public/share/imanager.php +++ b/lib/public/Share/IManager.php diff --git a/lib/public/share/iproviderfactory.php b/lib/public/Share/IProviderFactory.php index 3a8baccf33b..3a8baccf33b 100644 --- a/lib/public/share/iproviderfactory.php +++ b/lib/public/Share/IProviderFactory.php diff --git a/lib/public/share/ishare.php b/lib/public/Share/IShare.php index fdf40f19e56..ee71715f436 100644 --- a/lib/public/share/ishare.php +++ b/lib/public/Share/IShare.php @@ -25,6 +25,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\NotFoundException; +use OCP\Share\Exceptions\IllegalIDChangeException; /** * Interface IShare @@ -35,6 +36,19 @@ use OCP\Files\NotFoundException; interface IShare { /** + * Set the internal id of the share + * It is only allowed to set the internal id of a share once. + * Attempts to override the internal id will result in an IllegalIDChangeException + * + * @param string $id + * @return \OCP\Share\IShare + * @throws IllegalIDChangeException + * @throws \InvalidArgumentException + * @since 9.1.0 + */ + public function setId($id); + + /** * Get the internal id of the share. * * @return string @@ -53,6 +67,19 @@ interface IShare { public function getFullId(); /** + * Set the provider id of the share + * It is only allowed to set the provider id of a share once. + * Attempts to override the provider id will result in an IllegalIDChangeException + * + * @param string $id + * @return \OCP\Share\IShare + * @throws IllegalIDChangeException + * @throws \InvalidArgumentException + * @since 9.1.0 + */ + public function setProviderId($id); + + /** * Set the node of the file/folder that is shared * * @param Node $node diff --git a/lib/public/share/ishareprovider.php b/lib/public/Share/IShareProvider.php index d00b9da7b59..d00b9da7b59 100644 --- a/lib/public/share/ishareprovider.php +++ b/lib/public/Share/IShareProvider.php diff --git a/lib/public/app.php b/lib/public/app.php index 032116eb43f..c3ba90cea41 100644 --- a/lib/public/app.php +++ b/lib/public/app.php @@ -112,7 +112,7 @@ class App { * Read app metadata from the info.xml file * @param string $app id of the app or the path of the info.xml file * @param boolean $path (optional) - * @return array + * @return array|null * @since 4.0.0 */ public static function getAppInfo( $app, $path=false ) { |