From 75d111fdc1f30ad13d74a9824522c816b4fd5bbf Mon Sep 17 00:00:00 2001 From: Sujith H Date: Thu, 7 Mar 2019 16:32:13 +0530 Subject: [PATCH] Bypass signature mismatch in the encrypted file Bypass signature mismatch in the encrypted file. This would be handy when files are transferred to other users. In this changeset the HintException is not re-thrown. Instead it is catched and the exception is logged. Signed-off-by: Sujith H --- apps/files/lib/Command/TransferOwnership.php | 27 ++++++++++++++++++- .../Files/Storage/Wrapper/Encryption.php | 19 ++++++++++++- lib/private/Files/Stream/Encryption.php | 3 +++ .../Files/Storage/Wrapper/EncryptionTest.php | 11 +++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/apps/files/lib/Command/TransferOwnership.php b/apps/files/lib/Command/TransferOwnership.php index e91d2ef98175..5fe8743f1ecc 100644 --- a/apps/files/lib/Command/TransferOwnership.php +++ b/apps/files/lib/Command/TransferOwnership.php @@ -40,6 +40,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; class TransferOwnership extends Command { @@ -61,6 +63,9 @@ class TransferOwnership extends Command { /** @var ProviderFactory */ private $shareProviderFactory; + /** @var EventDispatcherInterface */ + private $eventDispatcher; + /** @var bool */ private $filesExist = false; @@ -85,13 +90,20 @@ class TransferOwnership extends Command { /** @var string */ private $finalTarget; - public function __construct(IUserManager $userManager, IManager $shareManager, IMountManager $mountManager, Manager $encryptionManager, ILogger $logger, ProviderFactory $shareProviderFactory) { + /** @var array , Files affected with signature mismatch */ + private $affectedFiles = []; + + public function __construct(IUserManager $userManager, IManager $shareManager, + IMountManager $mountManager, Manager $encryptionManager, + ILogger $logger, ProviderFactory $shareProviderFactory, + EventDispatcherInterface $eventDispatcher) { $this->userManager = $userManager; $this->shareManager = $shareManager; $this->mountManager = $mountManager; $this->encryptionManager = $encryptionManager; $this->logger = $logger; $this->shareProviderFactory = $shareProviderFactory; + $this->eventDispatcher = $eventDispatcher; parent::__construct(); } @@ -287,7 +299,20 @@ protected function transfer(OutputInterface $output) { } } + $this->eventDispatcher->addListener('files.aftersignaturemismatch', function (GenericEvent $event) { + if ($event->hasArgument('signatureMismatch') && $event->hasArgument('fileName')) { + $fileName = $event->getArgument('fileName'); + if (!isset($this->affectedFiles[$fileName])) { + $this->affectedFiles[$fileName] = 1; + } + } + }, 10); + $view->rename($sourcePath, $this->finalTarget); + if (\count($this->affectedFiles) >= 1) { + $glueString = (\count($this->affectedFiles) > 1) ? ", " : ""; + $output->writeln("The affected files are : " . \implode($glueString, \array_keys($this->affectedFiles)) . ""); + } if (!\is_dir("$this->sourceUser/files")) { // because the files folder is moved away we need to recreate it diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php index 2a9bff7e2fbd..a0668853ebc3 100644 --- a/lib/private/Files/Storage/Wrapper/Encryption.php +++ b/lib/private/Files/Storage/Wrapper/Encryption.php @@ -42,6 +42,8 @@ use OCP\Files\Storage; use OCP\ILogger; use OCP\Files\Cache\ICacheEntry; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; class Encryption extends Wrapper { use LocalTempFileTrait; @@ -85,6 +87,9 @@ class Encryption extends Wrapper { /** @var ArrayCache */ private $arrayCache; + /** @var EventDispatcherInterface */ + private $eventDispatcher; + /** @var array which has information of sourcePath during rename operation */ private $sourcePath; @@ -112,7 +117,8 @@ public function __construct( IStorage $keyStorage = null, Update $update = null, Manager $mountManager = null, - ArrayCache $arrayCache = null + ArrayCache $arrayCache = null, + EventDispatcherInterface $eventDispatcher = null ) { $this->mountPoint = $parameters['mountPoint']; $this->mount = $parameters['mount']; @@ -126,6 +132,7 @@ public function __construct( $this->update = $update; $this->mountManager = $mountManager; $this->arrayCache = $arrayCache; + $this->eventDispatcher = ($eventDispatcher === null) ? \OC::$server->getEventDispatcher() : $eventDispatcher; parent::__construct($parameters); } @@ -774,6 +781,13 @@ private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInte } } else { try { + $signatureMismatch = false; + $this->eventDispatcher->addListener('files.aftersignaturemismatch', function (GenericEvent $event) use ($sourceInternalPath, &$signatureMismatch) { + if ($event->hasArgument('signatureMismatch') && ($event->getArgument('signatureMismatch') === true)) { + $event->setArgument('fileName', $sourceInternalPath); + $signatureMismatch = true; + } + }, 20); $source = $sourceStorage->fopen($sourceInternalPath, 'r'); if ($isRename && (\count($mount) === 1)) { $sourceStorageMountPoint = $mount[0]->getMountPoint(); @@ -785,6 +799,9 @@ private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInte list(, $result) = \OC_Helper::streamCopy($source, $target); \fclose($source); \fclose($target); + if ($signatureMismatch === true) { + $this->logger->warning("The file $sourceInternalPath has signature mismatch"); + } } catch (\Exception $e) { Encryption::setDisableWriteEncryption(false); \fclose($source); diff --git a/lib/private/Files/Stream/Encryption.php b/lib/private/Files/Stream/Encryption.php index f59ba9d641ad..6e66712dfc43 100644 --- a/lib/private/Files/Stream/Encryption.php +++ b/lib/private/Files/Stream/Encryption.php @@ -28,6 +28,7 @@ use Icewind\Streams\Wrapper; use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException; +use Symfony\Component\EventDispatcher\GenericEvent; class Encryption extends Wrapper { @@ -281,6 +282,7 @@ public function stream_eof() { public function stream_read($count) { $result = ''; + $genericEvent = new GenericEvent('SignatureMismatch', []); $count = \min($count, $this->unencryptedSize - $this->position); while ($count > 0) { @@ -302,6 +304,7 @@ public function stream_read($count) { $count -= ($this->unencryptedBlockSize - $blockPosition); } } + \OC::$server->getEventDispatcher()->dispatch('files.aftersignaturemismatch', $genericEvent); return $result; } diff --git a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php index f2fdca552911..ed55545b58ae 100644 --- a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php +++ b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php @@ -7,6 +7,8 @@ use OC\Files\View; use OC\User\Manager; use OCP\Files\Storage\IStorage; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; use Test\Files\Storage\Storage; use OC\Files\Storage\Wrapper\Encryption; use OC\Files\Cache\Cache; @@ -101,6 +103,9 @@ class EncryptionTest extends Storage { /** @var \OC\Memcache\ArrayCache | \PHPUnit_Framework_MockObject_MockObject */ private $arrayCache; + /** @var EventDispatcherInterface | \PHPUnit_Framework_MockObject_MockObject */ + private $eventDisaptcher; + /** @var integer dummy unencrypted size */ private $dummySize = -1; @@ -178,6 +183,8 @@ protected function setUp() { ->disableOriginalConstructor()->getMock(); $this->mountManager->expects($this->any())->method('findByStorageId')->willReturn([]); + $this->eventDisaptcher = $this->createMock(EventDispatcherInterface::class); + $this->instance = $this->getMockBuilder(Encryption::class) ->setConstructorArgs( [ @@ -187,7 +194,9 @@ protected function setUp() { 'mountPoint' => '/', 'mount' => $this->mount ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache + $this->encryptionManager, $this->util, $this->logger, + $this->file, null, $this->keyStore, $this->update, + $this->mountManager, $this->arrayCache, $this->eventDisaptcher ] ) ->setMethods(['getMetaData', 'getCache', 'getEncryptionModule'])