diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/ajax/update.php | 7 | ||||
-rw-r--r-- | core/command/integrity/signapp.php | 98 | ||||
-rw-r--r-- | core/command/integrity/signcore.php | 98 | ||||
-rw-r--r-- | core/command/upgrade.php | 10 | ||||
-rw-r--r-- | core/js/integritycheck-failed-notification.js | 38 | ||||
-rw-r--r-- | core/js/setupchecks.js | 14 | ||||
-rw-r--r-- | core/js/tests/specs/setupchecksSpec.js | 7 | ||||
-rw-r--r-- | core/register_command.php | 8 |
8 files changed, 277 insertions, 3 deletions
diff --git a/core/ajax/update.php b/core/ajax/update.php index 7da9b71b751..a5c1f79e3ea 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -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(); diff --git a/core/command/integrity/signapp.php b/core/command/integrity/signapp.php new file mode 100644 index 00000000000..83a7972068f --- /dev/null +++ b/core/command/integrity/signapp.php @@ -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.'"'); + } +} diff --git a/core/command/integrity/signcore.php b/core/command/integrity/signcore.php new file mode 100644 index 00000000000..4d097ad4f96 --- /dev/null +++ b/core/command/integrity/signcore.php @@ -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"'); + } +} diff --git a/core/command/upgrade.php b/core/command/upgrade.php index d1a7c09c30c..9031e284f85 100644 --- a/core/command/upgrade.php +++ b/core/command/upgrade.php @@ -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); diff --git a/core/js/integritycheck-failed-notification.js b/core/js/integritycheck-failed-notification.js new file mode 100644 index 00000000000..5bc758d54cb --- /dev/null +++ b/core/js/integritycheck-failed-notification.js @@ -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 + } + ); +}); + diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js index 6e2058d54fc..5ac1945da73 100644 --- a/core/js/setupchecks.js +++ b/core/js/setupchecks.js @@ -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'), diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js index 8dd2214621a..4bad893cf37 100644 --- a/core/js/tests/specs/setupchecksSpec.js +++ b/core/js/tests/specs/setupchecksSpec.js @@ -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, }) ); diff --git a/core/register_command.php b/core/register_command.php index 4044d2d200c..16dda55878e 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -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()); |