You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
8.1 KiB

11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Björn Schießle <bjoern@schiessle.org>
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Clark Tomlinson <fallen013@gmail.com>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Lukas Reschke <lukas@statuscode.ch>
  10. * @author Roeland Jago Douma <roeland@famdouma.nl>
  11. *
  12. * @license AGPL-3.0
  13. *
  14. * This code is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License, version 3,
  16. * as published by the Free Software Foundation.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License, version 3,
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>
  25. *
  26. */
  27. namespace OCA\Encryption;
  28. use OC\Files\View;
  29. use OCA\Encryption\Crypto\Crypt;
  30. use OCP\Encryption\IFile;
  31. use OCP\IConfig;
  32. use OCP\IUser;
  33. use OCP\IUserSession;
  34. use OCP\PreConditionNotMetException;
  35. class Recovery {
  36. /**
  37. * @var null|IUser
  38. */
  39. protected $user;
  40. /**
  41. * @var Crypt
  42. */
  43. protected $crypt;
  44. /**
  45. * @var KeyManager
  46. */
  47. private $keyManager;
  48. /**
  49. * @var IConfig
  50. */
  51. private $config;
  52. /**
  53. * @var View
  54. */
  55. private $view;
  56. /**
  57. * @var IFile
  58. */
  59. private $file;
  60. /**
  61. * @param IUserSession $userSession
  62. * @param Crypt $crypt
  63. * @param KeyManager $keyManager
  64. * @param IConfig $config
  65. * @param IFile $file
  66. * @param View $view
  67. */
  68. public function __construct(IUserSession $userSession,
  69. Crypt $crypt,
  70. KeyManager $keyManager,
  71. IConfig $config,
  72. IFile $file,
  73. View $view) {
  74. $this->user = ($userSession->isLoggedIn()) ? $userSession->getUser() : null;
  75. $this->crypt = $crypt;
  76. $this->keyManager = $keyManager;
  77. $this->config = $config;
  78. $this->view = $view;
  79. $this->file = $file;
  80. }
  81. /**
  82. * @param string $password
  83. * @return bool
  84. */
  85. public function enableAdminRecovery($password) {
  86. $appConfig = $this->config;
  87. $keyManager = $this->keyManager;
  88. if (!$keyManager->recoveryKeyExists()) {
  89. $keyPair = $this->crypt->createKeyPair();
  90. if (!is_array($keyPair)) {
  91. return false;
  92. }
  93. $this->keyManager->setRecoveryKey($password, $keyPair);
  94. }
  95. if ($keyManager->checkRecoveryPassword($password)) {
  96. $appConfig->setAppValue('encryption', 'recoveryAdminEnabled', '1');
  97. return true;
  98. }
  99. return false;
  100. }
  101. /**
  102. * change recovery key id
  103. *
  104. * @param string $newPassword
  105. * @param string $oldPassword
  106. * @return bool
  107. */
  108. public function changeRecoveryKeyPassword($newPassword, $oldPassword) {
  109. $recoveryKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId());
  110. $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $oldPassword);
  111. if ($decryptedRecoveryKey === false) {
  112. return false;
  113. }
  114. $encryptedRecoveryKey = $this->crypt->encryptPrivateKey($decryptedRecoveryKey, $newPassword);
  115. $header = $this->crypt->generateHeader();
  116. if ($encryptedRecoveryKey) {
  117. $this->keyManager->setSystemPrivateKey($this->keyManager->getRecoveryKeyId(), $header . $encryptedRecoveryKey);
  118. return true;
  119. }
  120. return false;
  121. }
  122. /**
  123. * @param string $recoveryPassword
  124. * @return bool
  125. */
  126. public function disableAdminRecovery($recoveryPassword) {
  127. $keyManager = $this->keyManager;
  128. if ($keyManager->checkRecoveryPassword($recoveryPassword)) {
  129. // Set recoveryAdmin as disabled
  130. $this->config->setAppValue('encryption', 'recoveryAdminEnabled', '0');
  131. return true;
  132. }
  133. return false;
  134. }
  135. /**
  136. * check if recovery is enabled for user
  137. *
  138. * @param string $user if no user is given we check the current logged-in user
  139. *
  140. * @return bool
  141. */
  142. public function isRecoveryEnabledForUser($user = '') {
  143. $uid = $user === '' ? $this->user->getUID() : $user;
  144. $recoveryMode = $this->config->getUserValue($uid,
  145. 'encryption',
  146. 'recoveryEnabled',
  147. 0);
  148. return ($recoveryMode === '1');
  149. }
  150. /**
  151. * check if recovery is key is enabled by the administrator
  152. *
  153. * @return bool
  154. */
  155. public function isRecoveryKeyEnabled() {
  156. $enabled = $this->config->getAppValue('encryption', 'recoveryAdminEnabled', '0');
  157. return ($enabled === '1');
  158. }
  159. /**
  160. * @param string $value
  161. * @return bool
  162. */
  163. public function setRecoveryForUser($value) {
  164. try {
  165. $this->config->setUserValue($this->user->getUID(),
  166. 'encryption',
  167. 'recoveryEnabled',
  168. $value);
  169. if ($value === '1') {
  170. $this->addRecoveryKeys('/' . $this->user->getUID() . '/files/');
  171. } else {
  172. $this->removeRecoveryKeys('/' . $this->user->getUID() . '/files/');
  173. }
  174. return true;
  175. } catch (PreConditionNotMetException $e) {
  176. return false;
  177. }
  178. }
  179. /**
  180. * add recovery key to all encrypted files
  181. */
  182. private function addRecoveryKeys(string $path): void {
  183. $dirContent = $this->view->getDirectoryContent($path);
  184. foreach ($dirContent as $item) {
  185. $filePath = $item->getPath();
  186. if ($item['type'] === 'dir') {
  187. $this->addRecoveryKeys($filePath . '/');
  188. } else {
  189. $fileKey = $this->keyManager->getFileKey($filePath, $this->user->getUID(), null);
  190. if (!empty($fileKey)) {
  191. $accessList = $this->file->getAccessList($filePath);
  192. $publicKeys = [];
  193. foreach ($accessList['users'] as $uid) {
  194. $publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
  195. }
  196. $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->user->getUID());
  197. $shareKeys = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
  198. $this->keyManager->deleteLegacyFileKey($filePath);
  199. foreach ($shareKeys as $uid => $keyFile) {
  200. $this->keyManager->setShareKey($filePath, $uid, $keyFile);
  201. }
  202. }
  203. }
  204. }
  205. }
  206. /**
  207. * remove recovery key to all encrypted files
  208. */
  209. private function removeRecoveryKeys(string $path): void {
  210. $dirContent = $this->view->getDirectoryContent($path);
  211. foreach ($dirContent as $item) {
  212. $filePath = $item->getPath();
  213. if ($item['type'] === 'dir') {
  214. $this->removeRecoveryKeys($filePath . '/');
  215. } else {
  216. $this->keyManager->deleteShareKey($filePath, $this->keyManager->getRecoveryKeyId());
  217. }
  218. }
  219. }
  220. /**
  221. * recover users files with the recovery key
  222. */
  223. public function recoverUsersFiles(string $recoveryPassword, string $user): void {
  224. $encryptedKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId());
  225. $privateKey = $this->crypt->decryptPrivateKey($encryptedKey, $recoveryPassword);
  226. if ($privateKey !== false) {
  227. $this->recoverAllFiles('/' . $user . '/files/', $privateKey, $user);
  228. }
  229. }
  230. /**
  231. * recover users files
  232. */
  233. private function recoverAllFiles(string $path, string $privateKey, string $uid): void {
  234. $dirContent = $this->view->getDirectoryContent($path);
  235. foreach ($dirContent as $item) {
  236. // Get relative path from encryption/keyfiles
  237. $filePath = $item->getPath();
  238. if ($this->view->is_dir($filePath)) {
  239. $this->recoverAllFiles($filePath . '/', $privateKey, $uid);
  240. } else {
  241. $this->recoverFile($filePath, $privateKey, $uid);
  242. }
  243. }
  244. }
  245. /**
  246. * recover file
  247. */
  248. private function recoverFile(string $path, string $privateKey, string $uid): void {
  249. $encryptedFileKey = $this->keyManager->getEncryptedFileKey($path);
  250. $shareKey = $this->keyManager->getShareKey($path, $this->keyManager->getRecoveryKeyId());
  251. if ($encryptedFileKey && $shareKey && $privateKey) {
  252. $fileKey = $this->crypt->multiKeyDecryptLegacy($encryptedFileKey,
  253. $shareKey,
  254. $privateKey);
  255. } elseif ($shareKey && $privateKey) {
  256. $fileKey = $this->crypt->multiKeyDecrypt($shareKey, $privateKey);
  257. }
  258. if (!empty($fileKey)) {
  259. $accessList = $this->file->getAccessList($path);
  260. $publicKeys = [];
  261. foreach ($accessList['users'] as $user) {
  262. $publicKeys[$user] = $this->keyManager->getPublicKey($user);
  263. }
  264. $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $uid);
  265. $shareKeys = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
  266. $this->keyManager->deleteLegacyFileKey($path);
  267. foreach ($shareKeys as $uid => $keyFile) {
  268. $this->keyManager->setShareKey($path, $uid, $keyFile);
  269. }
  270. }
  271. }
  272. }