Merge pull request #20285 from owncloud/add-integrity-checker

Add code integrity checker foundation
This commit is contained in:
Thomas Müller 2015-12-01 15:27:50 +01:00
commit 74e8c25a5b
43 changed files with 3262 additions and 11 deletions

View File

@ -47,6 +47,7 @@ if (OC::checkUpgrade(false)) {
$updater = new \OC\Updater(
\OC::$server->getHTTPHelper(),
$config,
\OC::$server->getIntegrityCodeChecker(),
$logger
);
$incompatibleApps = [];
@ -108,6 +109,12 @@ if (OC::checkUpgrade(false)) {
$updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Reset log level to "%s"', [ $logLevelName ]));
});
$updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Starting code integrity check'));
});
$updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Finished code integrity check'));
});
try {
$updater->upgrade();

View File

@ -0,0 +1,98 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\Core\Command\Integrity;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use phpseclib\Crypt\RSA;
use phpseclib\File\X509;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class SignApp
*
* @package OC\Core\Command\Integrity
*/
class SignApp extends Command {
/** @var Checker */
private $checker;
/** @var FileAccessHelper */
private $fileAccessHelper;
/**
* @param Checker $checker
* @param FileAccessHelper $fileAccessHelper
*/
public function __construct(Checker $checker,
FileAccessHelper $fileAccessHelper) {
parent::__construct(null);
$this->checker = $checker;
$this->fileAccessHelper = $fileAccessHelper;
}
protected function configure() {
$this
->setName('integrity:sign-app')
->setDescription('Sign app using a private key.')
->addOption('appId', null, InputOption::VALUE_REQUIRED, 'Application to sign')
->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
}
/**
* {@inheritdoc }
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$appId = $input->getOption('appId');
$privateKeyPath = $input->getOption('privateKey');
$keyBundlePath = $input->getOption('certificate');
if(is_null($appId) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
$output->writeln('--appId, --privateKey and --certificate are required.');
return null;
}
$privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
$keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
if($privateKey === false) {
$output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
return null;
}
if($keyBundle === false) {
$output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
return null;
}
$rsa = new RSA();
$rsa->loadKey($privateKey);
$x509 = new X509();
$x509->loadX509($keyBundle);
$x509->setPrivateKey($rsa);
$this->checker->writeAppSignature($appId, $x509, $rsa);
$output->writeln('Successfully signed "'.$appId.'"');
}
}

View File

@ -0,0 +1,98 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\Core\Command\Integrity;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use phpseclib\Crypt\RSA;
use phpseclib\File\X509;
use Symfony\Component\Console\Command\Command;
use OCP\IConfig;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class SignCore
*
* @package OC\Core\Command\Integrity
*/
class SignCore extends Command {
/** @var Checker */
private $checker;
/** @var FileAccessHelper */
private $fileAccessHelper;
/**
* @param Checker $checker
* @param FileAccessHelper $fileAccessHelper
*/
public function __construct(Checker $checker,
FileAccessHelper $fileAccessHelper) {
parent::__construct(null);
$this->checker = $checker;
$this->fileAccessHelper = $fileAccessHelper;
}
protected function configure() {
$this
->setName('integrity:sign-core')
->setDescription('Sign core using a private key.')
->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
}
/**
* {@inheritdoc }
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$privateKeyPath = $input->getOption('privateKey');
$keyBundlePath = $input->getOption('certificate');
if(is_null($privateKeyPath) || is_null($keyBundlePath)) {
$output->writeln('--privateKey and --certificate are required.');
return null;
}
$privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
$keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
if($privateKey === false) {
$output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
return null;
}
if($keyBundle === false) {
$output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
return null;
}
$rsa = new RSA();
$rsa->loadKey($privateKey);
$x509 = new X509();
$x509->loadX509($keyBundle);
$x509->setPrivateKey($rsa);
$this->checker->writeCoreSignature($x509, $rsa);
$output->writeln('Successfully signed "core"');
}
}

View File

@ -53,6 +53,7 @@ class Upgrade extends Command {
/**
* @param IConfig $config
* @param ILogger $logger
*/
public function __construct(IConfig $config, ILogger $logger) {
parent::__construct();
@ -122,9 +123,12 @@ class Upgrade extends Command {
}
$self = $this;
$updater = new Updater(\OC::$server->getHTTPHelper(),
$this->config,
$this->logger);
$updater = new Updater(
\OC::$server->getHTTPHelper(),
$this->config,
\OC::$server->getIntegrityCodeChecker(),
$this->logger
);
$updater->setSimulateStepEnabled($simulateStepEnabled);
$updater->setUpdateStepEnabled($updateStepEnabled);

View File

@ -0,0 +1,38 @@
/**
* @author Lukas Reschke
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
*
* 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 <http://www.gnu.org/licenses/>
*/
/**
* This gets only loaded if the integrity check has failed and then shows a notification
*/
$(document).ready(function(){
var text = t(
'core',
'<a href="{docUrl}">There were problems with the code integrity check. More information…</a>',
{
docUrl: OC.generateUrl('/settings/admin#security-warning')
}
);
OC.Notification.showHtml(
text,
{
isHTML: true
}
);
});

View File

@ -104,6 +104,20 @@
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
if(!data.hasPassedCodeIntegrityCheck) {
messages.push({
msg: t(
'core',
'Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a href="{docLink}">documentation</a>. (<a href="{codeIntegrityDownloadEndpoint}">List of invalid files…</a> / <a href="{rescanEndpoint}">Rescan…</a>)',
{
docLink: data.codeIntegrityCheckerDocumentation,
codeIntegrityDownloadEndpoint: OC.generateUrl('/settings/integrity/failed'),
rescanEndpoint: OC.generateUrl('/settings/integrity/rescan?requesttoken={requesttoken}', {'requesttoken': OC.requestToken})
}
),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
} else {
messages.push({
msg: t('core', 'Error occurred while checking server setup'),

View File

@ -75,6 +75,7 @@ describe('OC.SetupChecks tests', function() {
memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
hasPassedCodeIntegrityCheck: true,
})
);
@ -109,6 +110,7 @@ describe('OC.SetupChecks tests', function() {
memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
hasPassedCodeIntegrityCheck: true,
})
);
@ -145,6 +147,7 @@ describe('OC.SetupChecks tests', function() {
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
hasPassedCodeIntegrityCheck: true,
})
);
@ -178,6 +181,7 @@ describe('OC.SetupChecks tests', function() {
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
hasPassedCodeIntegrityCheck: true,
})
);
@ -206,6 +210,7 @@ describe('OC.SetupChecks tests', function() {
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: false,
hasPassedCodeIntegrityCheck: true,
})
);
@ -234,6 +239,7 @@ describe('OC.SetupChecks tests', function() {
forwardedForHeadersWorking: false,
reverseProxyDocs: 'https://docs.owncloud.org/foo/bar.html',
isCorrectMemcachedPHPModuleInstalled: true,
hasPassedCodeIntegrityCheck: true,
})
);
@ -283,6 +289,7 @@ describe('OC.SetupChecks tests', function() {
forwardedForHeadersWorking: true,
phpSupported: {eol: true, version: '5.4.0'},
isCorrectMemcachedPHPModuleInstalled: true,
hasPassedCodeIntegrityCheck: true,
})
);

View File

@ -32,6 +32,14 @@ $application->add(new OC\Core\Command\Check(\OC::$server->getConfig()));
$infoParser = new \OC\App\InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator());
$application->add(new OC\Core\Command\App\CheckCode($infoParser));
$application->add(new OC\Core\Command\L10n\CreateJs());
$application->add(new \OC\Core\Command\Integrity\SignApp(
\OC::$server->getIntegrityCodeChecker(),
new \OC\IntegrityCheck\Helpers\FileAccessHelper()
));
$application->add(new \OC\Core\Command\Integrity\SignCore(
\OC::$server->getIntegrityCodeChecker(),
new \OC\IntegrityCheck\Helpers\FileAccessHelper()
));
if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\App\Disable());

View File

@ -0,0 +1,449 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck;
use OC\IntegrityCheck\Exceptions\InvalidSignatureException;
use OC\IntegrityCheck\Helpers\AppLocator;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use OC\Integritycheck\Iterator\ExcludeFileByNameFilterIterator;
use OC\IntegrityCheck\Iterator\ExcludeFoldersByPathFilterIterator;
use OCP\App\IAppManager;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use phpseclib\Crypt\RSA;
use phpseclib\File\X509;
/**
* Class Checker handles the code signing using X.509 and RSA. ownCloud ships with
* a public root certificate certificate that allows to issue new certificates that
* will be trusted for signing code. The CN will be used to verify that a certificate
* given to a third-party developer may not be used for other applications. For
* example the author of the application "calendar" would only receive a certificate
* only valid for this application.
*
* @package OC\IntegrityCheck
*/
class Checker {
const CACHE_KEY = 'oc.integritycheck.checker';
/** @var EnvironmentHelper */
private $environmentHelper;
/** @var AppLocator */
private $appLocator;
/** @var FileAccessHelper */
private $fileAccessHelper;
/** @var IConfig */
private $config;
/** @var ICache */
private $cache;
/** @var IAppManager */
private $appManager;
/**
* @param EnvironmentHelper $environmentHelper
* @param FileAccessHelper $fileAccessHelper
* @param AppLocator $appLocator
* @param IConfig $config
* @param ICacheFactory $cacheFactory
* @param IAppManager $appManager
*/
public function __construct(EnvironmentHelper $environmentHelper,
FileAccessHelper $fileAccessHelper,
AppLocator $appLocator,
IConfig $config = null,
ICacheFactory $cacheFactory,
IAppManager $appManager = null) {
$this->environmentHelper = $environmentHelper;
$this->fileAccessHelper = $fileAccessHelper;
$this->appLocator = $appLocator;
$this->config = $config;
$this->cache = $cacheFactory->create(self::CACHE_KEY);
$this->appManager = $appManager;
}
/**
* Enumerates all files belonging to the folder. Sensible defaults are excluded.
*
* @param string $folderToIterate
* @return \RecursiveIteratorIterator
*/
private function getFolderIterator($folderToIterate) {
$dirItr = new \RecursiveDirectoryIterator(
$folderToIterate,
\RecursiveDirectoryIterator::SKIP_DOTS
);
$excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
$excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator);
return new \RecursiveIteratorIterator(
$excludeFoldersIterator,
\RecursiveIteratorIterator::SELF_FIRST
);
}
/**
* Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
* in the iterator.
*
* @param \RecursiveIteratorIterator $iterator
* @param string $path
* @return array Array of hashes.
*/
private function generateHashes(\RecursiveIteratorIterator $iterator,
$path) {
$hashes = [];
$baseDirectoryLength = strlen($path);
foreach($iterator as $filename => $data) {
/** @var \DirectoryIterator $data */
if($data->isDir()) {
continue;
}
$relativeFileName = substr($filename, $baseDirectoryLength);
// Exclude signature.json files in the appinfo and root folder
if($relativeFileName === '/appinfo/signature.json') {
continue;
}
// Exclude signature.json files in the appinfo and core folder
if($relativeFileName === '/core/signature.json') {
continue;
}
$hashes[$relativeFileName] = hash_file('sha512', $filename);
}
return $hashes;
}
/**
* Creates the signature data
*
* @param array $hashes
* @param X509 $certificate
* @param RSA $privateKey
* @return string
*/
private function createSignatureData(array $hashes,
X509 $certificate,
RSA $privateKey) {
ksort($hashes);
$privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
$privateKey->setMGFHash('sha512');
$signature = $privateKey->sign(json_encode($hashes));
return [
'hashes' => $hashes,
'signature' => base64_encode($signature),
'certificate' => $certificate->saveX509($certificate->currentCert),
];
}
/**
* Write the signature of the specified app
*
* @param string $appId
* @param X509 $certificate
* @param RSA $privateKey
* @throws \Exception
*/
public function writeAppSignature($appId,
X509 $certificate,
RSA $privateKey) {
$path = $this->appLocator->getAppPath($appId);
$iterator = $this->getFolderIterator($path);
$hashes = $this->generateHashes($iterator, $path);
$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
$this->fileAccessHelper->file_put_contents(
$path . '/appinfo/signature.json',
json_encode($signature, JSON_PRETTY_PRINT)
);
}
/**
* Write the signature of core
*
* @param X509 $certificate
* @param RSA $rsa
*/
public function writeCoreSignature(X509 $certificate,
RSA $rsa) {
$iterator = $this->getFolderIterator($this->environmentHelper->getServerRoot());
$hashes = $this->generateHashes($iterator, $this->environmentHelper->getServerRoot());
$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
$this->fileAccessHelper->file_put_contents(
$this->environmentHelper->getServerRoot() . '/core/signature.json',
json_encode($signatureData, JSON_PRETTY_PRINT)
);
}
/**
* Verifies the signature for the specified path.
*
* @param string $signaturePath
* @param string $basePath
* @param string $certificateCN
* @return array
* @throws InvalidSignatureException
* @throws \Exception
*/
private function verify($signaturePath, $basePath, $certificateCN) {
$signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true);
if(!is_array($signatureData)) {
throw new InvalidSignatureException('Signature data not found.');
}
$expectedHashes = $signatureData['hashes'];
ksort($expectedHashes);
$signature = base64_decode($signatureData['signature']);
$certificate = $signatureData['certificate'];
// Check if certificate is signed by ownCloud Root Authority
$x509 = new \phpseclib\File\X509();
$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
$x509->loadCA($rootCertificatePublicKey);
$x509->loadX509($certificate);
if(!$x509->validateSignature()) {
throw new InvalidSignatureException('Certificate is not valid.');
}
// Verify if certificate has proper CN. "core" CN is always trusted.
if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
throw new InvalidSignatureException(
sprintf('Certificate is not valid for required scope. (Requested: %s, current: %s)', $certificateCN, $x509->getDN(true))
);
}
// Check if the signature of the files is valid
$rsa = new \phpseclib\Crypt\RSA();
$rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
$rsa->setMGFHash('sha512');
if(!$rsa->verify(json_encode($expectedHashes), $signature)) {
throw new InvalidSignatureException('Signature could not get verified.');
}
// Compare the list of files which are not identical
$currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
$differencesA = array_diff($expectedHashes, $currentInstanceHashes);
$differencesB = array_diff($currentInstanceHashes, $expectedHashes);
$differences = array_unique(array_merge($differencesA, $differencesB));
$differenceArray = [];
foreach($differences as $filename => $hash) {
// Check if file should not exist in the new signature table
if(!array_key_exists($filename, $expectedHashes)) {
$differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
$differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
continue;
}
// Check if file is missing
if(!array_key_exists($filename, $currentInstanceHashes)) {
$differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
$differenceArray['FILE_MISSING'][$filename]['current'] = '';
continue;
}
// Check if hash does mismatch
if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
$differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
$differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
continue;
}
// Should never happen.
throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
}
return $differenceArray;
}
/**
* Whether the code integrity check has passed successful or not
*
* @return bool
*/
public function hasPassedCheck() {
$results = $this->getResults();
if(empty($results)) {
return true;
}
return false;
}
/**
* @return array
*/
public function getResults() {
$cachedResults = $this->cache->get(self::CACHE_KEY);
if(!is_null($cachedResults)) {
return json_decode($cachedResults, true);
}
return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true);
}
/**
* Stores the results in the app config as well as cache
*
* @param string $scope
* @param array $result
*/
private function storeResults($scope, array $result) {
$resultArray = $this->getResults();
unset($resultArray[$scope]);
if(!empty($result)) {
$resultArray[$scope] = $result;
}
$this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray));
$this->cache->set(self::CACHE_KEY, json_encode($resultArray));
}
/**
* Verify the signature of $appId. Returns an array with the following content:
* [
* 'FILE_MISSING' =>
* [
* 'filename' => [
* 'expected' => 'expectedSHA512',
* 'current' => 'currentSHA512',
* ],
* ],
* 'EXTRA_FILE' =>
* [
* 'filename' => [
* 'expected' => 'expectedSHA512',
* 'current' => 'currentSHA512',
* ],
* ],
* 'INVALID_HASH' =>
* [
* 'filename' => [
* 'expected' => 'expectedSHA512',
* 'current' => 'currentSHA512',
* ],
* ],
* ]
*
* Array may be empty in case no problems have been found.
*
* @param string $appId
* @return array
*/
public function verifyAppSignature($appId) {
try {
$path = $this->appLocator->getAppPath($appId);
$result = $this->verify(
$path . '/appinfo/signature.json',
$path,
$appId
);
} catch (\Exception $e) {
$result = [
'EXCEPTION' => [
'class' => get_class($e),
'message' => $e->getMessage(),
],
];
}
$this->storeResults($appId, $result);
return $result;
}
/**
* Verify the signature of core. Returns an array with the following content:
* [
* 'FILE_MISSING' =>
* [
* 'filename' => [
* 'expected' => 'expectedSHA512',
* 'current' => 'currentSHA512',
* ],
* ],
* 'EXTRA_FILE' =>
* [
* 'filename' => [
* 'expected' => 'expectedSHA512',
* 'current' => 'currentSHA512',
* ],
* ],
* 'INVALID_HASH' =>
* [
* 'filename' => [
* 'expected' => 'expectedSHA512',
* 'current' => 'currentSHA512',
* ],
* ],
* ]
*
* Array may be empty in case no problems have been found.
*
* @return array
*/
public function verifyCoreSignature() {
try {
$result = $this->verify(
$this->environmentHelper->getServerRoot() . '/core/signature.json',
$this->environmentHelper->getServerRoot(),
'core'
);
} catch (\Exception $e) {
$result = [
'EXCEPTION' => [
'class' => get_class($e),
'message' => $e->getMessage(),
],
];
}
$this->storeResults('core', $result);
return $result;
}
/**
* Verify the core code of the instance as well as all applicable applications
* and store the results.
*/
public function runInstanceVerification() {
$this->verifyCoreSignature();
$appIds = $this->appLocator->getAllApps();
foreach($appIds as $appId) {
// If an application is shipped a valid signature is required
$isShipped = $this->appManager->isShipped($appId);
$appNeedsToBeChecked = false;
if ($isShipped) {
$appNeedsToBeChecked = true;
} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
// Otherwise only if the application explicitly ships a signature.json file
$appNeedsToBeChecked = true;
}
if($appNeedsToBeChecked) {
$this->verifyAppSignature($appId);
}
}
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck\Exceptions;
/**
* Class InvalidSignatureException is thrown in case the signature of the hashes
* cannot be properly validated. This indicates that either files
*
* @package OC\IntegrityCheck\Exceptions
*/
class InvalidSignatureException extends \Exception {}

View File

@ -0,0 +1,56 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck\Helpers;
/**
* Class AppLocator provides a non-static helper for OC_App::getPath($appId)
* it is not possible to use IAppManager at this point as IAppManager has a
* dependency on a running ownCloud.
*
* @package OC\IntegrityCheck\Helpers
*/
class AppLocator {
/**
* Provides \OC_App::getAppPath($appId)
*
* @param string $appId
* @return string
* @throws \Exception If the app cannot be found
*/
public function getAppPath($appId) {
$path = \OC_App::getAppPath($appId);
if($path === false) {
throw new \Exception('App not found');
}
return $path;
}
/**
* Providers \OC_App::getAllApps()
*
* @return array
*/
public function getAllApps() {
return \OC_App::getAllApps();
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck\Helpers;
/**
* Class EnvironmentHelper provides a non-static helper for access to static
* variables such as \OC::$SERVERROOT.
*
* @package OC\IntegrityCheck\Helpers
*/
class EnvironmentHelper {
/**
* Provides \OC::$SERVERROOT
*
* @return string
*/
public function getServerRoot() {
return \OC::$SERVERROOT;
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck\Helpers;
/**
* Class FileAccessHelper provides a helper around file_get_contents and
* file_put_contents
*
* @package OC\IntegrityCheck\Helpers
*/
class FileAccessHelper {
/**
* Wrapper around file_get_contents($filename, $data)
*
* @param string $filename
* @return string|false
*/
public function file_get_contents($filename) {
return file_get_contents($filename);
}
/**
* Wrapper around file_exists($filename)
*
* @param string $filename
* @return bool
*/
public function file_exists($filename) {
return file_exists($filename);
}
/**
* Wrapper around file_put_contents($filename, $data)
*
* @param string $filename
* @param $data
* @return int|false
*/
public function file_put_contents($filename, $data) {
return file_put_contents($filename, $data);
}
}

View File

@ -0,0 +1,58 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck\Iterator;
/**
* Class ExcludeFileByNameFilterIterator provides a custom iterator which excludes
* entries with the specified file name from the file list.
*
* @package OC\Integritycheck\Iterator
*/
class ExcludeFileByNameFilterIterator extends \RecursiveFilterIterator {
/**
* Array of excluded file names. Those are not scanned by the integrity checker.
* This is used to exclude files which administrators could upload by mistakes
* such as .DS_Store files.
*
* @var array
*/
private $excludedFilenames = [
'.DS_Store', // Mac OS X
'Thumbs.db', // Microsoft Windows
'.directory', // Dolphin (KDE)
];
/**
* @return bool
*/
public function accept() {
if($this->isDir()) {
return true;
}
return !in_array(
$this->current()->getFilename(),
$this->excludedFilenames,
true
);
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace OC\IntegrityCheck\Iterator;
class ExcludeFoldersByPathFilterIterator extends \RecursiveFilterIterator {
private $excludedFolders = [];
public function __construct(\RecursiveIterator $iterator) {
parent::__construct($iterator);
$appFolders = \OC::$APPSROOTS;
foreach($appFolders as $key => $appFolder) {
$appFolders[$key] = rtrim($appFolder['path'], '/');
}
$this->excludedFolders = array_merge([
rtrim(\OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'), '/'),
rtrim(\OC::$SERVERROOT.'/themes', '/'),
], $appFolders);
}
/**
* @return bool
*/
public function accept() {
return !in_array(
$this->current()->getPathName(),
$this->excludedFolders,
true
);
}
}

View File

@ -51,6 +51,10 @@ use OC\Files\Node\HookConnector;
use OC\Files\Node\Root;
use OC\Files\View;
use OC\Http\Client\ClientService;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\AppLocator;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use OC\Lock\DBLockingProvider;
use OC\Lock\MemcacheLockingProvider;
use OC\Lock\NoopLockingProvider;
@ -409,6 +413,26 @@ class Server extends SimpleContainer implements IServerContainer {
$this->registerService('TrustedDomainHelper', function ($c) {
return new TrustedDomainHelper($this->getConfig());
});
$this->registerService('IntegrityCodeChecker', function (Server $c) {
// IConfig and IAppManager requires a working database. This code
// might however be called when ownCloud is not yet setup.
if(\OC::$server->getSystemConfig()->getValue('installed', false)) {
$config = $c->getConfig();
$appManager = $c->getAppManager();
} else {
$config = null;
$appManager = null;
}
return new Checker(
new EnvironmentHelper(),
new FileAccessHelper(),
new AppLocator(),
$config,
$c->getMemCacheFactory(),
$appManager
);
});
$this->registerService('Request', function ($c) {
if (isset($this['urlParams'])) {
$urlParams = $this['urlParams'];
@ -1093,6 +1117,13 @@ class Server extends SimpleContainer implements IServerContainer {
return $this->query('NotificationManager');
}
/**
* @return \OC\IntegrityCheck\Checker
*/
public function getIntegrityCodeChecker() {
return $this->query('IntegrityCodeChecker');
}
/**
* @return \OC\Session\CryptoWrapper
*/

View File

@ -78,8 +78,12 @@ class OC_TemplateLayout extends OC_Template {
// Update notification
if($this->config->getSystemValue('updatechecker', true) === true &&
OC_User::isAdminUser(OC_User::getUser())) {
$updater = new \OC\Updater(\OC::$server->getHTTPHelper(),
\OC::$server->getConfig(), \OC::$server->getLogger());
$updater = new \OC\Updater(
\OC::$server->getHTTPHelper(),
\OC::$server->getConfig(),
\OC::$server->getIntegrityCodeChecker(),
\OC::$server->getLogger()
);
$data = $updater->check();
if(isset($data['version']) && $data['version'] != '' and $data['version'] !== Array()) {
@ -96,8 +100,13 @@ class OC_TemplateLayout extends OC_Template {
$this->assign('updateAvailable', false); // Update check is disabled
}
// Add navigation entry
// Code integrity notification
$integrityChecker = \OC::$server->getIntegrityCodeChecker();
if(!$integrityChecker->hasPassedCheck()) {
\OCP\Util::addScript('core', 'integritycheck-failed-notification');
}
// Add navigation entry
$this->assign( 'application', '');
$this->assign( 'appid', $appId );
$navigation = OC_App::getNavigation();

View File

@ -34,6 +34,8 @@
namespace OC;
use OC\Hooks\BasicEmitter;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Storage;
use OC_App;
use OC_Installer;
use OC_Util;
@ -61,6 +63,9 @@ class Updater extends BasicEmitter {
/** @var IConfig */
private $config;
/** @var Checker */
private $checker;
/** @var bool */
private $simulateStepEnabled;
@ -81,14 +86,17 @@ class Updater extends BasicEmitter {
/**
* @param HTTPHelper $httpHelper
* @param IConfig $config
* @param Checker $checker
* @param ILogger $log
*/
public function __construct(HTTPHelper $httpHelper,
IConfig $config,
Checker $checker,
ILogger $log = null) {
$this->httpHelper = $httpHelper;
$this->log = $log;
$this->config = $config;
$this->checker = $checker;
$this->simulateStepEnabled = true;
$this->updateStepEnabled = true;
}
@ -335,6 +343,13 @@ class Updater extends BasicEmitter {
//Invalidate update feed
$this->config->setAppValue('core', 'lastupdatedat', 0);
// Check for code integrity on the stable channel
if(\OC_Util::getChannel() === 'stable') {
$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
$this->checker->runInstanceVerification();
$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
}
// only set the final version if everything went well
$this->config->setSystemValue('version', implode('.', \OC_Util::getVersion()));
}

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
V204fGUkJqW5CrKy3P3XvY9X
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
+LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -40,6 +40,7 @@ use OC\Settings\Middleware\SubadminMiddleware;
use \OCP\AppFramework\App;
use OCP\IContainer;
use \OCP\Util;
use OC\Server;
/**
* @package OC\Settings
@ -154,7 +155,8 @@ class Application extends App {
$c->query('ClientService'),
$c->query('URLGenerator'),
$c->query('Util'),
$c->query('L10N')
$c->query('L10N'),
$c->query('Checker')
);
});
@ -240,5 +242,10 @@ class Application extends App {
$container->registerService('CertificateManager', function(IContainer $c){
return $c->query('ServerContainer')->getCertificateManager();
});
$container->registerService('Checker', function(IContainer $c) {
/** @var Server $server */
$server = $c->query('ServerContainer');
return $server->getIntegrityCodeChecker();
});
}
}

View File

@ -26,8 +26,12 @@
namespace OC\Settings\Controller;
use GuzzleHttp\Exception\ClientException;
use OC\AppFramework\Http;
use OC\IntegrityCheck\Checker;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IL10N;
@ -49,6 +53,8 @@ class CheckSetupController extends Controller {
private $urlGenerator;
/** @var IL10N */
private $l10n;
/** @var Checker */
private $checker;
/**
* @param string $AppName
@ -58,6 +64,7 @@ class CheckSetupController extends Controller {
* @param IURLGenerator $urlGenerator
* @param \OC_Util $util
* @param IL10N $l10n
* @param Checker $checker
*/
public function __construct($AppName,
IRequest $request,
@ -65,13 +72,15 @@ class CheckSetupController extends Controller {
IClientService $clientService,
IURLGenerator $urlGenerator,
\OC_Util $util,
IL10N $l10n) {
IL10N $l10n,
Checker $checker) {
parent::__construct($AppName, $request);
$this->config = $config;
$this->clientService = $clientService;
$this->util = $util;
$this->urlGenerator = $urlGenerator;
$this->l10n = $l10n;
$this->checker = $checker;
}
/**
@ -247,6 +256,72 @@ class CheckSetupController extends Controller {
return !(!extension_loaded('memcached') && extension_loaded('memcache'));
}
/**
* @return RedirectResponse
*/
public function rescanFailedIntegrityCheck() {
$this->checker->runInstanceVerification();
return new RedirectResponse(
$this->urlGenerator->linkToRoute('settings_admin')
);
}
/**
* @NoCSRFRequired
* @return DataResponse
*/
public function getFailedIntegrityCheckFiles() {
$completeResults = $this->checker->getResults();
if(!empty($completeResults)) {
$formattedTextResponse = 'Technical information
=====================
The following list covers which files have failed the integrity check. Please read
the previous linked documentation to learn more about the errors and how to fix
them.
Results
=======
';
foreach($completeResults as $context => $contextResult) {
$formattedTextResponse .= "- $context\n";
foreach($contextResult as $category => $result) {
$formattedTextResponse .= "\t- $category\n";
if($category !== 'EXCEPTION') {
foreach ($result as $key => $results) {
$formattedTextResponse .= "\t\t- $key\n";
}
} else {
foreach ($result as $key => $results) {
$formattedTextResponse .= "\t\t- $results\n";
}
}
}
}
$formattedTextResponse .= '
Raw output
==========
';
$formattedTextResponse .= print_r($completeResults, true);
} else {
$formattedTextResponse = 'No errors have been found.';
}
$response = new DataDisplayResponse(
$formattedTextResponse,
Http::STATUS_OK,
[
'Content-Type' => 'text/plain',
]
);
return $response;
}
/**
* @return DataResponse
*/
@ -263,7 +338,9 @@ class CheckSetupController extends Controller {
'phpSupported' => $this->isPhpSupported(),
'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled()
'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
]
);
}

View File

@ -53,6 +53,8 @@ $application->registerRoutes($this, [
['name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'],
['name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'],
['name' => 'CheckSetup#check', 'url' => '/settings/ajax/checksetup', 'verb' => 'GET'],
['name' => 'CheckSetup#getFailedIntegrityCheckFiles', 'url' => '/settings/integrity/failed', 'verb' => 'GET'],
['name' => 'CheckSetup#rescanFailedIntegrityCheck', 'url' => '/settings/integrity/rescan', 'verb' => 'GET'],
['name' => 'Certificate#addPersonalRootCertificate', 'url' => '/settings/personal/certificate', 'verb' => 'POST'],
['name' => 'Certificate#removePersonalRootCertificate', 'url' => '/settings/personal/certificate/{certificateIdentifier}', 'verb' => 'DELETE'],
]

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf/qTQvoyKAwDQYJKoZIhvcNAQEF
BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
MzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk
F8cAobMMi50qHCv9IrOn/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl/j
+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+
A+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M
w0xDv30D5UkE/2N7Pa/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4
GB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6
dol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj
t5O7Zn2wA7I4ddDS/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC
cxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT/x/mqN3PfRmlnFBNACUw9bpZ
SOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz
tFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4
6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf
FWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS
HVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70/4imPoKxbAVCpd/cveVcFyDC19j1yB
Bapwu87oh+muoeaZxOlqQI4UxjBlR/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v
0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3
pPhEpo1dRpiXaF7WGIV1X6DI/ipWvfrF7CEy6I/kP1InY/vMDjQjeDnJ/VrXIWXO
yZvHXVaN/m+1RlETsH7YO/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF
49/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7
7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW
UO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS/P4RB1NkHA9+NTvmBpTonS
SFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP/Y7xwxLv7
4B+pXTAcRK0zECDEaX3npS8xWzrB
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1 @@
Another file with some Content.

View File

@ -0,0 +1 @@
A file with some content.

View File

@ -0,0 +1 @@
TAMPERED FILE.

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
V204fGUkJqW5CrKy3P3XvY9X
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
+LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,247 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace Test\Command\Integrity;
use OC\Core\Command\Integrity\SignApp;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use Test\TestCase;
class SignAppTest extends TestCase {
/** @var Checker */
private $checker;
/** @var SignApp */
private $signApp;
/** @var FileAccessHelper */
private $fileAccessHelper;
public function setUp() {
parent::setUp();
$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
->disableOriginalConstructor()->getMock();
$this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
->disableOriginalConstructor()->getMock();
$this->signApp = new SignApp(
$this->checker,
$this->fileAccessHelper
);
}
public function testExecuteWithMissingAppId() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('appId')
->will($this->returnValue(null));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('privateKey')
->will($this->returnValue('PrivateKey'));
$inputInterface
->expects($this->at(2))
->method('getOption')
->with('certificate')
->will($this->returnValue('Certificate'));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('--appId, --privateKey and --certificate are required.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithMissingPrivateKey() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('appId')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('privateKey')
->will($this->returnValue(null));
$inputInterface
->expects($this->at(2))
->method('getOption')
->with('certificate')
->will($this->returnValue('Certificate'));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('--appId, --privateKey and --certificate are required.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithMissingCertificate() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('appId')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(2))
->method('getOption')
->with('certificate')
->will($this->returnValue(null));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('--appId, --privateKey and --certificate are required.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithNotExistingPrivateKey() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('appId')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(2))
->method('getOption')
->with('certificate')
->will($this->returnValue('certificate'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with('privateKey')
->will($this->returnValue(false));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('Private key "privateKey" does not exists.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithNotExistingCertificate() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('appId')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(2))
->method('getOption')
->with('certificate')
->will($this->returnValue('certificate'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with('privateKey')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with('certificate')
->will($this->returnValue(false));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('Certificate "certificate" does not exists.');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecute() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('appId')
->will($this->returnValue('AppId'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(2))
->method('getOption')
->with('certificate')
->will($this->returnValue('certificate'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with('privateKey')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with('certificate')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'));
$this->checker
->expects($this->once())
->method('writeAppSignature');
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('Successfully signed "AppId"');
$this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
}
}

View File

@ -0,0 +1,194 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace Test\Command\Integrity;
use OC\Core\Command\Integrity\SignCore;
use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use Test\TestCase;
class SignCoreTest extends TestCase {
/** @var Checker */
private $checker;
/** @var SignCore */
private $signCore;
/** @var FileAccessHelper */
private $fileAccessHelper;
public function setUp() {
parent::setUp();
$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
->disableOriginalConstructor()->getMock();
$this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
->disableOriginalConstructor()->getMock();
$this->signCore = new SignCore(
$this->checker,
$this->fileAccessHelper
);
}
public function testExecuteWithMissingPrivateKey() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('privateKey')
->will($this->returnValue(null));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('certificate')
->will($this->returnValue('Certificate'));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('--privateKey and --certificate are required.');
$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithMissingCertificate() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('certificate')
->will($this->returnValue(null));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('--privateKey and --certificate are required.');
$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithNotExistingPrivateKey() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('certificate')
->will($this->returnValue('certificate'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with('privateKey')
->will($this->returnValue(false));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('Private key "privateKey" does not exists.');
$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecuteWithNotExistingCertificate() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('certificate')
->will($this->returnValue('certificate'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with('privateKey')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with('certificate')
->will($this->returnValue(false));
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('Certificate "certificate" does not exists.');
$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
}
public function testExecute() {
$inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
$outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
$inputInterface
->expects($this->at(0))
->method('getOption')
->with('privateKey')
->will($this->returnValue('privateKey'));
$inputInterface
->expects($this->at(1))
->method('getOption')
->with('certificate')
->will($this->returnValue('certificate'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with('privateKey')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with('certificate')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'));
$this->checker
->expects($this->once())
->method('writeCoreSignature');
$outputInterface
->expects($this->at(0))
->method('writeln')
->with('Successfully signed "core"');
$this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
}
}

View File

@ -0,0 +1,671 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace Test\IntegrityCheck;
use OC\IntegrityCheck\Checker;
use OC\Memcache\NullCache;
use phpseclib\Crypt\RSA;
use phpseclib\File\X509;
use Test\TestCase;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use OC\IntegrityCheck\Helpers\AppLocator;
use OCP\IConfig;
use OCP\ICacheFactory;
use OCP\App\IAppManager;
class CheckerTest extends TestCase {
/** @var EnvironmentHelper */
private $environmentHelper;
/** @var AppLocator */
private $appLocator;
/** @var Checker */
private $checker;
/** @var FileAccessHelper */
private $fileAccessHelper;
/** @var IConfig */
private $config;
/** @var ICacheFactory */
private $cacheFactory;
/** @var IAppManager */
private $appManager;
public function setUp() {
parent::setUp();
$this->environmentHelper = $this->getMock('\OC\IntegrityCheck\Helpers\EnvironmentHelper');
$this->fileAccessHelper = $this->getMock('\OC\IntegrityCheck\Helpers\FileAccessHelper');
$this->appLocator = $this->getMock('\OC\IntegrityCheck\Helpers\AppLocator');
$this->config = $this->getMock('\OCP\IConfig');
$this->cacheFactory = $this->getMock('\OCP\ICacheFactory');
$this->appManager = $this->getMock('\OCP\App\IAppManager');
$this->cacheFactory
->expects($this->any())
->method('create')
->with('oc.integritycheck.checker')
->will($this->returnValue(new NullCache()));
$this->checker = new Checker(
$this->environmentHelper,
$this->fileAccessHelper,
$this->appLocator,
$this->config,
$this->cacheFactory,
$this->appManager
);
}
/**
* @expectedException \Exception
* @expectedExceptionMessage Directory name must not be empty.
*/
public function testWriteAppSignatureOfNotExistingApp() {
$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
$rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.key');
$rsa = new RSA();
$rsa->loadKey($rsaPrivateKey);
$x509 = new X509();
$x509->loadX509($keyBundle);
$this->checker->writeAppSignature('NotExistingApp', $x509, $rsa);
}
public function testWriteAppSignature() {
$expectedSignatureFileData = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "Y5yvXvcGHVPuRRatKVDUONWq1FpLXugZd6Km\/+aEHsQj7coVl9FeMj9OsWamBf7yRIw3dtNLguTLlAA9QAv\/b0uHN3JnbNZN+dwFOve4NMtqXfSDlWftqKN00VS+RJXpG1S2IIx9Poyp2NoghL\/5AuTv4GHiNb7zU\/DT\/kt71pUGPgPR6IIFaE+zHOD96vjYkrH+GfWZzKR0FCdLib9yyNvk+EGrcjKM6qjs2GKfS\/XFjj\/\/neDnh\/0kcPuKE3ZbofnI4TIDTv0CGqvOp7PtqVNc3Vy\/UKa7uF1PT0MAUKMww6EiMUSFZdUVP4WWF0Y72W53Qdtf1hrAZa2kfKyoK5kd7sQmCSKUPSU8978AUVZlBtTRlyT803IKwMV0iHMkw+xYB1sN2FlHup\/DESADqxhdgYuK35bCPvgkb4SBe4B8Voz\/izTvcP7VT5UvkYdAO+05\/jzdaHEmzmsD92CFfvX0q8O\/Y\/29ubftUJsqcHeMDKgcR4eZOE8+\/QVc\/89QO6WnKNuNuV+5bybO6g6PAdC9ZPsCvnihS61O2mwRXHLR3jv2UleFWm+lZEquPKtkhi6SLtDiijA4GV6dmS+dzujSLb7hGeD5o1plZcZ94uhWljl+QIp82+zU\/lYB1Zfr4Mb4e+V7r2gv7Fbv7y6YtjE2GIQwRhC5jq56bD0ZB+I=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
}';
$this->appLocator
->expects($this->once())
->method('getAppPath')
->with('SomeExistingApp')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$this->fileAccessHelper
->expects($this->once())
->method('file_put_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json',
$expectedSignatureFileData
);
$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
$rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.key');
$rsa = new RSA();
$rsa->loadKey($rsaPrivateKey);
$x509 = new X509();
$x509->loadX509($keyBundle);
$this->checker->writeAppSignature('SomeExistingApp', $x509, $rsa);
}
public function testVerifyAppSignatureWithoutSignatureData() {
$expected = [
'EXCEPTION' => [
'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException',
'message' => 'Signature data not found.',
],
];
$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
}
public function testVerifyAppSignatureWithValidSignatureData() {
$this->appLocator
->expects($this->once())
->method('getAppPath')
->with('SomeApp')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
'/resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
}
public function testVerifyAppSignatureWithTamperedSignatureData() {
$this->appLocator
->expects($this->once())
->method('getAppPath')
->with('SomeApp')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "tampered",
"subfolder\/file.txt": "tampered"
},
"signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
'/resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'EXCEPTION' => [
'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
'message' => 'Signature could not get verified.',
],
];
$this->assertEquals($expected, $this->checker->verifyAppSignature('SomeApp'));
}
public function testVerifyAppSignatureWithTamperedFiles() {
$this->appLocator
->expects($this->once())
->method('getAppPath')
->with('SomeApp')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
'/resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'INVALID_HASH' => [
'AnotherFile.txt' => [
'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
],
],
'FILE_MISSING' => [
'subfolder/file.txt' => [
'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
'current' => '',
],
],
'EXTRA_FILE' => [
'UnecessaryFile' => [
'expected' => '',
'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
],
],
];
$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
}
public function testVerifyAppWithDifferentScope() {
$this->appLocator
->expects($this->once())
->method('getAppPath')
->with('SomeApp')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIExjCCAq6gAwIBAgIUHSJjhJqMwr+3TkoiQFg4SVVYQ1gwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIzMjc1NVoXDTE2MTEwMzIzMjc1NVowFzEVMBMGA1UEAwwMQW5vdGhlclNjb3Bl\r\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA33npb5RmUkXrDT+TbwMf\r\n0zQ33SlzsjoGxCrbSwJOn6leGGInJ6ZrdzLL0WTi\/dTpg+Y\/JS+72XWm5NSjaTxo\r\n7OHc3cQBwXQj4tN6j\/y5qqY0GDLYufEkx2rpazqt9lBSJ72u1bGl2yoOXzYCz5i0\r\n60KsJXC9K44LKzGsarzbwAgskSVNkjAsPgjnCWZmcl6icpLi5Fz9rs2UMOWbdvdI\r\nAROsn0eC9E\/akmXTy5YMu6bAIGpvjZFHzyA83FQRbvv5o1V5Gsye\/VQLEgh7rqfz\r\nT\/jgWifP+JgoeB6otzuRZ3fFsmbBiyCIRtIOzQQflozhUlWtmiEGwg4GySuMUjEH\r\nA1LF86LO+ZzDQgd2oYNKmrQ8O+EcLqx9BpV4AFhEvqdk7uycJYPHs6yl+yfbzTeJ\r\n2Xd0yVAfd9r\/iDr36clLj2bzEObdl9xzKjcCIXE4Q0G4Pur41\/BJUDK9PI390ccQ\r\nnFjjVYBMsC859OwW64tMP0zkM9Vv72LCaEzaR8jqH0j11catqxunr+StfMcmxLTN\r\nbqBJbSEq4ER3mJxCTI2UrIVmdQ7+wRxgv3QTDNOZyqrz2L8A1Rpb3h0APxtQv+oA\r\n8KIZYID5\/qsS2V2jITkMQ8Nd1W3b0cZhZ600z+znh3jLJ0TYLvwN6\/qBQTUDaM2o\r\ng1+icMqXIXIeKuoPCVVsG7cCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAHc4F\/kOV\r\nHc8In5MmGg2YtjwZzjdeoC5TIPZczRqz0B+wRbJzN6aYryKZKLmP+wKpgRnJWDzp\r\nrgKGyyEQIAfK63DEv4B9p4N1B+B3aeMKsSpVcw7wbFTD57V5A7pURGoo31d0mw5L\r\nUIXZ2u+TUfGbzucMxLdFhTwjGpz9M6Kkm\/POxmV0tvLija5LdbdKnYR9BFmyu4IX\r\nqyoIAtComATNLl+3URu3SZxhE3NxhzMz+eAeNfh1KuIf2gWIIeDCXalVSJLym+OQ\r\nHFDpqRhJqfTMprrRlmmU7Zntgbj8\/RRZuXnBvH9cQ2KykLOb4UoCPlGUqOqKyP9m\r\nDJSFRiMJfpgMQUaJk1TLhKF+IR6FnmwURLEtkONJumtDQju9KaWPlhueONdyGi0p\r\nqxLVUo1Vb52XnPhk2GEEduxpDc9V5ePJ+pdcEdMifY\/uPNBRuBj2c87yq1DLH+U4\r\n3XzP1MlwjnBWZYuoFo0j6Jq0r\/MG6HjGdmkGIsRoheRi8Z8Scz5AW5QRkNz8pKop\r\nTELFqQy9g6TyQzzC8t6HZcpNe842ZUk4raEAbCZe\/XqxWMw5svPgNceBqM3fh7sZ\r\nBSykOHLaL8kiRO\/IS3y1yZEAuiWBvtxcTNLzBb+hdRpm2y8\/qH\/pKo+CMj1VzjNT\r\nD8YRQg0cjmDytJzHDrtV\/aTc9W1aPHun0vw=\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json')
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
'/resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'EXCEPTION' => [
'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
'message' => 'Certificate is not valid for required scope. (Requested: SomeApp, current: CN=AnotherScope)',
],
];
$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
}
public function testVerifyAppWithDifferentScopeAndAlwaysTrustedCore() {
$this->appLocator
->expects($this->once())
->method('getAppPath')
->with('SomeApp')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json')
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
'/resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
}
public function testWriteCoreSignature() {
$expectedSignatureFileData = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$this->fileAccessHelper
->expects($this->once())
->method('file_put_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
$expectedSignatureFileData
);
$keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/core.crt');
$rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/core.key');
$rsa = new RSA();
$rsa->loadKey($rsaPrivateKey);
$x509 = new X509();
$x509->loadX509($keyBundle);
$this->checker->writeCoreSignature($x509, $rsa);
}
public function testVerifyCoreSignatureWithoutSignatureData() {
$expected = [
'EXCEPTION' => [
'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
'message' => 'Signature data not found.',
],
];
$this->assertSame($expected, $this->checker->verifyCoreSignature());
}
public function testVerifyCoreSignatureWithValidSignatureData() {
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$this->assertSame([], $this->checker->verifyCoreSignature());
}
public function testVerifyCoreSignatureWithValidSignatureDataAndNotAlphabeticOrder() {
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$this->assertSame([], $this->checker->verifyCoreSignature());
}
public function testVerifyCoreSignatureWithTamperedSignatureData() {
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "tampered",
"subfolder\/file.txt": "tampered"
},
"signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'EXCEPTION' => [
'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
'message' => 'Signature could not get verified.',
]
];
$this->assertSame($expected, $this->checker->verifyCoreSignature());
}
public function testVerifyCoreSignatureWithTamperedFiles() {
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'INVALID_HASH' => [
'AnotherFile.txt' => [
'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
],
],
'FILE_MISSING' => [
'subfolder/file.txt' => [
'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
'current' => '',
],
],
'EXTRA_FILE' => [
'UnecessaryFile' => [
'expected' => '',
'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
],
],
];
$this->assertSame($expected, $this->checker->verifyCoreSignature());
}
public function testVerifyCoreWithInvalidCertificate() {
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUPYoweUxCPqbDW4ntuh7QvgyqSrgwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDIwNloXDTE2MTEwMzIyNDIwNlowDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJui3nDbjOIjxNnthdBZplphujsN6u8K\r\nQ\/62zAuSwzXVp0+3IMgM\/2sepklVE8YfCyVJ5+SUJqnqHoUWVRVfs8jL0wW6nrHM\r\n\/lsscAguWCee4iAdNOqI9kq4+DUau8J45e62XA9mrAo\/8\/NKzFE2y2WduDoQZcm+\r\n8+dwcUUHXw2jl8dfrmvEMYSqTNDdb4rGmQpeV+dr9BLqr+x03U1Q08qCG9j7mSOz\r\ncvJENjOvC5uzAh5LCuCgxqG4o+mPzB0FtNnwoRRu6IsF3Y3KacRqPc30fB\/iXDn5\r\nBPr14uNxTTYWoZJ1F0tZrLzRbXdjJJOC+dnQurTtXWZ8WjPB1BWQYK7fW6t82mkN\r\n2Qe2xen99gs9nX5yY\/sHM3TKSJdM7AVCEv\/emW3gNjkvWTtRlN\/Nc7X2ckNwXcvo\r\n0yi3fSPjzXpDgLbhp1FzrMlHDn1VzmRT3r8wLByWa\/hsxrJDsBzwunMJYhXhmeKb\r\n3wX0tN\/EUJTWBntpwVOIGnRPD51oBoQUOMaEAq\/kz8PgN181bWZkJbRuf+FWkijQ\r\no+HR2lVF1jWXXst5Uc+s9HN81Uly7X4O9MMg0QxT4+wymtGDs6AOkwMi9rgBTrRB\r\n3tLU3XL2UIwRXgmd8cPtTu\/I6Bm7LdyaYtZ3yJTxRewq3nZdWypqBhD8uhpIYVkf\r\no4bxmGkVAQVTAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAKKAX5EHgU1grODnJ0of\r\nspFpgB1K67YvclNUyuU6NQ6zBJx1\/w1RnM7uxLcxiiWj1BbUhwZQ0ojmEHeUyi6O\r\nGrDVajwhTccDMmja3u5adhEncx65\/H+lD85IPRRkS2qBDssMDdJHhZ0uI+40nI7M\r\nMq1kFjl+6wiuqZXqps66DuLbk45g\/ZlrFIrIo3Ix5vj0OVqwT+gO4LYirJK6KgVS\r\nUttbcEsc\/yKU9ThnM8\/n4m2jstZXfzKPgOsJrQcZrFOtpj+CWmBzVElBSPlDT3Nh\r\nHSgOeTFJ8bQBxj2iG5dLA+JZJQKxyJ1gy2ZtxIJ2GyvLtSe8NUSqvfPWOaAKEUV2\r\ngniytnEFLr+PcD+9EGux6jZNuj6HmtWVThTfD5VGFmtlVU2z71ZRYY0kn6J3mmFc\r\nS2ecEcCUwqG5YNLncEUCyZhC2klWql2SHyGctCEyWWY7ikIDjVzYt2EbcFvLNBnP\r\ntybN1TYHRRZxlug00CCoOE9EZfk46FkZpDvU6KmqJRofkNZ5sj+SffyGcwYwNrDH\r\nKqe8m+9lHf3CRTIDeMu8r2xl1I6M6ZZfjabbmVP9Jd6WN4s6f1FlXDWzhlT1N0Qw\r\nGzJj6xB+SPtS3UV05tBlvbfA4e06D5G9uD7Q8ONcINtMS0xsSJ2oo82AqlpvlF\/q\r\noj7YKHsaTVGA+FxBktZHfoxD\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'EXCEPTION' => [
'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
'message' => 'Certificate is not valid.',
]
];
$this->assertSame($expected, $this->checker->verifyCoreSignature());
}
public function testVerifyCoreWithDifferentScope() {
$this->environmentHelper
->expects($this->any())
->method('getServerRoot')
->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
$signatureDataFile = '{
"hashes": {
"AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
"subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
},
"signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
}';
$this->fileAccessHelper
->expects($this->at(0))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
)
->will($this->returnValue($signatureDataFile));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_get_contents')
->with(
\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
)
->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
$expected = [
'EXCEPTION' => [
'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
'message' => 'Certificate is not valid for required scope. (Requested: core, current: CN=SomeApp)',
]
];
$this->assertSame($expected, $this->checker->verifyCoreSignature());
}
public function testRunInstanceVerification() {
$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
->setConstructorArgs([
$this->environmentHelper,
$this->fileAccessHelper,
$this->appLocator,
$this->config,
$this->cacheFactory,
$this->appManager,
])
->setMethods([
'verifyCoreSignature',
'verifyAppSignature',
])
->getMock();
$this->checker
->expects($this->at(0))
->method('verifyCoreSignature');
$this->appLocator
->expects($this->at(0))
->Method('getAllApps')
->will($this->returnValue([
'files',
'calendar',
'contacts',
'dav',
]));
$this->appManager
->expects($this->at(0))
->method('isShipped')
->with('files')
->will($this->returnValue(true));
$this->checker
->expects($this->at(1))
->method('verifyAppSignature')
->with('files');
$this->appManager
->expects($this->at(1))
->method('isShipped')
->with('calendar')
->will($this->returnValue(false));
$this->appLocator
->expects($this->at(1))
->method('getAppPath')
->with('calendar')
->will($this->returnValue('/apps/calendar'));
$this->fileAccessHelper
->expects($this->at(0))
->method('file_exists')
->with('/apps/calendar/appinfo/signature.json')
->will($this->returnValue(true));
$this->checker
->expects($this->at(2))
->method('verifyAppSignature')
->with('calendar');
$this->appManager
->expects($this->at(2))
->method('isShipped')
->with('contacts')
->will($this->returnValue(false));
$this->appLocator
->expects($this->at(2))
->method('getAppPath')
->with('contacts')
->will($this->returnValue('/apps/contacts'));
$this->fileAccessHelper
->expects($this->at(1))
->method('file_exists')
->with('/apps/contacts/appinfo/signature.json')
->will($this->returnValue(false));
$this->appManager
->expects($this->at(3))
->method('isShipped')
->with('dav')
->will($this->returnValue(true));
$this->checker
->expects($this->at(3))
->method('verifyAppSignature')
->with('dav');
$this->checker->runInstanceVerification();
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace Test\IntegrityCheck\Helpers;
use OC\IntegrityCheck\Helpers\AppLocator;
use Test\TestCase;
class AppLocatorTest extends TestCase {
/** @var AppLocator */
private $locator;
public function setUp() {
parent::setUp();
$this->locator = new AppLocator();
}
public function testGetAppPath() {
$this->assertSame(\OC_App::getAppPath('files'), $this->locator->getAppPath('files'));
}
/**
* @expectedException \Exception
* @expectedExceptionMessage App not found
*/
public function testGetAppPathNotExistentApp() {
$this->locator->getAppPath('aTotallyNotExistingApp');
}
public function testGetAllApps() {
$this->assertSame(\OC_App::getAllApps(), $this->locator->getAllApps());
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace Test\IntegrityCheck\Factories;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use Test\TestCase;
class EnvironmentHelperTest extends TestCase {
public function testGetServerRoot() {
$factory = new EnvironmentHelper();
$this->assertSame(\OC::$SERVERROOT, $factory->getServerRoot());
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @author Lukas Reschke <lukas@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @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 <http://www.gnu.org/licenses/>
*
*/
namespace Test\IntegrityCheck\Helpers;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
use Test\TestCase;
class FileAccessHelperTest extends TestCase {
/** @var FileAccessHelper */
private $fileAccessHelper;
public function setUp() {
parent::setUp();
$this->fileAccessHelper = new FileAccessHelper();
}
public function testReadAndWrite() {
$tempManager = \OC::$server->getTempManager();
$filePath = $tempManager->getTemporaryFile();
$data = 'SomeDataGeneratedByIntegrityCheck';
$this->fileAccessHelper->file_put_contents($filePath, $data);
$this->assertSame($data, $this->fileAccessHelper->file_get_contents($filePath));
}
}

View File

@ -24,6 +24,7 @@ namespace OC;
use OCP\IConfig;
use OCP\ILogger;
use OC\IntegrityCheck\Checker;
class UpdaterTest extends \Test\TestCase {
/** @var IConfig */
@ -34,6 +35,8 @@ class UpdaterTest extends \Test\TestCase {
private $logger;
/** @var Updater */
private $updater;
/** @var Checker */
private $checker;
public function setUp() {
parent::setUp();
@ -46,10 +49,14 @@ class UpdaterTest extends \Test\TestCase {
$this->logger = $this->getMockBuilder('\\OCP\\ILogger')
->disableOriginalConstructor()
->getMock();
$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
->disableOriginalConstructor()
->getMock();
$this->updater = new Updater(
$this->httpHelper,
$this->config,
$this->checker,
$this->logger
);
}
@ -158,11 +165,11 @@ class UpdaterTest extends \Test\TestCase {
*
* @param string $oldVersion
* @param string $newVersion
* @param string $allowedVersion
* @param bool $result
*/
public function testIsUpgradePossible($oldVersion, $newVersion, $allowedVersion, $result) {
$updater = new Updater($this->httpHelper, $this->config, $this->logger);
$this->assertSame($result, $updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersion));
$this->assertSame($result, $this->updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersion));
}
public function testCheckInCache() {

File diff suppressed because one or more lines are too long