Skip to content

Commit e2861b4

Browse files
committed
fix(encryption): Correctly handle file opening and copying failures
Signed-off-by: Côme Chilliet <[email protected]>
1 parent eb91798 commit e2861b4

File tree

2 files changed

+39
-18
lines changed

2 files changed

+39
-18
lines changed

apps/encryption/lib/Crypto/EncryptAll.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,14 @@ protected function encryptFile($path) {
249249
$target = $path . '.encrypted.' . time();
250250

251251
try {
252-
$this->rootView->copy($source, $target);
252+
$copySuccess = $this->rootView->copy($source, $target);
253+
if ($copySuccess === false) {
254+
/* Copy failed, abort */
255+
if ($this->rootView->file_exists($target)) {
256+
$this->rootView->unlink($target);
257+
}
258+
throw new \Exception('Copy failed for ' . $source);
259+
}
253260
$this->rootView->rename($target, $source);
254261
} catch (DecryptionFailedException $e) {
255262
if ($this->rootView->file_exists($target)) {

lib/private/Files/Storage/Wrapper/Encryption.php

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use OCP\Encryption\IManager;
2424
use OCP\Encryption\Keys\IStorage;
2525
use OCP\Files\Cache\ICacheEntry;
26+
use OCP\Files\GenericFileException;
2627
use OCP\Files\Mount\IMountPoint;
2728
use OCP\Files\Storage;
2829
use Psr\Log\LoggerInterface;
@@ -185,11 +186,11 @@ public function unlink(string $path): bool {
185186
public function rename(string $source, string $target): bool {
186187
$result = $this->storage->rename($source, $target);
187188

188-
if ($result &&
189+
if ($result
189190
// versions always use the keys from the original file, so we can skip
190191
// this step for versions
191-
$this->isVersion($target) === false &&
192-
$this->encryptionManager->isEnabled()) {
192+
&& $this->isVersion($target) === false
193+
&& $this->encryptionManager->isEnabled()) {
193194
$sourcePath = $this->getFullPath($source);
194195
if (!$this->util->isExcluded($sourcePath)) {
195196
$targetPath = $this->getFullPath($target);
@@ -210,9 +211,9 @@ public function rename(string $source, string $target): bool {
210211
public function rmdir(string $path): bool {
211212
$result = $this->storage->rmdir($path);
212213
$fullPath = $this->getFullPath($path);
213-
if ($result &&
214-
$this->util->isExcluded($fullPath) === false &&
215-
$this->encryptionManager->isEnabled()
214+
if ($result
215+
&& $this->util->isExcluded($fullPath) === false
216+
&& $this->encryptionManager->isEnabled()
216217
) {
217218
$this->keyStorage->deleteAllFileKeys($fullPath);
218219
}
@@ -225,9 +226,9 @@ public function isReadable(string $path): bool {
225226

226227
$metaData = $this->getMetaData($path);
227228
if (
228-
!$this->is_dir($path) &&
229-
isset($metaData['encrypted']) &&
230-
$metaData['encrypted'] === true
229+
!$this->is_dir($path)
230+
&& isset($metaData['encrypted'])
231+
&& $metaData['encrypted'] === true
231232
) {
232233
$fullPath = $this->getFullPath($path);
233234
$module = $this->getEncryptionModule($path);
@@ -384,9 +385,9 @@ protected function verifyUnencryptedSize(string $path, int $unencryptedSize): in
384385
$size = $this->storage->filesize($path);
385386
$result = $unencryptedSize;
386387

387-
if ($unencryptedSize < 0 ||
388-
($size > 0 && $unencryptedSize === $size) ||
389-
$unencryptedSize > $size
388+
if ($unencryptedSize < 0
389+
|| ($size > 0 && $unencryptedSize === $size)
390+
|| $unencryptedSize > $size
390391
) {
391392
// check if we already calculate the unencrypted size for the
392393
// given path to avoid recursions
@@ -622,8 +623,8 @@ private function copyBetweenStorage(
622623
): bool {
623624
// for versions we have nothing to do, because versions should always use the
624625
// key from the original file. Just create a 1:1 copy and done
625-
if ($this->isVersion($targetInternalPath) ||
626-
$this->isVersion($sourceInternalPath)) {
626+
if ($this->isVersion($targetInternalPath)
627+
|| $this->isVersion($sourceInternalPath)) {
627628
// remember that we try to create a version so that we can detect it during
628629
// fopen($sourceInternalPath) and by-pass the encryption in order to
629630
// create a 1:1 copy of the file
@@ -673,12 +674,16 @@ private function copyBetweenStorage(
673674
try {
674675
$source = $sourceStorage->fopen($sourceInternalPath, 'r');
675676
$target = $this->fopen($targetInternalPath, 'w');
676-
[, $result] = \OC_Helper::streamCopy($source, $target);
677+
if ($source === false || $target === false) {
678+
$result = false;
679+
} else {
680+
[, $result] = \OC_Helper::streamCopy($source, $target);
681+
}
677682
} finally {
678-
if (is_resource($source)) {
683+
if (isset($source) && $source !== false) {
679684
fclose($source);
680685
}
681-
if (is_resource($target)) {
686+
if (isset($target) && $target !== false) {
682687
fclose($target);
683688
}
684689
}
@@ -728,6 +733,9 @@ public function stat(string $path): array|false {
728733

729734
public function hash(string $type, string $path, bool $raw = false): string|false {
730735
$fh = $this->fopen($path, 'rb');
736+
if ($fh === false) {
737+
return false;
738+
}
731739
$ctx = hash_init($type);
732740
hash_update_stream($ctx, $fh);
733741
fclose($fh);
@@ -752,6 +760,9 @@ protected function readFirstBlock(string $path): string {
752760
$firstBlock = '';
753761
if ($this->storage->is_file($path)) {
754762
$handle = $this->storage->fopen($path, 'r');
763+
if ($handle === false) {
764+
return '';
765+
}
755766
$firstBlock = fread($handle, $this->util->getHeaderSize());
756767
fclose($handle);
757768
}
@@ -882,6 +893,9 @@ protected function shouldEncrypt(string $path): bool {
882893
public function writeStream(string $path, $stream, ?int $size = null): int {
883894
// always fall back to fopen
884895
$target = $this->fopen($path, 'w');
896+
if ($target === false) {
897+
throw new GenericFileException("Failed to open $path for writing");
898+
}
885899
[$count, $result] = \OC_Helper::streamCopy($stream, $target);
886900
fclose($stream);
887901
fclose($target);

0 commit comments

Comments
 (0)