diff options
Diffstat (limited to 'lib/private/Security/CertificateManager.php')
-rw-r--r-- | lib/private/Security/CertificateManager.php | 139 |
1 files changed, 51 insertions, 88 deletions
diff --git a/lib/private/Security/CertificateManager.php b/lib/private/Security/CertificateManager.php index fa26c19ceae..00babff735f 100644 --- a/lib/private/Security/CertificateManager.php +++ b/lib/private/Security/CertificateManager.php @@ -1,38 +1,14 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @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/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; -use OC\Files\Filesystem; +use OC\Files\View; use OCP\ICertificate; use OCP\ICertificateManager; use OCP\IConfig; @@ -43,31 +19,14 @@ use Psr\Log\LoggerInterface; * Manage trusted certificates for users */ class CertificateManager implements ICertificateManager { - /** - * @var \OC\Files\View - */ - protected $view; - - /** - * @var IConfig - */ - protected $config; - - protected LoggerInterface $logger; - - /** @var ISecureRandom */ - protected $random; - private ?string $bundlePath = null; - public function __construct(\OC\Files\View $view, - IConfig $config, - LoggerInterface $logger, - ISecureRandom $random) { - $this->view = $view; - $this->config = $config; - $this->logger = $logger; - $this->random = $random; + public function __construct( + protected View $view, + protected IConfig $config, + protected LoggerInterface $logger, + protected ISecureRandom $random, + ) { } /** @@ -76,7 +35,7 @@ class CertificateManager implements ICertificateManager { * @return \OCP\ICertificate[] */ public function listCertificates(): array { - if (!$this->config->getSystemValue('installed', false)) { + if (!$this->config->getSystemValueBool('installed', false)) { return []; } @@ -92,8 +51,14 @@ class CertificateManager implements ICertificateManager { while (false !== ($file = readdir($handle))) { if ($file != '.' && $file != '..') { try { - $result[] = new Certificate($this->view->file_get_contents($path . $file), $file); + $content = $this->view->file_get_contents($path . $file); + if ($content !== false) { + $result[] = new Certificate($content, $file); + } else { + $this->logger->error("Failed to read certificate from $path"); + } } catch (\Exception $e) { + $this->logger->error("Failed to read certificate from $path", ['exception' => $e]); } } } @@ -102,7 +67,7 @@ class CertificateManager implements ICertificateManager { } private function hasCertificates(): bool { - if (!$this->config->getSystemValue('installed', false)) { + if (!$this->config->getSystemValueBool('installed', false)) { return false; } @@ -147,6 +112,10 @@ class CertificateManager implements ICertificateManager { $tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS); $fhCerts = $this->view->fopen($tmpPath, 'w'); + if (!is_resource($fhCerts)) { + throw new \RuntimeException('Unable to open file handler to create certificate bundle "' . $tmpPath . '".'); + } + // Write user certificates foreach ($certs as $cert) { $file = $path . '/uploads/' . $cert->getName(); @@ -177,24 +146,22 @@ class CertificateManager implements ICertificateManager { * * @param string $certificate the certificate data * @param string $name the filename for the certificate - * @return \OCP\ICertificate * @throws \Exception If the certificate could not get added */ public function addCertificate(string $certificate, string $name): ICertificate { - if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) { - throw new \Exception('Filename is not valid'); - } + $path = $this->getPathToCertificates() . 'uploads/' . $name; + $directory = dirname($path); + + $this->view->verifyPath($directory, basename($path)); $this->bundlePath = null; - $dir = $this->getPathToCertificates() . 'uploads/'; - if (!$this->view->file_exists($dir)) { - $this->view->mkdir($dir); + if (!$this->view->file_exists($directory)) { + $this->view->mkdir($directory); } try { - $file = $dir . $name; $certificateObject = new Certificate($certificate, $name); - $this->view->file_put_contents($file, $certificate); + $this->view->file_put_contents($path, $certificate); $this->createCertificateBundle(); return $certificateObject; } catch (\Exception $e) { @@ -204,19 +171,19 @@ class CertificateManager implements ICertificateManager { /** * Remove the certificate and re-generate the certificate bundle - * - * @param string $name - * @return bool */ public function removeCertificate(string $name): bool { - if (!Filesystem::isValidPath($name)) { + $path = $this->getPathToCertificates() . 'uploads/' . $name; + + try { + $this->view->verifyPath(dirname($path), basename($path)); + } catch (\Exception) { return false; } - $this->bundlePath = null; - $path = $this->getPathToCertificates() . 'uploads/'; - if ($this->view->file_exists($path . $name)) { - $this->view->unlink($path . $name); + $this->bundlePath = null; + if ($this->view->file_exists($path)) { + $this->view->unlink($path); $this->createCertificateBundle(); } return true; @@ -224,8 +191,6 @@ class CertificateManager implements ICertificateManager { /** * Get the path to the certificate bundle - * - * @return string */ public function getCertificateBundle(): string { return $this->getPathToCertificates() . 'rootcerts.crt'; @@ -233,39 +198,39 @@ class CertificateManager implements ICertificateManager { /** * Get the full local path to the certificate bundle - * - * @return string + * @throws \Exception when getting bundle path fails */ public function getAbsoluteBundlePath(): string { try { - if (!$this->bundlePath) { + if ($this->bundlePath === null) { if (!$this->hasCertificates()) { $this->bundlePath = \OC::$SERVERROOT . '/resources/config/ca-bundle.crt'; - } + } else { + if ($this->needsRebundling()) { + $this->createCertificateBundle(); + } - if ($this->needsRebundling()) { - $this->createCertificateBundle(); - } + $certificateBundle = $this->getCertificateBundle(); + $this->bundlePath = $this->view->getLocalFile($certificateBundle) ?: null; - $this->bundlePath = $this->view->getLocalFile($this->getCertificateBundle()); + if ($this->bundlePath === null) { + throw new \RuntimeException('Unable to get certificate bundle "' . $certificateBundle . '".'); + } + } } return $this->bundlePath; } catch (\Exception $e) { + $this->logger->error('Failed to get absolute bundle path. Fallback to default ca-bundle.crt', ['exception' => $e]); return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt'; } } - /** - * @return string - */ private function getPathToCertificates(): string { return '/files_external/'; } /** * Check if we need to re-bundle the certificates because one of the sources has updated - * - * @return bool */ private function needsRebundling(): bool { $targetBundle = $this->getCertificateBundle(); @@ -279,8 +244,6 @@ class CertificateManager implements ICertificateManager { /** * get mtime of ca-bundle shipped by Nextcloud - * - * @return int */ protected function getFilemtimeOfCaBundle(): int { return filemtime(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); |