@@ -142,32 +142,67 @@ class Hooks { | |||
* @brief Change a user's encryption passphrase | |||
* @param array $params keys: uid, password | |||
*/ | |||
public static function setPassphrase( $params ) { | |||
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 | |||
// the necessary keys) | |||
if ( Crypt::mode() == 'server' ) { | |||
if (Crypt::mode() == 'server') { | |||
$view = new \OC_FilesystemView( '/' ); | |||
if ($params['uid'] == \OCP\User::getUser()) { | |||
$session = new Session($view); | |||
// Get existing decrypted private key | |||
$privateKey = $session->getPrivateKey(); | |||
// Encrypt private key with new user pwd as passphrase | |||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $privateKey, $params['password'] ); | |||
// Save private key | |||
Keymanager::setPrivateKey( $encryptedPrivateKey ); | |||
// NOTE: Session does not need to be updated as the | |||
// private key has not changed, only the passphrase | |||
// used to decrypt it has changed | |||
$view = new \OC_FilesystemView('/'); | |||
$session = new Session($view); | |||
// Get existing decrypted private key | |||
$privateKey = $session->getPrivateKey(); | |||
// Encrypt private key with new user pwd as passphrase | |||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']); | |||
// Save private key | |||
Keymanager::setPrivateKey($encryptedPrivateKey); | |||
// 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']; | |||
$recoveryPassword = $params['recoveryPassword']; | |||
$newUserPassword = $params['password']; | |||
$view = new \OC_FilesystemView('/'); | |||
// 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 | |||
$view->file_put_contents( '/public-keys/'.$user.'.public.key', $keypair['publicKey'] ); | |||
// Encrypt private key empthy passphrase | |||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent( $keypair['privateKey'], $newUserPassword ); | |||
// Save private key | |||
$view->file_put_contents( '/'.$user.'/files_encryption/'.$user.'.private.key', $encryptedPrivateKey ); | |||
if ( $recoveryPassword ) { // if recovery key is set we can re-encrypt the key files | |||
$util = new Util($view, $user); | |||
$util->recoverUsersFiles($recoveryPassword); | |||
} | |||
\OC_FileProxy::$enabled = $proxyStatus; | |||
} | |||
} | |||
} | |||
/* |
@@ -69,7 +69,7 @@ $(document).ready(function(){ | |||
} | |||
); | |||
// change password | |||
// change recovery password | |||
$('input:password[name="changeRecoveryPassword"]').keyup(function(event) { | |||
var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val(); |
@@ -46,7 +46,7 @@ class Helper { | |||
public static function registerUserHooks() { | |||
\OCP\Util::connectHook( 'OC_User', 'post_login', 'OCA\Encryption\Hooks', 'login' ); | |||
\OCP\Util::connectHook( 'OC_User', 'pre_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' ); | |||
\OCP\Util::connectHook( 'OC_User', 'post_setPassword', 'OCA\Encryption\Hooks', 'setPassphrase' ); | |||
\OCP\Util::connectHook( 'OC_User', 'post_createUser', 'OCA\Encryption\Hooks', 'postCreateUser' ); | |||
\OCP\Util::connectHook( 'OC_User', 'post_deleteUser', 'OCA\Encryption\Hooks', 'postDeleteUser' ); | |||
} |
@@ -929,7 +929,7 @@ class Util { | |||
// Get the current users's private key for decrypting existing keyfile | |||
$privateKey = $session->getPrivateKey(); | |||
$fileOwner = \OC\Files\Filesystem::getOwner( $filePath ); | |||
// Decrypt keyfile | |||
@@ -1336,7 +1336,7 @@ class Util { | |||
} | |||
} | |||
/** | |||
/** | |||
* @brief remove recovery key to all encrypted files | |||
*/ | |||
public function removeRecoveryKeys($path = '/') { | |||
@@ -1351,4 +1351,84 @@ class Util { | |||
} | |||
} | |||
} | |||
/** | |||
* @brief decrypt given file with recovery key and encrypt it again to the owner and his new key | |||
* @param type $file | |||
* @param type $privateKey recovery key to decrypt the file | |||
*/ | |||
private function recoverFile($file, $privateKey) { | |||
$sharingEnabled = \OCP\Share::isEnabled(); | |||
// Find out who, if anyone, is sharing the file | |||
if ($sharingEnabled) { | |||
$result = \OCP\Share::getUsersSharingFile($file, $this->userId, true, true, true); | |||
$userIds = $result['users']; | |||
$userIds[] = $this->recoveryKeyId; | |||
if ($result['public']) { | |||
$userIds[] = $this->publicShareKeyId; | |||
} | |||
} else { | |||
$userIds = array($this->userId, $this->recoveryKeyId); | |||
} | |||
$filteredUids = $this->filterShareReadyUsers($userIds); | |||
$proxyStatus = \OC_FileProxy::$enabled; | |||
\OC_FileProxy::$enabled = false; | |||
//decrypt file key | |||
$encKeyfile = $this->view->file_get_contents($this->keyfilesPath.$file.".key"); | |||
$shareKey = $this->view->file_get_contents($this->shareKeysPath.$file.".".$this->recoveryKeyId.".shareKey"); | |||
$plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); | |||
// encrypt file key again to all users, this time with the new public key for the recovered use | |||
$userPubKeys = Keymanager::getPublicKeys($this->view, $filteredUids['ready']); | |||
$multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys); | |||
// write new keys to filesystem TDOO! | |||
$this->view->file_put_contents($this->keyfilesPath.$file.'.key', $multiEncKey['data']); | |||
foreach ($multiEncKey['keys'] as $userId => $shareKey) { | |||
$shareKeyPath = $this->shareKeysPath.$file.'.'.$userId.'.shareKey'; | |||
$this->view->file_put_contents($shareKeyPath, $shareKey); | |||
} | |||
// Return proxy to original status | |||
\OC_FileProxy::$enabled = $proxyStatus; | |||
} | |||
/** | |||
* @brief collect all files and recover them one by one | |||
* @param type $path to look for files keys | |||
* @param type $privateKey private recovery key which is used to decrypt the files | |||
*/ | |||
private function recoverAllFiles($path, $privateKey) { | |||
$dirContent = $this->view->getDirectoryContent($this->keyfilesPath . $path); | |||
foreach ($dirContent as $item) { | |||
$filePath = substr($item['path'], 25); | |||
if ($item['type'] == 'dir') { | |||
$this->addRecoveryKey($filePath . '/', $privateKey); | |||
} else { | |||
$file = substr($filePath, 0, -4); | |||
$this->recoverFile($file, $privateKey); | |||
} | |||
} | |||
} | |||
/** | |||
* @brief recover users files in case of password lost | |||
* @param type $recoveryPassword | |||
*/ | |||
public function recoverUsersFiles($recoveryPassword) { | |||
// Disable encryption proxy to prevent recursive calls | |||
$proxyStatus = \OC_FileProxy::$enabled; | |||
\OC_FileProxy::$enabled = false; | |||
$encryptedKey = $this->view->file_get_contents( '/owncloud_private_key/'.$this->recoveryKeyId.'.private.key' ); | |||
$privateKey = Crypt::symmetricDecryptFileContent( $encryptedKey, $recoveryPassword ); | |||
\OC_FileProxy::$enabled = $proxyStatus; | |||
$this->recoverAllFiles('/', $privateKey); | |||
} | |||
} |
@@ -393,13 +393,14 @@ class OC_User { | |||
* @brief Set password | |||
* @param $uid The username | |||
* @param $password The new password | |||
* @param $recoveryPassword for the encryption app to reset encryption keys | |||
* @returns true/false | |||
* | |||
* Change the password of a user | |||
*/ | |||
public static function setPassword( $uid, $password ) { | |||
public static function setPassword( $uid, $password, $recoveryPassword = null ) { | |||
$run = true; | |||
OC_Hook::emit( "OC_User", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $password )); | |||
OC_Hook::emit( "OC_User", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $password, "recoveryPassword" => $recoveryPassword )); | |||
if( $run ) { | |||
$success = false; | |||
@@ -412,7 +413,7 @@ class OC_User { | |||
} | |||
// invalidate all login cookies | |||
OC_Preferences::deleteApp($uid, 'login_token'); | |||
OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password )); | |||
OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password, "recoveryPassword" => $recoveryPassword )); | |||
return $success; | |||
} | |||
else{ |
@@ -8,8 +8,9 @@ OC_JSON::checkLoggedIn(); | |||
OC_APP::loadApps(); | |||
$username = isset($_POST["username"]) ? $_POST["username"] : OC_User::getUser(); | |||
$password = isset($_POST["newpassword"]) ? $_POST["newpassword"] : null; | |||
$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())) { | |||
@@ -28,7 +29,7 @@ if(is_null($userstatus)) { | |||
} | |||
// Return Success story | |||
if(!is_null($password) && OC_User::setPassword( $username, $password )) { | |||
if(!is_null($password) && OC_User::setPassword( $username, $password, $recoveryPassword )) { | |||
OC_JSON::success(array("data" => array( "username" => $username ))); | |||
} | |||
else{ |
@@ -45,6 +45,8 @@ table:not(.nostyle) { width:100%; } | |||
#rightcontent { padding-left: 1em; } | |||
div.quota { float:right; display:block; position:absolute; right:25em; top:-1px; } | |||
div.quota-select-wrapper { position: relative; } | |||
div.recoveryPassword { left:50em; display:block; position:absolute; top:-1px; } | |||
input#recoveryPassword {width:15em;} | |||
select.quota { position:absolute; left:0; top:0; width:10em; } | |||
select.quota-user { position:relative; left:0; top:0; width:10em; } | |||
div.quota>span { position:absolute; right:0; white-space:nowrap; top:.7em; color:#888; text-shadow:0 1px 0 #fff; } |
@@ -351,9 +351,11 @@ $(document).ready(function () { | |||
input.keypress(function (event) { | |||
if (event.keyCode == 13) { | |||
if ($(this).val().length > 0) { | |||
var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val(); | |||
console.log("RECOVERY PASSWD: " + recoveryPasswordVal); | |||
$.post( | |||
OC.filePath('settings', 'ajax', 'changepassword.php'), | |||
{username: uid, password: $(this).val()}, | |||
{username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal}, | |||
function (result) { | |||
} | |||
); |
@@ -38,7 +38,7 @@ if($_['passwordChangeSupported']) { | |||
<div id="passwordchanged"><?php echo $l->t('Your password was changed');?></div> | |||
<div id="passworderror"><?php echo $l->t('Unable to change your password');?></div> | |||
<input type="password" id="pass1" name="oldpassword" placeholder="<?php echo $l->t('Current password');?>" /> | |||
<input type="password" id="pass2" name="newpassword" | |||
<input type="password" id="pass2" name="password" | |||
placeholder="<?php echo $l->t('New password');?>" data-typetoggle="#personal-show" /> | |||
<input type="checkbox" id="personal-show" name="show" /><label for="personal-show"></label> | |||
<input id="passwordbutton" type="submit" value="<?php echo $l->t('Change password');?>" /> |
@@ -29,6 +29,11 @@ $_['subadmingroups'] = array_flip($items); | |||
<?php endforeach;?> | |||
</select> <input type="submit" value="<?php p($l->t('Create'))?>" /> | |||
</form> | |||
<?php if((bool)$_['recoveryAdminEnabled']): ?> | |||
<div class="recoveryPassword"> | |||
<input id="recoveryPassword" type="password" placeholder="<?php p($l->t('Admin Recovery Password'))?>" /> | |||
</div> | |||
<?php endif; ?> | |||
<div class="quota"> | |||
<span><?php p($l->t('Default Storage'));?></span> | |||
<?php if((bool) $_['isadmin']): ?> |
@@ -20,6 +20,8 @@ $users = array(); | |||
$groups = array(); | |||
$isadmin = OC_User::isAdminUser(OC_User::getUser()); | |||
$recoveryAdminEnabled = OC_App::isEnabled('files_encryption') && | |||
OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' ); | |||
if($isadmin) { | |||
$accessiblegroups = OC_Group::getGroups(); | |||
@@ -77,4 +79,5 @@ $tmpl->assign( 'numofgroups', count($accessiblegroups)); | |||
$tmpl->assign( 'quota_preset', $quotaPreset); | |||
$tmpl->assign( 'default_quota', $defaultQuota); | |||
$tmpl->assign( 'defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined); | |||
$tmpl->assign( 'recoveryAdminEnabled', $recoveryAdminEnabled); | |||
$tmpl->printPage(); |