PageRenderTime 39ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/apps/files_encryption/lib/proxy.php

https://github.com/sezuan/core
PHP | 431 lines | 196 code | 119 blank | 116 comment | 41 complexity | bf74dd81f3caf35c3306d348473896e2 MD5 | raw file
Possible License(s): AGPL-3.0, AGPL-1.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Sam Tuke, Robin Appelman
  6. * @copyright 2012 Sam Tuke samtuke@owncloud.com, Robin Appelman
  7. * icewind1991@gmail.com
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11. * License as published by the Free Software Foundation; either
  12. * version 3 of the License, or any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public
  20. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. /**
  24. * @brief Encryption proxy which handles filesystem operations before and after
  25. * execution and encrypts, and handles keyfiles accordingly. Used for
  26. * webui.
  27. */
  28. namespace OCA\Encryption;
  29. /**
  30. * Class Proxy
  31. * @package OCA\Encryption
  32. */
  33. class Proxy extends \OC_FileProxy {
  34. private static $blackList = null; //mimetypes blacklisted from encryption
  35. private static $enableEncryption = null;
  36. /**
  37. * Check if a file requires encryption
  38. * @param string $path
  39. * @return bool
  40. *
  41. * Tests if server side encryption is enabled, and file is allowed by blacklists
  42. */
  43. private static function shouldEncrypt($path) {
  44. if (is_null(self::$enableEncryption)) {
  45. if (
  46. \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') === 'true'
  47. && Crypt::mode() === 'server'
  48. ) {
  49. self::$enableEncryption = true;
  50. } else {
  51. self::$enableEncryption = false;
  52. }
  53. }
  54. if (!self::$enableEncryption) {
  55. return false;
  56. }
  57. if (is_null(self::$blackList)) {
  58. self::$blackList = explode(',', \OCP\Config::getAppValue('files_encryption', 'type_blacklist', ''));
  59. }
  60. if (Crypt::isCatfileContent($path)) {
  61. return true;
  62. }
  63. $extension = substr($path, strrpos($path, '.') + 1);
  64. if (array_search($extension, self::$blackList) === false) {
  65. return true;
  66. }
  67. return false;
  68. }
  69. /**
  70. * @param $path
  71. * @param $data
  72. * @return bool
  73. */
  74. public function preFile_put_contents($path, &$data) {
  75. if (self::shouldEncrypt($path)) {
  76. if (!is_resource($data)) {
  77. // get root view
  78. $view = new \OC_FilesystemView('/');
  79. // get relative path
  80. $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
  81. if (!isset($relativePath)) {
  82. return true;
  83. }
  84. $handle = fopen('crypt://' . $relativePath . '.etmp', 'w');
  85. if (is_resource($handle)) {
  86. // write data to stream
  87. fwrite($handle, $data);
  88. // close stream
  89. fclose($handle);
  90. // disable encryption proxy to prevent recursive calls
  91. $proxyStatus = \OC_FileProxy::$enabled;
  92. \OC_FileProxy::$enabled = false;
  93. // get encrypted content
  94. $data = $view->file_get_contents($path . '.etmp');
  95. // remove our temp file
  96. $view->unlink($path . '.etmp');
  97. // re-enable proxy - our work is done
  98. \OC_FileProxy::$enabled = $proxyStatus;
  99. }
  100. }
  101. }
  102. return true;
  103. }
  104. /**
  105. * @param string $path Path of file from which has been read
  106. * @param string $data Data that has been read from file
  107. */
  108. public function postFile_get_contents($path, $data) {
  109. $plainData = null;
  110. $view = new \OC_FilesystemView('/');
  111. // get relative path
  112. $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
  113. // init session
  114. $session = new \OCA\Encryption\Session($view);
  115. // If data is a catfile
  116. if (
  117. Crypt::mode() === 'server'
  118. && Crypt::isCatfileContent($data)
  119. ) {
  120. $handle = fopen('crypt://' . $relativePath, 'r');
  121. if (is_resource($handle)) {
  122. while (($plainDataChunk = fgets($handle, 8192)) !== false) {
  123. $plainData .= $plainDataChunk;
  124. }
  125. }
  126. } elseif (
  127. Crypt::mode() == 'server'
  128. && \OC::$session->exists('legacyenckey')
  129. && Crypt::isEncryptedMeta($path)
  130. ) {
  131. // Disable encryption proxy to prevent recursive calls
  132. $proxyStatus = \OC_FileProxy::$enabled;
  133. \OC_FileProxy::$enabled = false;
  134. $plainData = Crypt::legacyBlockDecrypt($data, $session->getLegacyKey());
  135. \OC_FileProxy::$enabled = $proxyStatus;
  136. }
  137. if (!isset($plainData)) {
  138. $plainData = $data;
  139. }
  140. return $plainData;
  141. }
  142. /**
  143. * @brief When a file is deleted, remove its keyfile also
  144. */
  145. public function preUnlink($path) {
  146. // let the trashbin handle this
  147. if (\OCP\App::isEnabled('files_trashbin')) {
  148. return true;
  149. }
  150. // Disable encryption proxy to prevent recursive calls
  151. $proxyStatus = \OC_FileProxy::$enabled;
  152. \OC_FileProxy::$enabled = false;
  153. $view = new \OC_FilesystemView('/');
  154. $userId = \OCP\USER::getUser();
  155. $util = new Util($view, $userId);
  156. // get relative path
  157. $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
  158. list($owner, $ownerPath) = $util->getUidAndFilename($relativePath);
  159. // Delete keyfile & shareKey so it isn't orphaned
  160. if (!Keymanager::deleteFileKey($view, $owner, $ownerPath)) {
  161. \OCP\Util::writeLog('Encryption library',
  162. 'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OCP\Util::ERROR);
  163. }
  164. Keymanager::delAllShareKeys($view, $owner, $ownerPath);
  165. \OC_FileProxy::$enabled = $proxyStatus;
  166. // If we don't return true then file delete will fail; better
  167. // to leave orphaned keyfiles than to disallow file deletion
  168. return true;
  169. }
  170. /**
  171. * @param $path
  172. * @return bool
  173. */
  174. public function postTouch($path) {
  175. $this->handleFile($path);
  176. return true;
  177. }
  178. /**
  179. * @param $path
  180. * @param $result
  181. * @return resource
  182. */
  183. public function postFopen($path, &$result) {
  184. $path = \OC\Files\Filesystem::normalizePath($path);
  185. if (!$result) {
  186. return $result;
  187. }
  188. // split the path parts
  189. $pathParts = explode('/', $path);
  190. // get relative path
  191. $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
  192. // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted
  193. if (isset($pathParts[2]) && $pathParts[2] === 'cache') {
  194. return $result;
  195. }
  196. // Disable encryption proxy to prevent recursive calls
  197. $proxyStatus = \OC_FileProxy::$enabled;
  198. \OC_FileProxy::$enabled = false;
  199. $meta = stream_get_meta_data($result);
  200. $view = new \OC_FilesystemView('');
  201. $util = new Util($view, \OCP\USER::getUser());
  202. // If file is already encrypted, decrypt using crypto protocol
  203. if (
  204. Crypt::mode() === 'server'
  205. && $util->isEncryptedPath($path)
  206. ) {
  207. // Close the original encrypted file
  208. fclose($result);
  209. // Open the file using the crypto stream wrapper
  210. // protocol and let it do the decryption work instead
  211. $result = fopen('crypt://' . $relativePath, $meta['mode']);
  212. } elseif (
  213. self::shouldEncrypt($path)
  214. and $meta ['mode'] !== 'r'
  215. and $meta['mode'] !== 'rb'
  216. ) {
  217. $result = fopen('crypt://' . $relativePath, $meta['mode']);
  218. }
  219. // Re-enable the proxy
  220. \OC_FileProxy::$enabled = $proxyStatus;
  221. return $result;
  222. }
  223. /**
  224. * @param $path
  225. * @param $data
  226. * @return array
  227. */
  228. public function postGetFileInfo($path, $data) {
  229. // if path is a folder do nothing
  230. if (is_array($data) && array_key_exists('size', $data)) {
  231. // Disable encryption proxy to prevent recursive calls
  232. $proxyStatus = \OC_FileProxy::$enabled;
  233. \OC_FileProxy::$enabled = false;
  234. // get file size
  235. $data['size'] = self::postFileSize($path, $data['size']);
  236. // Re-enable the proxy
  237. \OC_FileProxy::$enabled = $proxyStatus;
  238. }
  239. return $data;
  240. }
  241. /**
  242. * @param $path
  243. * @param $size
  244. * @return bool
  245. */
  246. public function postFileSize($path, $size) {
  247. $view = new \OC_FilesystemView('/');
  248. // if path is a folder do nothing
  249. if ($view->is_dir($path)) {
  250. return $size;
  251. }
  252. // get relative path
  253. $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
  254. // if path is empty we cannot resolve anything
  255. if (empty($relativePath)) {
  256. return $size;
  257. }
  258. $fileInfo = false;
  259. // get file info from database/cache if not .part file
  260. if (!Keymanager::isPartialFilePath($path)) {
  261. $fileInfo = $view->getFileInfo($path);
  262. }
  263. // if file is encrypted return real file size
  264. if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
  265. $size = $fileInfo['unencrypted_size'];
  266. } else {
  267. // self healing if file was removed from file cache
  268. if (!is_array($fileInfo)) {
  269. $fileInfo = array();
  270. }
  271. $userId = \OCP\User::getUser();
  272. $util = new Util($view, $userId);
  273. $fixSize = $util->getFileSize($path);
  274. if ($fixSize > 0) {
  275. $size = $fixSize;
  276. $fileInfo['encrypted'] = true;
  277. $fileInfo['unencrypted_size'] = $size;
  278. // put file info if not .part file
  279. if (!Keymanager::isPartialFilePath($relativePath)) {
  280. $view->putFileInfo($path, $fileInfo);
  281. }
  282. }
  283. }
  284. return $size;
  285. }
  286. /**
  287. * @param $path
  288. */
  289. public function handleFile($path) {
  290. // Disable encryption proxy to prevent recursive calls
  291. $proxyStatus = \OC_FileProxy::$enabled;
  292. \OC_FileProxy::$enabled = false;
  293. $view = new \OC_FilesystemView('/');
  294. $session = new \OCA\Encryption\Session($view);
  295. $userId = \OCP\User::getUser();
  296. $util = new Util($view, $userId);
  297. // split the path parts
  298. $pathParts = explode('/', $path);
  299. // get relative path
  300. $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
  301. // only if file is on 'files' folder fix file size and sharing
  302. if (isset($pathParts[2]) && $pathParts[2] === 'files' && $util->fixFileSize($path)) {
  303. // get sharing app state
  304. $sharingEnabled = \OCP\Share::isEnabled();
  305. // get users
  306. $usersSharing = $util->getSharingUsersArray($sharingEnabled, $relativePath);
  307. // update sharing-keys
  308. $util->setSharedFileKeyfiles($session, $usersSharing, $relativePath);
  309. }
  310. \OC_FileProxy::$enabled = $proxyStatus;
  311. }
  312. }