nextcloud/apps/encryption/hooks/userhooks.php

305 lines
8.4 KiB
PHP
Raw Normal View History

2015-02-24 21:05:19 +03:00
<?php
/**
* @author Clark Tomlinson <clark@owncloud.com>
* @since 2/19/15, 10:02 AM
* @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\Hooks;
use OCA\Encryption\Hooks\Contracts\IHook;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Migrator;
use OCA\Encryption\RequirementsChecker;
use OCA\Encryption\Users\Setup;
use OCP\App;
use OCP\ILogger;
use OCP\IUserSession;
use OCP\Util;
use Test\User;
class UserHooks implements IHook {
/**
* @var KeyManager
*/
private $keyManager;
/**
* @var ILogger
*/
private $logger;
/**
* @var Setup
*/
private $userSetup;
/**
* @var Migrator
*/
private $migrator;
/**
* @var IUserSession
*/
private $user;
/**
* UserHooks constructor.
*
* @param KeyManager $keyManager
* @param ILogger $logger
* @param Setup $userSetup
* @param Migrator $migrator
* @param IUserSession $user
*/
public function __construct(
KeyManager $keyManager, ILogger $logger, Setup $userSetup, Migrator $migrator, IUserSession $user) {
$this->keyManager = $keyManager;
$this->logger = $logger;
$this->userSetup = $userSetup;
$this->migrator = $migrator;
$this->user = $user;
}
/**
* Connects Hooks
*
* @return null
*/
public function addHooks() {
Util::connectHook('OC_User', 'post_login', $this, 'login');
Util::connectHook('OC_User', 'logout', $this, 'logout');
Util::connectHook('OC_User', 'post_setPassword', $this, 'setPassphrase');
Util::connectHook('OC_User', 'pre_setPassword', $this, 'preSetPassphrase');
Util::connectHook('OC_User', 'post_createUser', $this, 'postCreateUser');
Util::connectHook('OC_User', 'post_deleteUser', $this, 'postDeleteUser');
}
/**
* Startup encryption backend upon user login
*
* @note This method should never be called for users using client side encryption
*/
public function login($params) {
if (!App::isEnabled('encryption')) {
return true;
}
// ensure filesystem is loaded
// Todo: update?
if (!\OC\Files\Filesystem::$loaded) {
\OC_Util::setupFS($params['uid']);
}
// setup user, if user not ready force relogin
if (!$this->userSetup->setupUser($params['password'])) {
return false;
}
$cache = $this->keyManager->init();
// Check if first-run file migration has already been performed
$ready = false;
$migrationStatus = $this->migrator->getStatus($params['uid']);
if ($migrationStatus === Migrator::$migrationOpen && $cache !== false) {
$ready = $this->migrator->beginMigration();
} elseif ($migrationStatus === Migrator::$migrationInProgress) {
// refuse login as long as the initial encryption is running
sleep(5);
$this->user->logout();
return false;
}
$result = true;
// If migration not yet done
if ($ready) {
// Encrypt existing user files
try {
$result = $util->encryptAll('/' . $params['uid'] . '/' . 'files');
} catch (\Exception $ex) {
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL);
$result = false;
}
if ($result) {
\OC_Log::write(
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
, \OC_Log::INFO
);
// Register successful migration in DB
$util->finishMigration();
} else {
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed!', \OCP\Util::FATAL);
$util->resetMigrationStatus();
\OCP\User::logout();
}
}
return $result;
}
/**
* remove keys from session during logout
*/
public function logout() {
$session = new Session(new \OC\Files\View());
$session->removeKeys();
}
/**
* setup encryption backend upon user created
*
* @note This method should never be called for users using client side encryption
*/
public function postCreateUser($params) {
if (App::isEnabled('files_encryption')) {
$view = new \OC\Files\View('/');
$util = new Util($view, $params['uid']);
Helper::setupUser($util, $params['password']);
}
}
/**
* cleanup encryption backend upon user deleted
*
* @note This method should never be called for users using client side encryption
*/
public function postDeleteUser($params) {
if (App::isEnabled('files_encryption')) {
Keymanager::deletePublicKey(new \OC\Files\View(), $params['uid']);
}
}
/**
* If the password can't be changed within ownCloud, than update the key password in advance.
*/
public function preSetPassphrase($params) {
if (App::isEnabled('files_encryption')) {
if (!\OC_User::canUserChangePassword($params['uid'])) {
self::setPassphrase($params);
}
}
}
/**
* Change a user's encryption passphrase
*
* @param array $params keys: uid, password
*/
public function setPassphrase($params) {
if (App::isEnabled('files_encryption') === false) {
return true;
}
// Only attempt to change passphrase if server-side encryption
// is in use (client-side encryption does not have access to
// the necessary keys)
if (Crypt::mode() === 'server') {
$view = new \OC\Files\View('/');
$session = new Session($view);
// Get existing decrypted private key
$privateKey = $session->getPrivateKey();
if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
// Encrypt private key with new user pwd as passphrase
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password'], Helper::getCipher());
// Save private key
if ($encryptedPrivateKey) {
Keymanager::setPrivateKey($encryptedPrivateKey, \OCP\User::getUser());
} else {
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
}
// NOTE: Session does not need to be updated as the
// private key has not changed, only the passphrase
// used to decrypt it has changed
} else { // admin changed the password for a different user, create new keys and reencrypt file keys
$user = $params['uid'];
$util = new Util($view, $user);
$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
// we generate new keys if...
// ...we have a recovery password and the user enabled the recovery key
// ...encryption was activated for the first time (no keys exists)
// ...the user doesn't have any files
if (($util->recoveryEnabledForUser() && $recoveryPassword)
|| !$util->userKeysExists()
|| !$view->file_exists($user . '/files')
) {
// backup old keys
$util->backupAllKeys('recovery');
$newUserPassword = $params['password'];
// make sure that the users home is mounted
\OC\Files\Filesystem::initMountPoints($user);
$keypair = Crypt::createKeypair();
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
// Save public key
Keymanager::setPublicKey($keypair['publicKey'], $user);
// Encrypt private key with new password
$encryptedKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword, Helper::getCipher());
if ($encryptedKey) {
Keymanager::setPrivateKey($encryptedKey, $user);
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
$util = new Util($view, $user);
$util->recoverUsersFiles($recoveryPassword);
}
} else {
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
}
\OC_FileProxy::$enabled = $proxyStatus;
}
}
}
}
/**
* after password reset we create a new key pair for the user
*
* @param array $params
*/
public function postPasswordReset($params) {
$uid = $params['uid'];
$password = $params['password'];
$util = new Util(new \OC\Files\View(), $uid);
$util->replaceUserKeys($password);
}
}