From c1f20fe37a28c6f596dd55a27962d77d0ade1892 Mon Sep 17 00:00:00 2001 From: Sam Tuke Date: Tue, 29 Jan 2013 19:54:40 +0000 Subject: [PATCH] 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 --- apps/files_encryption/appinfo/spec.txt | 12 +++++ apps/files_encryption/hooks/hooks.php | 15 ++++-- apps/files_encryption/lib/keymanager.php | 44 ++++++++-------- apps/files_encryption/lib/proxy.php | 67 ++++++++++++++++++++++++ apps/files_encryption/lib/util.php | 38 +++++++++++--- 5 files changed, 142 insertions(+), 34 deletions(-) diff --git a/apps/files_encryption/appinfo/spec.txt b/apps/files_encryption/appinfo/spec.txt index ab248be64d5..2d22dffe08d 100644 --- a/apps/files_encryption/appinfo/spec.txt +++ b/apps/files_encryption/appinfo/spec.txt @@ -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 ----- diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 73d7957541a..dafa14fc000 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -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; diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 301e5b33fd8..8656bb96758 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -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; + + } } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 025af3c7f14..6d2a574abd2 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -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 ) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index fd4d7a9fcc6..2a69bba43c9 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -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; + } } -- 2.39.5