123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- <?php
- /**
- * ownCloud
- *
- * @author Sam Tuke, Robin Appelman
- * @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman
- * icewind1991@gmail.com
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- /**
- * @brief Encryption proxy which handles filesystem operations before and after
- * execution and encrypts, and handles keyfiles accordingly. Used for
- * webui.
- */
- namespace OCA\Encryption;
- /**
- * Class Proxy
- * @package OCA\Encryption
- */
- class Proxy extends \OC_FileProxy {
- private static $blackList = null; //mimetypes blacklisted from encryption
- private static $enableEncryption = null;
- /**
- * Check if a file requires encryption
- * @param string $path
- * @return bool
- *
- * Tests if server side encryption is enabled, and file is allowed by blacklists
- */
- private static function shouldEncrypt($path) {
- if (is_null(self::$enableEncryption)) {
- if (
- \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') === 'true'
- && Crypt::mode() === 'server'
- ) {
- self::$enableEncryption = true;
- } else {
- self::$enableEncryption = false;
- }
- }
- if (!self::$enableEncryption) {
- return false;
- }
- if (is_null(self::$blackList)) {
- self::$blackList = explode(',', \OCP\Config::getAppValue('files_encryption', 'type_blacklist', ''));
- }
- if (Crypt::isCatfileContent($path)) {
- return true;
- }
- $extension = substr($path, strrpos($path, '.') + 1);
- if (array_search($extension, self::$blackList) === false) {
- return true;
- }
- return false;
- }
- /**
- * @param $path
- * @param $data
- * @return bool
- */
- public function preFile_put_contents($path, &$data) {
- if (self::shouldEncrypt($path)) {
- if (!is_resource($data)) {
- // get root view
- $view = new \OC_FilesystemView('/');
- // get relative path
- $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
- if (!isset($relativePath)) {
- return true;
- }
- $handle = fopen('crypt://' . $path . '.etmp', 'w');
- if (is_resource($handle)) {
- // write data to stream
- fwrite($handle, $data);
- // close stream
- fclose($handle);
- // disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- // get encrypted content
- $data = $view->file_get_contents($path . '.etmp');
- // remove our temp file
- $view->unlink($path . '.etmp');
- // re-enable proxy - our work is done
- \OC_FileProxy::$enabled = $proxyStatus;
- }
- }
- }
- return true;
- }
- /**
- * @param string $path Path of file from which has been read
- * @param string $data Data that has been read from file
- */
- public function postFile_get_contents($path, $data) {
- $plainData = null;
- $view = new \OC_FilesystemView('/');
- // init session
- $session = new \OCA\Encryption\Session($view);
- // If data is a catfile
- if (
- Crypt::mode() === 'server'
- && Crypt::isCatfileContent($data)
- ) {
- $handle = fopen('crypt://' . $path, 'r');
- if (is_resource($handle)) {
- while (($plainDataChunk = fgets($handle, 8192)) !== false) {
- $plainData .= $plainDataChunk;
- }
- }
- } elseif (
- Crypt::mode() == 'server'
- && \OC::$session->exists('legacyenckey')
- && Crypt::isEncryptedMeta($path)
- ) {
- // Disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- $plainData = Crypt::legacyBlockDecrypt($data, $session->getLegacyKey());
- \OC_FileProxy::$enabled = $proxyStatus;
- }
- if (!isset($plainData)) {
- $plainData = $data;
- }
- return $plainData;
- }
- /**
- * @brief When a file is deleted, remove its keyfile also
- */
- public function preUnlink($path) {
- // let the trashbin handle this
- if (\OCP\App::isEnabled('files_trashbin')) {
- return true;
- }
- // Disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- $view = new \OC_FilesystemView('/');
- $userId = \OCP\USER::getUser();
- $util = new Util($view, $userId);
- // get relative path
- $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
- list($owner, $ownerPath) = $util->getUidAndFilename($relativePath);
- // Delete keyfile & shareKey so it isn't orphaned
- if (!Keymanager::deleteFileKey($view, $owner, $ownerPath)) {
- \OCP\Util::writeLog('Encryption library',
- 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OCP\Util::ERROR);
- }
- Keymanager::delAllShareKeys($view, $owner, $ownerPath);
- \OC_FileProxy::$enabled = $proxyStatus;
- // If we don't return true then file delete will fail; better
- // to leave orphaned keyfiles than to disallow file deletion
- return true;
- }
- /**
- * @param $path
- * @return bool
- */
- public function postTouch($path) {
- $this->handleFile($path);
- return true;
- }
- /**
- * @param $path
- * @param $result
- * @return resource
- */
- public function postFopen($path, &$result) {
- $path = \OC\Files\Filesystem::normalizePath($path);
- if (!$result) {
- return $result;
- }
- // split the path parts
- $pathParts = explode('/', $path);
- // get relative path
- $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
- // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted
- if (isset($pathParts[2]) && $pathParts[2] === 'cache') {
- return $result;
- }
- // Disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- $meta = stream_get_meta_data($result);
- $view = new \OC_FilesystemView('');
- $util = new Util($view, \OCP\USER::getUser());
- // If file is already encrypted, decrypt using crypto protocol
- if (
- Crypt::mode() === 'server'
- && $util->isEncryptedPath($path)
- ) {
- // Close the original encrypted file
- fclose($result);
- // Open the file using the crypto stream wrapper
- // protocol and let it do the decryption work instead
- $result = fopen('crypt://' . $path, $meta['mode']);
- } elseif (
- self::shouldEncrypt($path)
- and $meta ['mode'] !== 'r'
- and $meta['mode'] !== 'rb'
- ) {
- $result = fopen('crypt://' . $path, $meta['mode']);
- }
- // Re-enable the proxy
- \OC_FileProxy::$enabled = $proxyStatus;
- return $result;
- }
- /**
- * @param $path
- * @param $data
- * @return array
- */
- public function postGetFileInfo($path, $data) {
- // if path is a folder do nothing
- if (is_array($data) && array_key_exists('size', $data)) {
- // Disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- // get file size
- $data['size'] = self::postFileSize($path, $data['size']);
- // Re-enable the proxy
- \OC_FileProxy::$enabled = $proxyStatus;
- }
- return $data;
- }
- /**
- * @param $path
- * @param $size
- * @return bool
- */
- public function postFileSize($path, $size) {
- $view = new \OC_FilesystemView('/');
- // if path is a folder do nothing
- if ($view->is_dir($path)) {
- return $size;
- }
- // get relative path
- $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
- // if path is empty we cannot resolve anything
- if (empty($relativePath)) {
- return $size;
- }
- $fileInfo = false;
- // get file info from database/cache if not .part file
- if (!Keymanager::isPartialFilePath($path)) {
- $fileInfo = $view->getFileInfo($path);
- }
- // if file is encrypted return real file size
- if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
- $size = $fileInfo['unencrypted_size'];
- } else {
- // self healing if file was removed from file cache
- if (!is_array($fileInfo)) {
- $fileInfo = array();
- }
- $userId = \OCP\User::getUser();
- $util = new Util($view, $userId);
- $fixSize = $util->getFileSize($path);
- if ($fixSize > 0) {
- $size = $fixSize;
- $fileInfo['encrypted'] = true;
- $fileInfo['unencrypted_size'] = $size;
- // put file info if not .part file
- if (!Keymanager::isPartialFilePath($relativePath)) {
- $view->putFileInfo($path, $fileInfo);
- }
- }
- }
- return $size;
- }
- /**
- * @param $path
- */
- public function handleFile($path) {
- // Disable encryption proxy to prevent recursive calls
- $proxyStatus = \OC_FileProxy::$enabled;
- \OC_FileProxy::$enabled = false;
- $view = new \OC_FilesystemView('/');
- $session = new \OCA\Encryption\Session($view);
- $userId = \OCP\User::getUser();
- $util = new Util($view, $userId);
- // split the path parts
- $pathParts = explode('/', $path);
- // get relative path
- $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
- // only if file is on 'files' folder fix file size and sharing
- if (isset($pathParts[2]) && $pathParts[2] === 'files' && $util->fixFileSize($path)) {
- // get sharing app state
- $sharingEnabled = \OCP\Share::isEnabled();
- // get users
- $usersSharing = $util->getSharingUsersArray($sharingEnabled, $relativePath);
- // update sharing-keys
- $util->setSharedFileKeyfiles($session, $usersSharing, $relativePath);
- }
- \OC_FileProxy::$enabled = $proxyStatus;
- }
- }
|