Skip to content

Commit 6a40066

Browse files
committed
fix(encryption): Correctly handle file opening and copying failures
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
1 parent b45a006 commit 6a40066

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\Keys\IStorage;
2424
use OCP\Files;
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
@@ -634,8 +635,8 @@ private function copyBetweenStorage(
634635
): bool {
635636
// for versions we have nothing to do, because versions should always use the
636637
// key from the original file. Just create a 1:1 copy and done
637-
if ($this->isVersion($targetInternalPath) ||
638-
$this->isVersion($sourceInternalPath)) {
638+
if ($this->isVersion($targetInternalPath)
639+
|| $this->isVersion($sourceInternalPath)) {
639640
// remember that we try to create a version so that we can detect it during
640641
// fopen($sourceInternalPath) and by-pass the encryption in order to
641642
// create a 1:1 copy of the file
@@ -685,12 +686,16 @@ private function copyBetweenStorage(
685686
try {
686687
$source = $sourceStorage->fopen($sourceInternalPath, 'r');
687688
$target = $this->fopen($targetInternalPath, 'w');
688-
[, $result] = Files::streamCopy($source, $target, true);
689+
if ($source === false || $target === false) {
690+
$result = false;
691+
} else {
692+
[, $result] = Files::streamCopy($source, $target, true);
693+
}
689694
} finally {
690-
if (is_resource($source)) {
695+
if (isset($source) && $source !== false) {
691696
fclose($source);
692697
}
693-
if (is_resource($target)) {
698+
if (isset($target) && $target !== false) {
694699
fclose($target);
695700
}
696701
}
@@ -740,6 +745,9 @@ public function stat(string $path): array|false {
740745

741746
public function hash(string $type, string $path, bool $raw = false): string|false {
742747
$fh = $this->fopen($path, 'rb');
748+
if ($fh === false) {
749+
return false;
750+
}
743751
$ctx = hash_init($type);
744752
hash_update_stream($ctx, $fh);
745753
fclose($fh);
@@ -764,6 +772,9 @@ protected function readFirstBlock(string $path): string {
764772
$firstBlock = '';
765773
if ($this->storage->is_file($path)) {
766774
$handle = $this->storage->fopen($path, 'r');
775+
if ($handle === false) {
776+
return '';
777+
}
767778
$firstBlock = fread($handle, $this->util->getHeaderSize());
768779
fclose($handle);
769780
}
@@ -894,6 +905,9 @@ protected function shouldEncrypt(string $path): bool {
894905
public function writeStream(string $path, $stream, ?int $size = null): int {
895906
// always fall back to fopen
896907
$target = $this->fopen($path, 'w');
908+
if ($target === false) {
909+
throw new GenericFileException("Failed to open $path for writing");
910+
}
897911
[$count, $result] = Files::streamCopy($stream, $target, true);
898912
fclose($stream);
899913
fclose($target);

0 commit comments

Comments
 (0)