diff options
author | Bjoern Schiessle <schiessle@owncloud.com> | 2015-08-24 15:56:04 +0200 |
---|---|---|
committer | Bjoern Schiessle <schiessle@owncloud.com> | 2015-08-26 14:58:22 +0200 |
commit | 8c08dd0ac26290829b10e28c333358ae10d953eb (patch) | |
tree | a80791395ea10b3bc2573d2576a12ceb396d71da /apps/encryption/lib | |
parent | 6c8c8a328abb0823a8b20e30c2ace06f2b568ea3 (diff) | |
download | nextcloud-server-8c08dd0ac26290829b10e28c333358ae10d953eb.tar.gz nextcloud-server-8c08dd0ac26290829b10e28c333358ae10d953eb.zip |
occ tool to encrypt all files
Diffstat (limited to 'apps/encryption/lib')
-rw-r--r-- | apps/encryption/lib/crypto/encryptall.php | 424 | ||||
-rw-r--r-- | apps/encryption/lib/crypto/encryption.php | 19 | ||||
-rw-r--r-- | apps/encryption/lib/users/setup.php | 2 |
3 files changed, 445 insertions, 0 deletions
diff --git a/apps/encryption/lib/crypto/encryptall.php b/apps/encryption/lib/crypto/encryptall.php new file mode 100644 index 00000000000..a0c69c13fdd --- /dev/null +++ b/apps/encryption/lib/crypto/encryptall.php @@ -0,0 +1,424 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2015, 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 OCA\Encryption\Crypto; + +use OC\Encryption\Exceptions\DecryptionFailedException; +use OC\Files\View; +use OCA\Encryption\KeyManager; +use OCA\Encryption\Users\Setup; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IUserManager; +use OCP\Mail\IMailer; +use OCP\Security\ISecureRandom; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +class EncryptAll { + + /** @var Setup */ + protected $userSetup; + + /** @var IUserManager */ + protected $userManager; + + /** @var View */ + protected $rootView; + + /** @var KeyManager */ + protected $keyManager; + + /** @var array */ + protected $userPasswords; + + /** @var IConfig */ + protected $config; + + /** @var IMailer */ + protected $mailer; + + /** @var IL10N */ + protected $l; + + /** @var QuestionHelper */ + protected $questionHelper; + + /** @var OutputInterface */ + protected $output; + + /** @var InputInterface */ + protected $input; + + /** @var ISecureRandom */ + protected $secureRandom; + + /** + * @param Setup $userSetup + * @param IUserManager $userManager + * @param View $rootView + * @param KeyManager $keyManager + * @param IConfig $config + * @param IMailer $mailer + * @param IL10N $l + * @param QuestionHelper $questionHelper + * @param ISecureRandom $secureRandom + */ + public function __construct( + Setup $userSetup, + IUserManager $userManager, + View $rootView, + KeyManager $keyManager, + IConfig $config, + IMailer $mailer, + IL10N $l, + QuestionHelper $questionHelper, + ISecureRandom $secureRandom + ) { + $this->userSetup = $userSetup; + $this->userManager = $userManager; + $this->rootView = $rootView; + $this->keyManager = $keyManager; + $this->config = $config; + $this->mailer = $mailer; + $this->l = $l; + $this->questionHelper = $questionHelper; + $this->secureRandom = $secureRandom; + // store one time passwords for the users + $this->userPasswords = array(); + } + + /** + * start to encrypt all files + * + * @param InputInterface $input + * @param OutputInterface $output + */ + public function encryptAll(InputInterface $input, OutputInterface $output) { + + $this->input = $input; + $this->output = $output; + + $headline = 'Encrypt all files with the ' . Encryption::DISPLAY_NAME; + $this->output->writeln("\n"); + $this->output->writeln($headline); + $this->output->writeln(str_pad('', strlen($headline), '=')); + + //create private/public keys for each user and store the private key password + $this->output->writeln("\n"); + $this->output->writeln('Create key-pair for every user'); + $this->output->writeln('------------------------------'); + $this->output->writeln(''); + $this->output->writeln('This module will encrypt all files in the users files folder initially.'); + $this->output->writeln('Already existing versions and files in the trash bin will not be encrypted.'); + $this->output->writeln(''); + $this->createKeyPairs(); + + //setup users file system and encrypt all files one by one (take should encrypt setting of storage into account) + $this->output->writeln("\n"); + $this->output->writeln('Start to encrypt users files'); + $this->output->writeln('----------------------------'); + $this->output->writeln(''); + $this->encryptAllUsersFiles(); + //send-out or display password list and write it to a file + $this->output->writeln("\n"); + $this->output->writeln('Generated encryption key passwords'); + $this->output->writeln('----------------------------------'); + $this->output->writeln(''); + $this->outputPasswords(); + $this->output->writeln("\n"); + } + + /** + * create key-pair for every user + */ + protected function createKeyPairs() { + $this->output->writeln("\n"); + $progress = new ProgressBar($this->output); + $progress->setFormat(" %message% \n [%bar%]"); + $progress->start(); + + foreach($this->userManager->getBackends() as $backend) { + $limit = 500; + $offset = 0; + do { + $users = $backend->getUsers('', $limit, $offset); + foreach ($users as $user) { + if ($this->keyManager->userHasKeys($user) === false) { + $progress->setMessage('Create key-pair for ' . $user); + $progress->advance(); + $this->setupUserFS($user); + $password = $this->generateOneTimePassword($user); + $this->userSetup->setupUser($user, $password); + } else { + // users which already have a key-pair will be stored with a + // empty password and filtered out later + $this->userPasswords[$user] = ''; + } + } + $offset += $limit; + } while(count($users) >= $limit); + } + + $progress->setMessage('Key-pair created for all users'); + $progress->finish(); + } + + /** + * iterate over all user and encrypt their files + */ + protected function encryptAllUsersFiles() { + $this->output->writeln("\n"); + $progress = new ProgressBar($this->output); + $progress->setFormat(" %message% \n [%bar%]"); + $progress->start(); + $numberOfUsers = count($this->userPasswords); + $userNo = 1; + foreach ($this->userPasswords as $uid => $password) { + $userCount = "$uid ($userNo of $numberOfUsers)"; + $this->encryptUsersFiles($uid, $progress, $userCount); + $userNo++; + } + $progress->setMessage("all files encrypted"); + $progress->finish(); + + } + + /** + * encrypt files from the given user + * + * @param string $uid + * @param ProgressBar $progress + * @param string $userCount + */ + protected function encryptUsersFiles($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 { + $progress->setMessage("encrypt files for user $userCount: $path"); + $progress->advance(); + if($this->encryptFile($path) === false) { + $progress->setMessage("encrypt files for user $userCount: $path (already encrypted)"); + $progress->advance(); + } + } + } + } + } + + /** + * encrypt file + * + * @param string $path + * @return bool + */ + protected function encryptFile($path) { + + $source = $path; + $target = $path . '.encrypted.' . time(); + + 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; + } + + /** + * output one-time encryption passwords + */ + protected function outputPasswords() { + $table = new Table($this->output); + $table->setHeaders(array('Username', 'Private key password')); + + //create rows + $newPasswords = array(); + $unchangedPasswords = array(); + foreach ($this->userPasswords as $uid => $password) { + if (empty($password)) { + $unchangedPasswords[] = $uid; + } else { + $newPasswords[] = [$uid, $password]; + } + } + $table->setRows($newPasswords); + $table->render(); + + if (!empty($unchangedPasswords)) { + $this->output->writeln("\nThe following users already had a key-pair which was reused without setting a new password:\n"); + foreach ($unchangedPasswords as $uid) { + $this->output->writeln(" $uid"); + } + } + + $this->writePasswordsToFile($newPasswords); + + $this->output->writeln(''); + $question = new ConfirmationQuestion('Do you want to send the passwords directly to the users by mail? (y/n) ', false); + if ($this->questionHelper->ask($this->input, $this->output, $question)) { + $this->sendPasswordsByMail(); + } + } + + /** + * write one-time encryption passwords to a csv file + * + * @param array $passwords + */ + protected function writePasswordsToFile(array $passwords) { + $fp = $this->rootView->fopen('oneTimeEncryptionPasswords.csv', 'w'); + foreach ($passwords as $pwd) { + fputcsv($fp, $pwd); + } + fclose($fp); + $this->output->writeln("\n"); + $this->output->writeln('A list of all newly created passwords was written to data/oneTimeEncryptionPasswords.csv'); + $this->output->writeln(''); + $this->output->writeln('Each of these users need to login to the web interface, go to the'); + $this->output->writeln('personal settings section "ownCloud basic encryption module" and'); + $this->output->writeln('update the private key password to match the login password again by'); + $this->output->writeln('entering the one-time password into the "old log-in password" field'); + $this->output->writeln('and their current login password'); + } + + /** + * setup user file system + * + * @param string $uid + */ + protected function setupUserFS($uid) { + \OC_Util::tearDownFS(); + \OC_Util::setupFS($uid); + } + + /** + * generate one time password for the user and store it in a array + * + * @param string $uid + * @return string password + */ + protected function generateOneTimePassword($uid) { + $password = $this->secureRandom->getMediumStrengthGenerator()->generate(8); + $this->userPasswords[$uid] = $password; + return $password; + } + + /** + * send encryption key passwords to the users by mail + */ + protected function sendPasswordsByMail() { + $noMail = []; + + $this->output->writeln(''); + $progress = new ProgressBar($this->output, count($this->userPasswords)); + $progress->start(); + + foreach ($this->userPasswords as $recipient => $password) { + $progress->advance(); + if (!empty($password)) { + $recipientDisplayName = $this->userManager->get($recipient)->getDisplayName(); + $to = $this->config->getUserValue($recipient, 'settings', 'email', ''); + + if ($to === '') { + $noMail[] = $recipient; + continue; + } + + $subject = (string)$this->l->t('one-time password for server-side-encryption'); + list($htmlBody, $textBody) = $this->createMailBody($password); + + // send it out now + try { + $message = $this->mailer->createMessage(); + $message->setSubject($subject); + $message->setTo([$to => $recipientDisplayName]); + $message->setHtmlBody($htmlBody); + $message->setPlainBody($textBody); + $message->setFrom([ + \OCP\Util::getDefaultEmailAddress('admin-noreply') + ]); + + $this->mailer->send($message); + } catch (\Exception $e) { + $noMail[] = $recipient; + } + } + } + + $progress->finish(); + + if (empty($noMail)) { + $this->output->writeln("\n\nPassword successfully send to all users"); + } else { + $table = new Table($this->output); + $table->setHeaders(array('Username', 'Private key password')); + $this->output->writeln("\n\nCould not send password to following users:\n"); + $rows = []; + foreach ($noMail as $uid) { + $rows[] = [$uid, $this->userPasswords[$uid]]; + } + $table->setRows($rows); + $table->render(); + } + + } + + /** + * create mail body for plain text and html mail + * + * @param string $password one-time encryption password + * @return array an array of the html mail body and the plain text mail body + */ + protected function createMailBody($password) { + + $html = new \OC_Template("encryption", "mail", ""); + $html->assign ('password', $password); + $htmlMail = $html->fetchPage(); + + $plainText = new \OC_Template("encryption", "altmail", ""); + $plainText->assign ('password', $password); + $plainTextMail = $plainText->fetchPage(); + + return [$htmlMail, $plainTextMail]; + } + +} diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php index 1fa0581506b..1bd6af2eca7 100644 --- a/apps/encryption/lib/crypto/encryption.php +++ b/apps/encryption/lib/crypto/encryption.php @@ -35,6 +35,8 @@ use OCP\Encryption\IEncryptionModule; use OCA\Encryption\KeyManager; use OCP\IL10N; use OCP\ILogger; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; class Encryption implements IEncryptionModule { @@ -79,22 +81,28 @@ class Encryption implements IEncryptionModule { /** @var IL10N */ private $l; + /** @var EncryptAll */ + private $encryptAll; + /** * * @param Crypt $crypt * @param KeyManager $keyManager * @param Util $util + * @param EncryptAll $encryptAll * @param ILogger $logger * @param IL10N $il10n */ public function __construct(Crypt $crypt, KeyManager $keyManager, Util $util, + EncryptAll $encryptAll, ILogger $logger, IL10N $il10n) { $this->crypt = $crypt; $this->keyManager = $keyManager; $this->util = $util; + $this->encryptAll = $encryptAll; $this->logger = $logger; $this->l = $il10n; } @@ -398,6 +406,17 @@ class Encryption implements IEncryptionModule { } /** + * Initial encryption of all files + * + * @param InputInterface $input + * @param OutputInterface $output write some status information to the terminal during encryption + * @return bool + */ + public function encryptAll(InputInterface $input, OutputInterface $output) { + return $this->encryptAll->encryptAll($input, $output); + } + + /** * @param string $path * @return string */ diff --git a/apps/encryption/lib/users/setup.php b/apps/encryption/lib/users/setup.php index f224826ed52..433ea824c9b 100644 --- a/apps/encryption/lib/users/setup.php +++ b/apps/encryption/lib/users/setup.php @@ -76,6 +76,8 @@ class Setup { } /** + * check if user has a key pair, if not we create one + * * @param string $uid userid * @param string $password user password * @return bool |