Made encyrption keyfiles be deleted when their parents are

Made encryption keyfiles be renamed when their parents are
Fixed bugs with encryptAll() execution on login
remotes/origin/stable5
Sam Tuke 12 years ago
parent 094213e231
commit c1f20fe37a
  1. 12
      apps/files_encryption/appinfo/spec.txt
  2. 15
      apps/files_encryption/hooks/hooks.php
  3. 44
      apps/files_encryption/lib/keymanager.php
  4. 67
      apps/files_encryption/lib/proxy.php
  5. 38
      apps/files_encryption/lib/util.php

@ -1,3 +1,15 @@
Encrypted files
---------------
- Each encrypted file has at least two components: the encrypted data file
('catfile'), and it's corresponding key file ('keyfile'). Shared files have an
additional key file ('share key'). The catfile contains the encrypted data
concatenated with delimiter text, followed by the initialisation vector ('IV'),
and padding. e.g.:
[encrypted data string][delimiter][IV][padding]
[anhAAjAmcGXqj1X9g==][00iv00][MSHU5N5gECP7aAg7][xx] (square braces added)
Notes
-----

@ -37,8 +37,6 @@ class Hooks {
* @note This method should never be called for users using client side encryption
*/
public static function login( $params ) {
// TODO: use lots of dependency injection here
$view = new \OC_FilesystemView( '/' );
@ -83,8 +81,17 @@ class Hooks {
// Encrypt existing user files:
// This serves to upgrade old versions of the encryption
// app (see appinfo/spec.txt
$this->encryptAll( $publicKey, $this->userFilesDir, $session->getLegacyKey(), $params['password'] );
// app (see appinfo/spec.txt)
if (
$util->encryptAll( $publicKey, '/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'] )
) {
\OC_Log::write(
'Encryption library', 'Encryption of file belonging to "' . $params['uid'] . '" was started at login'
, \OC_Log::INFO
);
}
return true;

@ -234,33 +234,33 @@ class Keymanager {
}
/**
* @brief retrieve file encryption key
* @brief Delete a keyfile
*
* @param string file name
* @return string file key or false
* @param OC_FilesystemView $view
* @param string $userId username
* @param string $path path of the file the key belongs to
* @return bool Outcome of unlink operation
* @note $path must be relative to data/user/files. e.g. mydoc.txt NOT
* /data/admin/files/mydoc.txt
*/
public static function deleteFileKey( $path, $staticUserClass = 'OCP\User' ) {
public static function deleteFileKey( \OC_FilesystemView $view, $userId, $path ) {
$keypath = ltrim( $path, '/' );
$user = $staticUserClass::getUser();
// update $keypath and $user if path point to a file shared by someone else
// $query = \OC_DB::prepare( "SELECT uid_owner, source, target FROM `*PREFIX*sharing` WHERE target = ? AND uid_shared_with = ?" );
//
// $result = $query->execute( array ('/'.$user.'/files/'.$keypath, $user));
//
// if ($row = $result->fetchRow()) {
//
// $keypath = $row['source'];
// $keypath_parts = explode( '/', $keypath );
// $user = $keypath_parts[1];
// $keypath = str_replace( '/' . $user . '/files/', '', $keypath );
//
// }
$trimmed = ltrim( $path, '/' );
$keyPath = '/' . $userId . '/files_encryption/keyfiles/' . $trimmed . '.key';
$view = new \OC_FilesystemView( '/' . $user. '/files_encryption/keyfiles/' );
// Unlink doesn't tell us if file was deleted (not found returns
// true), so we perform our own test
if ( $view->file_exists( $keyPath ) ) {
return $view->unlink( $keypath . '.key' );
return $view->unlink( $keyPath );
} else {
\OC_Log::write( 'Encryption library', 'Could not delete keyfile; does not exist: "' . $keyPath, \OC_Log::ERROR );
return false;
}
}

@ -192,6 +192,73 @@ class Proxy extends \OC_FileProxy {
}
/**
* @brief When a file is deleted, remove its keyfile also
*/
public function postUnlink( $path ) {
// Disable encryption proxy to prevent recursive calls
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\USER::getUser();
// Format path to be relative to user files dir
$trimmed = ltrim( $path, '/' );
$split = explode( '/', $trimmed );
$sliced = array_slice( $split, 2 );
$relPath = implode( '/', $sliced );
// Delete keyfile so it isn't orphaned
$result = Keymanager::deleteFileKey( $view, $userId, $relPath );
\OC_FileProxy::$enabled = true;
return $result;
}
/**
* @brief When a file is renamed, rename its keyfile also
* @return bool Result of rename()
* @note This is pre rather than post because using post didn't work
*/
public function preRename( $oldPath, $newPath ) {
// trigger_error( "PATHS = ".var_export($oldPath, 1).' '.var_export($newPath, 1));
// Disable encryption proxy to prevent recursive calls
\OC_FileProxy::$enabled = false;
$view = new \OC_FilesystemView( '/' );
$userId = \OCP\USER::getUser();
// Format paths to be relative to user files dir
$oldTrimmed = ltrim( $oldPath, '/' );
$oldSplit = explode( '/', $oldTrimmed );
$oldSliced = array_slice( $oldSplit, 2 );
$oldRelPath = implode( '/', $oldSliced );
$oldKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $oldRelPath . '.key';
$newTrimmed = ltrim( $newPath, '/' );
$newSplit = explode( '/', $newTrimmed );
$newSliced = array_slice( $newSplit, 2 );
$newRelPath = implode( '/', $newSliced );
$newKeyfilePath = $userId . '/' . 'files_encryption' . '/' . 'keyfiles' . '/' . $newRelPath . '.key';
// trigger_error("RENAMING = ".var_export($oldKeyfilePath, 1).' -> '.var_export($newKeyfilePath, 1));
// Rename keyfile so it isn't orphaned
$result = $view->rename( $oldKeyfilePath, $newKeyfilePath );
\OC_FileProxy::$enabled = true;
return $result;
}
public function postFopen( $path, &$result ){
if ( !$result ) {

@ -225,6 +225,9 @@ class Util {
* @brief Find all files and their encryption status within a directory
* @param string $directory The path of the parent directory to search
* @return mixed false if 0 found, array on success. Keys: name, path
* @note $directory needs to be a path relative to OC data dir. e.g.
* /admin/files NOT /backup OR /home/www/oc/data/admin/files
*/
public function findFiles( $directory ) {
@ -293,7 +296,7 @@ class Util {
return false;
} else {
return $found;
}
@ -334,20 +337,29 @@ class Util {
if ( $found = $this->findFiles( $dirPath ) ) {
// Disable proxy to prevent file being encrypted twice
\OC_FileProxy::$enabled = false;
// Encrypt unencrypted files
foreach ( $found['plain'] as $plainFilePath ) {
foreach ( $found['plain'] as $plainFile ) {
// Fetch data from file
$plainData = $this->view->file_get_contents( $plainFilePath );
$plainData = $this->view->file_get_contents( $plainFile['path'] );
// Encrypt data, generate catfile
$encrypted = Crypt::keyEncryptKeyfile( $plainData, $publicKey );
// Format path to be relative to user files dir
$trimmed = ltrim( $plainFile['path'], '/' );
$split = explode( '/', $trimmed );
$sliced = array_slice( $split, 2 );
$relPath = implode( '/', $sliced );
// Save catfile
Keymanager::setFileKey( $this->view, $plainFilePath, $this->userId, $encrypted['key'] );
Keymanager::setFileKey( $this->view, $relPath, $this->userId, $encrypted['key'] );
// Overwrite the existing file with the encrypted one
$this->view->file_put_contents( $plainFilePath, $encrypted['data'] );
$this->view->file_put_contents( $plainFile['path'], $encrypted['data'] );
}
@ -367,15 +379,25 @@ class Util {
$recrypted = Crypt::legacyKeyRecryptKeyfile( $legacyData, $legacyPassphrase, $publicKey, $newPassphrase );
// Save catfile
Keymanager::setFileKey( $this->view, $plainFilePath, $this->userId, $recrypted['key'] );
Keymanager::setFileKey( $this->view, $plainFile['path'], $this->userId, $recrypted['key'] );
// Overwrite the existing file with the encrypted one
$this->view->file_put_contents( $plainFilePath, $recrypted['data'] );
$this->view->file_put_contents( $plainFile['path'], $recrypted['data'] );
}
}
\OC_FileProxy::$enabled = true;
// If files were found, return true
return true;
} else {
// If no files were found, return false
return false;
}
}

Loading…
Cancel
Save