summaryrefslogtreecommitdiffstats
path: root/lib/private/Encryption
diff options
context:
space:
mode:
authorRoeland Jago Douma <rullzer@owncloud.com>2016-04-22 17:34:01 +0200
committerRoeland Jago Douma <rullzer@owncloud.com>2016-04-22 17:34:01 +0200
commitd379157289d2e09f41b10253d90ed0867e267a7e (patch)
tree8007eb9cdc0b8ef4860159d4ee15c06375d49623 /lib/private/Encryption
parent8c7c713e86ed144b1a5e80ea27fdba4744b6d09d (diff)
downloadnextcloud-server-d379157289d2e09f41b10253d90ed0867e267a7e.tar.gz
nextcloud-server-d379157289d2e09f41b10253d90ed0867e267a7e.zip
Move \OC\Encryption to PSR-4
Diffstat (limited to 'lib/private/Encryption')
-rw-r--r--lib/private/Encryption/DecryptAll.php276
-rw-r--r--lib/private/Encryption/EncryptionWrapper.php124
-rw-r--r--lib/private/Encryption/Exceptions/DecryptionFailedException.php30
-rw-r--r--lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php30
-rw-r--r--lib/private/Encryption/Exceptions/EncryptionFailedException.php30
-rw-r--r--lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php35
-rw-r--r--lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php34
-rw-r--r--lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php37
-rw-r--r--lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php29
-rw-r--r--lib/private/Encryption/Exceptions/UnknownCipherException.php30
-rw-r--r--lib/private/Encryption/File.php99
-rw-r--r--lib/private/Encryption/HookManager.php75
-rw-r--r--lib/private/Encryption/Keys/Storage.php326
-rw-r--r--lib/private/Encryption/Manager.php284
-rw-r--r--lib/private/Encryption/Update.php185
-rw-r--r--lib/private/Encryption/Util.php393
16 files changed, 2017 insertions, 0 deletions
diff --git a/lib/private/Encryption/DecryptAll.php b/lib/private/Encryption/DecryptAll.php
new file mode 100644
index 00000000000..7a965a5f227
--- /dev/null
+++ b/lib/private/Encryption/DecryptAll.php
@@ -0,0 +1,276 @@
+<?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\Encryption\Exceptions\DecryptionFailedException;
+use OC\Files\View;
+use \OCP\Encryption\IEncryptionModule;
+use OCP\IUserManager;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DecryptAll {
+
+ /** @var OutputInterface */
+ protected $output;
+
+ /** @var InputInterface */
+ protected $input;
+
+ /** @var Manager */
+ protected $encryptionManager;
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ /** @var View */
+ protected $rootView;
+
+ /** @var array files which couldn't be decrypted */
+ protected $failed;
+
+ /**
+ * @param Manager $encryptionManager
+ * @param IUserManager $userManager
+ * @param View $rootView
+ */
+ public function __construct(
+ Manager $encryptionManager,
+ IUserManager $userManager,
+ View $rootView
+ ) {
+ $this->encryptionManager = $encryptionManager;
+ $this->userManager = $userManager;
+ $this->rootView = $rootView;
+ $this->failed = [];
+ }
+
+ /**
+ * start to decrypt all files
+ *
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param string $user which users data folder should be decrypted, default = all users
+ * @return bool
+ * @throws \Exception
+ */
+ public function decryptAll(InputInterface $input, OutputInterface $output, $user = '') {
+
+ $this->input = $input;
+ $this->output = $output;
+
+ if (!empty($user) && $this->userManager->userExists($user) === false) {
+ $this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again');
+ return false;
+ }
+
+ $this->output->writeln('prepare encryption modules...');
+ if ($this->prepareEncryptionModules($user) === false) {
+ return false;
+ }
+ $this->output->writeln(' done.');
+
+ $this->decryptAllUsersFiles($user);
+
+ if (empty($this->failed)) {
+ $this->output->writeln('all files could be decrypted successfully!');
+ } else {
+ $this->output->writeln('Files for following users couldn\'t be decrypted, ');
+ $this->output->writeln('maybe the user is not set up in a way that supports this operation: ');
+ foreach ($this->failed as $uid => $paths) {
+ $this->output->writeln(' ' . $uid);
+ }
+ $this->output->writeln('');
+ }
+
+ return true;
+ }
+
+ /**
+ * prepare encryption modules to perform the decrypt all function
+ *
+ * @param $user
+ * @return bool
+ */
+ protected function prepareEncryptionModules($user) {
+ // prepare all encryption modules for decrypt all
+ $encryptionModules = $this->encryptionManager->getEncryptionModules();
+ foreach ($encryptionModules as $moduleDesc) {
+ /** @var IEncryptionModule $module */
+ $module = call_user_func($moduleDesc['callback']);
+ $this->output->writeln('');
+ $this->output->writeln('Prepare "' . $module->getDisplayName() . '"');
+ $this->output->writeln('');
+ if ($module->prepareDecryptAll($this->input, $this->output, $user) === false) {
+ $this->output->writeln('Module "' . $moduleDesc['displayName'] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!');
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * iterate over all user and encrypt their files
+ * @param string $user which users files should be decrypted, default = all users
+ */
+ protected function decryptAllUsersFiles($user = '') {
+
+ $this->output->writeln("\n");
+
+ $userList = [];
+ if (empty($user)) {
+
+ $fetchUsersProgress = new ProgressBar($this->output);
+ $fetchUsersProgress->setFormat(" %message% \n [%bar%]");
+ $fetchUsersProgress->start();
+ $fetchUsersProgress->setMessage("Fetch list of users...");
+ $fetchUsersProgress->advance();
+
+ foreach ($this->userManager->getBackends() as $backend) {
+ $limit = 500;
+ $offset = 0;
+ do {
+ $users = $backend->getUsers('', $limit, $offset);
+ foreach ($users as $user) {
+ $userList[] = $user;
+ }
+ $offset += $limit;
+ $fetchUsersProgress->advance();
+ } while (count($users) >= $limit);
+ $fetchUsersProgress->setMessage("Fetch list of users... finished");
+ $fetchUsersProgress->finish();
+ }
+ } else {
+ $userList[] = $user;
+ }
+
+ $this->output->writeln("\n\n");
+
+ $progress = new ProgressBar($this->output);
+ $progress->setFormat(" %message% \n [%bar%]");
+ $progress->start();
+ $progress->setMessage("starting to decrypt files...");
+ $progress->advance();
+
+ $numberOfUsers = count($userList);
+ $userNo = 1;
+ foreach ($userList as $uid) {
+ $userCount = "$uid ($userNo of $numberOfUsers)";
+ $this->decryptUsersFiles($uid, $progress, $userCount);
+ $userNo++;
+ }
+
+ $progress->setMessage("starting to decrypt files... finished");
+ $progress->finish();
+
+ $this->output->writeln("\n\n");
+
+ }
+
+ /**
+ * encrypt files from the given user
+ *
+ * @param string $uid
+ * @param ProgressBar $progress
+ * @param string $userCount
+ */
+ protected function decryptUsersFiles($uid, ProgressBar $progress, $userCount) {
+
+ $this->setupUserFS($uid);
+ $directories = array();
+ $directories[] = '/' . $uid . '/files';
+
+ while($root = array_pop($directories)) {
+ $content = $this->rootView->getDirectoryContent($root);
+ foreach ($content as $file) {
+ $path = $root . '/' . $file['name'];
+ if ($this->rootView->is_dir($path)) {
+ $directories[] = $path;
+ continue;
+ } else {
+ try {
+ $progress->setMessage("decrypt files for user $userCount: $path");
+ $progress->advance();
+ if ($this->decryptFile($path) === false) {
+ $progress->setMessage("decrypt files for user $userCount: $path (already decrypted)");
+ $progress->advance();
+ }
+ } catch (\Exception $e) {
+ if (isset($this->failed[$uid])) {
+ $this->failed[$uid][] = $path;
+ } else {
+ $this->failed[$uid] = [$path];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * encrypt file
+ *
+ * @param string $path
+ * @return bool
+ */
+ protected function decryptFile($path) {
+
+ $source = $path;
+ $target = $path . '.decrypted.' . $this->getTimestamp();
+
+ try {
+ $this->rootView->copy($source, $target);
+ $this->rootView->rename($target, $source);
+ } catch (DecryptionFailedException $e) {
+ if ($this->rootView->file_exists($target)) {
+ $this->rootView->unlink($target);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * get current timestamp
+ *
+ * @return int
+ */
+ protected function getTimestamp() {
+ return time();
+ }
+
+
+ /**
+ * setup user file system
+ *
+ * @param string $uid
+ */
+ protected function setupUserFS($uid) {
+ \OC_Util::tearDownFS();
+ \OC_Util::setupFS($uid);
+ }
+
+}
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/Exceptions/DecryptionFailedException.php b/lib/private/Encryption/Exceptions/DecryptionFailedException.php
new file mode 100644
index 00000000000..a0cbbc5cce0
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/DecryptionFailedException.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class DecryptionFailedException extends GenericEncryptionException {
+
+}
diff --git a/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php b/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php
new file mode 100644
index 00000000000..2c90c2db7df
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class EmptyEncryptionDataException extends GenericEncryptionException{
+
+}
diff --git a/lib/private/Encryption/Exceptions/EncryptionFailedException.php b/lib/private/Encryption/Exceptions/EncryptionFailedException.php
new file mode 100644
index 00000000000..98e92eb199c
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/EncryptionFailedException.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class EncryptionFailedException extends GenericEncryptionException{
+
+}
diff --git a/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php b/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php
new file mode 100644
index 00000000000..ab1a166018c
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class EncryptionHeaderKeyExistsException extends GenericEncryptionException {
+
+ /**
+ * @param string $key
+ */
+ public function __construct($key) {
+ parent::__construct('header key "'. $key . '" already reserved by ownCloud');
+ }
+}
diff --git a/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php b/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php
new file mode 100644
index 00000000000..7b706e621de
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class EncryptionHeaderToLargeException extends GenericEncryptionException {
+
+ public function __construct() {
+ parent::__construct('max header size exceeded');
+ }
+
+}
diff --git a/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php b/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php
new file mode 100644
index 00000000000..fcd08679acc
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class ModuleAlreadyExistsException extends GenericEncryptionException {
+
+ /**
+ * @param string $id
+ * @param string $name
+ */
+ public function __construct($id, $name) {
+ parent::__construct('Id "' . $id . '" already used by encryption module "' . $name . '"');
+ }
+
+}
diff --git a/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php b/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php
new file mode 100644
index 00000000000..282c9ec080b
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class ModuleDoesNotExistsException extends GenericEncryptionException {
+
+}
diff --git a/lib/private/Encryption/Exceptions/UnknownCipherException.php b/lib/private/Encryption/Exceptions/UnknownCipherException.php
new file mode 100644
index 00000000000..beb4cb7f2e5
--- /dev/null
+++ b/lib/private/Encryption/Exceptions/UnknownCipherException.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Exceptions;
+
+use OCP\Encryption\Exceptions\GenericEncryptionException;
+
+class UnknownCipherException extends GenericEncryptionException {
+
+}
diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php
new file mode 100644
index 00000000000..ec55c2cea00
--- /dev/null
+++ b/lib/private/Encryption/File.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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;
+
+class File implements \OCP\Encryption\IFile {
+
+ /** @var Util */
+ protected $util;
+
+ /**
+ * cache results of already checked folders
+ *
+ * @var array
+ */
+ protected $cache;
+
+ public function __construct(Util $util) {
+ $this->util = $util;
+ }
+
+
+ /**
+ * get list of users with access to the file
+ *
+ * @param string $path to the file
+ * @return array ['users' => $uniqueUserIds, 'public' => $public]
+ */
+ public function getAccessList($path) {
+
+ // Make sure that a share key is generated for the owner too
+ list($owner, $ownerPath) = $this->util->getUidAndFilename($path);
+
+ // always add owner to the list of users with access to the file
+ $userIds = array($owner);
+
+ if (!$this->util->isFile($owner . '/' . $ownerPath)) {
+ return array('users' => $userIds, 'public' => false);
+ }
+
+ $ownerPath = substr($ownerPath, strlen('/files'));
+ $ownerPath = $this->util->stripPartialFileExtension($ownerPath);
+
+
+ // first get the shares for the parent and cache the result so that we don't
+ // need to check all parents for every file
+ $parent = dirname($ownerPath);
+ if (isset($this->cache[$parent])) {
+ $resultForParents = $this->cache[$parent];
+ } else {
+ $resultForParents = \OCP\Share::getUsersSharingFile($parent, $owner);
+ $this->cache[$parent] = $resultForParents;
+ }
+ $userIds = \array_merge($userIds, $resultForParents['users']);
+ $public = $resultForParents['public'] || $resultForParents['remote'];
+
+
+ // Find out who, if anyone, is sharing the file
+ $resultForFile = \OCP\Share::getUsersSharingFile($ownerPath, $owner, false, false, false);
+ $userIds = \array_merge($userIds, $resultForFile['users']);
+ $public = $resultForFile['public'] || $resultForFile['remote'] || $public;
+
+ // check if it is a group mount
+ if (\OCP\App::isEnabled("files_external")) {
+ $mounts = \OC_Mount_Config::getSystemMountPoints();
+ foreach ($mounts as $mount) {
+ if ($mount['mountpoint'] == substr($ownerPath, 1, strlen($mount['mountpoint']))) {
+ $mountedFor = $this->util->getUserWithAccessToMountPoint($mount['applicable']['users'], $mount['applicable']['groups']);
+ $userIds = array_merge($userIds, $mountedFor);
+ }
+ }
+ }
+
+ // Remove duplicate UIDs
+ $uniqueUserIds = array_unique($userIds);
+
+ return array('users' => $uniqueUserIds, 'public' => $public);
+ }
+
+}
diff --git a/lib/private/Encryption/HookManager.php b/lib/private/Encryption/HookManager.php
new file mode 100644
index 00000000000..0bc42ec8159
--- /dev/null
+++ b/lib/private/Encryption/HookManager.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Files\Filesystem;
+use OC\Files\View;
+
+class HookManager {
+ /**
+ * @var Update
+ */
+ private static $updater;
+
+ public static function postShared($params) {
+ self::getUpdate()->postShared($params);
+ }
+ public static function postUnshared($params) {
+ self::getUpdate()->postUnshared($params);
+ }
+
+ public static function postRename($params) {
+ self::getUpdate()->postRename($params);
+ }
+
+ public static function postRestore($params) {
+ self::getUpdate()->postRestore($params);
+ }
+
+ /**
+ * @return Update
+ */
+ private static function getUpdate() {
+ if (is_null(self::$updater)) {
+ $user = \OC::$server->getUserSession()->getUser();
+ $uid = '';
+ if ($user) {
+ $uid = $user->getUID();
+ }
+ self::$updater = new Update(
+ new View(),
+ new Util(
+ new View(),
+ \OC::$server->getUserManager(),
+ \OC::$server->getGroupManager(),
+ \OC::$server->getConfig()),
+ Filesystem::getMountManager(),
+ \OC::$server->getEncryptionManager(),
+ \OC::$server->getEncryptionFilesHelper(),
+ $uid
+ );
+ }
+
+ return self::$updater;
+ }
+}
diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php
new file mode 100644
index 00000000000..47360f45aa5
--- /dev/null
+++ b/lib/private/Encryption/Keys/Storage.php
@@ -0,0 +1,326 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Keys;
+
+use OC\Encryption\Util;
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCP\Encryption\Keys\IStorage;
+
+class Storage implements IStorage {
+
+ // hidden file which indicate that the folder is a valid key storage
+ const KEY_STORAGE_MARKER = '.oc_key_storage';
+
+ /** @var View */
+ private $view;
+
+ /** @var Util */
+ private $util;
+
+ // base dir where all the file related keys are stored
+ /** @var string */
+ private $keys_base_dir;
+
+ // root of the key storage default is empty which means that we use the data folder
+ /** @var string */
+ private $root_dir;
+
+ /** @var string */
+ private $encryption_base_dir;
+
+ /** @var array */
+ private $keyCache = [];
+
+ /**
+ * @param View $view
+ * @param Util $util
+ */
+ public function __construct(View $view, Util $util) {
+ $this->view = $view;
+ $this->util = $util;
+
+ $this->encryption_base_dir = '/files_encryption';
+ $this->keys_base_dir = $this->encryption_base_dir .'/keys';
+ $this->root_dir = $this->util->getKeyStorageRoot();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getUserKey($uid, $keyId, $encryptionModuleId) {
+ $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
+ return $this->getKey($path);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getFileKey($path, $keyId, $encryptionModuleId) {
+ $realFile = $this->util->stripPartialFileExtension($path);
+ $keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile);
+ $key = $this->getKey($keyDir . $keyId);
+
+ if ($key === '' && $realFile !== $path) {
+ // Check if the part file has keys and use them, if no normal keys
+ // exist. This is required to fix copyBetweenStorage() when we
+ // rename a .part file over storage borders.
+ $keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
+ $key = $this->getKey($keyDir . $keyId);
+ }
+
+ return $key;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getSystemUserKey($keyId, $encryptionModuleId) {
+ $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
+ return $this->getKey($path);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setUserKey($uid, $keyId, $key, $encryptionModuleId) {
+ $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
+ return $this->setKey($path, $key);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setFileKey($path, $keyId, $key, $encryptionModuleId) {
+ $keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
+ return $this->setKey($keyDir . $keyId, $key);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setSystemUserKey($keyId, $key, $encryptionModuleId) {
+ $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
+ return $this->setKey($path, $key);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function deleteUserKey($uid, $keyId, $encryptionModuleId) {
+ $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
+ return !$this->view->file_exists($path) || $this->view->unlink($path);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function deleteFileKey($path, $keyId, $encryptionModuleId) {
+ $keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
+ return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function deleteAllFileKeys($path) {
+ $keyDir = $this->getFileKeyDir('', $path);
+ return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function deleteSystemUserKey($keyId, $encryptionModuleId) {
+ $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
+ return !$this->view->file_exists($path) || $this->view->unlink($path);
+ }
+
+ /**
+ * construct path to users key
+ *
+ * @param string $encryptionModuleId
+ * @param string $keyId
+ * @param string $uid
+ * @return string
+ */
+ protected function constructUserKeyPath($encryptionModuleId, $keyId, $uid) {
+
+ if ($uid === null) {
+ $path = $this->root_dir . '/' . $this->encryption_base_dir . '/' . $encryptionModuleId . '/' . $keyId;
+ } else {
+ $path = $this->root_dir . '/' . $uid . $this->encryption_base_dir . '/'
+ . $encryptionModuleId . '/' . $uid . '.' . $keyId;
+ }
+
+ return \OC\Files\Filesystem::normalizePath($path);
+ }
+
+ /**
+ * read key from hard disk
+ *
+ * @param string $path to key
+ * @return string
+ */
+ private function getKey($path) {
+
+ $key = '';
+
+ if ($this->view->file_exists($path)) {
+ if (isset($this->keyCache[$path])) {
+ $key = $this->keyCache[$path];
+ } else {
+ $key = $this->view->file_get_contents($path);
+ $this->keyCache[$path] = $key;
+ }
+ }
+
+ return $key;
+ }
+
+ /**
+ * write key to disk
+ *
+ *
+ * @param string $path path to key directory
+ * @param string $key key
+ * @return bool
+ */
+ private function setKey($path, $key) {
+ $this->keySetPreparation(dirname($path));
+
+ $result = $this->view->file_put_contents($path, $key);
+
+ if (is_int($result) && $result > 0) {
+ $this->keyCache[$path] = $key;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * get path to key folder for a given file
+ *
+ * @param string $encryptionModuleId
+ * @param string $path path to the file, relative to data/
+ * @return string
+ */
+ private function getFileKeyDir($encryptionModuleId, $path) {
+
+ list($owner, $filename) = $this->util->getUidAndFilename($path);
+
+ // in case of system wide mount points the keys are stored directly in the data directory
+ if ($this->util->isSystemWideMountPoint($filename, $owner)) {
+ $keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/';
+ } else {
+ $keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/';
+ }
+
+ return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false);
+ }
+
+ /**
+ * move keys if a file was renamed
+ *
+ * @param string $source
+ * @param string $target
+ * @return boolean
+ */
+ public function renameKeys($source, $target) {
+
+ $sourcePath = $this->getPathToKeys($source);
+ $targetPath = $this->getPathToKeys($target);
+
+ if ($this->view->file_exists($sourcePath)) {
+ $this->keySetPreparation(dirname($targetPath));
+ $this->view->rename($sourcePath, $targetPath);
+
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * copy keys if a file was renamed
+ *
+ * @param string $source
+ * @param string $target
+ * @return boolean
+ */
+ public function copyKeys($source, $target) {
+
+ $sourcePath = $this->getPathToKeys($source);
+ $targetPath = $this->getPathToKeys($target);
+
+ if ($this->view->file_exists($sourcePath)) {
+ $this->keySetPreparation(dirname($targetPath));
+ $this->view->copy($sourcePath, $targetPath);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * get system wide path and detect mount points
+ *
+ * @param string $path
+ * @return string
+ */
+ protected function getPathToKeys($path) {
+ list($owner, $relativePath) = $this->util->getUidAndFilename($path);
+ $systemWideMountPoint = $this->util->isSystemWideMountPoint($relativePath, $owner);
+
+ if ($systemWideMountPoint) {
+ $systemPath = $this->root_dir . '/' . $this->keys_base_dir . $relativePath . '/';
+ } else {
+ $systemPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $relativePath . '/';
+ }
+
+ return Filesystem::normalizePath($systemPath, false);
+ }
+
+ /**
+ * Make preparations to filesystem for saving a key file
+ *
+ * @param string $path relative to the views root
+ */
+ protected function keySetPreparation($path) {
+ // If the file resides within a subdirectory, create it
+ if (!$this->view->file_exists($path)) {
+ $sub_dirs = explode('/', ltrim($path, '/'));
+ $dir = '';
+ foreach ($sub_dirs as $sub_dir) {
+ $dir .= '/' . $sub_dir;
+ if (!$this->view->is_dir($dir)) {
+ $this->view->mkdir($dir);
+ }
+ }
+ }
+ }
+
+}
diff --git a/lib/private/Encryption/Manager.php b/lib/private/Encryption/Manager.php
new file mode 100644
index 00000000000..8714d161807
--- /dev/null
+++ b/lib/private/Encryption/Manager.php
@@ -0,0 +1,284 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\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;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+
+class Manager implements IManager {
+
+ /** @var array */
+ protected $encryptionModules;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var ILogger */
+ protected $logger;
+
+ /** @var Il10n */
+ protected $l;
+
+ /** @var View */
+ protected $rootView;
+
+ /** @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, ArrayCache $arrayCache) {
+ $this->encryptionModules = array();
+ $this->config = $config;
+ $this->logger = $logger;
+ $this->l = $l10n;
+ $this->rootView = $rootView;
+ $this->util = $util;
+ $this->arrayCache = $arrayCache;
+ }
+
+ /**
+ * Check if encryption is enabled
+ *
+ * @return bool true if enabled, false if not
+ */
+ public function isEnabled() {
+
+ $installed = $this->config->getSystemValue('installed', false);
+ if (!$installed) {
+ return false;
+ }
+
+ $enabled = $this->config->getAppValue('core', 'encryption_enabled', 'no');
+ return $enabled === 'yes';
+ }
+
+ /**
+ * check if new encryption is ready
+ *
+ * @return bool
+ * @throws ServiceUnavailableException
+ */
+ public function isReady() {
+ // check if we are still in transit between the old and the new encryption
+ $oldEncryption = $this->config->getAppValue('files_encryption', 'installed_version');
+ if (!empty($oldEncryption)) {
+ $warning = 'Installation is in transit between the old Encryption (ownCloud <= 8.0)
+ and the new encryption. Please enable the "Default encryption module"
+ and run \'occ encryption:migrate\'';
+ $this->logger->warning($warning);
+ return false;
+ }
+
+ if ($this->isKeyStorageReady() === false) {
+ throw new ServiceUnavailableException('Key Storage is not ready');
+ }
+
+ return true;
+ }
+
+ /**
+ * @param string $user
+ */
+ public function isReadyForUser($user) {
+ if (!$this->isReady()) {
+ return false;
+ }
+
+ foreach ($this->getEncryptionModules() as $module) {
+ /** @var IEncryptionModule $m */
+ $m = call_user_func($module['callback']);
+ if (!$m->isReadyForUser($user)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Registers an callback function which must return an encryption module instance
+ *
+ * @param string $id
+ * @param string $displayName
+ * @param callable $callback
+ * @throws Exceptions\ModuleAlreadyExistsException
+ */
+ public function registerEncryptionModule($id, $displayName, callable $callback) {
+
+ if (isset($this->encryptionModules[$id])) {
+ throw new Exceptions\ModuleAlreadyExistsException($id, $displayName);
+ }
+
+ $this->encryptionModules[$id] = [
+ 'id' => $id,
+ 'displayName' => $displayName,
+ 'callback' => $callback,
+ ];
+
+ $defaultEncryptionModuleId = $this->getDefaultEncryptionModuleId();
+
+ if (empty($defaultEncryptionModuleId)) {
+ $this->setDefaultEncryptionModule($id);
+ }
+ }
+
+ /**
+ * Unregisters an encryption module
+ *
+ * @param string $moduleId
+ */
+ public function unregisterEncryptionModule($moduleId) {
+ unset($this->encryptionModules[$moduleId]);
+ }
+
+ /**
+ * get a list of all encryption modules
+ *
+ * @return array [id => ['id' => $id, 'displayName' => $displayName, 'callback' => callback]]
+ */
+ public function getEncryptionModules() {
+ return $this->encryptionModules;
+ }
+
+ /**
+ * get a specific encryption module
+ *
+ * @param string $moduleId
+ * @return IEncryptionModule
+ * @throws Exceptions\ModuleDoesNotExistsException
+ */
+ public function getEncryptionModule($moduleId = '') {
+ if (!empty($moduleId)) {
+ if (isset($this->encryptionModules[$moduleId])) {
+ return call_user_func($this->encryptionModules[$moduleId]['callback']);
+ } else {
+ $message = "Module with id: $moduleId does not exist.";
+ $hint = $this->l->t('Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator.', [$moduleId]);
+ throw new Exceptions\ModuleDoesNotExistsException($message, $hint);
+ }
+ } else {
+ return $this->getDefaultEncryptionModule();
+ }
+ }
+
+ /**
+ * get default encryption module
+ *
+ * @return \OCP\Encryption\IEncryptionModule
+ * @throws Exceptions\ModuleDoesNotExistsException
+ */
+ protected function getDefaultEncryptionModule() {
+ $defaultModuleId = $this->getDefaultEncryptionModuleId();
+ if (!empty($defaultModuleId)) {
+ if (isset($this->encryptionModules[$defaultModuleId])) {
+ return call_user_func($this->encryptionModules[$defaultModuleId]['callback']);
+ } else {
+ $message = 'Default encryption module not loaded';
+ throw new Exceptions\ModuleDoesNotExistsException($message);
+ }
+ } else {
+ $message = 'No default encryption module defined';
+ throw new Exceptions\ModuleDoesNotExistsException($message);
+ }
+
+ }
+
+ /**
+ * set default encryption module Id
+ *
+ * @param string $moduleId
+ * @return bool
+ */
+ public function setDefaultEncryptionModule($moduleId) {
+ try {
+ $this->getEncryptionModule($moduleId);
+ } catch (\Exception $e) {
+ return false;
+ }
+
+ $this->config->setAppValue('core', 'default_encryption_module', $moduleId);
+ return true;
+ }
+
+ /**
+ * get default encryption module Id
+ *
+ * @return string
+ */
+ public function getDefaultEncryptionModuleId() {
+ return $this->config->getAppValue('core', 'default_encryption_module');
+ }
+
+ /**
+ * Add storage wrapper
+ */
+ public function setupStorage() {
+ $encryptionWrapper = new EncryptionWrapper($this->arrayCache, $this, $this->logger);
+ Filesystem::addStorageWrapper('oc_encryption', array($encryptionWrapper, 'wrapStorage'), 2);
+ }
+
+
+ /**
+ * check if key storage is ready
+ *
+ * @return bool
+ */
+ protected function isKeyStorageReady() {
+
+ $rootDir = $this->util->getKeyStorageRoot();
+
+ // the default root is always valid
+ if ($rootDir === '') {
+ return true;
+ }
+
+ // check if key storage is mounted correctly
+ if ($this->rootView->file_exists($rootDir . '/' . Storage::KEY_STORAGE_MARKER)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+}
diff --git a/lib/private/Encryption/Update.php b/lib/private/Encryption/Update.php
new file mode 100644
index 00000000000..62c23c1fe0c
--- /dev/null
+++ b/lib/private/Encryption/Update.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Files\Filesystem;
+use \OC\Files\Mount;
+use \OC\Files\View;
+
+/**
+ * update encrypted files, e.g. because a file was shared
+ */
+class Update {
+
+ /** @var \OC\Files\View */
+ protected $view;
+
+ /** @var \OC\Encryption\Util */
+ protected $util;
+
+ /** @var \OC\Files\Mount\Manager */
+ protected $mountManager;
+
+ /** @var \OC\Encryption\Manager */
+ protected $encryptionManager;
+
+ /** @var string */
+ protected $uid;
+
+ /** @var \OC\Encryption\File */
+ protected $file;
+
+ /**
+ *
+ * @param \OC\Files\View $view
+ * @param \OC\Encryption\Util $util
+ * @param \OC\Files\Mount\Manager $mountManager
+ * @param \OC\Encryption\Manager $encryptionManager
+ * @param \OC\Encryption\File $file
+ * @param string $uid
+ */
+ public function __construct(
+ View $view,
+ Util $util,
+ Mount\Manager $mountManager,
+ Manager $encryptionManager,
+ File $file,
+ $uid
+ ) {
+
+ $this->view = $view;
+ $this->util = $util;
+ $this->mountManager = $mountManager;
+ $this->encryptionManager = $encryptionManager;
+ $this->file = $file;
+ $this->uid = $uid;
+ }
+
+ /**
+ * hook after file was shared
+ *
+ * @param array $params
+ */
+ public function postShared($params) {
+ if ($this->encryptionManager->isEnabled()) {
+ if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
+ $path = Filesystem::getPath($params['fileSource']);
+ list($owner, $ownerPath) = $this->getOwnerPath($path);
+ $absPath = '/' . $owner . '/files/' . $ownerPath;
+ $this->update($absPath);
+ }
+ }
+ }
+
+ /**
+ * hook after file was unshared
+ *
+ * @param array $params
+ */
+ public function postUnshared($params) {
+ if ($this->encryptionManager->isEnabled()) {
+ if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
+ $path = Filesystem::getPath($params['fileSource']);
+ list($owner, $ownerPath) = $this->getOwnerPath($path);
+ $absPath = '/' . $owner . '/files/' . $ownerPath;
+ $this->update($absPath);
+ }
+ }
+ }
+
+ /**
+ * inform encryption module that a file was restored from the trash bin,
+ * e.g. to update the encryption keys
+ *
+ * @param array $params
+ */
+ public function postRestore($params) {
+ if ($this->encryptionManager->isEnabled()) {
+ $path = Filesystem::normalizePath('/' . $this->uid . '/files/' . $params['filePath']);
+ $this->update($path);
+ }
+ }
+
+ /**
+ * inform encryption module that a file was renamed,
+ * e.g. to update the encryption keys
+ *
+ * @param array $params
+ */
+ public function postRename($params) {
+ $source = $params['oldpath'];
+ $target = $params['newpath'];
+ if(
+ $this->encryptionManager->isEnabled() &&
+ dirname($source) !== dirname($target)
+ ) {
+ list($owner, $ownerPath) = $this->getOwnerPath($target);
+ $absPath = '/' . $owner . '/files/' . $ownerPath;
+ $this->update($absPath);
+ }
+ }
+
+ /**
+ * get owner and path relative to data/<owner>/files
+ *
+ * @param string $path path to file for current user
+ * @return array ['owner' => $owner, 'path' => $path]
+ * @throw \InvalidArgumentException
+ */
+ protected function getOwnerPath($path) {
+ $info = Filesystem::getFileInfo($path);
+ $owner = Filesystem::getOwner($path);
+ $view = new View('/' . $owner . '/files');
+ $path = $view->getPath($info->getId());
+ if ($path === null) {
+ throw new \InvalidArgumentException('No file found for ' . $info->getId());
+ }
+
+ return array($owner, $path);
+ }
+
+ /**
+ * notify encryption module about added/removed users from a file/folder
+ *
+ * @param string $path relative to data/
+ * @throws Exceptions\ModuleDoesNotExistsException
+ */
+ public function update($path) {
+
+ // if a folder was shared, get a list of all (sub-)folders
+ if ($this->view->is_dir($path)) {
+ $allFiles = $this->util->getAllFiles($path);
+ } else {
+ $allFiles = array($path);
+ }
+
+ $encryptionModule = $this->encryptionManager->getEncryptionModule();
+
+ foreach ($allFiles as $file) {
+ $usersSharing = $this->file->getAccessList($file);
+ $encryptionModule->update($file, $this->uid, $usersSharing);
+ }
+ }
+
+}
diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php
new file mode 100644
index 00000000000..9e0cfca830d
--- /dev/null
+++ b/lib/private/Encryption/Util.php
@@ -0,0 +1,393 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\Encryption\Exceptions\EncryptionHeaderKeyExistsException;
+use OC\Encryption\Exceptions\EncryptionHeaderToLargeException;
+use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCP\Encryption\IEncryptionModule;
+use OCP\Files\Storage;
+use OCP\IConfig;
+
+class Util {
+
+ const HEADER_START = 'HBEGIN';
+ const HEADER_END = 'HEND';
+ const HEADER_PADDING_CHAR = '-';
+
+ const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
+
+ /**
+ * block size will always be 8192 for a PHP stream
+ * @see https://bugs.php.net/bug.php?id=21641
+ * @var integer
+ */
+ protected $headerSize = 8192;
+
+ /**
+ * block size will always be 8192 for a PHP stream
+ * @see https://bugs.php.net/bug.php?id=21641
+ * @var integer
+ */
+ protected $blockSize = 8192;
+
+ /** @var View */
+ protected $rootView;
+
+ /** @var array */
+ protected $ocHeaderKeys;
+
+ /** @var \OC\User\Manager */
+ protected $userManager;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var array paths excluded from encryption */
+ protected $excludedPaths;
+
+ /** @var \OC\Group\Manager $manager */
+ protected $groupManager;
+
+ /**
+ *
+ * @param View $rootView
+ * @param \OC\User\Manager $userManager
+ * @param \OC\Group\Manager $groupManager
+ * @param IConfig $config
+ */
+ public function __construct(
+ View $rootView,
+ \OC\User\Manager $userManager,
+ \OC\Group\Manager $groupManager,
+ IConfig $config) {
+
+ $this->ocHeaderKeys = [
+ self::HEADER_ENCRYPTION_MODULE_KEY
+ ];
+
+ $this->rootView = $rootView;
+ $this->userManager = $userManager;
+ $this->groupManager = $groupManager;
+ $this->config = $config;
+
+ $this->excludedPaths[] = 'files_encryption';
+ }
+
+ /**
+ * read encryption module ID from header
+ *
+ * @param array $header
+ * @return string
+ * @throws ModuleDoesNotExistsException
+ */
+ public function getEncryptionModuleId(array $header = null) {
+ $id = '';
+ $encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
+
+ if (isset($header[$encryptionModuleKey])) {
+ $id = $header[$encryptionModuleKey];
+ } elseif (isset($header['cipher'])) {
+ if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
+ // fall back to default encryption if the user migrated from
+ // ownCloud <= 8.0 with the old encryption
+ $id = \OCA\Encryption\Crypto\Encryption::ID;
+ } else {
+ throw new ModuleDoesNotExistsException('Default encryption module missing');
+ }
+ }
+
+ return $id;
+ }
+
+ /**
+ * create header for encrypted file
+ *
+ * @param array $headerData
+ * @param IEncryptionModule $encryptionModule
+ * @return string
+ * @throws EncryptionHeaderToLargeException if header has to many arguments
+ * @throws EncryptionHeaderKeyExistsException if header key is already in use
+ */
+ public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
+ $header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
+ foreach ($headerData as $key => $value) {
+ if (in_array($key, $this->ocHeaderKeys)) {
+ throw new EncryptionHeaderKeyExistsException($key);
+ }
+ $header .= $key . ':' . $value . ':';
+ }
+ $header .= self::HEADER_END;
+
+ if (strlen($header) > $this->getHeaderSize()) {
+ throw new EncryptionHeaderToLargeException();
+ }
+
+ $paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
+
+ return $paddedHeader;
+ }
+
+ /**
+ * go recursively through a dir and collect all files and sub files.
+ *
+ * @param string $dir relative to the users files folder
+ * @return array with list of files relative to the users files folder
+ */
+ public function getAllFiles($dir) {
+ $result = array();
+ $dirList = array($dir);
+
+ while ($dirList) {
+ $dir = array_pop($dirList);
+ $content = $this->rootView->getDirectoryContent($dir);
+
+ foreach ($content as $c) {
+ if ($c->getType() === 'dir') {
+ $dirList[] = $c->getPath();
+ } else {
+ $result[] = $c->getPath();
+ }
+ }
+
+ }
+
+ return $result;
+ }
+
+ /**
+ * check if it is a file uploaded by the user stored in data/user/files
+ * or a metadata file
+ *
+ * @param string $path relative to the data/ folder
+ * @return boolean
+ */
+ public function isFile($path) {
+ $parts = explode('/', Filesystem::normalizePath($path), 4);
+ if (isset($parts[2]) && $parts[2] === 'files') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * return size of encryption header
+ *
+ * @return integer
+ */
+ public function getHeaderSize() {
+ return $this->headerSize;
+ }
+
+ /**
+ * return size of block read by a PHP stream
+ *
+ * @return integer
+ */
+ public function getBlockSize() {
+ return $this->blockSize;
+ }
+
+ /**
+ * get the owner and the path for the file relative to the owners files folder
+ *
+ * @param string $path
+ * @return array
+ * @throws \BadMethodCallException
+ */
+ public function getUidAndFilename($path) {
+
+ $parts = explode('/', $path);
+ $uid = '';
+ if (count($parts) > 2) {
+ $uid = $parts[1];
+ }
+ if (!$this->userManager->userExists($uid)) {
+ throw new \BadMethodCallException(
+ 'path needs to be relative to the system wide data folder and point to a user specific file'
+ );
+ }
+
+ $ownerPath = implode('/', array_slice($parts, 2));
+
+ return array($uid, Filesystem::normalizePath($ownerPath));
+
+ }
+
+ /**
+ * Remove .path extension from a file path
+ * @param string $path Path that may identify a .part file
+ * @return string File path without .part extension
+ * @note this is needed for reusing keys
+ */
+ public function stripPartialFileExtension($path) {
+ $extension = pathinfo($path, PATHINFO_EXTENSION);
+
+ if ( $extension === 'part') {
+
+ $newLength = strlen($path) - 5; // 5 = strlen(".part")
+ $fPath = substr($path, 0, $newLength);
+
+ // if path also contains a transaction id, we remove it too
+ $extension = pathinfo($fPath, PATHINFO_EXTENSION);
+ if(substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
+ $newLength = strlen($fPath) - strlen($extension) -1;
+ $fPath = substr($fPath, 0, $newLength);
+ }
+ return $fPath;
+
+ } else {
+ return $path;
+ }
+ }
+
+ public function getUserWithAccessToMountPoint($users, $groups) {
+ $result = array();
+ if (in_array('all', $users)) {
+ $result = \OCP\User::getUsers();
+ } else {
+ $result = array_merge($result, $users);
+ foreach ($groups as $group) {
+ $result = array_merge($result, \OC_Group::usersInGroup($group));
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * check if the file is stored on a system wide mount point
+ * @param string $path relative to /data/user with leading '/'
+ * @param string $uid
+ * @return boolean
+ */
+ public function isSystemWideMountPoint($path, $uid) {
+ if (\OCP\App::isEnabled("files_external")) {
+ $mounts = \OC_Mount_Config::getSystemMountPoints();
+ foreach ($mounts as $mount) {
+ if (strpos($path, '/files/' . $mount['mountpoint']) === 0) {
+ if ($this->isMountPointApplicableToUser($mount, $uid)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * check if mount point is applicable to user
+ *
+ * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
+ * @param string $uid
+ * @return boolean
+ */
+ private function isMountPointApplicableToUser($mount, $uid) {
+ $acceptedUids = array('all', $uid);
+ // check if mount point is applicable for the user
+ $intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
+ if (!empty($intersection)) {
+ return true;
+ }
+ // check if mount point is applicable for group where the user is a member
+ foreach ($mount['applicable']['groups'] as $gid) {
+ if ($this->groupManager->isInGroup($uid, $gid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * check if it is a path which is excluded by ownCloud from encryption
+ *
+ * @param string $path
+ * @return boolean
+ */
+ public function isExcluded($path) {
+ $normalizedPath = Filesystem::normalizePath($path);
+ $root = explode('/', $normalizedPath, 4);
+ if (count($root) > 1) {
+
+ // detect alternative key storage root
+ $rootDir = $this->getKeyStorageRoot();
+ if ($rootDir !== '' &&
+ 0 === strpos(
+ Filesystem::normalizePath($path),
+ Filesystem::normalizePath($rootDir)
+ )
+ ) {
+ return true;
+ }
+
+
+ //detect system wide folders
+ if (in_array($root[1], $this->excludedPaths)) {
+ return true;
+ }
+
+ // detect user specific folders
+ if ($this->userManager->userExists($root[1])
+ && in_array($root[2], $this->excludedPaths)) {
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * check if recovery key is enabled for user
+ *
+ * @param string $uid
+ * @return boolean
+ */
+ public function recoveryEnabled($uid) {
+ $enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
+
+ return ($enabled === '1') ? true : false;
+ }
+
+ /**
+ * set new key storage root
+ *
+ * @param string $root new key store root relative to the data folder
+ */
+ public function setKeyStorageRoot($root) {
+ $this->config->setAppValue('core', 'encryption_key_storage_root', $root);
+ }
+
+ /**
+ * get key storage root
+ *
+ * @return string key storage root
+ */
+ public function getKeyStorageRoot() {
+ return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
+ }
+
+}