diff --git a/config/config.sample.php b/config/config.sample.php index 380bc62016c5..da8329606d7b 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1240,6 +1240,11 @@ '.htaccess', '.user.ini', ), +/** + * The list of apps that are allowed to have no signature.json + */ +'integrity.ignore.missing.app.signature' => [], + /** * Define a default folder for shared files and folders other than root. */ diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php index e02a57e15ab1..e26649145179 100644 --- a/lib/private/IntegrityCheck/Checker.php +++ b/lib/private/IntegrityCheck/Checker.php @@ -26,6 +26,7 @@ namespace OC\IntegrityCheck; use OC\IntegrityCheck\Exceptions\InvalidSignatureException; +use OC\IntegrityCheck\Exceptions\MissingSignatureException; use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; @@ -316,6 +317,7 @@ public function writeCoreSignature(X509 $certificate, * @param boolean $force * @return array * @throws InvalidSignatureException + * @throws MissingSignatureException * @throws \Exception */ private function verify($signaturePath, $basePath, $certificateCN, $force = false) { @@ -325,7 +327,7 @@ private function verify($signaturePath, $basePath, $certificateCN, $force = fals $signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true); if(!is_array($signatureData)) { - throw new InvalidSignatureException('Signature data not found.'); + throw new MissingSignatureException('Signature data not found.'); } $expectedHashes = $signatureData['hashes']; @@ -485,6 +487,21 @@ private function getSystemValue($key, $default = '') { return $default; } + /** + * Get a list of apps that are allowed to have no signature.json + * @return string[] + */ + private function getIgnoredUnsignedApps() { + $ignoredUnsignedApps = $this->getSystemValue( + 'integrity.ignore.missing.app.signature', + [] + ); + if (is_array($ignoredUnsignedApps)===false) { + $ignoredUnsignedApps = []; + } + return $ignoredUnsignedApps; + } + /** * Sanity wrapper for getAppValue * @param string $key @@ -554,15 +571,27 @@ private function deleteAppValue($key) { */ public function verifyAppSignature($appId, $path = '', $force = false) { try { - if($path === '') { + if ($path === '') { $path = $this->appLocator->getAppPath($appId); } + $result = $this->verify( - $path . '/appinfo/signature.json', - $path, - $appId, - $force + $path . '/appinfo/signature.json', + $path, + $appId, + $force ); + } catch (MissingSignatureException $e) { + if (!in_array($appId, $this->getIgnoredUnsignedApps())) { + $result = [ + 'EXCEPTION' => [ + 'class' => get_class($e), + 'message' => $e->getMessage(), + ], + ]; + } else { + $result = []; + } } catch (\Exception $e) { $result = [ 'EXCEPTION' => [ diff --git a/lib/private/IntegrityCheck/Exceptions/MissingSignatureException.php b/lib/private/IntegrityCheck/Exceptions/MissingSignatureException.php new file mode 100644 index 000000000000..626a8154d87f --- /dev/null +++ b/lib/private/IntegrityCheck/Exceptions/MissingSignatureException.php @@ -0,0 +1,30 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\IntegrityCheck\Exceptions; + +/** + * Class MissingSignatureException + * + * @package OC\IntegrityCheck\Exceptions + */ +class MissingSignatureException extends InvalidSignatureException { +} diff --git a/tests/lib/IntegrityCheck/CheckerTest.php b/tests/lib/IntegrityCheck/CheckerTest.php index 454f1013c8ec..652b53c16f6b 100644 --- a/tests/lib/IntegrityCheck/CheckerTest.php +++ b/tests/lib/IntegrityCheck/CheckerTest.php @@ -138,20 +138,42 @@ public function testWriteAppSignature() { $this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa); } + public function testIgnoredAppSignatureWithoutSignatureData() { + $this->environmentHelper + ->expects($this->once()) + ->method('getChannel') + ->will($this->returnValue('stable')); + $configMap = [ + ['integrity.check.disabled', false, false], + ['integrity.ignore.missing.app.signature', [], ['SomeApp']] + ]; + $this->config + ->expects($this->any()) + ->method('getSystemValue') + ->will($this->returnValueMap($configMap)); + + $expected = []; + $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp')); + } + + public function testVerifyAppSignatureWithoutSignatureData() { $this->environmentHelper ->expects($this->once()) ->method('getChannel') ->will($this->returnValue('stable')); + $configMap = [ + ['integrity.check.disabled', false, false], + ['integrity.ignore.missing.app.signature', [], []] + ]; $this->config - ->expects($this->any()) - ->method('getSystemValue') - ->with('integrity.check.disabled', false) - ->will($this->returnValue(false)); + ->expects($this->any()) + ->method('getSystemValue') + ->will($this->returnValueMap($configMap)); $expected = [ 'EXCEPTION' => [ - 'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException', + 'class' => 'OC\IntegrityCheck\Exceptions\MissingSignatureException', 'message' => 'Signature data not found.', ], ]; @@ -626,7 +648,7 @@ public function testVerifyCoreSignatureWithoutSignatureData() { $expected = [ 'EXCEPTION' => [ - 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', + 'class' => 'OC\\IntegrityCheck\\Exceptions\\MissingSignatureException', 'message' => 'Signature data not found.', ], ];