Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Util often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Util, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 37 | class Util { |
||
| 38 | |||
| 39 | const MIGRATION_COMPLETED = 1; // migration to new encryption completed |
||
| 40 | const MIGRATION_IN_PROGRESS = -1; // migration is running |
||
| 41 | const MIGRATION_OPEN = 0; // user still needs to be migrated |
||
| 42 | |||
| 43 | const FILE_TYPE_FILE = 0; |
||
| 44 | const FILE_TYPE_VERSION = 1; |
||
| 45 | const FILE_TYPE_CACHE = 2; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @var \OC\Files\View |
||
| 49 | */ |
||
| 50 | private $view; // OC\Files\View object for filesystem operations |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @var string |
||
| 54 | */ |
||
| 55 | private $userId; // ID of the user we use to encrypt/decrypt files |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @var string |
||
| 59 | */ |
||
| 60 | private $keyId; // ID of the key we want to manipulate |
||
| 61 | |||
| 62 | /** |
||
| 63 | * @var bool |
||
| 64 | */ |
||
| 65 | private $client; // Client side encryption mode flag |
||
| 66 | |||
| 67 | /** |
||
| 68 | * @var string |
||
| 69 | */ |
||
| 70 | private $publicKeyDir; // Dir containing all public user keys |
||
| 71 | |||
| 72 | /** |
||
| 73 | * @var string |
||
| 74 | */ |
||
| 75 | private $encryptionDir; // Dir containing user's files_encryption |
||
| 76 | |||
| 77 | /** |
||
| 78 | * @var string |
||
| 79 | */ |
||
| 80 | private $keysPath; // Dir containing all file related encryption keys |
||
| 81 | |||
| 82 | /** |
||
| 83 | * @var string |
||
| 84 | */ |
||
| 85 | private $publicKeyPath; // Path to user's public key |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @var string |
||
| 89 | */ |
||
| 90 | private $privateKeyPath; // Path to user's private key |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @var string |
||
| 94 | */ |
||
| 95 | private $userFilesDir; |
||
| 96 | |||
| 97 | /** |
||
| 98 | * @var string |
||
| 99 | */ |
||
| 100 | private $publicShareKeyId; |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @var string |
||
| 104 | */ |
||
| 105 | private $recoveryKeyId; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * @var bool |
||
| 109 | */ |
||
| 110 | private $isPublic; |
||
| 111 | |||
| 112 | /** |
||
| 113 | * @param \OC\Files\View $view |
||
| 114 | * @param string $userId |
||
| 115 | * @param bool $client |
||
| 116 | */ |
||
| 117 | public function __construct($view, $userId, $client = false) { |
||
| 118 | |||
| 119 | $this->view = $view; |
||
| 120 | $this->client = $client; |
||
| 121 | $this->userId = $userId; |
||
| 122 | |||
| 123 | $appConfig = \OC::$server->getAppConfig(); |
||
| 124 | |||
| 125 | $this->publicShareKeyId = $appConfig->getValue('files_encryption', 'publicShareKeyId'); |
||
| 126 | $this->recoveryKeyId = $appConfig->getValue('files_encryption', 'recoveryKeyId'); |
||
| 127 | |||
| 128 | $this->userDir = '/' . $this->userId; |
||
|
|
|||
| 129 | $this->fileFolderName = 'files'; |
||
| 130 | $this->userFilesDir = |
||
| 131 | '/' . $userId . '/' . $this->fileFolderName; // TODO: Does this need to be user configurable? |
||
| 132 | $this->publicKeyDir = Keymanager::getPublicKeyPath(); |
||
| 133 | $this->encryptionDir = '/' . $this->userId . '/' . 'files_encryption'; |
||
| 134 | $this->keysPath = $this->encryptionDir . '/' . 'keys'; |
||
| 135 | $this->publicKeyPath = |
||
| 136 | $this->publicKeyDir . '/' . $this->userId . '.publicKey'; // e.g. data/public-keys/admin.publicKey |
||
| 137 | $this->privateKeyPath = |
||
| 138 | $this->encryptionDir . '/' . $this->userId . '.privateKey'; // e.g. data/admin/admin.privateKey |
||
| 139 | // make sure that the owners home is mounted |
||
| 140 | \OC\Files\Filesystem::initMountPoints($userId); |
||
| 141 | |||
| 142 | if (Helper::isPublicAccess()) { |
||
| 143 | $this->keyId = $this->publicShareKeyId; |
||
| 144 | $this->isPublic = true; |
||
| 145 | } else { |
||
| 146 | $this->keyId = $this->userId; |
||
| 147 | $this->isPublic = false; |
||
| 148 | } |
||
| 149 | } |
||
| 150 | |||
| 151 | /** |
||
| 152 | * @return bool |
||
| 153 | */ |
||
| 154 | public function ready() { |
||
| 155 | |||
| 156 | if ( |
||
| 157 | !$this->view->file_exists($this->encryptionDir) |
||
| 158 | or !$this->view->file_exists($this->keysPath) |
||
| 159 | or !$this->view->file_exists($this->publicKeyPath) |
||
| 160 | or !$this->view->file_exists($this->privateKeyPath) |
||
| 161 | ) { |
||
| 162 | return false; |
||
| 163 | } else { |
||
| 164 | return true; |
||
| 165 | } |
||
| 166 | } |
||
| 167 | |||
| 168 | /** |
||
| 169 | * check if the users private & public key exists |
||
| 170 | * @return boolean |
||
| 171 | */ |
||
| 172 | public function userKeysExists() { |
||
| 173 | if ( |
||
| 174 | $this->view->file_exists($this->privateKeyPath) && |
||
| 175 | $this->view->file_exists($this->publicKeyPath)) { |
||
| 176 | return true; |
||
| 177 | } else { |
||
| 178 | return false; |
||
| 179 | } |
||
| 180 | } |
||
| 181 | |||
| 182 | /** |
||
| 183 | * create a new public/private key pair for the user |
||
| 184 | * |
||
| 185 | * @param string $password password for the private key |
||
| 186 | */ |
||
| 187 | public function replaceUserKeys($password) { |
||
| 188 | $this->backupAllKeys('password_reset'); |
||
| 189 | $this->view->unlink($this->publicKeyPath); |
||
| 190 | $this->view->unlink($this->privateKeyPath); |
||
| 191 | $this->setupServerSide($password); |
||
| 192 | } |
||
| 193 | |||
| 194 | /** |
||
| 195 | * Sets up user folders and keys for serverside encryption |
||
| 196 | * |
||
| 197 | * @param string $passphrase to encrypt server-stored private key with |
||
| 198 | * @return bool |
||
| 199 | */ |
||
| 200 | public function setupServerSide($passphrase = null) { |
||
| 201 | |||
| 202 | // Set directories to check / create |
||
| 203 | $setUpDirs = array( |
||
| 204 | $this->userDir, |
||
| 205 | $this->publicKeyDir, |
||
| 206 | $this->encryptionDir, |
||
| 207 | $this->keysPath |
||
| 208 | ); |
||
| 209 | |||
| 210 | // Check / create all necessary dirs |
||
| 211 | foreach ($setUpDirs as $dirPath) { |
||
| 212 | |||
| 213 | if (!$this->view->file_exists($dirPath)) { |
||
| 214 | |||
| 215 | $this->view->mkdir($dirPath); |
||
| 216 | |||
| 217 | } |
||
| 218 | |||
| 219 | } |
||
| 220 | |||
| 221 | // Create user keypair |
||
| 222 | // we should never override a keyfile |
||
| 223 | if ( |
||
| 224 | !$this->view->file_exists($this->publicKeyPath) |
||
| 225 | && !$this->view->file_exists($this->privateKeyPath) |
||
| 226 | ) { |
||
| 227 | |||
| 228 | // Generate keypair |
||
| 229 | $keypair = Crypt::createKeypair(); |
||
| 230 | |||
| 231 | if ($keypair) { |
||
| 232 | |||
| 233 | \OC_FileProxy::$enabled = false; |
||
| 234 | |||
| 235 | // Encrypt private key with user pwd as passphrase |
||
| 236 | $encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $passphrase, Helper::getCipher()); |
||
| 237 | |||
| 238 | // Save key-pair |
||
| 239 | if ($encryptedPrivateKey) { |
||
| 240 | $header = crypt::generateHeader(); |
||
| 241 | $this->view->file_put_contents($this->privateKeyPath, $header . $encryptedPrivateKey); |
||
| 242 | $this->view->file_put_contents($this->publicKeyPath, $keypair['publicKey']); |
||
| 243 | } |
||
| 244 | |||
| 245 | \OC_FileProxy::$enabled = true; |
||
| 246 | } |
||
| 247 | |||
| 248 | } else { |
||
| 249 | // check if public-key exists but private-key is missing |
||
| 250 | if ($this->view->file_exists($this->publicKeyPath) && !$this->view->file_exists($this->privateKeyPath)) { |
||
| 251 | \OCP\Util::writeLog('Encryption library', |
||
| 252 | 'public key exists but private key is missing for "' . $this->keyId . '"', \OCP\Util::FATAL); |
||
| 253 | return false; |
||
| 254 | } else { |
||
| 255 | if (!$this->view->file_exists($this->publicKeyPath) && $this->view->file_exists($this->privateKeyPath) |
||
| 256 | ) { |
||
| 257 | \OCP\Util::writeLog('Encryption library', |
||
| 258 | 'private key exists but public key is missing for "' . $this->keyId . '"', \OCP\Util::FATAL); |
||
| 259 | return false; |
||
| 260 | } |
||
| 261 | } |
||
| 262 | } |
||
| 263 | |||
| 264 | return true; |
||
| 265 | |||
| 266 | } |
||
| 267 | |||
| 268 | /** |
||
| 269 | * @return string |
||
| 270 | */ |
||
| 271 | public function getPublicShareKeyId() { |
||
| 272 | return $this->publicShareKeyId; |
||
| 273 | } |
||
| 274 | |||
| 275 | /** |
||
| 276 | * Check whether pwd recovery is enabled for a given user |
||
| 277 | * @return bool 1 = yes, 0 = no, false = no record |
||
| 278 | * |
||
| 279 | * @note If records are not being returned, check for a hidden space |
||
| 280 | * at the start of the uid in db |
||
| 281 | */ |
||
| 282 | public function recoveryEnabledForUser() { |
||
| 283 | |||
| 284 | $recoveryMode = \OC::$server->getConfig()->getUserValue($this->userId, 'files_encryption', 'recovery_enabled', '0'); |
||
| 285 | |||
| 286 | return ($recoveryMode === '1') ? true : false; |
||
| 287 | |||
| 288 | } |
||
| 289 | |||
| 290 | /** |
||
| 291 | * Enable / disable pwd recovery for a given user |
||
| 292 | * @param bool $enabled Whether to enable or disable recovery |
||
| 293 | * @return bool |
||
| 294 | */ |
||
| 295 | public function setRecoveryForUser($enabled) { |
||
| 296 | |||
| 297 | $value = $enabled ? '1' : '0'; |
||
| 298 | try { |
||
| 299 | \OC::$server->getConfig()->setUserValue($this->userId, 'files_encryption', 'recovery_enabled', $value); |
||
| 300 | return true; |
||
| 301 | } catch(\OCP\PreConditionNotMetException $e) { |
||
| 302 | return false; |
||
| 303 | } |
||
| 304 | |||
| 305 | } |
||
| 306 | |||
| 307 | /** |
||
| 308 | * Find all files and their encryption status within a directory |
||
| 309 | * @param string $directory The path of the parent directory to search |
||
| 310 | * @param bool $found the founded files if called again |
||
| 311 | * @return array keys: plain, encrypted, broken |
||
| 312 | * @note $directory needs to be a path relative to OC data dir. e.g. |
||
| 313 | * /admin/files NOT /backup OR /home/www/oc/data/admin/files |
||
| 314 | */ |
||
| 315 | public function findEncFiles($directory, &$found = false) { |
||
| 316 | |||
| 317 | // Disable proxy - we don't want files to be decrypted before |
||
| 318 | // we handle them |
||
| 319 | \OC_FileProxy::$enabled = false; |
||
| 320 | |||
| 321 | if ($found === false) { |
||
| 322 | $found = array( |
||
| 323 | 'plain' => array(), |
||
| 324 | 'encrypted' => array(), |
||
| 325 | 'broken' => array(), |
||
| 326 | ); |
||
| 327 | } |
||
| 328 | |||
| 329 | if ($this->view->is_dir($directory) && $handle = $this->view->opendir($directory)){ |
||
| 330 | if (is_resource($handle)) { |
||
| 331 | while (false !== ($file = readdir($handle))) { |
||
| 332 | |||
| 333 | if ($file !== "." && $file !== "..") { |
||
| 334 | // skip stray part files |
||
| 335 | if (Helper::isPartialFilePath($file)) { |
||
| 336 | continue; |
||
| 337 | } |
||
| 338 | |||
| 339 | $filePath = $directory . '/' . $this->view->getRelativePath('/' . $file); |
||
| 340 | $relPath = Helper::stripUserFilesPath($filePath); |
||
| 341 | |||
| 342 | // If the path is a directory, search |
||
| 343 | // its contents |
||
| 344 | if ($this->view->is_dir($filePath)) { |
||
| 345 | |||
| 346 | $this->findEncFiles($filePath, $found); |
||
| 347 | |||
| 348 | // If the path is a file, determine |
||
| 349 | // its encryption status |
||
| 350 | } elseif ($this->view->is_file($filePath)) { |
||
| 351 | |||
| 352 | // Disable proxies again, some- |
||
| 353 | // where they got re-enabled :/ |
||
| 354 | \OC_FileProxy::$enabled = false; |
||
| 355 | |||
| 356 | $isEncryptedPath = $this->isEncryptedPath($filePath); |
||
| 357 | // If the file is encrypted |
||
| 358 | // NOTE: If the userId is |
||
| 359 | // empty or not set, file will |
||
| 360 | // detected as plain |
||
| 361 | // NOTE: This is inefficient; |
||
| 362 | // scanning every file like this |
||
| 363 | // will eat server resources :( |
||
| 364 | if ($isEncryptedPath) { |
||
| 365 | |||
| 366 | $fileKey = Keymanager::getFileKey($this->view, $this, $relPath); |
||
| 367 | $shareKey = Keymanager::getShareKey($this->view, $this->userId, $this, $relPath); |
||
| 368 | // if file is encrypted but now file key is available, throw exception |
||
| 369 | if ($fileKey === false || $shareKey === false) { |
||
| 370 | \OCP\Util::writeLog('encryption library', 'No keys available to decrypt the file: ' . $filePath, \OCP\Util::ERROR); |
||
| 371 | $found['broken'][] = array( |
||
| 372 | 'name' => $file, |
||
| 373 | 'path' => $filePath, |
||
| 374 | ); |
||
| 375 | } else { |
||
| 376 | $found['encrypted'][] = array( |
||
| 377 | 'name' => $file, |
||
| 378 | 'path' => $filePath, |
||
| 379 | ); |
||
| 380 | } |
||
| 381 | |||
| 382 | // If the file is not encrypted |
||
| 383 | } else { |
||
| 384 | |||
| 385 | $found['plain'][] = array( |
||
| 386 | 'name' => $file, |
||
| 387 | 'path' => $relPath |
||
| 388 | ); |
||
| 389 | } |
||
| 390 | } |
||
| 391 | } |
||
| 392 | } |
||
| 393 | } |
||
| 394 | } |
||
| 395 | |||
| 396 | \OC_FileProxy::$enabled = true; |
||
| 397 | |||
| 398 | return $found; |
||
| 399 | } |
||
| 400 | |||
| 401 | /** |
||
| 402 | * Check if a given path identifies an encrypted file |
||
| 403 | * @param string $path |
||
| 404 | * @return boolean |
||
| 405 | */ |
||
| 406 | public function isEncryptedPath($path) { |
||
| 407 | |||
| 408 | // Disable encryption proxy so data retrieved is in its |
||
| 409 | // original form |
||
| 410 | $proxyStatus = \OC_FileProxy::$enabled; |
||
| 411 | \OC_FileProxy::$enabled = false; |
||
| 412 | |||
| 413 | $data = ''; |
||
| 414 | |||
| 415 | // we only need 24 byte from the last chunk |
||
| 416 | if ($this->view->file_exists($path)) { |
||
| 417 | $handle = $this->view->fopen($path, 'r'); |
||
| 418 | if (is_resource($handle)) { |
||
| 419 | // suppress fseek warining, we handle the case that fseek doesn't |
||
| 420 | // work in the else branch |
||
| 421 | if (@fseek($handle, -24, SEEK_END) === 0) { |
||
| 422 | $data = fgets($handle); |
||
| 423 | } else { |
||
| 424 | // if fseek failed on the storage we create a local copy from the file |
||
| 425 | // and read this one |
||
| 426 | fclose($handle); |
||
| 427 | $localFile = $this->view->getLocalFile($path); |
||
| 428 | $handle = fopen($localFile, 'r'); |
||
| 429 | if (is_resource($handle) && fseek($handle, -24, SEEK_END) === 0) { |
||
| 430 | $data = fgets($handle); |
||
| 431 | } |
||
| 432 | } |
||
| 433 | fclose($handle); |
||
| 434 | } |
||
| 435 | } |
||
| 436 | |||
| 437 | // re-enable proxy |
||
| 438 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 439 | |||
| 440 | return Crypt::isCatfileContent($data); |
||
| 441 | } |
||
| 442 | |||
| 443 | /** |
||
| 444 | * get the file size of the unencrypted file |
||
| 445 | * @param string $path absolute path |
||
| 446 | * @return bool |
||
| 447 | */ |
||
| 448 | public function getFileSize($path) { |
||
| 449 | |||
| 450 | $result = 0; |
||
| 451 | |||
| 452 | // Disable encryption proxy to prevent recursive calls |
||
| 453 | $proxyStatus = \OC_FileProxy::$enabled; |
||
| 454 | \OC_FileProxy::$enabled = false; |
||
| 455 | |||
| 456 | // split the path parts |
||
| 457 | $pathParts = explode('/', $path); |
||
| 458 | |||
| 459 | if (isset($pathParts[2]) && $pathParts[2] === 'files' && $this->view->file_exists($path) |
||
| 460 | && $this->isEncryptedPath($path) |
||
| 461 | ) { |
||
| 462 | |||
| 463 | $cipher = 'AES-128-CFB'; |
||
| 464 | $realSize = 0; |
||
| 465 | |||
| 466 | // get the size from filesystem |
||
| 467 | $size = $this->view->filesize($path); |
||
| 468 | |||
| 469 | // open stream |
||
| 470 | $stream = $this->view->fopen($path, "r"); |
||
| 471 | |||
| 472 | if (is_resource($stream)) { |
||
| 473 | |||
| 474 | // if the file contains a encryption header we |
||
| 475 | // we set the cipher |
||
| 476 | // and we update the size |
||
| 477 | if ($this->containHeader($path)) { |
||
| 478 | $data = fread($stream,Crypt::BLOCKSIZE); |
||
| 479 | $header = Crypt::parseHeader($data); |
||
| 480 | $cipher = Crypt::getCipher($header); |
||
| 481 | $size -= Crypt::BLOCKSIZE; |
||
| 482 | } |
||
| 483 | |||
| 484 | // fast path, else the calculation for $lastChunkNr is bogus |
||
| 485 | if ($size === 0) { |
||
| 486 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 487 | return 0; |
||
| 488 | } |
||
| 489 | |||
| 490 | // calculate last chunk nr |
||
| 491 | // next highest is end of chunks, one subtracted is last one |
||
| 492 | // we have to read the last chunk, we can't just calculate it (because of padding etc) |
||
| 493 | $lastChunkNr = ceil($size/Crypt::BLOCKSIZE)-1; |
||
| 494 | |||
| 495 | // calculate last chunk position |
||
| 496 | $lastChunkPos = ($lastChunkNr * Crypt::BLOCKSIZE); |
||
| 497 | |||
| 498 | // get the content of the last chunk |
||
| 499 | if (@fseek($stream, $lastChunkPos, SEEK_CUR) === 0) { |
||
| 500 | $realSize+=$lastChunkNr*6126; |
||
| 501 | } |
||
| 502 | $lastChunkContentEncrypted=''; |
||
| 503 | $count=Crypt::BLOCKSIZE; |
||
| 504 | while ($count>0) { |
||
| 505 | $data=fread($stream,Crypt::BLOCKSIZE); |
||
| 506 | $count=strlen($data); |
||
| 507 | $lastChunkContentEncrypted.=$data; |
||
| 508 | if(strlen($lastChunkContentEncrypted)>Crypt::BLOCKSIZE) { |
||
| 509 | $realSize+=6126; |
||
| 510 | $lastChunkContentEncrypted=substr($lastChunkContentEncrypted,Crypt::BLOCKSIZE); |
||
| 511 | } |
||
| 512 | } |
||
| 513 | fclose($stream); |
||
| 514 | $relPath = Helper::stripUserFilesPath($path); |
||
| 515 | $shareKey = Keymanager::getShareKey($this->view, $this->keyId, $this, $relPath); |
||
| 516 | if($shareKey===false) { |
||
| 517 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 518 | return $result; |
||
| 519 | } |
||
| 520 | $session = new Session($this->view); |
||
| 521 | $privateKey = $session->getPrivateKey(); |
||
| 522 | $plainKeyfile = $this->decryptKeyfile($relPath, $privateKey); |
||
| 523 | $plainKey = Crypt::multiKeyDecrypt($plainKeyfile, $shareKey, $privateKey); |
||
| 524 | $lastChunkContent=Crypt::symmetricDecryptFileContent($lastChunkContentEncrypted, $plainKey, $cipher); |
||
| 525 | |||
| 526 | // calc the real file size with the size of the last chunk |
||
| 527 | $realSize += strlen($lastChunkContent); |
||
| 528 | |||
| 529 | // store file size |
||
| 530 | $result = $realSize; |
||
| 531 | } |
||
| 532 | } |
||
| 533 | |||
| 534 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 535 | |||
| 536 | return $result; |
||
| 537 | } |
||
| 538 | |||
| 539 | /** |
||
| 540 | * check if encrypted file contain a encryption header |
||
| 541 | * |
||
| 542 | * @param string $path |
||
| 543 | * @return boolean |
||
| 544 | */ |
||
| 545 | private function containHeader($path) { |
||
| 546 | // Disable encryption proxy to read the raw data |
||
| 547 | $proxyStatus = \OC_FileProxy::$enabled; |
||
| 548 | \OC_FileProxy::$enabled = false; |
||
| 549 | |||
| 550 | $isHeader = false; |
||
| 551 | $handle = $this->view->fopen($path, 'r'); |
||
| 552 | |||
| 553 | if (is_resource($handle)) { |
||
| 554 | $firstBlock = fread($handle, Crypt::BLOCKSIZE); |
||
| 555 | $isHeader = Crypt::isHeader($firstBlock); |
||
| 556 | } |
||
| 557 | |||
| 558 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 559 | |||
| 560 | return $isHeader; |
||
| 561 | } |
||
| 562 | |||
| 563 | /** |
||
| 564 | * fix the file size of the encrypted file |
||
| 565 | * @param string $path absolute path |
||
| 566 | * @return boolean true / false if file is encrypted |
||
| 567 | */ |
||
| 568 | public function fixFileSize($path) { |
||
| 569 | |||
| 570 | $result = false; |
||
| 571 | |||
| 572 | // Disable encryption proxy to prevent recursive calls |
||
| 573 | $proxyStatus = \OC_FileProxy::$enabled; |
||
| 574 | \OC_FileProxy::$enabled = false; |
||
| 575 | |||
| 576 | $realSize = $this->getFileSize($path); |
||
| 577 | |||
| 578 | if ($realSize > 0) { |
||
| 579 | |||
| 580 | $cached = $this->view->getFileInfo($path); |
||
| 581 | $cached['encrypted'] = true; |
||
| 582 | |||
| 583 | // set the size |
||
| 584 | $cached['unencrypted_size'] = $realSize; |
||
| 585 | |||
| 586 | // put file info |
||
| 587 | $this->view->putFileInfo($path, $cached); |
||
| 588 | |||
| 589 | $result = true; |
||
| 590 | |||
| 591 | } |
||
| 592 | |||
| 593 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 594 | |||
| 595 | return $result; |
||
| 596 | } |
||
| 597 | |||
| 598 | /** |
||
| 599 | * encrypt versions from given file |
||
| 600 | * @param array $filelist list of encrypted files, relative to data/user/files |
||
| 601 | * @return boolean |
||
| 602 | */ |
||
| 603 | View Code Duplication | private function encryptVersions($filelist) { |
|
| 604 | |||
| 605 | $successful = true; |
||
| 606 | |||
| 607 | if (\OCP\App::isEnabled('files_versions')) { |
||
| 608 | |||
| 609 | foreach ($filelist as $filename) { |
||
| 610 | |||
| 611 | $versions = \OCA\Files_Versions\Storage::getVersions($this->userId, $filename); |
||
| 612 | foreach ($versions as $version) { |
||
| 613 | |||
| 614 | $path = '/' . $this->userId . '/files_versions/' . $version['path'] . '.v' . $version['version']; |
||
| 615 | |||
| 616 | $encHandle = fopen('crypt://' . $path . '.part', 'wb'); |
||
| 617 | |||
| 618 | if ($encHandle === false) { |
||
| 619 | \OCP\Util::writeLog('Encryption library', 'couldn\'t open "' . $path . '", decryption failed!', \OCP\Util::FATAL); |
||
| 620 | $successful = false; |
||
| 621 | continue; |
||
| 622 | } |
||
| 623 | |||
| 624 | $plainHandle = $this->view->fopen($path, 'rb'); |
||
| 625 | if ($plainHandle === false) { |
||
| 626 | \OCP\Util::writeLog('Encryption library', 'couldn\'t open "' . $path . '.part", decryption failed!', \OCP\Util::FATAL); |
||
| 627 | $successful = false; |
||
| 628 | continue; |
||
| 629 | } |
||
| 630 | |||
| 631 | stream_copy_to_stream($plainHandle, $encHandle); |
||
| 632 | |||
| 633 | fclose($encHandle); |
||
| 634 | fclose($plainHandle); |
||
| 635 | |||
| 636 | $this->view->rename($path . '.part', $path); |
||
| 637 | } |
||
| 638 | } |
||
| 639 | } |
||
| 640 | |||
| 641 | return $successful; |
||
| 642 | } |
||
| 643 | |||
| 644 | /** |
||
| 645 | * decrypt versions from given file |
||
| 646 | * @param string $filelist list of decrypted files, relative to data/user/files |
||
| 647 | * @return boolean |
||
| 648 | */ |
||
| 649 | View Code Duplication | private function decryptVersions($filelist) { |
|
| 650 | |||
| 651 | $successful = true; |
||
| 652 | |||
| 653 | if (\OCP\App::isEnabled('files_versions')) { |
||
| 654 | |||
| 655 | foreach ($filelist as $filename) { |
||
| 656 | |||
| 657 | $versions = \OCA\Files_Versions\Storage::getVersions($this->userId, $filename); |
||
| 658 | foreach ($versions as $version) { |
||
| 659 | |||
| 660 | $path = '/' . $this->userId . '/files_versions/' . $version['path'] . '.v' . $version['version']; |
||
| 661 | |||
| 662 | $encHandle = fopen('crypt://' . $path, 'rb'); |
||
| 663 | |||
| 664 | if ($encHandle === false) { |
||
| 665 | \OCP\Util::writeLog('Encryption library', 'couldn\'t open "' . $path . '", decryption failed!', \OCP\Util::FATAL); |
||
| 666 | $successful = false; |
||
| 667 | continue; |
||
| 668 | } |
||
| 669 | |||
| 670 | $plainHandle = $this->view->fopen($path . '.part', 'wb'); |
||
| 671 | if ($plainHandle === false) { |
||
| 672 | \OCP\Util::writeLog('Encryption library', 'couldn\'t open "' . $path . '.part", decryption failed!', \OCP\Util::FATAL); |
||
| 673 | $successful = false; |
||
| 674 | continue; |
||
| 675 | } |
||
| 676 | |||
| 677 | stream_copy_to_stream($encHandle, $plainHandle); |
||
| 678 | |||
| 679 | fclose($encHandle); |
||
| 680 | fclose($plainHandle); |
||
| 681 | |||
| 682 | $this->view->rename($path . '.part', $path); |
||
| 683 | } |
||
| 684 | } |
||
| 685 | } |
||
| 686 | |||
| 687 | return $successful; |
||
| 688 | } |
||
| 689 | |||
| 690 | /** |
||
| 691 | * Decrypt all files |
||
| 692 | * @return bool |
||
| 693 | */ |
||
| 694 | public function decryptAll() { |
||
| 695 | |||
| 696 | $found = $this->findEncFiles($this->userId . '/files'); |
||
| 697 | |||
| 698 | $successful = true; |
||
| 699 | |||
| 700 | if ($found) { |
||
| 701 | |||
| 702 | $versionStatus = \OCP\App::isEnabled('files_versions'); |
||
| 703 | \OC_App::disable('files_versions'); |
||
| 704 | |||
| 705 | $decryptedFiles = array(); |
||
| 706 | |||
| 707 | // Encrypt unencrypted files |
||
| 708 | foreach ($found['encrypted'] as $encryptedFile) { |
||
| 709 | |||
| 710 | //relative to data/<user>/file |
||
| 711 | $relPath = Helper::stripUserFilesPath($encryptedFile['path']); |
||
| 712 | |||
| 713 | //get file info |
||
| 714 | $fileInfo = \OC\Files\Filesystem::getFileInfo($relPath); |
||
| 715 | |||
| 716 | //relative to /data |
||
| 717 | $rawPath = $encryptedFile['path']; |
||
| 718 | |||
| 719 | //get timestamp |
||
| 720 | $timestamp = $fileInfo['mtime']; |
||
| 721 | |||
| 722 | //enable proxy to use OC\Files\View to access the original file |
||
| 723 | \OC_FileProxy::$enabled = true; |
||
| 724 | |||
| 725 | // Open enc file handle for binary reading |
||
| 726 | $encHandle = $this->view->fopen($rawPath, 'rb'); |
||
| 727 | |||
| 728 | // Disable proxy to prevent file being encrypted again |
||
| 729 | \OC_FileProxy::$enabled = false; |
||
| 730 | |||
| 731 | View Code Duplication | if ($encHandle === false) { |
|
| 732 | \OCP\Util::writeLog('Encryption library', 'couldn\'t open "' . $rawPath . '", decryption failed!', \OCP\Util::FATAL); |
||
| 733 | $successful = false; |
||
| 734 | continue; |
||
| 735 | } |
||
| 736 | |||
| 737 | // Open plain file handle for binary writing, with same filename as original plain file |
||
| 738 | $plainHandle = $this->view->fopen($rawPath . '.part', 'wb'); |
||
| 739 | View Code Duplication | if ($plainHandle === false) { |
|
| 740 | \OCP\Util::writeLog('Encryption library', 'couldn\'t open "' . $rawPath . '.part", decryption failed!', \OCP\Util::FATAL); |
||
| 741 | $successful = false; |
||
| 742 | continue; |
||
| 743 | } |
||
| 744 | |||
| 745 | // Move plain file to a temporary location |
||
| 746 | $size = stream_copy_to_stream($encHandle, $plainHandle); |
||
| 747 | View Code Duplication | if ($size === 0) { |
|
| 748 | \OCP\Util::writeLog('Encryption library', 'Zero bytes copied of "' . $rawPath . '", decryption failed!', \OCP\Util::FATAL); |
||
| 749 | $successful = false; |
||
| 750 | continue; |
||
| 751 | } |
||
| 752 | |||
| 753 | fclose($encHandle); |
||
| 754 | fclose($plainHandle); |
||
| 755 | |||
| 756 | $fakeRoot = $this->view->getRoot(); |
||
| 757 | $this->view->chroot('/' . $this->userId . '/files'); |
||
| 758 | |||
| 759 | $this->view->rename($relPath . '.part', $relPath); |
||
| 760 | |||
| 761 | //set timestamp |
||
| 762 | $this->view->touch($relPath, $timestamp); |
||
| 763 | |||
| 764 | $this->view->chroot($fakeRoot); |
||
| 765 | |||
| 766 | // Add the file to the cache |
||
| 767 | \OC\Files\Filesystem::putFileInfo($relPath, array( |
||
| 768 | 'encrypted' => false, |
||
| 769 | 'size' => $size, |
||
| 770 | 'unencrypted_size' => 0, |
||
| 771 | 'etag' => $fileInfo['etag'] |
||
| 772 | )); |
||
| 773 | |||
| 774 | $decryptedFiles[] = $relPath; |
||
| 775 | |||
| 776 | } |
||
| 777 | |||
| 778 | if ($versionStatus) { |
||
| 779 | \OC_App::enable('files_versions'); |
||
| 780 | } |
||
| 781 | |||
| 782 | if (!$this->decryptVersions($decryptedFiles)) { |
||
| 783 | $successful = false; |
||
| 784 | } |
||
| 785 | |||
| 786 | // if there are broken encrypted files than the complete decryption |
||
| 787 | // was not successful |
||
| 788 | if (!empty($found['broken'])) { |
||
| 789 | $successful = false; |
||
| 790 | } |
||
| 791 | |||
| 792 | if ($successful) { |
||
| 793 | $this->backupAllKeys('decryptAll', false, false); |
||
| 794 | $this->view->deleteAll($this->keysPath); |
||
| 795 | } |
||
| 796 | |||
| 797 | \OC_FileProxy::$enabled = true; |
||
| 798 | } |
||
| 799 | |||
| 800 | return $successful; |
||
| 801 | } |
||
| 802 | |||
| 803 | /** |
||
| 804 | * Encrypt all files in a directory |
||
| 805 | * @param string $dirPath the directory whose files will be encrypted |
||
| 806 | * @return bool |
||
| 807 | * @note Encryption is recursive |
||
| 808 | */ |
||
| 809 | public function encryptAll($dirPath) { |
||
| 810 | |||
| 811 | $result = true; |
||
| 812 | |||
| 813 | $found = $this->findEncFiles($dirPath); |
||
| 814 | |||
| 815 | // Disable proxy to prevent file being encrypted twice |
||
| 816 | \OC_FileProxy::$enabled = false; |
||
| 817 | |||
| 818 | $versionStatus = \OCP\App::isEnabled('files_versions'); |
||
| 819 | \OC_App::disable('files_versions'); |
||
| 820 | |||
| 821 | $encryptedFiles = array(); |
||
| 822 | |||
| 823 | // Encrypt unencrypted files |
||
| 824 | foreach ($found['plain'] as $plainFile) { |
||
| 825 | |||
| 826 | //get file info |
||
| 827 | $fileInfo = \OC\Files\Filesystem::getFileInfo($plainFile['path']); |
||
| 828 | |||
| 829 | //relative to data/<user>/file |
||
| 830 | $relPath = $plainFile['path']; |
||
| 831 | |||
| 832 | //relative to /data |
||
| 833 | $rawPath = '/' . $this->userId . '/files/' . $plainFile['path']; |
||
| 834 | |||
| 835 | // keep timestamp |
||
| 836 | $timestamp = $fileInfo['mtime']; |
||
| 837 | |||
| 838 | // Open plain file handle for binary reading |
||
| 839 | $plainHandle = $this->view->fopen($rawPath, 'rb'); |
||
| 840 | |||
| 841 | // Open enc file handle for binary writing, with same filename as original plain file |
||
| 842 | $encHandle = fopen('crypt://' . $rawPath . '.part', 'wb'); |
||
| 843 | |||
| 844 | if (is_resource($encHandle) && is_resource($plainHandle)) { |
||
| 845 | // Move plain file to a temporary location |
||
| 846 | $size = stream_copy_to_stream($plainHandle, $encHandle); |
||
| 847 | |||
| 848 | fclose($encHandle); |
||
| 849 | fclose($plainHandle); |
||
| 850 | |||
| 851 | $fakeRoot = $this->view->getRoot(); |
||
| 852 | $this->view->chroot('/' . $this->userId . '/files'); |
||
| 853 | |||
| 854 | $this->view->rename($relPath . '.part', $relPath); |
||
| 855 | |||
| 856 | // set timestamp |
||
| 857 | $this->view->touch($relPath, $timestamp); |
||
| 858 | |||
| 859 | $encSize = $this->view->filesize($relPath); |
||
| 860 | |||
| 861 | $this->view->chroot($fakeRoot); |
||
| 862 | |||
| 863 | // Add the file to the cache |
||
| 864 | \OC\Files\Filesystem::putFileInfo($relPath, array( |
||
| 865 | 'encrypted' => true, |
||
| 866 | 'size' => $encSize, |
||
| 867 | 'unencrypted_size' => $size, |
||
| 868 | 'etag' => $fileInfo['etag'] |
||
| 869 | )); |
||
| 870 | |||
| 871 | $encryptedFiles[] = $relPath; |
||
| 872 | } else { |
||
| 873 | \OCP\Util::writeLog('files_encryption', 'initial encryption: could not encrypt ' . $rawPath, \OCP\Util::FATAL); |
||
| 874 | $result = false; |
||
| 875 | } |
||
| 876 | } |
||
| 877 | |||
| 878 | \OC_FileProxy::$enabled = true; |
||
| 879 | |||
| 880 | if ($versionStatus) { |
||
| 881 | \OC_App::enable('files_versions'); |
||
| 882 | } |
||
| 883 | |||
| 884 | $result = $result && $this->encryptVersions($encryptedFiles); |
||
| 885 | |||
| 886 | return $result; |
||
| 887 | |||
| 888 | } |
||
| 889 | |||
| 890 | /** |
||
| 891 | * Return important encryption related paths |
||
| 892 | * @param string $pathName Name of the directory to return the path of |
||
| 893 | * @return string path |
||
| 894 | */ |
||
| 895 | public function getPath($pathName) { |
||
| 896 | |||
| 897 | switch ($pathName) { |
||
| 898 | |||
| 899 | case 'publicKeyDir': |
||
| 900 | |||
| 901 | return $this->publicKeyDir; |
||
| 902 | |||
| 903 | break; |
||
| 904 | |||
| 905 | case 'encryptionDir': |
||
| 906 | |||
| 907 | return $this->encryptionDir; |
||
| 908 | |||
| 909 | break; |
||
| 910 | |||
| 911 | case 'keysPath': |
||
| 912 | |||
| 913 | return $this->keysPath; |
||
| 914 | |||
| 915 | break; |
||
| 916 | |||
| 917 | case 'publicKeyPath': |
||
| 918 | |||
| 919 | return $this->publicKeyPath; |
||
| 920 | |||
| 921 | break; |
||
| 922 | |||
| 923 | case 'privateKeyPath': |
||
| 924 | |||
| 925 | return $this->privateKeyPath; |
||
| 926 | |||
| 927 | break; |
||
| 928 | } |
||
| 929 | |||
| 930 | return false; |
||
| 931 | |||
| 932 | } |
||
| 933 | |||
| 934 | /** |
||
| 935 | * Returns whether the given user is ready for encryption. |
||
| 936 | * Also returns true if the given user is the public user |
||
| 937 | * or the recovery key user. |
||
| 938 | * |
||
| 939 | * @param string $user user to check |
||
| 940 | * |
||
| 941 | * @return boolean true if the user is ready, false otherwise |
||
| 942 | */ |
||
| 943 | private function isUserReady($user) { |
||
| 944 | if ($user === $this->publicShareKeyId |
||
| 945 | || $user === $this->recoveryKeyId |
||
| 946 | ) { |
||
| 947 | return true; |
||
| 948 | } |
||
| 949 | try { |
||
| 950 | $util = new Util($this->view, $user); |
||
| 951 | return $util->ready(); |
||
| 952 | } catch (NoUserException $e) { |
||
| 953 | \OCP\Util::writeLog('Encryption library', |
||
| 954 | 'No User object for '.$user, \OCP\Util::DEBUG); |
||
| 955 | return false; |
||
| 956 | } |
||
| 957 | } |
||
| 958 | |||
| 959 | /** |
||
| 960 | * Filter an array of UIDs to return only ones ready for sharing |
||
| 961 | * @param array $unfilteredUsers users to be checked for sharing readiness |
||
| 962 | * @return array as multi-dimensional array. keys: ready, unready |
||
| 963 | */ |
||
| 964 | public function filterShareReadyUsers($unfilteredUsers) { |
||
| 965 | |||
| 966 | // This array will collect the filtered IDs |
||
| 967 | $readyIds = $unreadyIds = array(); |
||
| 968 | |||
| 969 | // Loop through users and create array of UIDs that need new keyfiles |
||
| 970 | foreach ($unfilteredUsers as $user) { |
||
| 971 | // Check that the user is encryption capable, or is the |
||
| 972 | // public system user (for public shares) |
||
| 973 | if ($this->isUserReady($user)) { |
||
| 974 | |||
| 975 | // Construct array of ready UIDs for Keymanager{} |
||
| 976 | $readyIds[] = $user; |
||
| 977 | |||
| 978 | } else { |
||
| 979 | |||
| 980 | // Construct array of unready UIDs for Keymanager{} |
||
| 981 | $unreadyIds[] = $user; |
||
| 982 | |||
| 983 | // Log warning; we can't do necessary setup here |
||
| 984 | // because we don't have the user passphrase |
||
| 985 | \OCP\Util::writeLog('Encryption library', |
||
| 986 | '"' . $user . '" is not setup for encryption', \OCP\Util::WARN); |
||
| 987 | |||
| 988 | } |
||
| 989 | |||
| 990 | } |
||
| 991 | |||
| 992 | return array( |
||
| 993 | 'ready' => $readyIds, |
||
| 994 | 'unready' => $unreadyIds |
||
| 995 | ); |
||
| 996 | |||
| 997 | } |
||
| 998 | |||
| 999 | /** |
||
| 1000 | * Decrypt a keyfile |
||
| 1001 | * @param string $filePath |
||
| 1002 | * @param string $privateKey |
||
| 1003 | * @return false|string |
||
| 1004 | */ |
||
| 1005 | private function decryptKeyfile($filePath, $privateKey) { |
||
| 1006 | |||
| 1007 | // Get the encrypted keyfile |
||
| 1008 | $encKeyfile = Keymanager::getFileKey($this->view, $this, $filePath); |
||
| 1009 | |||
| 1010 | // The file has a shareKey and must use it for decryption |
||
| 1011 | $shareKey = Keymanager::getShareKey($this->view, $this->keyId, $this, $filePath); |
||
| 1012 | |||
| 1013 | $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); |
||
| 1014 | |||
| 1015 | return $plainKeyfile; |
||
| 1016 | } |
||
| 1017 | |||
| 1018 | /** |
||
| 1019 | * Encrypt keyfile to multiple users |
||
| 1020 | * @param Session $session |
||
| 1021 | * @param array $users list of users which should be able to access the file |
||
| 1022 | * @param string $filePath path of the file to be shared |
||
| 1023 | * @return bool |
||
| 1024 | */ |
||
| 1025 | public function setSharedFileKeyfiles(Session $session, array $users, $filePath) { |
||
| 1026 | |||
| 1027 | // Make sure users are capable of sharing |
||
| 1028 | $filteredUids = $this->filterShareReadyUsers($users); |
||
| 1029 | |||
| 1030 | // If we're attempting to share to unready users |
||
| 1031 | if (!empty($filteredUids['unready'])) { |
||
| 1032 | |||
| 1033 | \OCP\Util::writeLog('Encryption library', |
||
| 1034 | 'Sharing to these user(s) failed as they are unready for encryption:"' |
||
| 1035 | . print_r($filteredUids['unready'], 1), \OCP\Util::WARN); |
||
| 1036 | |||
| 1037 | return false; |
||
| 1038 | |||
| 1039 | } |
||
| 1040 | |||
| 1041 | // Get public keys for each user, ready for generating sharekeys |
||
| 1042 | $userPubKeys = Keymanager::getPublicKeys($this->view, $filteredUids['ready']); |
||
| 1043 | |||
| 1044 | // Note proxy status then disable it |
||
| 1045 | $proxyStatus = \OC_FileProxy::$enabled; |
||
| 1046 | \OC_FileProxy::$enabled = false; |
||
| 1047 | |||
| 1048 | // Get the current users's private key for decrypting existing keyfile |
||
| 1049 | $privateKey = $session->getPrivateKey(); |
||
| 1050 | |||
| 1051 | try { |
||
| 1052 | // Decrypt keyfile |
||
| 1053 | $plainKeyfile = $this->decryptKeyfile($filePath, $privateKey); |
||
| 1054 | // Re-enc keyfile to (additional) sharekeys |
||
| 1055 | $multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys); |
||
| 1056 | } catch (Exception\EncryptionException $e) { |
||
| 1057 | $msg = 'set shareFileKeyFailed (code: ' . $e->getCode() . '): ' . $e->getMessage(); |
||
| 1058 | \OCP\Util::writeLog('files_encryption', $msg, \OCP\Util::FATAL); |
||
| 1059 | return false; |
||
| 1060 | } catch (\Exception $e) { |
||
| 1061 | $msg = 'set shareFileKeyFailed (unknown error): ' . $e->getMessage(); |
||
| 1062 | \OCP\Util::writeLog('files_encryption', $msg, \OCP\Util::FATAL); |
||
| 1063 | return false; |
||
| 1064 | } |
||
| 1065 | |||
| 1066 | // Save the recrypted key to it's owner's keyfiles directory |
||
| 1067 | // Save new sharekeys to all necessary user directory |
||
| 1068 | if ( |
||
| 1069 | !Keymanager::setFileKey($this->view, $this, $filePath, $multiEncKey['data']) |
||
| 1070 | || !Keymanager::setShareKeys($this->view, $this, $filePath, $multiEncKey['keys']) |
||
| 1071 | ) { |
||
| 1072 | |||
| 1073 | \OCP\Util::writeLog('Encryption library', |
||
| 1074 | 'Keyfiles could not be saved for users sharing ' . $filePath, \OCP\Util::ERROR); |
||
| 1075 | |||
| 1076 | return false; |
||
| 1077 | |||
| 1078 | } |
||
| 1079 | |||
| 1080 | // Return proxy to original status |
||
| 1081 | \OC_FileProxy::$enabled = $proxyStatus; |
||
| 1082 | |||
| 1083 | return true; |
||
| 1084 | } |
||
| 1085 | |||
| 1086 | /** |
||
| 1087 | * Find, sanitise and format users sharing a file |
||
| 1088 | * @note This wraps other methods into a portable bundle |
||
| 1089 | * @param boolean $sharingEnabled |
||
| 1090 | * @param string $filePath path relativ to current users files folder |
||
| 1091 | */ |
||
| 1092 | public function getSharingUsersArray($sharingEnabled, $filePath) { |
||
| 1093 | |||
| 1094 | $appConfig = \OC::$server->getAppConfig(); |
||
| 1095 | |||
| 1096 | // Check if key recovery is enabled |
||
| 1097 | if ( |
||
| 1098 | $appConfig->getValue('files_encryption', 'recoveryAdminEnabled') |
||
| 1099 | && $this->recoveryEnabledForUser() |
||
| 1100 | ) { |
||
| 1101 | $recoveryEnabled = true; |
||
| 1102 | } else { |
||
| 1103 | $recoveryEnabled = false; |
||
| 1104 | } |
||
| 1105 | |||
| 1106 | // Make sure that a share key is generated for the owner too |
||
| 1107 | list($owner, $ownerPath) = $this->getUidAndFilename($filePath); |
||
| 1108 | |||
| 1109 | $ownerPath = Helper::stripPartialFileExtension($ownerPath); |
||
| 1110 | |||
| 1111 | // always add owner to the list of users with access to the file |
||
| 1112 | $userIds = array($owner); |
||
| 1113 | |||
| 1114 | if ($sharingEnabled) { |
||
| 1115 | |||
| 1116 | // Find out who, if anyone, is sharing the file |
||
| 1117 | $result = \OCP\Share::getUsersSharingFile($ownerPath, $owner); |
||
| 1118 | $userIds = \array_merge($userIds, $result['users']); |
||
| 1119 | if ($result['public'] || $result['remote']) { |
||
| 1120 | $userIds[] = $this->publicShareKeyId; |
||
| 1121 | } |
||
| 1122 | |||
| 1123 | } |
||
| 1124 | |||
| 1125 | // If recovery is enabled, add the |
||
| 1126 | // Admin UID to list of users to share to |
||
| 1127 | if ($recoveryEnabled) { |
||
| 1128 | // Find recoveryAdmin user ID |
||
| 1129 | $recoveryKeyId = $appConfig->getValue('files_encryption', 'recoveryKeyId'); |
||
| 1130 | // Add recoveryAdmin to list of users sharing |
||
| 1131 | $userIds[] = $recoveryKeyId; |
||
| 1132 | } |
||
| 1133 | |||
| 1134 | // check if it is a group mount |
||
| 1135 | if (\OCP\App::isEnabled("files_external")) { |
||
| 1136 | $mounts = \OC_Mount_Config::getSystemMountPoints(); |
||
| 1137 | foreach ($mounts as $mount) { |
||
| 1138 | if ($mount['mountpoint'] == substr($ownerPath, 1, strlen($mount['mountpoint']))) { |
||
| 1139 | $userIds = array_merge($userIds, $this->getUserWithAccessToMountPoint($mount['applicable']['users'], $mount['applicable']['groups'])); |
||
| 1140 | } |
||
| 1141 | } |
||
| 1142 | } |
||
| 1143 | |||
| 1144 | // Remove duplicate UIDs |
||
| 1145 | $uniqueUserIds = array_unique($userIds); |
||
| 1146 | |||
| 1147 | return $uniqueUserIds; |
||
| 1148 | |||
| 1149 | } |
||
| 1150 | |||
| 1151 | private function getUserWithAccessToMountPoint($users, $groups) { |
||
| 1152 | $result = array(); |
||
| 1153 | if (in_array('all', $users)) { |
||
| 1154 | $result = \OCP\User::getUsers(); |
||
| 1155 | } else { |
||
| 1156 | $result = array_merge($result, $users); |
||
| 1157 | foreach ($groups as $group) { |
||
| 1158 | $result = array_merge($result, \OC_Group::usersInGroup($group)); |
||
| 1159 | } |
||
| 1160 | } |
||
| 1161 | |||
| 1162 | return $result; |
||
| 1163 | } |
||
| 1164 | |||
| 1165 | /** |
||
| 1166 | * set migration status |
||
| 1167 | * @param int $status |
||
| 1168 | * @param int $preCondition only update migration status if the previous value equals $preCondition |
||
| 1169 | * @return boolean |
||
| 1170 | */ |
||
| 1171 | private function setMigrationStatus($status, $preCondition = null) { |
||
| 1172 | |||
| 1173 | // convert to string if preCondition is set |
||
| 1174 | $preCondition = ($preCondition === null) ? null : (string)$preCondition; |
||
| 1175 | |||
| 1176 | try { |
||
| 1177 | \OC::$server->getConfig()->setUserValue($this->userId, 'files_encryption', 'migration_status', (string)$status, $preCondition); |
||
| 1178 | return true; |
||
| 1179 | } catch(\OCP\PreConditionNotMetException $e) { |
||
| 1180 | return false; |
||
| 1181 | } |
||
| 1182 | |||
| 1183 | } |
||
| 1184 | |||
| 1185 | /** |
||
| 1186 | * start migration mode to initially encrypt users data |
||
| 1187 | * @return boolean |
||
| 1188 | */ |
||
| 1189 | View Code Duplication | public function beginMigration() { |
|
| 1190 | |||
| 1191 | $result = $this->setMigrationStatus(self::MIGRATION_IN_PROGRESS, self::MIGRATION_OPEN); |
||
| 1192 | |||
| 1193 | if ($result) { |
||
| 1194 | \OCP\Util::writeLog('Encryption library', "Start migration to encryption mode for " . $this->userId, \OCP\Util::INFO); |
||
| 1195 | } else { |
||
| 1196 | \OCP\Util::writeLog('Encryption library', "Could not activate migration mode for " . $this->userId . ". Probably another process already started the initial encryption", \OCP\Util::WARN); |
||
| 1197 | } |
||
| 1198 | |||
| 1199 | return $result; |
||
| 1200 | } |
||
| 1201 | |||
| 1202 | public function resetMigrationStatus() { |
||
| 1203 | return $this->setMigrationStatus(self::MIGRATION_OPEN); |
||
| 1204 | |||
| 1205 | } |
||
| 1206 | |||
| 1207 | /** |
||
| 1208 | * close migration mode after users data has been encrypted successfully |
||
| 1209 | * @return boolean |
||
| 1210 | */ |
||
| 1211 | View Code Duplication | public function finishMigration() { |
|
| 1212 | $result = $this->setMigrationStatus(self::MIGRATION_COMPLETED); |
||
| 1213 | |||
| 1214 | if ($result) { |
||
| 1215 | \OCP\Util::writeLog('Encryption library', "Finish migration successfully for " . $this->userId, \OCP\Util::INFO); |
||
| 1216 | } else { |
||
| 1217 | \OCP\Util::writeLog('Encryption library', "Could not deactivate migration mode for " . $this->userId, \OCP\Util::WARN); |
||
| 1218 | } |
||
| 1219 | |||
| 1220 | return $result; |
||
| 1221 | } |
||
| 1222 | |||
| 1223 | /** |
||
| 1224 | * check if files are already migrated to the encryption system |
||
| 1225 | * @return int|false migration status, false = in case of no record |
||
| 1226 | * @note If records are not being returned, check for a hidden space |
||
| 1227 | * at the start of the uid in db |
||
| 1228 | */ |
||
| 1229 | public function getMigrationStatus() { |
||
| 1230 | |||
| 1231 | $migrationStatus = false; |
||
| 1232 | if (\OCP\User::userExists($this->userId)) { |
||
| 1233 | $migrationStatus = \OC::$server->getConfig()->getUserValue($this->userId, 'files_encryption', 'migration_status', null); |
||
| 1234 | if ($migrationStatus === null) { |
||
| 1235 | \OC::$server->getConfig()->setUserValue($this->userId, 'files_encryption', 'migration_status', (string)self::MIGRATION_OPEN); |
||
| 1236 | $migrationStatus = self::MIGRATION_OPEN; |
||
| 1237 | } |
||
| 1238 | } |
||
| 1239 | |||
| 1240 | return (int)$migrationStatus; |
||
| 1241 | |||
| 1242 | } |
||
| 1243 | |||
| 1244 | /** |
||
| 1245 | * get uid of the owners of the file and the path to the file |
||
| 1246 | * @param string $path Path of the file to check |
||
| 1247 | * @throws \Exception |
||
| 1248 | * @note $shareFilePath must be relative to data/UID/files. Files |
||
| 1249 | * relative to /Shared are also acceptable |
||
| 1250 | * @return array |
||
| 1251 | */ |
||
| 1252 | public function getUidAndFilename($path) { |
||
| 1253 | |||
| 1254 | $pathinfo = pathinfo($path); |
||
| 1255 | $partfile = false; |
||
| 1256 | $parentFolder = false; |
||
| 1257 | if (array_key_exists('extension', $pathinfo) && $pathinfo['extension'] === 'part') { |
||
| 1258 | // if the real file exists we check this file |
||
| 1259 | $filePath = $this->userFilesDir . '/' .$pathinfo['dirname'] . '/' . $pathinfo['filename']; |
||
| 1260 | if ($this->view->file_exists($filePath)) { |
||
| 1261 | $pathToCheck = $pathinfo['dirname'] . '/' . $pathinfo['filename']; |
||
| 1262 | } else { // otherwise we look for the parent |
||
| 1263 | $pathToCheck = $pathinfo['dirname']; |
||
| 1264 | $parentFolder = true; |
||
| 1265 | } |
||
| 1266 | $partfile = true; |
||
| 1267 | } else { |
||
| 1268 | $pathToCheck = $path; |
||
| 1269 | } |
||
| 1270 | |||
| 1271 | $view = new \OC\Files\View($this->userFilesDir); |
||
| 1272 | $fileOwnerUid = $view->getOwner($pathToCheck); |
||
| 1273 | |||
| 1274 | // handle public access |
||
| 1275 | if ($this->isPublic) { |
||
| 1276 | return array($this->userId, $path); |
||
| 1277 | } else { |
||
| 1278 | |||
| 1279 | // Check that UID is valid |
||
| 1280 | if (!\OCP\User::userExists($fileOwnerUid)) { |
||
| 1281 | throw new \Exception( |
||
| 1282 | 'Could not find owner (UID = "' . var_export($fileOwnerUid, 1) . '") of file "' . $path . '"'); |
||
| 1283 | } |
||
| 1284 | |||
| 1285 | // NOTE: Bah, this dependency should be elsewhere |
||
| 1286 | \OC\Files\Filesystem::initMountPoints($fileOwnerUid); |
||
| 1287 | |||
| 1288 | // If the file owner is the currently logged in user |
||
| 1289 | if ($fileOwnerUid === $this->userId) { |
||
| 1290 | |||
| 1291 | // Assume the path supplied is correct |
||
| 1292 | $filename = $path; |
||
| 1293 | |||
| 1294 | } else { |
||
| 1295 | $info = $view->getFileInfo($pathToCheck); |
||
| 1296 | $ownerView = new \OC\Files\View('/' . $fileOwnerUid . '/files'); |
||
| 1297 | |||
| 1298 | // Fetch real file path from DB |
||
| 1299 | $filename = $ownerView->getPath($info['fileid']); |
||
| 1300 | if ($parentFolder) { |
||
| 1301 | $filename = $filename . '/'. $pathinfo['filename']; |
||
| 1302 | } |
||
| 1303 | |||
| 1304 | if ($partfile) { |
||
| 1305 | $filename = $filename . '.' . $pathinfo['extension']; |
||
| 1306 | } |
||
| 1307 | |||
| 1308 | } |
||
| 1309 | |||
| 1310 | return array( |
||
| 1311 | $fileOwnerUid, |
||
| 1312 | \OC\Files\Filesystem::normalizePath($filename) |
||
| 1313 | ); |
||
| 1314 | } |
||
| 1315 | } |
||
| 1316 | |||
| 1317 | /** |
||
| 1318 | * go recursively through a dir and collect all files and sub files. |
||
| 1319 | * @param string $dir relative to the users files folder |
||
| 1320 | * @return array with list of files relative to the users files folder |
||
| 1321 | */ |
||
| 1322 | public function getAllFiles($dir, $mountPoint = '') { |
||
| 1323 | $result = array(); |
||
| 1324 | $dirList = array($dir); |
||
| 1325 | |||
| 1326 | while ($dirList) { |
||
| 1327 | $dir = array_pop($dirList); |
||
| 1328 | $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( |
||
| 1329 | $this->userFilesDir . '/' . $dir)); |
||
| 1330 | |||
| 1331 | foreach ($content as $c) { |
||
| 1332 | // getDirectoryContent() returns the paths relative to the mount points, so we need |
||
| 1333 | // to re-construct the complete path |
||
| 1334 | $path = ($mountPoint !== '') ? $mountPoint . '/' . $c['path'] : $c['path']; |
||
| 1335 | $path = \OC\Files\Filesystem::normalizePath($path); |
||
| 1336 | if ($c['type'] === 'dir') { |
||
| 1337 | $dirList[] = substr($path, strlen('/' . \OCP\User::getUser() . "/files")); |
||
| 1338 | } else { |
||
| 1339 | $result[] = substr($path, strlen('/' . \OCP\User::getUser() . "/files")); |
||
| 1340 | } |
||
| 1341 | } |
||
| 1342 | |||
| 1343 | } |
||
| 1344 | |||
| 1345 | return $result; |
||
| 1346 | } |
||
| 1347 | |||
| 1348 | /** |
||
| 1349 | * get owner of the shared files. |
||
| 1350 | * @param int $id ID of a share |
||
| 1351 | * @return string owner |
||
| 1352 | */ |
||
| 1353 | public function getOwnerFromSharedFile($id) { |
||
| 1354 | |||
| 1355 | $query = \OCP\DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); |
||
| 1356 | |||
| 1357 | $result = $query->execute(array($id)); |
||
| 1358 | |||
| 1359 | $source = null; |
||
| 1360 | if (\OCP\DB::isError($result)) { |
||
| 1361 | \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); |
||
| 1362 | } else { |
||
| 1363 | $source = $result->fetchRow(); |
||
| 1364 | } |
||
| 1365 | |||
| 1366 | $fileOwner = false; |
||
| 1367 | |||
| 1368 | if ($source && isset($source['parent'])) { |
||
| 1369 | |||
| 1370 | $parent = $source['parent']; |
||
| 1371 | |||
| 1372 | while (isset($parent)) { |
||
| 1373 | |||
| 1374 | $query = \OCP\DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); |
||
| 1375 | |||
| 1376 | $result = $query->execute(array($parent)); |
||
| 1377 | |||
| 1378 | $item = null; |
||
| 1379 | if (\OCP\DB::isError($result)) { |
||
| 1380 | \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); |
||
| 1381 | } else { |
||
| 1382 | $item = $result->fetchRow(); |
||
| 1383 | } |
||
| 1384 | |||
| 1385 | if ($item && isset($item['parent'])) { |
||
| 1386 | |||
| 1387 | $parent = $item['parent']; |
||
| 1388 | |||
| 1389 | } else { |
||
| 1390 | |||
| 1391 | $fileOwner = $item['uid_owner']; |
||
| 1392 | |||
| 1393 | break; |
||
| 1394 | |||
| 1395 | } |
||
| 1396 | } |
||
| 1397 | |||
| 1398 | } else { |
||
| 1399 | |||
| 1400 | $fileOwner = $source['uid_owner']; |
||
| 1401 | |||
| 1402 | } |
||
| 1403 | |||
| 1404 | return $fileOwner; |
||
| 1405 | |||
| 1406 | } |
||
| 1407 | |||
| 1408 | /** |
||
| 1409 | * @return string |
||
| 1410 | */ |
||
| 1411 | public function getUserId() { |
||
| 1412 | return $this->userId; |
||
| 1413 | } |
||
| 1414 | |||
| 1415 | /** |
||
| 1416 | * @return string |
||
| 1417 | */ |
||
| 1418 | public function getKeyId() { |
||
| 1419 | return $this->keyId; |
||
| 1420 | } |
||
| 1421 | |||
| 1422 | /** |
||
| 1423 | * @return string |
||
| 1424 | */ |
||
| 1425 | public function getUserFilesDir() { |
||
| 1426 | return $this->userFilesDir; |
||
| 1427 | } |
||
| 1428 | |||
| 1429 | /** |
||
| 1430 | * @param string $password |
||
| 1431 | * @return bool |
||
| 1432 | */ |
||
| 1433 | public function checkRecoveryPassword($password) { |
||
| 1434 | |||
| 1435 | $result = false; |
||
| 1436 | |||
| 1437 | $recoveryKey = Keymanager::getPrivateSystemKey($this->recoveryKeyId); |
||
| 1438 | $decryptedRecoveryKey = Crypt::decryptPrivateKey($recoveryKey, $password); |
||
| 1439 | |||
| 1440 | if ($decryptedRecoveryKey) { |
||
| 1441 | $result = true; |
||
| 1442 | } |
||
| 1443 | |||
| 1444 | return $result; |
||
| 1445 | } |
||
| 1446 | |||
| 1447 | /** |
||
| 1448 | * @return string |
||
| 1449 | */ |
||
| 1450 | public function getRecoveryKeyId() { |
||
| 1451 | return $this->recoveryKeyId; |
||
| 1452 | } |
||
| 1453 | |||
| 1454 | /** |
||
| 1455 | * add recovery key to all encrypted files |
||
| 1456 | */ |
||
| 1457 | public function addRecoveryKeys($path = '/') { |
||
| 1458 | $dirContent = $this->view->getDirectoryContent($this->keysPath . '/' . $path); |
||
| 1459 | foreach ($dirContent as $item) { |
||
| 1460 | // get relative path from files_encryption/keyfiles/ |
||
| 1461 | $filePath = substr($item['path'], strlen('files_encryption/keys')); |
||
| 1462 | if ($this->view->is_dir($this->userFilesDir . '/' . $filePath)) { |
||
| 1463 | $this->addRecoveryKeys($filePath . '/'); |
||
| 1464 | } else { |
||
| 1465 | $session = new Session(new \OC\Files\View('/')); |
||
| 1466 | $sharingEnabled = \OCP\Share::isEnabled(); |
||
| 1467 | $usersSharing = $this->getSharingUsersArray($sharingEnabled, $filePath); |
||
| 1468 | $this->setSharedFileKeyfiles($session, $usersSharing, $filePath); |
||
| 1469 | } |
||
| 1470 | } |
||
| 1471 | } |
||
| 1472 | |||
| 1473 | /** |
||
| 1474 | * remove recovery key to all encrypted files |
||
| 1475 | */ |
||
| 1476 | public function removeRecoveryKeys($path = '/') { |
||
| 1477 | $dirContent = $this->view->getDirectoryContent($this->keysPath . '/' . $path); |
||
| 1478 | foreach ($dirContent as $item) { |
||
| 1479 | // get relative path from files_encryption/keyfiles |
||
| 1480 | $filePath = substr($item['path'], strlen('files_encryption/keys')); |
||
| 1481 | if ($this->view->is_dir($this->userFilesDir . '/' . $filePath)) { |
||
| 1482 | $this->removeRecoveryKeys($filePath . '/'); |
||
| 1483 | } else { |
||
| 1484 | $this->view->unlink($this->keysPath . '/' . $filePath . '/' . $this->recoveryKeyId . '.shareKey'); |
||
| 1485 | } |
||
| 1486 | } |
||
| 1487 | } |
||
| 1488 | |||
| 1489 | /** |
||
| 1490 | * decrypt given file with recovery key and encrypt it again to the owner and his new key |
||
| 1491 | * @param string $file |
||
| 1492 | * @param string $privateKey recovery key to decrypt the file |
||
| 1493 | */ |
||
| 1494 | private function recoverFile($file, $privateKey) { |
||
| 1495 | |||
| 1496 | $sharingEnabled = \OCP\Share::isEnabled(); |
||
| 1497 | |||
| 1498 | // Find out who, if anyone, is sharing the file |
||
| 1499 | if ($sharingEnabled) { |
||
| 1500 | $result = \OCP\Share::getUsersSharingFile($file, $this->userId, true); |
||
| 1501 | $userIds = $result['users']; |
||
| 1502 | $userIds[] = $this->recoveryKeyId; |
||
| 1503 | if ($result['public']) { |
||
| 1504 | $userIds[] = $this->publicShareKeyId; |
||
| 1505 | } |
||
| 1506 | } else { |
||
| 1507 | $userIds = array( |
||
| 1508 | $this->userId, |
||
| 1509 | $this->recoveryKeyId |
||
| 1510 | ); |
||
| 1511 | } |
||
| 1512 | $filteredUids = $this->filterShareReadyUsers($userIds); |
||
| 1513 | |||
| 1514 | //decrypt file key |
||
| 1515 | $encKeyfile = Keymanager::getFileKey($this->view, $this, $file); |
||
| 1516 | $shareKey = Keymanager::getShareKey($this->view, $this->recoveryKeyId, $this, $file); |
||
| 1517 | $plainKeyfile = Crypt::multiKeyDecrypt($encKeyfile, $shareKey, $privateKey); |
||
| 1518 | // encrypt file key again to all users, this time with the new public key for the recovered use |
||
| 1519 | $userPubKeys = Keymanager::getPublicKeys($this->view, $filteredUids['ready']); |
||
| 1520 | $multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys); |
||
| 1521 | |||
| 1522 | Keymanager::setFileKey($this->view, $this, $file, $multiEncKey['data']); |
||
| 1523 | Keymanager::setShareKeys($this->view, $this, $file, $multiEncKey['keys']); |
||
| 1524 | |||
| 1525 | } |
||
| 1526 | |||
| 1527 | /** |
||
| 1528 | * collect all files and recover them one by one |
||
| 1529 | * @param string $path to look for files keys |
||
| 1530 | * @param string $privateKey private recovery key which is used to decrypt the files |
||
| 1531 | */ |
||
| 1532 | private function recoverAllFiles($path, $privateKey) { |
||
| 1533 | $dirContent = $this->view->getDirectoryContent($this->keysPath . '/' . $path); |
||
| 1534 | foreach ($dirContent as $item) { |
||
| 1535 | // get relative path from files_encryption/keyfiles |
||
| 1536 | $filePath = substr($item['path'], strlen('files_encryption/keys')); |
||
| 1537 | if ($this->view->is_dir($this->userFilesDir . '/' . $filePath)) { |
||
| 1538 | $this->recoverAllFiles($filePath . '/', $privateKey); |
||
| 1539 | } else { |
||
| 1540 | $this->recoverFile($filePath, $privateKey); |
||
| 1541 | } |
||
| 1542 | } |
||
| 1543 | } |
||
| 1544 | |||
| 1545 | /** |
||
| 1546 | * recover users files in case of password lost |
||
| 1547 | * @param string $recoveryPassword |
||
| 1548 | */ |
||
| 1549 | public function recoverUsersFiles($recoveryPassword) { |
||
| 1550 | |||
| 1551 | $encryptedKey = Keymanager::getPrivateSystemKey( $this->recoveryKeyId); |
||
| 1552 | $privateKey = Crypt::decryptPrivateKey($encryptedKey, $recoveryPassword); |
||
| 1553 | |||
| 1554 | $this->recoverAllFiles('/', $privateKey); |
||
| 1555 | } |
||
| 1556 | |||
| 1557 | /** |
||
| 1558 | * create a backup of all keys from the user |
||
| 1559 | * |
||
| 1560 | * @param string $purpose define the purpose of the backup, will be part of the backup folder name |
||
| 1561 | * @param boolean $timestamp (optional) should a timestamp be added, default true |
||
| 1562 | * @param boolean $includeUserKeys (optional) include users private-/public-key, default true |
||
| 1563 | */ |
||
| 1564 | public function backupAllKeys($purpose, $timestamp = true, $includeUserKeys = true) { |
||
| 1565 | $this->userId; |
||
| 1566 | $backupDir = $this->encryptionDir . '/backup.' . $purpose; |
||
| 1567 | $backupDir .= ($timestamp) ? '.' . date("Y-m-d_H-i-s") . '/' : '/'; |
||
| 1568 | $this->view->mkdir($backupDir); |
||
| 1569 | $this->view->copy($this->keysPath, $backupDir . 'keys/'); |
||
| 1570 | if ($includeUserKeys) { |
||
| 1571 | $this->view->copy($this->privateKeyPath, $backupDir . $this->userId . '.privateKey'); |
||
| 1572 | $this->view->copy($this->publicKeyPath, $backupDir . $this->userId . '.publicKey'); |
||
| 1573 | } |
||
| 1574 | } |
||
| 1575 | |||
| 1576 | /** |
||
| 1577 | * restore backup |
||
| 1578 | * |
||
| 1579 | * @param string $backup complete name of the backup |
||
| 1580 | * @return boolean |
||
| 1581 | */ |
||
| 1582 | public function restoreBackup($backup) { |
||
| 1583 | $backupDir = $this->encryptionDir . '/backup.' . $backup . '/'; |
||
| 1584 | |||
| 1585 | $fileKeysRestored = $this->view->rename($backupDir . 'keys', $this->encryptionDir . '/keys'); |
||
| 1586 | |||
| 1587 | $pubKeyRestored = $privKeyRestored = true; |
||
| 1588 | if ( |
||
| 1589 | $this->view->file_exists($backupDir . $this->userId . '.privateKey') && |
||
| 1590 | $this->view->file_exists($backupDir . $this->userId . '.privateKey') |
||
| 1591 | ) { |
||
| 1592 | |||
| 1593 | $pubKeyRestored = $this->view->rename($backupDir . $this->userId . '.publicKey', $this->publicKeyPath); |
||
| 1594 | $privKeyRestored = $this->view->rename($backupDir . $this->userId . '.privateKey', $this->privateKeyPath); |
||
| 1595 | } |
||
| 1596 | |||
| 1597 | if ($fileKeysRestored && $pubKeyRestored && $privKeyRestored) { |
||
| 1598 | $this->view->deleteAll($backupDir); |
||
| 1599 | |||
| 1600 | return true; |
||
| 1601 | } |
||
| 1602 | |||
| 1603 | return false; |
||
| 1604 | } |
||
| 1605 | |||
| 1606 | /** |
||
| 1607 | * delete backup |
||
| 1608 | * |
||
| 1609 | * @param string $backup complete name of the backup |
||
| 1610 | * @return boolean |
||
| 1611 | */ |
||
| 1612 | public function deleteBackup($backup) { |
||
| 1613 | $backupDir = $this->encryptionDir . '/backup.' . $backup . '/'; |
||
| 1614 | return $this->view->deleteAll($backupDir); |
||
| 1615 | } |
||
| 1616 | |||
| 1617 | /** |
||
| 1618 | * check if the file is stored on a system wide mount point |
||
| 1619 | * @param string $path relative to /data/user with leading '/' |
||
| 1620 | * @param string $uid |
||
| 1621 | * @return boolean |
||
| 1622 | */ |
||
| 1623 | public function isSystemWideMountPoint($path, $uid) { |
||
| 1637 | |||
| 1638 | /** |
||
| 1639 | * check if mount point is applicable to user |
||
| 1640 | * |
||
| 1641 | * @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups'] |
||
| 1642 | * @param string $uid |
||
| 1643 | * @return boolean |
||
| 1644 | */ |
||
| 1645 | protected function isMountPointApplicableToUser($mount, $uid) { |
||
| 1660 | |||
| 1661 | /** |
||
| 1662 | * decrypt private key and add it to the current session |
||
| 1663 | * @param array $params with 'uid' and 'password' |
||
| 1664 | * @return mixed session or false |
||
| 1665 | */ |
||
| 1666 | public function initEncryption($params) { |
||
| 1667 | |||
| 1668 | $session = new Session($this->view); |
||
| 1669 | |||
| 1670 | // we tried to initialize the encryption app for this session |
||
| 1671 | $session->setInitialized(Session::INIT_EXECUTED); |
||
| 1672 | |||
| 1673 | $encryptedKey = Keymanager::getPrivateKey($this->view, $params['uid']); |
||
| 1674 | |||
| 1675 | $privateKey = false; |
||
| 1676 | if ($encryptedKey) { |
||
| 1677 | $privateKey = Crypt::decryptPrivateKey($encryptedKey, $params['password']); |
||
| 1678 | } |
||
| 1679 | |||
| 1680 | if ($privateKey === false) { |
||
| 1681 | \OCP\Util::writeLog('Encryption library', 'Private key for user "' . $params['uid'] |
||
| 1682 | . '" is not valid! Maybe the user password was changed from outside if so please change it back to gain access', \OCP\Util::ERROR); |
||
| 1683 | return false; |
||
| 1684 | } |
||
| 1685 | |||
| 1686 | $session->setPrivateKey($privateKey); |
||
| 1687 | $session->setInitialized(Session::INIT_SUCCESSFUL); |
||
| 1688 | |||
| 1689 | return $session; |
||
| 1690 | } |
||
| 1691 | |||
| 1692 | /* |
||
| 1693 | * remove encryption related keys from the session |
||
| 1694 | */ |
||
| 1695 | public function closeEncryptionSession() { |
||
| 1696 | $session = new Session($this->view); |
||
| 1697 | $session->closeSession(); |
||
| 1698 | } |
||
| 1699 | |||
| 1700 | } |
||
| 1701 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: