Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/encryption/appinfo/application.php3
-rw-r--r--apps/encryption/lib/crypto/encryption.php40
-rw-r--r--apps/encryption/lib/util.php31
-rw-r--r--apps/encryption/settings/settings-personal.php3
-rw-r--r--apps/encryption/tests/lib/UtilTest.php18
-rw-r--r--apps/files_sharing/lib/sharedstorage.php8
-rw-r--r--lib/private/encryption/exceptions/decryptionfailedexception.php11
-rw-r--r--lib/private/files.php88
-rw-r--r--lib/private/files/storage/wrapper/encryption.php24
-rw-r--r--lib/public/encryption/exceptions/genericencryptionexception.php11
-rw-r--r--lib/public/encryption/iencryptionmodule.php12
-rw-r--r--settings/changepassword/controller.php3
-rw-r--r--tests/lib/files/storage/wrapper/encryption.php9
-rw-r--r--tests/lib/files/stream/encryption.php3
14 files changed, 208 insertions, 56 deletions
diff --git a/apps/encryption/appinfo/application.php b/apps/encryption/appinfo/application.php
index 0d6f57f46e9..79b2ad3abaf 100644
--- a/apps/encryption/appinfo/application.php
+++ b/apps/encryption/appinfo/application.php
@@ -209,7 +209,8 @@ class Application extends \OCP\AppFramework\App {
$c->query('Crypt'),
$server->getLogger(),
$server->getUserSession(),
- $server->getConfig());
+ $server->getConfig(),
+ $server->getUserManager());
});
}
diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php
index a4abcd7dc5a..0fd85fa4e9a 100644
--- a/apps/encryption/lib/crypto/encryption.php
+++ b/apps/encryption/lib/crypto/encryption.php
@@ -270,6 +270,15 @@ class Encryption implements IEncryptionModule {
* @return mixed decrypted data
*/
public function decrypt($data) {
+ if (empty($this->fileKey)) {
+ $msg = $this->l->t('Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
+ $this->logger->error('Can not decrypt this file,
+ probably this is a shared file.
+ Please ask the file owner to reshare the file with you.');
+
+ throw new DecryptionFailedException($msg);
+ }
+
$result = '';
if (!empty($data)) {
$result = $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher);
@@ -346,6 +355,36 @@ class Encryption implements IEncryptionModule {
}
/**
+ * check if the encryption module is able to read the file,
+ * e.g. if all encryption keys exists
+ *
+ * @param string $path
+ * @param string $uid user for whom we want to check if he can read the file
+ * @return bool
+ * @throws DecryptionFailedException
+ */
+ public function isReadable($path, $uid) {
+ $fileKey = $this->keyManager->getFileKey($path, $uid);
+ if (empty($fileKey)) {
+ $owner = $this->util->getOwner($path);
+ if ($owner !== $uid) {
+ // if it is a shared file we throw a exception with a useful
+ // error message because in this case it means that the file was
+ // shared with the user at a point where the user didn't had a
+ // valid private/public key
+ $msg = 'Encryption module "' . $this->getDisplayName() .
+ '" is not able to read ' . $path;
+ $hint = $this->l->t('Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
+ $this->logger->warning($msg);
+ throw new DecryptionFailedException($msg, 0, null, $hint);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* @param string $path
* @return string
*/
@@ -360,4 +399,5 @@ class Encryption implements IEncryptionModule {
return $realPath;
}
+
}
diff --git a/apps/encryption/lib/util.php b/apps/encryption/lib/util.php
index 51d5241122f..afed96aaa38 100644
--- a/apps/encryption/lib/util.php
+++ b/apps/encryption/lib/util.php
@@ -29,6 +29,7 @@ use OCA\Encryption\Crypto\Crypt;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IUser;
+use OCP\IUserManager;
use OCP\IUserSession;
use OCP\PreConditionNotMetException;
@@ -53,6 +54,10 @@ class Util {
* @var IConfig
*/
private $config;
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
/**
* Util constructor.
@@ -62,18 +67,21 @@ class Util {
* @param ILogger $logger
* @param IUserSession $userSession
* @param IConfig $config
+ * @param IUserManager $userManager
*/
public function __construct(View $files,
Crypt $crypt,
ILogger $logger,
IUserSession $userSession,
- IConfig $config
+ IConfig $config,
+ IUserManager $userManager
) {
$this->files = $files;
$this->crypt = $crypt;
$this->logger = $logger;
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
$this->config = $config;
+ $this->userManager = $userManager;
}
/**
@@ -117,5 +125,26 @@ class Util {
return $this->files->file_exists($uid . '/files');
}
+ /**
+ * get owner from give path, path relative to data/ expected
+ *
+ * @param string $path relative to data/
+ * @return string
+ * @throws \BadMethodCallException
+ */
+ public function getOwner($path) {
+ $owner = '';
+ $parts = explode('/', $path, 3);
+ if (count($parts) > 1) {
+ $owner = $parts[1];
+ if ($this->userManager->userExists($owner) === false) {
+ throw new \BadMethodCallException('Unknown user: ' .
+ 'method expects path to a user folder relative to the data folder');
+ }
+
+ }
+
+ return $owner;
+ }
}
diff --git a/apps/encryption/settings/settings-personal.php b/apps/encryption/settings/settings-personal.php
index 003a27da71d..3815626ee64 100644
--- a/apps/encryption/settings/settings-personal.php
+++ b/apps/encryption/settings/settings-personal.php
@@ -35,7 +35,8 @@ $util = new \OCA\Encryption\Util(
$crypt,
\OC::$server->getLogger(),
$userSession,
- \OC::$server->getConfig());
+ \OC::$server->getConfig(),
+ \OC::$server->getUserManager());
$keyManager = new \OCA\Encryption\KeyManager(
\OC::$server->getEncryptionKeyStorage(),
diff --git a/apps/encryption/tests/lib/UtilTest.php b/apps/encryption/tests/lib/UtilTest.php
index eab912b82d4..18cf0386793 100644
--- a/apps/encryption/tests/lib/UtilTest.php
+++ b/apps/encryption/tests/lib/UtilTest.php
@@ -28,11 +28,17 @@ use Test\TestCase;
class UtilTest extends TestCase {
private static $tempStorage = [];
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
private $configMock;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
private $filesMock;
- /**
- * @var Util
- */
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $userManagerMock;
+
+ /** @var Util */
private $instance;
public function testSetRecoveryForUser() {
@@ -40,9 +46,6 @@ class UtilTest extends TestCase {
$this->assertArrayHasKey('recoveryEnabled', self::$tempStorage);
}
- /**
- *
- */
public function testIsRecoveryEnabledForUser() {
$this->assertTrue($this->instance->isRecoveryEnabledForUser('admin'));
@@ -62,6 +65,7 @@ class UtilTest extends TestCase {
protected function setUp() {
parent::setUp();
$this->filesMock = $this->getMock('OC\Files\View');
+ $this->userManagerMock = $this->getMock('\OCP\IUserManager');
$cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
->disableOriginalConstructor()
@@ -98,7 +102,7 @@ class UtilTest extends TestCase {
->method('setUserValue')
->will($this->returnCallback([$this, 'setValueTester']));
- $this->instance = new Util($this->filesMock, $cryptMock, $loggerMock, $userSessionMock, $configMock);
+ $this->instance = new Util($this->filesMock, $cryptMock, $loggerMock, $userSessionMock, $configMock, $this->userManagerMock);
}
/**
diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php
index 33b7f887e19..10a37c7dae9 100644
--- a/apps/files_sharing/lib/sharedstorage.php
+++ b/apps/files_sharing/lib/sharedstorage.php
@@ -217,7 +217,13 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
}
public function isReadable($path) {
- return $this->file_exists($path);
+ $isReadable = false;
+ if ($source = $this->getSourcePath($path)) {
+ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
+ $isReadable = $storage->isReadable($internalPath);
+ }
+
+ return $isReadable && $this->file_exists($path);
}
public function isUpdatable($path) {
diff --git a/lib/private/encryption/exceptions/decryptionfailedexception.php b/lib/private/encryption/exceptions/decryptionfailedexception.php
index 406ae12968e..7e9fa21eaef 100644
--- a/lib/private/encryption/exceptions/decryptionfailedexception.php
+++ b/lib/private/encryption/exceptions/decryptionfailedexception.php
@@ -27,4 +27,15 @@ use OCP\Encryption\Exceptions\GenericEncryptionException;
class DecryptionFailedException extends GenericEncryptionException {
+ /**
+ * @param string $message
+ * @param int $code
+ * @param \Exception $previous
+ * @param string $hint
+ */
+ public function __construct($message = '', $code = 0, \Exception $previous = null, $hint = '') {
+ parent::__construct($message, $code, $previous, $hint);
+
+}
+
}
diff --git a/lib/private/files.php b/lib/private/files.php
index 97f9d8163b1..6a739fc844c 100644
--- a/lib/private/files.php
+++ b/lib/private/files.php
@@ -129,52 +129,60 @@ class OC_Files {
$zip = new ZipStreamer(false);
}
OC_Util::obEnd();
- if ($zip or \OC\Files\Filesystem::isReadable($filename)) {
- self::sendHeaders($filename, $name, $zip);
- } elseif (!\OC\Files\Filesystem::file_exists($filename)) {
- header("HTTP/1.0 404 Not Found");
- $tmpl = new OC_Template('', '404', 'guest');
- $tmpl->printPage();
- } else {
- header("HTTP/1.0 403 Forbidden");
- die('403 Forbidden');
- }
- if($only_header) {
- return ;
- }
- if ($zip) {
- $executionTime = intval(ini_get('max_execution_time'));
- set_time_limit(0);
- if ($get_type === self::ZIP_FILES) {
- foreach ($files as $file) {
- $file = $dir . '/' . $file;
- if (\OC\Files\Filesystem::is_file($file)) {
- $fh = \OC\Files\Filesystem::fopen($file, 'r');
- $zip->addFileFromStream($fh, basename($file));
- fclose($fh);
- } elseif (\OC\Files\Filesystem::is_dir($file)) {
- self::zipAddDir($file, $zip);
+
+ try {
+
+ if ($zip or \OC\Files\Filesystem::isReadable($filename)) {
+ self::sendHeaders($filename, $name, $zip);
+ } elseif (!\OC\Files\Filesystem::file_exists($filename)) {
+ header("HTTP/1.0 404 Not Found");
+ $tmpl = new OC_Template('', '404', 'guest');
+ $tmpl->printPage();
+ } else {
+ header("HTTP/1.0 403 Forbidden");
+ die('403 Forbidden');
+ }
+ if ($only_header) {
+ return;
+ }
+ if ($zip) {
+ $executionTime = intval(ini_get('max_execution_time'));
+ set_time_limit(0);
+ if ($get_type === self::ZIP_FILES) {
+ foreach ($files as $file) {
+ $file = $dir . '/' . $file;
+ if (\OC\Files\Filesystem::is_file($file)) {
+ $fh = \OC\Files\Filesystem::fopen($file, 'r');
+ $zip->addFileFromStream($fh, basename($file));
+ fclose($fh);
+ } elseif (\OC\Files\Filesystem::is_dir($file)) {
+ self::zipAddDir($file, $zip);
+ }
}
+ } elseif ($get_type === self::ZIP_DIR) {
+ $file = $dir . '/' . $files;
+ self::zipAddDir($file, $zip);
}
- } elseif ($get_type === self::ZIP_DIR) {
- $file = $dir . '/' . $files;
- self::zipAddDir($file, $zip);
- }
- $zip->finalize();
- set_time_limit($executionTime);
- } else {
- if ($xsendfile) {
- $view = \OC\Files\Filesystem::getView();
- /** @var $storage \OC\Files\Storage\Storage */
- list($storage) = $view->resolvePath($filename);
- if ($storage->isLocal()) {
- self::addSendfileHeader($filename);
+ $zip->finalize();
+ set_time_limit($executionTime);
+ } else {
+ if ($xsendfile) {
+ $view = \OC\Files\Filesystem::getView();
+ /** @var $storage \OC\Files\Storage\Storage */
+ list($storage) = $view->resolvePath($filename);
+ if ($storage->isLocal()) {
+ self::addSendfileHeader($filename);
+ } else {
+ \OC\Files\Filesystem::readfile($filename);
+ }
} else {
\OC\Files\Filesystem::readfile($filename);
}
- } else {
- \OC\Files\Filesystem::readfile($filename);
}
+ } catch (\Exception $ex) {
+ $l = \OC::$server->getL10N('core');
+ $hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
+ \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint);
}
}
diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php
index f7759d91497..1683ff1350f 100644
--- a/lib/private/files/storage/wrapper/encryption.php
+++ b/lib/private/files/storage/wrapper/encryption.php
@@ -253,6 +253,30 @@ class Encryption extends Wrapper {
}
/**
+ * check if a file can be read
+ *
+ * @param string $path
+ * @return bool
+ */
+ public function isReadable($path) {
+
+ $isReadable = true;
+
+ $metaData = $this->getMetaData($path);
+ if (
+ !$this->is_dir($path) &&
+ isset($metaData['encrypted']) &&
+ $metaData['encrypted'] === true
+ ) {
+ $fullPath = $this->getFullPath($path);
+ $module = $this->getEncryptionModule($path);
+ $isReadable = $module->isReadable($fullPath, $this->uid);
+ }
+
+ return $this->storage->isReadable($path) && $isReadable;
+ }
+
+ /**
* see http://php.net/manual/en/function.copy.php
*
* @param string $path1
diff --git a/lib/public/encryption/exceptions/genericencryptionexception.php b/lib/public/encryption/exceptions/genericencryptionexception.php
index be96450d431..e97f00c88bf 100644
--- a/lib/public/encryption/exceptions/genericencryptionexception.php
+++ b/lib/public/encryption/exceptions/genericencryptionexception.php
@@ -30,17 +30,26 @@ namespace OCP\Encryption\Exceptions;
*/
class GenericEncryptionException extends \Exception {
+ /** @var string */
+ protected $hint;
+
/**
* @param string $message
* @param int $code
* @param \Exception $previous
* @since 8.1.0
*/
- public function __construct($message = '', $code = 0, \Exception $previous = null) {
+ public function __construct($message = '', $code = 0, \Exception $previous = null, $hint = '') {
if (empty($message)) {
$message = 'Unspecified encryption exception';
}
parent::__construct($message, $code, $previous);
+
+ $this->hint = $hint;
+ }
+
+ public function getHint() {
+ return $this->hint;
}
}
diff --git a/lib/public/encryption/iencryptionmodule.php b/lib/public/encryption/iencryptionmodule.php
index 0dda042d759..975e57744e9 100644
--- a/lib/public/encryption/iencryptionmodule.php
+++ b/lib/public/encryption/iencryptionmodule.php
@@ -119,4 +119,16 @@ interface IEncryptionModule {
* @since 8.1.0
*/
public function getUnencryptedBlockSize();
+
+ /**
+ * check if the encryption module is able to read the file,
+ * e.g. if all encryption keys exists
+ *
+ * @param string $path
+ * @param string $uid user for whom we want to check if he can read the file
+ * @return boolean
+ * @since 8.1.0
+ */
+ public function isReadable($path, $uid);
+
}
diff --git a/settings/changepassword/controller.php b/settings/changepassword/controller.php
index 94323fc2fcc..69b7ca710a9 100644
--- a/settings/changepassword/controller.php
+++ b/settings/changepassword/controller.php
@@ -89,7 +89,8 @@ class Controller {
$crypt,
\OC::$server->getLogger(),
\OC::$server->getUserSession(),
- \OC::$server->getConfig());
+ \OC::$server->getConfig(),
+ \OC::$server->getUserManager());
$keyManager = new \OCA\Encryption\KeyManager(
$keyStorage,
$crypt,
diff --git a/tests/lib/files/storage/wrapper/encryption.php b/tests/lib/files/storage/wrapper/encryption.php
index 97810c9c5dd..361592f6ca1 100644
--- a/tests/lib/files/storage/wrapper/encryption.php
+++ b/tests/lib/files/storage/wrapper/encryption.php
@@ -117,7 +117,7 @@ class Encryption extends \Test\Files\Storage\Storage {
$this->encryptionManager, $this->util, $logger, $file, null, $this->keyStore, $this->update
]
)
- ->setMethods(['getMetaData', 'getCache'])
+ ->setMethods(['getMetaData', 'getCache', 'getEncryptionModule'])
->getMock();
$this->instance->expects($this->any())
@@ -127,6 +127,10 @@ class Encryption extends \Test\Files\Storage\Storage {
$this->instance->expects($this->any())
->method('getCache')
->willReturn($this->cache);
+
+ $this->instance->expects($this->any())
+ ->method('getEncryptionModule')
+ ->willReturn($mockModule);
}
/**
@@ -135,7 +139,7 @@ class Encryption extends \Test\Files\Storage\Storage {
protected function buildMockModule() {
$this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()
- ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize'])
+ ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable'])
->getMock();
$this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
@@ -147,6 +151,7 @@ class Encryption extends \Test\Files\Storage\Storage {
$this->encryptionModule->expects($this->any())->method('update')->willReturn(true);
$this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
$this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192);
+ $this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
return $this->encryptionModule;
}
diff --git a/tests/lib/files/stream/encryption.php b/tests/lib/files/stream/encryption.php
index 892491cbc32..113cd14ada7 100644
--- a/tests/lib/files/stream/encryption.php
+++ b/tests/lib/files/stream/encryption.php
@@ -253,13 +253,14 @@ class Encryption extends \Test\TestCase {
protected function buildMockModule() {
$encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()
- ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize'])
+ ->setMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable'])
->getMock();
$encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
$encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
$encryptionModule->expects($this->any())->method('begin')->willReturn([]);
$encryptionModule->expects($this->any())->method('end')->willReturn('');
+ $encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
$encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function($data) {
// simulate different block size by adding some padding to the data
if (isset($data[6125])) {