summaryrefslogtreecommitdiffstats
path: root/apps/encryption/lib/Crypto/EncryptAll.php
blob: 60c7b13b7082fe702cc4551263faa29e6157b542 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<?php
/**
 * @author Morris Jobke <hey@morrisjobke.de>
 *
 * @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/>
 *
 */


require_once(dirname(__DIR__) . '/3rdparty/autoload.php');

/**
 * Class SinceTagCheckVisitor
 *
 * this class checks all methods for the presence of the @since tag
 */
class SinceTagCheckVisitor extends \PhpParser\NodeVisitorAbstract {

	/** @var string */
	protected $namespace = '';
	/** @var string */
	protected $className = '';
	/** @var bool */
	protected $deprecatedClass = false;

	/** @var array */
	protected $errors = [];

	public function enterNode(\PhpParser\Node $node) {
		if($this->deprecatedClass) {
			return;
		}

		if($node instanceof \PhpParser\Node\Stmt\Namespace_) {
			$this->namespace = $node->name;
		}

		if($node instanceof \PhpParser\Node\Stmt\Interface_ or
			$node instanceof \PhpParser\Node\Stmt\Class_) {
			$this->className = $node->name;

			/** @var \PhpParser\Comment\Doc[] $comments */
			$comments = $node->getAttribute('comments');

			if(count($comments) === 0) {
				$this->errors[] = 'PHPDoc is needed for ' . $this->namespace . '\\' . $this->className . '::' . $node->name;
				return;
			}

			$comment = $comments[count($comments) - 1];
			$text = $comment->getText();
			if(strpos($text, '@deprecated') !== false) {
				$this->deprecatedClass = true;
			}

			if($this->deprecatedClass === false && strpos($text, '@since') === false && 
<?php
/**
 * @copyright Copyright (c) 2016, ownCloud, Inc.
 *
 * @author Björn Schießle <bjoern@schiessle.org>
 * @author Morris Jobke <hey@morrisjobke.de>
 * @author Roeland Jago Douma <roeland@famdouma.nl>
 * @author Thomas Müller <thomas.mueller@tmit.eu>
 *
 * @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 OCA\Encryption\Util;
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 Util */
	protected $util;

	/** @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 Util $util
	 * @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,
		Util $util,
		IConfig $config,
		IMailer $mailer,
		IL10N $l,
		QuestionHelper $questionHelper,
		ISecureRandom $secureRandom
	) {
		$this->userSetup = $userSetup;
		$this->userManager = $userManager;
		$this->rootView = $rootView;
		$this->keyManager = $keyManager;
		$this->util = $util;
		$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), '='));
		$this->output->writeln("\n");

		if ($this->util->isMasterKeyEnabled()) {
			$this->output->writeln('Use master key to encrypt all files.');
			$this->keyManager->validateMasterKey();
		} else {
			//create private/public keys for each user and store the private key password
			$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();
		if ($this->util->isMasterKeyEnabled() === false) {
			//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;
		if ($this->util->isMasterKeyEnabled()) {
			$this->encryptAllUserFilesWithMasterKey($progress);
		} else {
			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 all user files with the master key
	 *
	 * @param ProgressBar $progress
	 */
	protected function encryptAllUserFilesWithMasterKey(ProgressBar $progress) {
		$userNo = 1;
		foreach($this->userManager->getBackends() as $backend) {
			$limit = 500;
			$offset = 0;
			do {
				$users = $backend->getUsers('', $limit, $offset);
				foreach ($users as $user) {
					$userCount = "$user ($userNo)";
					$this->encryptUsersFiles($user, $progress, $userCount);
					$userNo++;
				}
				$offset += $limit;
			} while(count($users) >= $limit);
		}
	}

	/**
	 * 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];
			}
		}

		if (empty($newPasswords)) {
			$this->output->writeln("\nAll users already had a key-pair, no further action needed.\n");
			return;
		}

		$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 "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->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 $uid => $password) {
			$progress->advance();
			if (!empty($password)) {
				$recipient = $this->userManager->get($uid);
				$recipientDisplayName = $recipient->getDisplayName();
				$to = $recipient->getEMailAddress();

				if ($to === '') {
					$noMail[] = $uid;
					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[] = $uid;
				}
			}
		}

		$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];
	}

}