Skip to content

Commit

Permalink
Change to re-create masterkeys
Browse files Browse the repository at this point in the history
This change brings a new command to re-create
masterkey

Signed-off-by: Sujith H <sharidasan@owncloud.com>
  • Loading branch information
sharidas committed Jul 31, 2017
1 parent 4fdfce0 commit a19ae76
Show file tree
Hide file tree
Showing 5 changed files with 717 additions and 17 deletions.
1 change: 1 addition & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
</dependencies>
<commands>
<command>OCA\Encryption\Command\SelectEncryptionType</command>
<command>OCA\Encryption\Command\RecreateMasterKey</command>
<command>OCA\Encryption\Command\MigrateKeys</command>
</commands>
<settings>
Expand Down
319 changes: 319 additions & 0 deletions lib/Command/RecreateMasterKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
<?php
/**
* @author Sujith Haridasan <sharidasan@owncloud.com>
*
* @copyright Copyright (c) 2017, ownCloud GmbH
* @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 OCA\Encryption\Command;

use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Encryption\Manager;
use OC\Files\View;
use OCA\Encryption\Crypto\EncryptAll;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Users\Setup;
use OCA\Encryption\Util;
use OCP\App\IAppManager;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ISession;
use OCP\IUserManager;
use OCP\Mail\IMailer;
use OCP\Security\ISecureRandom;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class RecreateMasterKey extends Command {

/** @var Manager */
protected $encryptionManager;

/** @var IUserManager */
protected $userManager;

/** @var View */
protected $rootView;

/** @var KeyManager */
protected $keyManager;

/** @var Util */
protected $util;

/** @var IAppManager */
protected $IAppManager;

/** @var IAppConfig */
protected $appConfig;

/** @var IConfig */
protected $config;

/** @var ISession */
protected $session;

/** @var QuestionHelper */
protected $questionHelper;

/** @var Setup */
protected $userSetup;

/** @var IMailer */
protected $mailer;

/** @var ISecureRandom */
protected $secureRandom;

/** @var IL10N */
protected $l;

/** @var */
protected $encryptAll;

/** @var array files which couldn't be decrypted */
protected $failed;

/**
* RecreateMasterKey constructor.
*
* @param Manager $encryptionManager
* @param IUserManager $userManager
* @param View $rootView
* @param KeyManager $keyManager
* @param Util $util
* @param IAppManager $IAppManager
* @param IAppConfig $appConfig
* @param IConfig $config
* @param ISession $session
*/
public function __construct(Manager $encryptionManager, IUserManager $userManager,
View $rootView, KeyManager $keyManager, Util $util,
IAppManager $IAppManager, IAppConfig $appConfig,
IConfig $config, ISession $session, QuestionHelper $questionHelper,
Setup $userSetup, IMailer $mailer, ISecureRandom $secureRandom, IL10N $l) {

parent::__construct();
$this->encryptionManager = $encryptionManager;
$this->userManager = $userManager;
$this->rootView = $rootView;
$this->keyManager = $keyManager;
$this->util = $util;
$this->IAppManager = $IAppManager;
$this->appConfig = $appConfig;
$this->config = $config;
$this->session = $session;
$this->questionHelper = $questionHelper;
$this->userSetup = $userSetup;
$this->mailer = $mailer;
$this->secureRandom = $secureRandom;
$this->l = $l;
}

protected function configure() {
parent::configure();

$this
->setName('encryption:recreate-master-key')
->setDescription('Replace existing master key with new one. Encrypt the file system with newly created master key')
;

$this->addOption(
'yes',
'y',
InputOption::VALUE_NONE,
'Answer yes to all questions'
);
}

protected function execute(InputInterface $input, OutputInterface $output) {
$yes = $input->getOption('yes');
if ($this->util->isMasterKeyEnabled()) {
$question = new ConfirmationQuestion(
'Warning: Inorder to re-create master key, the entire ownCloud filesystem will be decrypted and then encrypted using new master key.'
. ' Do you want to continue? (y/n)', false);
if ($yes || $this->questionHelper->ask($input, $output, $question)) {
$output->writeln("Decryption started\n");
$progress = new ProgressBar($output);
$progress->start();
$progress->setMessage("Decryption progress...");
$progress->advance();

$this->decryptAllUsersFiles($progress, $output);
$progress->finish();

if (empty($this->failed)) {

$this->IAppManager->disableApp('encryption');

//Delete the files_encryption dir
$this->rootView->deleteAll('files_encryption');

$this->appConfig->setValue('core', 'encryption_enabled', 'no');
$this->appConfig->deleteKey('encryption', 'useMasterKey');
$this->appConfig->deleteKey('encryption', 'masterKeyId');
$this->appConfig->deleteKey('encryption', 'recoveryKeyId');
$this->appConfig->deleteKey('encryption', 'publicShareKeyId');
$this->appConfig->deleteKey('files_encryption', 'installed_version');

}
$output->writeln("\nDecryption completed\n");

//Reencrypt again
$this->IAppManager->enableApp('encryption');
$this->appConfig->setValue('core', 'encryption_enabled', 'yes');
$this->appConfig->setValue('encryption', 'enabled', 'yes');
$output->writeln("Encryption started\n");

$output->writeln("Waiting for creating new masterkey\n");

$this->keyManager->setPublicShareKeyIDAndMasterKeyId();

$output->writeln("New masterkey created successfully\n");

$this->appConfig->setValue('encryption', 'enabled', 'yes');
$this->appConfig->setValue('encryption', 'useMasterKey', '1');

$this->keyManager->validateShareKey();
$this->keyManager->validateMasterKey();
$this->encryptAllUsers($input, $output);
$output->writeln("\nEncryption completed successfully\n");
} else {
$output->writeln("The process is abandoned");
}
} else {
$output->writeln("Master key is not enabled.\n");
}
}

protected function encryptAllUsers(InputInterface $input, OutputInterface $output) {
$this->encryptAll = new EncryptAll(
$this->userSetup, $this->userManager, $this->rootView,
$this->keyManager, $this->util, $this->config,
$this->mailer, $this->l, $this->questionHelper,
$this->secureRandom);
$this->encryptAll->encryptAll($input, $output);
}

protected function decryptAllUsersFiles(ProgressBar $progress, OutputInterface $output) {
$userList = [];

foreach ($this->userManager->getBackends() as $backend) {
$limit = 500;
$offset = 0;
do {
$users = $backend->getUsers('', $limit, $offset);
foreach ($users as $user) {
$userList[] = $user;
}
$offset += $limit;
} while (count($users) >= $limit);
}

$userNo = 1;
foreach ($userList as $uid) {
$this->decryptUsersFiles($uid, $progress, $output);
$progress->advance();
$userNo++;
}
return true;
}

protected function decryptUsersFiles($uid, ProgressBar $progress, OutputInterface $output) {
$this->setupUserFS($uid);
$directories = [];
$directories[] = '/' . $uid . '/files';

while ($root = array_pop($directories)) {
$content = $this->rootView->getDirectoryContent($root);
foreach ($content as $file) {
// only decrypt files owned by the user
if($file->getStorage()->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
continue;
}
$path = $root . '/' . $file['name'];
if ($this->rootView->is_dir($path)) {
$directories[] = $path;
continue;
} else {
try {
if ($file->isEncrypted() !== false) {
if ($this->decryptFile($path) === false) {
$progress->setMessage("decrypt files for user $uid: $path (already decrypted)");
$progress->advance();
}
}
} catch (\Exception $e) {
if (isset($this->failed[$uid])) {
$this->failed[$uid][] = $path;
} else {
$this->failed[$uid] = [$path];
}
}
}
}
}

if (empty($this->failed)) {
$this->rootView->deleteAll("$uid/files_encryption");
} else {
$output->writeln('Files for following users couldn\'t be decrypted, ');
$output->writeln('maybe the user is not set up in a way that supports this operation: ');
foreach ($this->failed as $uid => $paths) {
$output->writeln(' ' . $uid);
}
$output->writeln('');
}
}

protected function decryptFile($path) {

$source = $path;
$target = $path . '.decrypted.' . $this->getTimestamp();

try {
$this->session->set('decryptAllCmd', true);
$this->rootView->copy($source, $target);
$this->rootView->rename($target, $source);
$this->keyManager->setVersion($source,0, $this->rootView);
$this->session->remove('decryptAllCmd');
} catch (DecryptionFailedException $e) {
if ($this->rootView->file_exists($target)) {
$this->rootView->unlink($target);
}
return false;
}

return true;
}

protected function getTimestamp() {
return time();
}

protected function setupUserFS($uid) {
\OC_Util::tearDownFS();
\OC_Util::setupFS($uid);
}
}
36 changes: 23 additions & 13 deletions lib/KeyManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,7 @@ public function __construct(
$this->recoveryKeyId);
}

$this->publicShareKeyId = $this->config->getAppValue('encryption',
'publicShareKeyId');
if (empty($this->publicShareKeyId)) {
$this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
$this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
}

$this->masterKeyId = $this->config->getAppValue('encryption',
'masterKeyId');
if (empty($this->masterKeyId)) {
$this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
$this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
}
$this->setPublicShareKeyIDAndMasterKeyId();

$this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
$this->log = $log;
Expand Down Expand Up @@ -701,6 +689,9 @@ public function getMasterKeyPassword() {
* @return string
*/
public function getMasterKeyId() {
if($this->config->getAppValue('encryption', 'masterKeyId') !== $this->masterKeyId) {
$this->masterKeyId = $this->config->getAppValue('encryption', 'masterKeyId');
}
return $this->masterKeyId;
}

Expand All @@ -712,4 +703,23 @@ public function getMasterKeyId() {
public function getPublicMasterKey() {
return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID);
}

/**
* set publicShareKeyId and masterKeyId if not set
*/
public function setPublicShareKeyIDAndMasterKeyId() {
$this->publicShareKeyId = $this->config->getAppValue('encryption',
'publicShareKeyId');
if (empty($this->publicShareKeyId)) {
$this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
$this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
}

$this->masterKeyId = $this->config->getAppValue('encryption',
'masterKeyId');
if (empty($this->masterKeyId)) {
$this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
$this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
}
}
}
Loading

0 comments on commit a19ae76

Please sign in to comment.