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.

326 lines
8.1 KiB

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