Merge pull request #3558 from owncloud/files_encryption_check_private_key

check if the decrypted private key is valid on login and on read/write files
This commit is contained in:
Björn Schießle 2013-06-13 04:18:32 -07:00
commit 4bafa4e81a
20 changed files with 461 additions and 159 deletions

View file

@ -22,28 +22,28 @@ $return = false;
$oldPassword = $_POST['oldPassword'];
$newPassword = $_POST['newPassword'];
$view = new \OC\Files\View('/');
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), \OCP\User::getUser());
$result = $util->checkRecoveryPassword($oldPassword);
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ($result) {
$keyId = $util->getRecoveryKeyId();
$keyPath = '/owncloud_private_key/' . $keyId . '.private.key';
$view = new \OC\Files\View('/');
$keyId = $util->getRecoveryKeyId();
$keyPath = '/owncloud_private_key/' . $keyId . '.private.key';
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptedRecoveryKey = $view->file_get_contents($keyPath);
$decryptedRecoveryKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedRecoveryKey, $oldPassword);
if ($decryptedRecoveryKey) {
$encryptedRecoveryKey = $view->file_get_contents($keyPath);
$decryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricDecryptFileContent($encryptedRecoveryKey, $oldPassword);
$encryptedRecoveryKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedRecoveryKey, $newPassword);
$view->file_put_contents($keyPath, $encryptedRecoveryKey);
\OC_FileProxy::$enabled = $proxyStatus;
$return = true;
}
\OC_FileProxy::$enabled = $proxyStatus;
// success or failure
if ($return) {
\OCP\JSON::success(array('data' => array('message' => $l->t('Password successfully changed.'))));

View file

@ -0,0 +1,54 @@
<?php
/**
* Copyright (c) 2013, Bjoern Schiessle <schiessle@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*
* @brief Script to change recovery key password
*
*/
use OCA\Encryption;
\OCP\JSON::checkLoggedIn();
\OCP\JSON::checkAppEnabled('files_encryption');
\OCP\JSON::callCheck();
$l = OC_L10N::get('core');
$return = false;
$oldPassword = $_POST['oldPassword'];
$newPassword = $_POST['newPassword'];
$view = new \OC\Files\View('/');
$session = new \OCA\Encryption\Session($view);
$user = \OCP\User::getUser();
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$keyPath = '/' . $user . '/files_encryption/' . $user . '.private.key';
$encryptedKey = $view->file_get_contents($keyPath);
$decryptedKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, $oldPassword);
if ($decryptedKey) {
$encryptedKey = \OCA\Encryption\Crypt::symmetricEncryptFileContent($decryptedKey, $newPassword);
$view->file_put_contents($keyPath, $encryptedKey);
$session->setPrivateKey($decryptedKey);
$return = true;
}
\OC_FileProxy::$enabled = $proxyStatus;
// success or failure
if ($return) {
\OCP\JSON::success(array('data' => array('message' => $l->t('Private key password successfully updated.'))));
} else {
\OCP\JSON::error(array('data' => array('message' => $l->t('Could not update the private key password. Maybe the old password was not correct.'))));
}

View file

@ -35,9 +35,10 @@ if (!OC_Config::getValue('maintenance', false)) {
$view = new OC_FilesystemView('/');
$session = new \OCA\Encryption\Session($view);
$user = \OCP\USER::getUser();
// check if user has a private key
if (
!$session->getPrivateKey(\OCP\USER::getUser())
!$view->file_exists('/' . $user . '/files_encryption/' . $user . '.private.key')
&& OCA\Encryption\Crypt::mode() === 'server'
) {

View file

@ -0,0 +1,24 @@
<?php
if (!isset($_)) { //also provide standalone error page
require_once '../../../lib/base.php';
$l = OC_L10N::get('files_encryption');
$errorMsg = $l->t('Your private key is not valid! Maybe your password was changed from outside. You can update your private key password in your personal settings to regain access to your files');
if(isset($_GET['p']) && $_GET['p'] === '1') {
header('HTTP/1.0 404 ' . $errorMsg);
}
// check if ajax request
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
\OCP\JSON::error(array('data' => array('message' => $errorMsg)));
} else {
header('HTTP/1.0 404 ' . $errorMsg);
$tmpl = new OC_Template('files_encryption', 'invalid_private_key', 'guest');
$tmpl->printPage();
}
exit;
}
?>

View file

@ -60,11 +60,16 @@ class Hooks {
$encryptedKey = Keymanager::getPrivateKey($view, $params['uid']);
$privateKey = Crypt::symmetricDecryptFileContent($encryptedKey, $params['password']);
$privateKey = Crypt::decryptPrivateKey($encryptedKey, $params['password']);
if ($privateKey === false) {
\OCP\Util::writeLog('Encryption library', 'Private key for user "' . $params['uid']
. '" is not valid! Maybe the user password was changed from outside if so please change it back to gain access', \OCP\Util::ERROR);
}
$session = new \OCA\Encryption\Session($view);
$session->setPrivateKey($privateKey, $params['uid']);
$session->setPrivateKey($privateKey);
// Check if first-run file migration has already been performed
$ready = false;
@ -160,7 +165,7 @@ class Hooks {
public static function setPassphrase($params) {
// Only attempt to change passphrase if server-side encryption
// is in use (client-side encryption does not have access to
// is in use (client-side encryption does not have access to
// the necessary keys)
if (Crypt::mode() === 'server') {
@ -345,7 +350,7 @@ class Hooks {
$sharingEnabled = \OCP\Share::isEnabled();
// get the path including mount point only if not a shared folder
if(strncmp($path, '/Shared' , strlen('/Shared') !== 0)) {
if (strncmp($path, '/Shared', strlen('/Shared') !== 0)) {
// get path including the the storage mount point
$path = $util->getPathWithMountPoint($params['itemSource']);
}
@ -422,14 +427,14 @@ class Hooks {
}
// get the path including mount point only if not a shared folder
if(strncmp($path, '/Shared' , strlen('/Shared') !== 0)) {
if (strncmp($path, '/Shared', strlen('/Shared') !== 0)) {
// get path including the the storage mount point
$path = $util->getPathWithMountPoint($params['itemSource']);
}
// if we unshare a folder we need a list of all (sub-)files
if ($params['itemType'] === 'folder') {
$allFiles = $util->getAllFiles( $path );
$allFiles = $util->getAllFiles($path);
} else {
$allFiles = array($path);
}

View file

@ -4,7 +4,25 @@
* See the COPYING-README file.
*/
function updatePrivateKeyPasswd() {
var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val();
var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val();
OC.msg.startSaving('#encryption .msg');
$.post(
OC.filePath( 'files_encryption', 'ajax', 'updatePrivateKeyPassword.php' )
, { oldPassword: oldPrivateKeyPassword, newPassword: newPrivateKeyPassword }
, function( data ) {
if (data.status === "error") {
OC.msg.finishedSaving('#encryption .msg', data);
} else {
OC.msg.finishedSaving('#encryption .msg', data);
}
}
);
}
$(document).ready(function(){
// Trigger ajax on recoveryAdmin status change
$( 'input:radio[name="userEnableRecovery"]' ).change(
function() {
@ -57,4 +75,24 @@ $(document).ready(function(){
}
);
// update private key password
$('input:password[name="changePrivateKeyPassword"]').keyup(function(event) {
var oldPrivateKeyPassword = $('input:password[id="oldPrivateKeyPassword"]').val();
var newPrivateKeyPassword = $('input:password[id="newPrivateKeyPassword"]').val();
if (newPrivateKeyPassword !== '' && oldPrivateKeyPassword !== '' ) {
$('button:button[name="submitChangePrivateKeyPassword"]').removeAttr("disabled");
if(event.which === 13) {
updatePrivateKeyPasswd();
}
} else {
$('button:button[name="submitChangePrivateKeyPassword"]').attr("disabled", "true");
}
});
$('button:button[name="submitChangePrivateKeyPassword"]').click(function() {
updatePrivateKeyPasswd();
});
});

View file

@ -351,6 +351,34 @@ class Crypt {
}
/**
* @brief Decrypt private key and check if the result is a valid keyfile
* @param string $encryptedKey encrypted keyfile
* @param string $passphrase to decrypt keyfile
* @returns encrypted private key or false
*
* This function decrypts a file
*/
public static function decryptPrivateKey($encryptedKey, $passphrase) {
$plainKey = self::symmetricDecryptFileContent($encryptedKey, $passphrase);
// check if this a valid private key
$res = openssl_pkey_get_private($plainKey);
if (is_resource($res)) {
$sslInfo = openssl_pkey_get_details($res);
if (!isset($sslInfo['key'])) {
$plainKey = false;
}
} else {
$plainKey = false;
}
return $plainKey;
}
/**
* @brief Creates symmetric keyfile content using a generated key
* @param string $plainContent content to be encrypted

View file

@ -74,7 +74,7 @@ class Helper {
if (!$util->ready()) {
\OCP\Util::writeLog('Encryption library', 'User account "' . $util->getUserId()
. '" is not ready for encryption; configuration started', \OCP\Util::DEBUG);
. '" is not ready for encryption; configuration started', \OCP\Util::DEBUG);
if (!$util->setupServerSide($password)) {
return false;
@ -94,6 +94,7 @@ class Helper {
* @return bool
*/
public static function adminEnableRecovery($recoveryKeyId, $recoveryPassword) {
$view = new \OC\Files\View('/');
if ($recoveryKeyId === null) {
@ -128,13 +129,6 @@ class Helper {
// Save private key
$view->file_put_contents('/owncloud_private_key/' . $recoveryKeyId . '.private.key', $encryptedPrivateKey);
// create control file which let us check later on if the entered password was correct.
$encryptedControlData = \OCA\Encryption\Crypt::keyEncrypt("ownCloud", $keypair['publicKey']);
if (!$view->is_dir('/control-file')) {
$view->mkdir('/control-file');
}
$view->file_put_contents('/control-file/controlfile.enc', $encryptedControlData);
\OC_FileProxy::$enabled = true;
// Set recoveryAdmin as enabled
@ -201,4 +195,17 @@ class Helper {
return $relPath;
}
/**
* @brief redirect to a error page
*/
public static function redirectToErrorPage() {
$location = \OC_Helper::linkToAbsolute('apps/files_encryption/files', 'error.php');
$post = 0;
if(count($_POST) > 0) {
$post = 1;
}
header('Location: ' . $location . '?p=' . $post);
exit();
}
}

View file

@ -88,9 +88,10 @@ class Session {
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/' . $publicShareKeyId . '.private.key' );
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, '' );
$this->setPublicSharePrivateKey( $privateKey );
$encryptedKey = $this->view->file_get_contents(
'/owncloud_private_key/' . $publicShareKeyId . '.private.key');
$privateKey = Crypt::decryptPrivateKey($encryptedKey, '');
$this->setPublicSharePrivateKey($privateKey);
\OC_FileProxy::$enabled = $proxyStatus;
}
@ -121,7 +122,7 @@ class Session {
if (\OCA\Encryption\Helper::isPublicAccess()) {
return $this->getPublicSharePrivateKey();
} else {
if (!is_null( \OC::$session->get('privateKey') )) {
if (!is_null(\OC::$session->get('privateKey'))) {
return \OC::$session->get('privateKey');
} else {
return false;
@ -136,7 +137,7 @@ class Session {
*/
public function setPublicSharePrivateKey($privateKey) {
\OC::$session->set('publicSharePrivateKey', $privateKey);
\OC::$session->set('publicSharePrivateKey', $privateKey);
return true;
@ -149,7 +150,7 @@ class Session {
*/
public function getPublicSharePrivateKey() {
if (!is_null( \OC::$session->get('publicSharePrivateKey') )) {
if (!is_null(\OC::$session->get('publicSharePrivateKey'))) {
return \OC::$session->get('publicSharePrivateKey');
} else {
return false;
@ -176,7 +177,7 @@ class Session {
*/
public function getLegacyKey() {
if ( !is_null( \OC::$session->get('legacyKey') ) ) {
if (!is_null(\OC::$session->get('legacyKey'))) {
return \OC::$session->get('legacyKey');

View file

@ -56,18 +56,21 @@ class Stream {
private $relPath; // rel path to users file dir
private $userId;
private $handle; // Resource returned by fopen
private $path;
private $readBuffer; // For streams that dont support seeking
private $meta = array(); // Header / meta for source stream
private $count;
private $writeCache;
private $size;
private $unencryptedSize;
private $publicKey;
private $keyfile;
private $encKeyfile;
private static $view; // a fsview object set to user dir
/**
* @var \OC\Files\View
*/
private $rootView; // a fsview object set to '/'
/**
* @var \OCA\Encryption\Session
*/
private $session;
private $privateKey;
/**
* @param $path
@ -82,6 +85,10 @@ class Stream {
$this->rootView = new \OC_FilesystemView('/');
}
$this->session = new \OCA\Encryption\Session($this->rootView);
$this->privateKey = $this->session->getPrivateKey($this->userId);
$util = new Util($this->rootView, \OCP\USER::getUser());
$this->userId = $util->getUserId();
@ -109,6 +116,11 @@ class Stream {
} else {
if($this->privateKey === false) {
// if private key is not valid redirect user to a error page
\OCA\Encryption\Helper::redirectToErrorPage();
}
$this->size = $this->rootView->filesize($this->rawPath, $mode);
}
@ -118,7 +130,7 @@ class Stream {
if (!is_resource($this->handle)) {
\OCP\Util::writeLog('files_encryption', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR);
\OCP\Util::writeLog('Encryption library', 'failed to open file "' . $this->rawPath . '"', \OCP\Util::ERROR);
} else {
@ -156,7 +168,7 @@ class Stream {
// $count will always be 8192 https://bugs.php.net/bug.php?id=21641
// This makes this function a lot simpler, but will break this class if the above 'bug' gets 'fixed'
\OCP\Util::writeLog('files_encryption', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL);
\OCP\Util::writeLog('Encryption library', 'PHP "bug" 21641 no longer holds, decryption system requires refactoring', \OCP\Util::FATAL);
die();
@ -165,7 +177,7 @@ class Stream {
// Get the data from the file handle
$data = fread($this->handle, 8192);
$result = '';
$result = null;
if (strlen($data)) {
@ -175,10 +187,11 @@ class Stream {
throw new \Exception(
'Encryption key not found for "' . $this->rawPath . '" during attempted read via stream');
}
} else {
// Decrypt data
$result = Crypt::symmetricDecryptFileContent($data, $this->plainKey);
// Decrypt data
$result = Crypt::symmetricDecryptFileContent($data, $this->plainKey);
}
}
@ -228,13 +241,18 @@ class Stream {
// If a keyfile already exists
if ($this->encKeyfile) {
$session = new \OCA\Encryption\Session( $this->rootView );
// if there is no valid private key return false
if ($this->privateKey === false) {
$privateKey = $session->getPrivateKey($this->userId);
// if private key is not valid redirect user to a error page
\OCA\Encryption\Helper::redirectToErrorPage();
return false;
}
$shareKey = Keymanager::getShareKey($this->rootView, $this->userId, $this->relPath);
$this->plainKey = Crypt::multiKeyDecrypt($this->encKeyfile, $shareKey, $privateKey);
$this->plainKey = Crypt::multiKeyDecrypt($this->encKeyfile, $shareKey, $this->privateKey);
return true;
@ -257,6 +275,12 @@ class Stream {
*/
public function stream_write($data) {
// if there is no valid private key return false
if ($this->privateKey === false) {
$this->size = 0;
return strlen($data);
}
// Disable the file proxies so that encryption is not
// automatically attempted when the file is written to disk -
// we are handling that separately here and we don't want to
@ -424,6 +448,28 @@ class Stream {
$this->flush();
// if there is no valid private key return false
if ($this->privateKey === false) {
// cleanup
if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb') {
// Disable encryption proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ($this->rootView->file_exists($this->rawPath) && $this->size === 0) {
$this->rootView->unlink($this->rawPath);
}
// Re-enable proxy - our work is done
\OC_FileProxy::$enabled = $proxyStatus;
}
// if private key is not valid redirect user to a error page
\OCA\Encryption\Helper::redirectToErrorPage();
}
if (
$this->meta['mode'] !== 'r'
and $this->meta['mode'] !== 'rb'
@ -450,16 +496,14 @@ class Stream {
// Encrypt enc key for all sharing users
$this->encKeyfiles = Crypt::multiKeyEncrypt($this->plainKey, $publicKeys);
$view = new \OC_FilesystemView('/');
// Save the new encrypted file key
Keymanager::setFileKey($this->rootView, $this->relPath, $this->userId, $this->encKeyfiles['data']);
// Save the sharekeys
Keymanager::setShareKeys($view, $this->relPath, $this->encKeyfiles['keys']);
Keymanager::setShareKeys($this->rootView, $this->relPath, $this->encKeyfiles['keys']);
// get file info
$fileInfo = $view->getFileInfo($this->rawPath);
$fileInfo = $this->rootView->getFileInfo($this->rawPath);
if (!is_array($fileInfo)) {
$fileInfo = array();
}
@ -473,7 +517,7 @@ class Stream {
$fileInfo['unencrypted_size'] = $this->unencryptedSize;
// set fileinfo
$view->putFileInfo($this->rawPath, $fileInfo);
$this->rootView->putFileInfo($this->rawPath, $fileInfo);
}
return fclose($this->handle);

View file

@ -305,7 +305,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$row = $result->fetchRow();
if (isset($row['recovery_enabled'])) {
$recoveryEnabled[] = $row['recovery_enabled'];
@ -445,7 +445,7 @@ class Util {
// If the file uses old
// encryption system
} elseif ( Crypt::isLegacyEncryptedContent( $data, $relPath ) ) {
} elseif (Crypt::isLegacyEncryptedContent($data, $relPath)) {
$found['legacy'][] = array(
'name' => $file,
@ -576,7 +576,9 @@ class Util {
// get relative path
$relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
if (isset($pathParts[2]) && $pathParts[2] === 'files' && $this->view->file_exists($path) && $this->isEncryptedPath($path)) {
if (isset($pathParts[2]) && $pathParts[2] === 'files' && $this->view->file_exists($path)
&& $this->isEncryptedPath($path)
) {
// get the size from filesystem
$fullPath = $this->view->getLocalFile($path);
@ -646,7 +648,7 @@ class Util {
return $result;
}
/**
* @param $path
* @return bool
@ -690,28 +692,32 @@ class Util {
$relPath = $plainFile['path'];
//relative to /data
$rawPath = '/'.$this->userId . '/files/' . $plainFile['path'];
$rawPath = '/' . $this->userId . '/files/' . $plainFile['path'];
// Open plain file handle for binary reading
$plainHandle = $this->view->fopen( $rawPath, 'rb' );
$plainHandle = $this->view->fopen($rawPath, 'rb');
// Open enc file handle for binary writing, with same filename as original plain file
$encHandle = fopen( 'crypt://' . $relPath.'.tmp', 'wb' );
$encHandle = fopen('crypt://' . $relPath . '.tmp', 'wb');
// Move plain file to a temporary location
$size = stream_copy_to_stream( $plainHandle, $encHandle );
$size = stream_copy_to_stream($plainHandle, $encHandle);
fclose($encHandle);
$fakeRoot = $this->view->getRoot();
$this->view->chroot('/'.$this->userId.'/files');
$this->view->chroot('/' . $this->userId . '/files');
$this->view->rename($relPath . '.tmp', $relPath);
$this->view->chroot($fakeRoot);
// Add the file to the cache
\OC\Files\Filesystem::putFileInfo( $relPath, array( 'encrypted' => true, 'size' => $size, 'unencrypted_size' => $size ) );
\OC\Files\Filesystem::putFileInfo($relPath, array(
'encrypted' => true,
'size' => $size,
'unencrypted_size' => $size
));
}
// Encrypt legacy encrypted files
@ -822,7 +828,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$row = $result->fetchRow();
$path = substr($row['path'], strlen('files'));
}
@ -1113,7 +1119,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$row = $result->fetchRow();
if (isset($row['migration_status'])) {
$migrationStatus[] = $row['migration_status'];
@ -1199,7 +1205,8 @@ class Util {
$result = array();
$content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath($this->userFilesDir . '/' . $dir));
$content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath(
$this->userFilesDir . '/' . $dir));
// handling for re shared folders
$pathSplit = explode('/', $dir);
@ -1260,7 +1267,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$row = $result->fetchRow();
}
}
@ -1286,7 +1293,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$row = $result->fetchRow();
}
}
@ -1311,7 +1318,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$source = $result->fetchRow();
}
}
@ -1332,7 +1339,7 @@ class Util {
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
} else {
if($result->numRows() > 0) {
if ($result->numRows() > 0) {
$item = $result->fetchRow();
}
}
@ -1380,26 +1387,24 @@ class Util {
*/
public function checkRecoveryPassword($password) {
$result = false;
$pathKey = '/owncloud_private_key/' . $this->recoveryKeyId . ".private.key";
$pathControlData = '/control-file/controlfile.enc';
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$recoveryKey = $this->view->file_get_contents($pathKey);
$decryptedRecoveryKey = Crypt::symmetricDecryptFileContent($recoveryKey, $password);
$decryptedRecoveryKey = Crypt::decryptPrivateKey($recoveryKey, $password);
$controlData = $this->view->file_get_contents($pathControlData);
$decryptedControlData = Crypt::keyDecrypt($controlData, $decryptedRecoveryKey);
if ($decryptedRecoveryKey) {
$result = true;
}
\OC_FileProxy::$enabled = $proxyStatus;
if ($decryptedControlData === 'ownCloud') {
return true;
}
return false;
return $result;
}
/**
@ -1528,7 +1533,7 @@ class Util {
$encryptedKey = $this->view->file_get_contents(
'/owncloud_private_key/' . $this->recoveryKeyId . '.private.key');
$privateKey = Crypt::symmetricDecryptFileContent($encryptedKey, $recoveryPassword);
$privateKey = Crypt::decryptPrivateKey($encryptedKey, $recoveryPassword);
\OC_FileProxy::$enabled = $proxyStatus;
@ -1544,7 +1549,7 @@ class Util {
list($storage, $internalPath) = \OC\Files\Cache\Cache::getById($id);
$mount = \OC\Files\Filesystem::getMountByStorageId($storage);
$mountPoint = $mount[0]->getMountPoint();
$path = \OC\Files\Filesystem::normalizePath($mountPoint.'/'.$internalPath);
$path = \OC\Files\Filesystem::normalizePath($mountPoint . '/' . $internalPath);
// reformat the path to be relative e.g. /user/files/folder becomes /folder/
$relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);

View file

@ -14,15 +14,26 @@ $tmpl = new OCP\Template('files_encryption', 'settings-personal');
$user = \OCP\USER::getUser();
$view = new \OC_FilesystemView('/');
$util = new \OCA\Encryption\Util($view, $user);
$session = new \OCA\Encryption\Session($view);
$privateKeySet = ($session->getPrivateKey() !== false) ? true : false;
$recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled');
$recoveryEnabledForUser = $util->recoveryEnabledForUser();
\OCP\Util::addscript('files_encryption', 'settings-personal');
\OCP\Util::addScript('settings', 'personal');
$result = false;
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
$tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser);
if ($recoveryAdminEnabled || !$privateKeySet) {
return $tmpl->fetchPage();
\OCP\Util::addscript('files_encryption', 'settings-personal');
\OCP\Util::addScript('settings', 'personal');
$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
$tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser);
$tmpl->assign('privateKeySet', $privateKeySet);
$result = $tmpl->fetchPage();
}
return $result;

View file

@ -0,0 +1,10 @@
<ul>
<li class='error'>
<?php $location = \OC_Helper::linkToRoute( "settings_personal" ).'#changePKPasswd' ?>
<?php p($l->t('Your private key is not valid! Maybe the your password was changed from outside.')); ?>
<br/>
<?php p($l->t('You can unlock your private key in your ')); ?> <a href="<?php echo $location?>"><?php p($l->t('personal settings')); ?>.</a>
<br/>
</li>
</ul>

View file

@ -1,54 +1,56 @@
<form id="encryption">
<fieldset class="personalblock">
<p>
<strong><?php p($l->t( 'Encryption' )); ?></strong>
<br />
<strong><?php p($l->t('Encryption')); ?></strong>
<br/>
</p>
<p>
<?php p($l->t( "Enable encryption passwords recovery key (allow sharing to recovery key):" )); ?>
<br />
<br />
<input type="password" name="recoveryPassword" id="recoveryPassword" />
<label for="recoveryPassword"><?php p($l->t( "Recovery account password" )); ?></label>
<br />
<input
type='radio'
name='adminEnableRecovery'
value='1'
<?php echo ( $_["recoveryEnabled"] == 1 ? 'checked="checked"' : 'disabled' ); ?> />
<?php p($l->t( "Enabled" )); ?>
<br />
<input
type='radio'
name='adminEnableRecovery'
value='0'
<?php echo ( $_["recoveryEnabled"] == 0 ? 'checked="checked"' : 'disabled' ); ?> />
<?php p($l->t( "Disabled" )); ?>
<?php p($l->t("Enable encryption passwords recovery key (allow sharing to recovery key):")); ?>
<br/>
<br/>
<input type="password" name="recoveryPassword" id="recoveryPassword"/>
<label for="recoveryPassword"><?php p($l->t("Recovery account password")); ?></label>
<br/>
<input
type='radio'
name='adminEnableRecovery'
value='1'
<?php echo($_["recoveryEnabled"] == 1 ? 'checked="checked"' : 'disabled'); ?> />
<?php p($l->t("Enabled")); ?>
<br/>
<input
type='radio'
name='adminEnableRecovery'
value='0'
<?php echo($_["recoveryEnabled"] == 0 ? 'checked="checked"' : 'disabled'); ?> />
<?php p($l->t("Disabled")); ?>
</p>
<br /><br />
<br/><br/>
<p>
<strong><?php p($l->t( "Change encryption passwords recovery key:" )); ?></strong>
<br /><br />
<input
<strong><?php p($l->t("Change encryption passwords recovery key:")); ?></strong>
<br/><br/>
<input
type="password"
name="changeRecoveryPassword"
id="oldRecoveryPassword"
<?php echo ( $_["recoveryEnabled"] == 0 ? 'disabled' : '' ); ?> />
<label for="oldRecoveryPassword"><?php p($l->t( "Old Recovery account password" )); ?></label>
<br />
<input
<?php echo($_["recoveryEnabled"] == 0 ? 'disabled' : ''); ?> />
<label for="oldRecoveryPassword"><?php p($l->t("Old Recovery account password")); ?></label>
<br/>
<input
type="password"
name="changeRecoveryPassword"
id="newRecoveryPassword"
<?php echo ( $_["recoveryEnabled"] == 0 ? 'disabled' : '' ); ?> />
<label for="newRecoveryPassword"><?php p($l->t( "New Recovery account password" )); ?></label>
<br />
<?php echo($_["recoveryEnabled"] == 0 ? 'disabled' : ''); ?> />
<label for="newRecoveryPassword"><?php p($l->t("New Recovery account password")); ?></label>
<br/>
<button
type="button"
name="submitChangeRecoveryKey"
disabled><?php p($l->t( "Change Password" )); ?>
disabled><?php p($l->t("Change Password")); ?>
</button>
<span class="msg"></span>
</p>

View file

@ -3,12 +3,48 @@
<legend>
<?php p( $l->t( 'Encryption' ) ); ?>
</legend>
<?php if ( $_["recoveryEnabled"] ): ?>
<?php if ( ! $_["privateKeySet"] ): ?>
<p>
<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery by sharing all files with your administrator:" ) ); ?></label>
<a name="changePKPasswd" />
<label for="changePrivateKeyPasswd">
<?php p( $l->t( "Your private key password no longer match your log-in password:" ) ); ?>
</label>
<br />
<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files if your password is lost" ) ); ?></em>
<em><?php p( $l->t( "Set your old private key password to your current log-in password." ) ); ?>
<?php if ( $_["recoveryEnabledForUser"] ):
p( $l->t( " If you don't remember your old password you can ask your administrator to recover your files." ) );
endif; ?>
</em>
<br />
<input
type="password"
name="changePrivateKeyPassword"
id="oldPrivateKeyPassword" />
<label for="oldPrivateKeyPassword"><?php p($l->t( "Old log-in password" )); ?></label>
<br />
<input
type="password"
name="changePrivateKeyPassword"
id="newPrivateKeyPassword" />
<label for="newRecoveryPassword"><?php p($l->t( "Current log-in password" )); ?></label>
<br />
<button
type="button"
name="submitChangePrivateKeyPassword"
disabled><?php p($l->t( "Update Private Key Password" )); ?>
</button>
<span class="msg"></span>
</p>
<?php endif; ?>
<br />
<?php if ( $_["recoveryEnabled"] && $_["privateKeySet"] ): ?>
<p>
<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery:" ) ); ?></label>
<br />
<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" ) ); ?></em>
<br />
<input
type='radio'
@ -28,6 +64,7 @@
<div id="recoveryEnabledError"><?php p( $l->t( 'Could not update file recovery' ) ); ?></div>
</p>
<?php endif; ?>
<br />
</fieldset>
</form>

View file

@ -92,8 +92,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
}
else {
} else {
OC_App::disable('files_trashbin');
}
}
@ -240,6 +239,23 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
}
function testDecryptPrivateKey() {
// test successful decrypt
$crypted = Encryption\Crypt::symmetricEncryptFileContent($this->genPrivateKey, 'hat');
$decrypted = Encryption\Crypt::decryptPrivateKey($crypted, 'hat');
$this->assertEquals($this->genPrivateKey, $decrypted);
//test private key decrypt with wrong password
$wrongPasswd = Encryption\Crypt::decryptPrivateKey($crypted, 'hat2');
$this->assertEquals(false, $wrongPasswd);
}
/**
* @medium
*/

View file

@ -111,8 +111,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
// reset app files_trashbin
if ($this->stateFilesTrashbin) {
OC_App::enable('files_trashbin');
}
else {
} else {
OC_App::disable('files_trashbin');
}
}
@ -656,9 +655,6 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// check if control file created
$this->assertTrue($this->view->file_exists('/control-file/controlfile.enc'));
// login as admin
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
@ -761,9 +757,6 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
// check if control file created
$this->assertTrue($this->view->file_exists('/control-file/controlfile.enc'));
// login as user1
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);

View file

@ -8,8 +8,11 @@
class OC_Core_LostPassword_Controller {
protected static function displayLostPasswordPage($error, $requested) {
$encrypted = OC_App::isEnabled('files_encryption');
OC_Template::printGuestPage('core/lostpassword', 'lostpassword',
array('error' => $error, 'requested' => $requested));
array('error' => $error,
'requested' => $requested,
'encrypted' => $encrypted));
}
protected static function displayResetPasswordPage($success, $args) {
@ -29,7 +32,14 @@ class OC_Core_LostPassword_Controller {
}
public static function sendEmail($args) {
if (OC_User::userExists($_POST['user'])) {
if(isset($_POST['noEncryption']) || isset($_POST['continue'])) {
$continue = true;
} else {
$continue = false;
}
if (OC_User::userExists($_POST['user']) && $continue) {
$token = hash('sha256', OC_Util::generate_random_bytes(30).OC_Config::getValue('passwordsalt', ''));
OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword',
hash('sha256', $token)); // Hash the token again to prevent timing attacks

View file

@ -17,6 +17,14 @@
<input type="text" name="user" id="user" placeholder="" value="" autocomplete="off" required autofocus />
<label for="user" class="infield"><?php print_unescaped($l->t( 'Username' )); ?></label>
<img class="svg" src="<?php print_unescaped(image_path('', 'actions/user.svg')); ?>" alt=""/>
<?php if ($_['encrypted']): ?>
<br /><br />
<?php print_unescaped($l->t('Your files seems to be encrypted. If you didn\'t have enabled the recovery key there will be no way to get your data back once the password was resetted. If you are not sure what to do, please contact your administrator first before continue. Do you really want to continue?')); ?><br />
<input type="checkbox" name="continue" value="Yes" />
<?php print_unescaped($l->t('Yes, I really want to reset my password now')); ?><br/><br/>
<?php else: ?>
<input type="checkbox" name="noEncryption" value="Yes" checked />
<?php endif; ?>
</p>
<input type="submit" id="submit" value="<?php print_unescaped($l->t('Request reset')); ?>" />
</fieldset>

View file

@ -7,44 +7,44 @@ OC_JSON::checkLoggedIn();
// Manually load apps to ensure hooks work correctly (workaround for issue 1503)
OC_APP::loadApps();
$username = isset($_POST["username"]) ? $_POST["username"] : OC_User::getUser();
$password = isset($_POST["password"]) ? $_POST["password"] : null;
$oldPassword=isset($_POST["oldpassword"])?$_POST["oldpassword"]:'';
$recoveryPassword=isset($_POST["recoveryPassword"])?$_POST["recoveryPassword"]:null;
$username = isset($_POST['username']) ? $_POST['username'] : OC_User::getUser();
$password = isset($_POST['password']) ? $_POST['password'] : null;
$oldPassword = isset($_POST['oldpassword']) ? $_POST['oldpassword'] : '';
$recoveryPassword = isset($_POST['recoveryPassword']) ? $_POST['recoveryPassword'] : null;
$userstatus = null;
if(OC_User::isAdminUser(OC_User::getUser())) {
if (OC_User::isAdminUser(OC_User::getUser())) {
$userstatus = 'admin';
}
if(OC_SubAdmin::isUserAccessible(OC_User::getUser(), $username)) {
if (OC_SubAdmin::isUserAccessible(OC_User::getUser(), $username)) {
$userstatus = 'subadmin';
}
if(OC_User::getUser() === $username && OC_User::checkPassword($username, $oldPassword)) {
if (OC_User::getUser() === $username && OC_User::checkPassword($username, $oldPassword)) {
$userstatus = 'user';
}
if(is_null($userstatus)) {
OC_JSON::error( array( "data" => array( "message" => "Authentication error" )));
if (is_null($userstatus)) {
OC_JSON::error(array('data' => array('message' => 'Authentication error')));
exit();
}
$recoveryAdminEnabled = OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' );
$validRecoveryPassword = false;
$recoveryPasswordSupported = false;
if ($recoveryAdminEnabled) {
if (\OCP\App::isEnabled('files_encryption') && $userstatus !== 'user') {
//handle the recovery case
$util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), $username);
$validRecoveryPassword = $util->checkRecoveryPassword($recoveryPassword);
$recoveryPasswordSupported = $util->recoveryEnabledForUser();
}
$recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled');
if ($recoveryPasswordSupported && $recoveryPassword == '') {
OC_JSON::error(array("data" => array( "message" => "Please provide a admin recovery password, otherwise all user data will be lost" )));
} elseif ( $recoveryPasswordSupported && ! $validRecoveryPassword) {
OC_JSON::error(array("data" => array( "message" => "Wrong admin recovery password. Please check the password and try again." )));
} else { // now we know that everything is file regarding the recovery password, let's try to change the password
$validRecoveryPassword = false;
$recoveryPasswordSupported = false;
if ($recoveryAdminEnabled) {
$validRecoveryPassword = $util->checkRecoveryPassword($recoveryPassword);
$recoveryEnabledForUser = $util->recoveryEnabledForUser();
}
if ($recoveryEnabledForUser && $recoveryPassword === '') {
OC_JSON::error(array('data' => array('message' => 'Please provide a admin recovery password, otherwise all user data will be lost')));
} elseif ($recoveryEnabledForUser && ! $validRecoveryPassword) {
OC_JSON::error(array('data' => array('message' => 'Wrong admin recovery password. Please check the password and try again.')));
} else { // now we know that everything is fine regarding the recovery password, let's try to change the password
$result = OC_User::setPassword($username, $password, $recoveryPassword);
if (!$result && $recoveryPasswordSupported) {
OC_JSON::error(array("data" => array( "message" => "Back-end doesn't support password change, but the users encryption key was successfully updated." )));
@ -53,4 +53,12 @@ if ($recoveryPasswordSupported && $recoveryPassword == '') {
} else {
OC_JSON::success(array("data" => array( "username" => $username )));
}
}
} else { // if user changes his own password or if encryption is disabled, proceed
if (!is_null($password) && OC_User::setPassword($username, $password)) {
OC_JSON::success(array('data' => array('username' => $username)));
} else {
OC_JSON::error(array('data' => array('message' => 'Unable to change password')));
}
}