summaryrefslogtreecommitdiffstats
path: root/apps/encryption/lib
diff options
context:
space:
mode:
authorBjoern Schiessle <schiessle@owncloud.com>2015-08-24 15:56:04 +0200
committerBjoern Schiessle <schiessle@owncloud.com>2015-08-26 14:58:22 +0200
commit8c08dd0ac26290829b10e28c333358ae10d953eb (patch)
treea80791395ea10b3bc2573d2576a12ceb396d71da /apps/encryption/lib
parent6c8c8a328abb0823a8b20e30c2ace06f2b568ea3 (diff)
downloadnextcloud-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.php424
-rw-r--r--apps/encryption/lib/crypto/encryption.php19
-rw-r--r--apps/encryption/lib/users/setup.php2
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