summaryrefslogtreecommitdiffstats
path: root/lib/private/Security
diff options
context:
space:
mode:
authorRoeland Jago Douma <rullzer@owncloud.com>2016-04-14 19:21:18 +0200
committerRoeland Jago Douma <rullzer@owncloud.com>2016-04-14 19:21:18 +0200
commit9050e76d955e06a5e5ed9b4b1c444bdf03699ba0 (patch)
treeeeded71dd4672fc9dc2f67296b0faff8cd62ab73 /lib/private/Security
parent5911ce530b003d46348f59e9280b610f684de85a (diff)
downloadnextcloud-server-9050e76d955e06a5e5ed9b4b1c444bdf03699ba0.tar.gz
nextcloud-server-9050e76d955e06a5e5ed9b4b1c444bdf03699ba0.zip
Move \OC\Security to PSR-4
Diffstat (limited to 'lib/private/Security')
-rw-r--r--lib/private/Security/CSP/ContentSecurityPolicy.php199
-rw-r--r--lib/private/Security/CSP/ContentSecurityPolicyManager.php73
-rw-r--r--lib/private/Security/CSRF/CsrfToken.php69
-rw-r--r--lib/private/Security/CSRF/CsrfTokenGenerator.php52
-rw-r--r--lib/private/Security/CSRF/CsrfTokenManager.php97
-rw-r--r--lib/private/Security/CSRF/TokenStorage/SessionStorage.php80
-rw-r--r--lib/private/Security/Certificate.php122
-rw-r--r--lib/private/Security/CertificateManager.php250
-rw-r--r--lib/private/Security/CredentialsManager.php125
-rw-r--r--lib/private/Security/Crypto.php134
-rw-r--r--lib/private/Security/Hasher.php160
-rw-r--r--lib/private/Security/SecureRandom.php86
-rw-r--r--lib/private/Security/TrustedDomainHelper.php90
13 files changed, 1537 insertions, 0 deletions
diff --git a/lib/private/Security/CSP/ContentSecurityPolicy.php b/lib/private/Security/CSP/ContentSecurityPolicy.php
new file mode 100644
index 00000000000..25eacfab1d6
--- /dev/null
+++ b/lib/private/Security/CSP/ContentSecurityPolicy.php
@@ -0,0 +1,199 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSP;
+
+/**
+ * Class ContentSecurityPolicy extends the public class and adds getter and setters.
+ * This is necessary since we don't want to expose the setters and getters to the
+ * public API.
+ *
+ * @package OC\Security\CSP
+ */
+class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy {
+ /**
+ * @return boolean
+ */
+ public function isInlineScriptAllowed() {
+ return $this->inlineScriptAllowed;
+ }
+
+ /**
+ * @param boolean $inlineScriptAllowed
+ */
+ public function setInlineScriptAllowed($inlineScriptAllowed) {
+ $this->inlineScriptAllowed = $inlineScriptAllowed;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isEvalScriptAllowed() {
+ return $this->evalScriptAllowed;
+ }
+
+ /**
+ * @param boolean $evalScriptAllowed
+ */
+ public function setEvalScriptAllowed($evalScriptAllowed) {
+ $this->evalScriptAllowed = $evalScriptAllowed;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedScriptDomains() {
+ return $this->allowedScriptDomains;
+ }
+
+ /**
+ * @param array $allowedScriptDomains
+ */
+ public function setAllowedScriptDomains($allowedScriptDomains) {
+ $this->allowedScriptDomains = $allowedScriptDomains;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isInlineStyleAllowed() {
+ return $this->inlineStyleAllowed;
+ }
+
+ /**
+ * @param boolean $inlineStyleAllowed
+ */
+ public function setInlineStyleAllowed($inlineStyleAllowed) {
+ $this->inlineStyleAllowed = $inlineStyleAllowed;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedStyleDomains() {
+ return $this->allowedStyleDomains;
+ }
+
+ /**
+ * @param array $allowedStyleDomains
+ */
+ public function setAllowedStyleDomains($allowedStyleDomains) {
+ $this->allowedStyleDomains = $allowedStyleDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedImageDomains() {
+ return $this->allowedImageDomains;
+ }
+
+ /**
+ * @param array $allowedImageDomains
+ */
+ public function setAllowedImageDomains($allowedImageDomains) {
+ $this->allowedImageDomains = $allowedImageDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedConnectDomains() {
+ return $this->allowedConnectDomains;
+ }
+
+ /**
+ * @param array $allowedConnectDomains
+ */
+ public function setAllowedConnectDomains($allowedConnectDomains) {
+ $this->allowedConnectDomains = $allowedConnectDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedMediaDomains() {
+ return $this->allowedMediaDomains;
+ }
+
+ /**
+ * @param array $allowedMediaDomains
+ */
+ public function setAllowedMediaDomains($allowedMediaDomains) {
+ $this->allowedMediaDomains = $allowedMediaDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedObjectDomains() {
+ return $this->allowedObjectDomains;
+ }
+
+ /**
+ * @param array $allowedObjectDomains
+ */
+ public function setAllowedObjectDomains($allowedObjectDomains) {
+ $this->allowedObjectDomains = $allowedObjectDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedFrameDomains() {
+ return $this->allowedFrameDomains;
+ }
+
+ /**
+ * @param array $allowedFrameDomains
+ */
+ public function setAllowedFrameDomains($allowedFrameDomains) {
+ $this->allowedFrameDomains = $allowedFrameDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedFontDomains() {
+ return $this->allowedFontDomains;
+ }
+
+ /**
+ * @param array $allowedFontDomains
+ */
+ public function setAllowedFontDomains($allowedFontDomains) {
+ $this->allowedFontDomains = $allowedFontDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedChildSrcDomains() {
+ return $this->allowedChildSrcDomains;
+ }
+
+ /**
+ * @param array $allowedChildSrcDomains
+ */
+ public function setAllowedChildSrcDomains($allowedChildSrcDomains) {
+ $this->allowedChildSrcDomains = $allowedChildSrcDomains;
+ }
+
+}
diff --git a/lib/private/Security/CSP/ContentSecurityPolicyManager.php b/lib/private/Security/CSP/ContentSecurityPolicyManager.php
new file mode 100644
index 00000000000..760cd36e56b
--- /dev/null
+++ b/lib/private/Security/CSP/ContentSecurityPolicyManager.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSP;
+
+use OCP\AppFramework\Http\ContentSecurityPolicy;
+use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
+use OCP\Security\IContentSecurityPolicyManager;
+
+class ContentSecurityPolicyManager implements IContentSecurityPolicyManager {
+ /** @var ContentSecurityPolicy[] */
+ private $policies = [];
+
+ /** {@inheritdoc} */
+ public function addDefaultPolicy(EmptyContentSecurityPolicy $policy) {
+ $this->policies[] = $policy;
+ }
+
+ /**
+ * Get the configured default policy. This is not in the public namespace
+ * as it is only supposed to be used by core itself.
+ *
+ * @return ContentSecurityPolicy
+ */
+ public function getDefaultPolicy() {
+ $defaultPolicy = new \OC\Security\CSP\ContentSecurityPolicy();
+ foreach($this->policies as $policy) {
+ $defaultPolicy = $this->mergePolicies($defaultPolicy, $policy);
+ }
+ return $defaultPolicy;
+ }
+
+ /**
+ * Merges the first given policy with the second one
+ *
+ * @param ContentSecurityPolicy $defaultPolicy
+ * @param EmptyContentSecurityPolicy $originalPolicy
+ * @return ContentSecurityPolicy
+ */
+ public function mergePolicies(ContentSecurityPolicy $defaultPolicy,
+ EmptyContentSecurityPolicy $originalPolicy) {
+ foreach((object)(array)$originalPolicy as $name => $value) {
+ $setter = 'set'.ucfirst($name);
+ if(is_array($value)) {
+ $getter = 'get'.ucfirst($name);
+ $currentValues = is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : [];
+ $defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value))));
+ } elseif (is_bool($value)) {
+ $defaultPolicy->$setter($value);
+ }
+ }
+
+ return $defaultPolicy;
+ }
+}
diff --git a/lib/private/Security/CSRF/CsrfToken.php b/lib/private/Security/CSRF/CsrfToken.php
new file mode 100644
index 00000000000..4524d0db6e6
--- /dev/null
+++ b/lib/private/Security/CSRF/CsrfToken.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSRF;
+
+/**
+ * Class CsrfToken represents the stored or provided CSRF token. To mitigate
+ * BREACH alike vulnerabilities the token is returned in an encrypted value as
+ * well in an unencrypted value. For display measures to the user always the
+ * unencrypted one should be chosen.
+ *
+ * @package OC\Security\CSRF
+ */
+class CsrfToken {
+ /** @var string */
+ private $value;
+
+ /**
+ * @param string $value Value of the token. Can be encrypted or not encrypted.
+ */
+ public function __construct($value) {
+ $this->value = $value;
+ }
+
+ /**
+ * Encrypted value of the token. This is used to mitigate BREACH alike
+ * vulnerabilities. For display measures do use this functionality.
+ *
+ * @return string
+ */
+ public function getEncryptedValue() {
+ $sharedSecret = base64_encode(random_bytes(strlen($this->value)));
+ return base64_encode($this->value ^ $sharedSecret) .':'.$sharedSecret;
+ }
+
+ /**
+ * The unencrypted value of the token. Used for decrypting an already
+ * encrypted token.
+ *
+ * @return int
+ */
+ public function getDecryptedValue() {
+ $token = explode(':', $this->value);
+ if (count($token) !== 2) {
+ return '';
+ }
+ $obfuscatedToken = $token[0];
+ $secret = $token[1];
+ return base64_decode($obfuscatedToken) ^ $secret;
+ }
+}
diff --git a/lib/private/Security/CSRF/CsrfTokenGenerator.php b/lib/private/Security/CSRF/CsrfTokenGenerator.php
new file mode 100644
index 00000000000..6ea71636d22
--- /dev/null
+++ b/lib/private/Security/CSRF/CsrfTokenGenerator.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSRF;
+
+use OCP\Security\ISecureRandom;
+
+/**
+ * Class CsrfTokenGenerator is used to generate a cryptographically secure
+ * pseudo-random number for the token.
+ *
+ * @package OC\Security\CSRF
+ */
+class CsrfTokenGenerator {
+ /** @var ISecureRandom */
+ private $random;
+
+ /**
+ * @param ISecureRandom $random
+ */
+ public function __construct(ISecureRandom $random) {
+ $this->random = $random;
+ }
+
+ /**
+ * Generate a new CSRF token.
+ *
+ * @param int $length Length of the token in characters.
+ * @return string
+ */
+ public function generateToken($length = 32) {
+ return $this->random->generate($length);
+ }
+}
diff --git a/lib/private/Security/CSRF/CsrfTokenManager.php b/lib/private/Security/CSRF/CsrfTokenManager.php
new file mode 100644
index 00000000000..8d1bf5c0819
--- /dev/null
+++ b/lib/private/Security/CSRF/CsrfTokenManager.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSRF;
+
+use OC\Security\CSRF\TokenStorage\SessionStorage;
+
+/**
+ * Class CsrfTokenManager is the manager for all CSRF token related activities.
+ *
+ * @package OC\Security\CSRF
+ */
+class CsrfTokenManager {
+ /** @var CsrfTokenGenerator */
+ private $tokenGenerator;
+ /** @var SessionStorage */
+ private $sessionStorage;
+
+ /**
+ * @param CsrfTokenGenerator $tokenGenerator
+ * @param SessionStorage $storageInterface
+ */
+ public function __construct(CsrfTokenGenerator $tokenGenerator,
+ SessionStorage $storageInterface) {
+ $this->tokenGenerator = $tokenGenerator;
+ $this->sessionStorage = $storageInterface;
+ }
+
+ /**
+ * Returns the current CSRF token, if none set it will create a new one.
+ *
+ * @return CsrfToken
+ */
+ public function getToken() {
+ if($this->sessionStorage->hasToken()) {
+ $value = $this->sessionStorage->getToken();
+ } else {
+ $value = $this->tokenGenerator->generateToken();
+ $this->sessionStorage->setToken($value);
+ }
+
+ return new CsrfToken($value);
+ }
+
+ /**
+ * Invalidates any current token and sets a new one.
+ *
+ * @return CsrfToken
+ */
+ public function refreshToken() {
+ $value = $this->tokenGenerator->generateToken();
+ $this->sessionStorage->setToken($value);
+ return new CsrfToken($value);
+ }
+
+ /**
+ * Remove the current token from the storage.
+ */
+ public function removeToken() {
+ $this->sessionStorage->removeToken();
+ }
+
+ /**
+ * Verifies whether the provided token is valid.
+ *
+ * @param CsrfToken $token
+ * @return bool
+ */
+ public function isTokenValid(CsrfToken $token) {
+ if(!$this->sessionStorage->hasToken()) {
+ return false;
+ }
+
+ return hash_equals(
+ $this->sessionStorage->getToken(),
+ $token->getDecryptedValue()
+ );
+ }
+}
diff --git a/lib/private/Security/CSRF/TokenStorage/SessionStorage.php b/lib/private/Security/CSRF/TokenStorage/SessionStorage.php
new file mode 100644
index 00000000000..e1c8c96e920
--- /dev/null
+++ b/lib/private/Security/CSRF/TokenStorage/SessionStorage.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSRF\TokenStorage;
+
+use OCP\ISession;
+
+/**
+ * Class SessionStorage provides the session storage
+ *
+ * @package OC\Security\CSRF\TokenStorage
+ */
+class SessionStorage {
+ /** @var ISession */
+ private $session;
+
+ /**
+ * @param ISession $session
+ */
+ public function __construct(ISession $session) {
+ $this->session = $session;
+ }
+
+ /**
+ * Returns the current token or throws an exception if none is found.
+ *
+ * @return string
+ * @throws \Exception
+ */
+ public function getToken() {
+ $token = $this->session->get('requesttoken');
+ if(empty($token)) {
+ throw new \Exception('Session does not contain a requesttoken');
+ }
+
+ return $token;
+ }
+
+ /**
+ * Set the valid current token to $value.
+ *
+ * @param string $value
+ */
+ public function setToken($value) {
+ $this->session->set('requesttoken', $value);
+ }
+
+ /**
+ * Removes the current token.
+ */
+ public function removeToken() {
+ $this->session->remove('requesttoken');
+ }
+ /**
+ * Whether the storage has a storage.
+ *
+ * @return bool
+ */
+ public function hasToken() {
+ return $this->session->exists('requesttoken');
+ }
+}
diff --git a/lib/private/Security/Certificate.php b/lib/private/Security/Certificate.php
new file mode 100644
index 00000000000..54486ff51fe
--- /dev/null
+++ b/lib/private/Security/Certificate.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+
+use OCP\ICertificate;
+
+class Certificate implements ICertificate {
+ protected $name;
+
+ protected $commonName;
+
+ protected $organization;
+
+ protected $serial;
+
+ protected $issueDate;
+
+ protected $expireDate;
+
+ protected $issuerName;
+
+ protected $issuerOrganization;
+
+ /**
+ * @param string $data base64 encoded certificate
+ * @param string $name
+ * @throws \Exception If the certificate could not get parsed
+ */
+ public function __construct($data, $name) {
+ $this->name = $name;
+ $gmt = new \DateTimeZone('GMT');
+ $info = openssl_x509_parse($data);
+ if(!is_array($info)) {
+ throw new \Exception('Certificate could not get parsed.');
+ }
+
+ $this->commonName = isset($info['subject']['CN']) ? $info['subject']['CN'] : null;
+ $this->organization = isset($info['subject']['O']) ? $info['subject']['O'] : null;
+ $this->issueDate = new \DateTime('@' . $info['validFrom_time_t'], $gmt);
+ $this->expireDate = new \DateTime('@' . $info['validTo_time_t'], $gmt);
+ $this->issuerName = isset($info['issuer']['CN']) ? $info['issuer']['CN'] : null;
+ $this->issuerOrganization = isset($info['issuer']['O']) ? $info['issuer']['O'] : null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getCommonName() {
+ return $this->commonName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getOrganization() {
+ return $this->organization;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getIssueDate() {
+ return $this->issueDate;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getExpireDate() {
+ return $this->expireDate;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isExpired() {
+ $now = new \DateTime();
+ return $this->issueDate > $now or $now > $this->expireDate;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getIssuerName() {
+ return $this->issuerName;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getIssuerOrganization() {
+ return $this->issuerOrganization;
+ }
+}
diff --git a/lib/private/Security/CertificateManager.php b/lib/private/Security/CertificateManager.php
new file mode 100644
index 00000000000..f4932ca568e
--- /dev/null
+++ b/lib/private/Security/CertificateManager.php
@@ -0,0 +1,250 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+
+use OC\Files\Filesystem;
+use OCP\ICertificateManager;
+use OCP\IConfig;
+
+/**
+ * Manage trusted certificates for users
+ */
+class CertificateManager implements ICertificateManager {
+ /**
+ * @var string
+ */
+ protected $uid;
+
+ /**
+ * @var \OC\Files\View
+ */
+ protected $view;
+
+ /**
+ * @var IConfig
+ */
+ protected $config;
+
+ /**
+ * @param string $uid
+ * @param \OC\Files\View $view relative to data/
+ * @param IConfig $config
+ */
+ public function __construct($uid, \OC\Files\View $view, IConfig $config) {
+ $this->uid = $uid;
+ $this->view = $view;
+ $this->config = $config;
+ }
+
+ /**
+ * Returns all certificates trusted by the user
+ *
+ * @return \OCP\ICertificate[]
+ */
+ public function listCertificates() {
+
+ if (!$this->config->getSystemValue('installed', false)) {
+ return array();
+ }
+
+ $path = $this->getPathToCertificates() . 'uploads/';
+ if (!$this->view->is_dir($path)) {
+ return array();
+ }
+ $result = array();
+ $handle = $this->view->opendir($path);
+ if (!is_resource($handle)) {
+ return array();
+ }
+ while (false !== ($file = readdir($handle))) {
+ if ($file != '.' && $file != '..') {
+ try {
+ $result[] = new Certificate($this->view->file_get_contents($path . $file), $file);
+ } catch (\Exception $e) {
+ }
+ }
+ }
+ closedir($handle);
+ return $result;
+ }
+
+ /**
+ * create the certificate bundle of all trusted certificated
+ */
+ public function createCertificateBundle() {
+ $path = $this->getPathToCertificates();
+ $certs = $this->listCertificates();
+
+ if (!$this->view->file_exists($path)) {
+ $this->view->mkdir($path);
+ }
+
+ $fhCerts = $this->view->fopen($path . '/rootcerts.crt', 'w');
+
+ // Write user certificates
+ foreach ($certs as $cert) {
+ $file = $path . '/uploads/' . $cert->getName();
+ $data = $this->view->file_get_contents($file);
+ if (strpos($data, 'BEGIN CERTIFICATE')) {
+ fwrite($fhCerts, $data);
+ fwrite($fhCerts, "\r\n");
+ }
+ }
+
+ // Append the default certificates
+ $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
+ fwrite($fhCerts, $defaultCertificates);
+
+ // Append the system certificate bundle
+ $systemBundle = $this->getCertificateBundle(null);
+ if ($this->view->file_exists($systemBundle)) {
+ $systemCertificates = $this->view->file_get_contents($systemBundle);
+ fwrite($fhCerts, $systemCertificates);
+ }
+
+ fclose($fhCerts);
+ }
+
+ /**
+ * Save the certificate and re-generate the certificate bundle
+ *
+ * @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($certificate, $name) {
+ if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) {
+ throw new \Exception('Filename is not valid');
+ }
+
+ $dir = $this->getPathToCertificates() . 'uploads/';
+ if (!$this->view->file_exists($dir)) {
+ $this->view->mkdir($dir);
+ }
+
+ try {
+ $file = $dir . $name;
+ $certificateObject = new Certificate($certificate, $name);
+ $this->view->file_put_contents($file, $certificate);
+ $this->createCertificateBundle();
+ return $certificateObject;
+ } catch (\Exception $e) {
+ throw $e;
+ }
+
+ }
+
+ /**
+ * Remove the certificate and re-generate the certificate bundle
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function removeCertificate($name) {
+ if (!Filesystem::isValidPath($name)) {
+ return false;
+ }
+ $path = $this->getPathToCertificates() . 'uploads/';
+ if ($this->view->file_exists($path . $name)) {
+ $this->view->unlink($path . $name);
+ $this->createCertificateBundle();
+ }
+ return true;
+ }
+
+ /**
+ * Get the path to the certificate bundle for this user
+ *
+ * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle
+ * @return string
+ */
+ public function getCertificateBundle($uid = '') {
+ if ($uid === '') {
+ $uid = $this->uid;
+ }
+ return $this->getPathToCertificates($uid) . 'rootcerts.crt';
+ }
+
+ /**
+ * Get the full local path to the certificate bundle for this user
+ *
+ * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle
+ * @return string
+ */
+ public function getAbsoluteBundlePath($uid = '') {
+ if ($uid === '') {
+ $uid = $this->uid;
+ }
+ if ($this->needsRebundling($uid)) {
+ if (is_null($uid)) {
+ $manager = new CertificateManager(null, $this->view, $this->config);
+ $manager->createCertificateBundle();
+ } else {
+ $this->createCertificateBundle();
+ }
+ }
+ return $this->view->getLocalFile($this->getCertificateBundle($uid));
+ }
+
+ /**
+ * @param string $uid (optional) user to get the certificate path for, use `null` to get the system path
+ * @return string
+ */
+ private function getPathToCertificates($uid = '') {
+ if ($uid === '') {
+ $uid = $this->uid;
+ }
+ $path = is_null($uid) ? '/files_external/' : '/' . $uid . '/files_external/';
+
+ return $path;
+ }
+
+ /**
+ * Check if we need to re-bundle the certificates because one of the sources has updated
+ *
+ * @param string $uid (optional) user to get the certificate path for, use `null` to get the system path
+ * @return bool
+ */
+ private function needsRebundling($uid = '') {
+ if ($uid === '') {
+ $uid = $this->uid;
+ }
+ $sourceMTimes = [filemtime(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt')];
+ $targetBundle = $this->getCertificateBundle($uid);
+ if (!$this->view->file_exists($targetBundle)) {
+ return true;
+ }
+ if (!is_null($uid)) { // also depend on the system bundle
+ $sourceBundles[] = $this->view->filemtime($this->getCertificateBundle(null));
+ }
+
+ $sourceMTime = array_reduce($sourceMTimes, function ($max, $mtime) {
+ return max($max, $mtime);
+ }, 0);
+ return $sourceMTime > $this->view->filemtime($targetBundle);
+ }
+}
diff --git a/lib/private/Security/CredentialsManager.php b/lib/private/Security/CredentialsManager.php
new file mode 100644
index 00000000000..d4104dbe712
--- /dev/null
+++ b/lib/private/Security/CredentialsManager.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+
+use OCP\Security\ICrypto;
+use OCP\IDBConnection;
+use OCP\Security\ICredentialsManager;
+use OCP\IConfig;
+
+/**
+ * Store and retrieve credentials for external services
+ *
+ * @package OC\Security
+ */
+class CredentialsManager implements ICredentialsManager {
+
+ const DB_TABLE = 'credentials';
+
+ /** @var ICrypto */
+ protected $crypto;
+
+ /** @var IDBConnection */
+ protected $dbConnection;
+
+ /**
+ * @param ICrypto $crypto
+ * @param IDBConnection $dbConnection
+ */
+ public function __construct(ICrypto $crypto, IDBConnection $dbConnection) {
+ $this->crypto = $crypto;
+ $this->dbConnection = $dbConnection;
+ }
+
+ /**
+ * Store a set of credentials
+ *
+ * @param string|null $userId Null for system-wide credentials
+ * @param string $identifier
+ * @param mixed $credentials
+ */
+ public function store($userId, $identifier, $credentials) {
+ $value = $this->crypto->encrypt(json_encode($credentials));
+
+ $this->dbConnection->setValues(self::DB_TABLE, [
+ 'user' => $userId,
+ 'identifier' => $identifier,
+ ], [
+ 'credentials' => $value,
+ ]);
+ }
+
+ /**
+ * Retrieve a set of credentials
+ *
+ * @param string|null $userId Null for system-wide credentials
+ * @param string $identifier
+ * @return mixed
+ */
+ public function retrieve($userId, $identifier) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->select('credentials')
+ ->from(self::DB_TABLE)
+ ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
+ ->andWhere($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier)))
+ ;
+ $result = $qb->execute()->fetch();
+
+ if (!$result) {
+ return null;
+ }
+ $value = $result['credentials'];
+
+ return json_decode($this->crypto->decrypt($value), true);
+ }
+
+ /**
+ * Delete a set of credentials
+ *
+ * @param string|null $userId Null for system-wide credentials
+ * @param string $identifier
+ * @return int rows removed
+ */
+ public function delete($userId, $identifier) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->delete(self::DB_TABLE)
+ ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
+ ->andWhere($qb->expr()->eq('identifier', $qb->createNamedParameter($identifier)))
+ ;
+ return $qb->execute();
+ }
+
+ /**
+ * Erase all credentials stored for a user
+ *
+ * @param string $userId
+ * @return int rows removed
+ */
+ public function erase($userId) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->delete(self::DB_TABLE)
+ ->where($qb->expr()->eq('user', $qb->createNamedParameter($userId)))
+ ;
+ return $qb->execute();
+ }
+
+}
diff --git a/lib/private/Security/Crypto.php b/lib/private/Security/Crypto.php
new file mode 100644
index 00000000000..3c3ffb47398
--- /dev/null
+++ b/lib/private/Security/Crypto.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+
+use phpseclib\Crypt\AES;
+use phpseclib\Crypt\Hash;
+use OCP\Security\ICrypto;
+use OCP\Security\ISecureRandom;
+use OCP\IConfig;
+
+/**
+ * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided
+ * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd.
+ *
+ * Usage:
+ * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText');
+ * $encryptWithCustompassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password');
+ *
+ * @package OC\Security
+ */
+class Crypto implements ICrypto {
+ /** @var AES $cipher */
+ private $cipher;
+ /** @var int */
+ private $ivLength = 16;
+ /** @var IConfig */
+ private $config;
+ /** @var ISecureRandom */
+ private $random;
+
+ /**
+ * @param IConfig $config
+ * @param ISecureRandom $random
+ */
+ function __construct(IConfig $config, ISecureRandom $random) {
+ $this->cipher = new AES();
+ $this->config = $config;
+ $this->random = $random;
+ }
+
+ /**
+ * @param string $message The message to authenticate
+ * @param string $password Password to use (defaults to `secret` in config.php)
+ * @return string Calculated HMAC
+ */
+ public function calculateHMAC($message, $password = '') {
+ if($password === '') {
+ $password = $this->config->getSystemValue('secret');
+ }
+
+ // Append an "a" behind the password and hash it to prevent reusing the same password as for encryption
+ $password = hash('sha512', $password . 'a');
+
+ $hash = new Hash('sha512');
+ $hash->setKey($password);
+ return $hash->hash($message);
+ }
+
+ /**
+ * Encrypts a value and adds an HMAC (Encrypt-Then-MAC)
+ * @param string $plaintext
+ * @param string $password Password to encrypt, if not specified the secret from config.php will be taken
+ * @return string Authenticated ciphertext
+ */
+ public function encrypt($plaintext, $password = '') {
+ if($password === '') {
+ $password = $this->config->getSystemValue('secret');
+ }
+ $this->cipher->setPassword($password);
+
+ $iv = $this->random->generate($this->ivLength);
+ $this->cipher->setIV($iv);
+
+ $ciphertext = bin2hex($this->cipher->encrypt($plaintext));
+ $hmac = bin2hex($this->calculateHMAC($ciphertext.$iv, $password));
+
+ return $ciphertext.'|'.$iv.'|'.$hmac;
+ }
+
+ /**
+ * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac)
+ * @param string $authenticatedCiphertext
+ * @param string $password Password to encrypt, if not specified the secret from config.php will be taken
+ * @return string plaintext
+ * @throws \Exception If the HMAC does not match
+ */
+ public function decrypt($authenticatedCiphertext, $password = '') {
+ if($password === '') {
+ $password = $this->config->getSystemValue('secret');
+ }
+ $this->cipher->setPassword($password);
+
+ $parts = explode('|', $authenticatedCiphertext);
+ if(sizeof($parts) !== 3) {
+ throw new \Exception('Authenticated ciphertext could not be decoded.');
+ }
+
+ $ciphertext = hex2bin($parts[0]);
+ $iv = $parts[1];
+ $hmac = hex2bin($parts[2]);
+
+ $this->cipher->setIV($iv);
+
+ if(!hash_equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) {
+ throw new \Exception('HMAC does not match.');
+ }
+
+ return $this->cipher->decrypt($ciphertext);
+ }
+
+}
diff --git a/lib/private/Security/Hasher.php b/lib/private/Security/Hasher.php
new file mode 100644
index 00000000000..a8b81aa60eb
--- /dev/null
+++ b/lib/private/Security/Hasher.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+
+use OCP\IConfig;
+use OCP\Security\IHasher;
+
+/**
+ * Class Hasher provides some basic hashing functions. Furthermore, it supports legacy hashes
+ * used by previous versions of ownCloud and helps migrating those hashes to newer ones.
+ *
+ * The hashes generated by this class are prefixed (version|hash) with a version parameter to allow possible
+ * updates in the future.
+ * Possible versions:
+ * - 1 (Initial version)
+ *
+ * Usage:
+ * // Hashing a message
+ * $hash = \OC::$server->getHasher()->hash('MessageToHash');
+ * // Verifying a message - $newHash will contain the newly calculated hash
+ * $newHash = null;
+ * var_dump(\OC::$server->getHasher()->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash));
+ * var_dump($newHash);
+ *
+ * @package OC\Security
+ */
+class Hasher implements IHasher {
+ /** @var IConfig */
+ private $config;
+ /** @var array Options passed to password_hash and password_needs_rehash */
+ private $options = array();
+ /** @var string Salt used for legacy passwords */
+ private $legacySalt = null;
+ /** @var int Current version of the generated hash */
+ private $currentVersion = 1;
+
+ /**
+ * @param IConfig $config
+ */
+ function __construct(IConfig $config) {
+ $this->config = $config;
+
+ $hashingCost = $this->config->getSystemValue('hashingCost', null);
+ if(!is_null($hashingCost)) {
+ $this->options['cost'] = $hashingCost;
+ }
+ }
+
+ /**
+ * Hashes a message using PHP's `password_hash` functionality.
+ * Please note that the size of the returned string is not guaranteed
+ * and can be up to 255 characters.
+ *
+ * @param string $message Message to generate hash from
+ * @return string Hash of the message with appended version parameter
+ */
+ public function hash($message) {
+ return $this->currentVersion . '|' . password_hash($message, PASSWORD_DEFAULT, $this->options);
+ }
+
+ /**
+ * Get the version and hash from a prefixedHash
+ * @param string $prefixedHash
+ * @return null|array Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo')
+ */
+ protected function splitHash($prefixedHash) {
+ $explodedString = explode('|', $prefixedHash, 2);
+ if(sizeof($explodedString) === 2) {
+ if((int)$explodedString[0] > 0) {
+ return array('version' => (int)$explodedString[0], 'hash' => $explodedString[1]);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Verify legacy hashes
+ * @param string $message Message to verify
+ * @param string $hash Assumed hash of the message
+ * @param null|string &$newHash Reference will contain the updated hash
+ * @return bool Whether $hash is a valid hash of $message
+ */
+ protected function legacyHashVerify($message, $hash, &$newHash = null) {
+ if(empty($this->legacySalt)) {
+ $this->legacySalt = $this->config->getSystemValue('passwordsalt', '');
+ }
+
+ // Verify whether it matches a legacy PHPass or SHA1 string
+ $hashLength = strlen($hash);
+ if($hashLength === 60 && password_verify($message.$this->legacySalt, $hash) ||
+ $hashLength === 40 && hash_equals($hash, sha1($message))) {
+ $newHash = $this->hash($message);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Verify V1 hashes
+ * @param string $message Message to verify
+ * @param string $hash Assumed hash of the message
+ * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
+ * @return bool Whether $hash is a valid hash of $message
+ */
+ protected function verifyHashV1($message, $hash, &$newHash = null) {
+ if(password_verify($message, $hash)) {
+ if(password_needs_rehash($hash, PASSWORD_DEFAULT, $this->options)) {
+ $newHash = $this->hash($message);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param string $message Message to verify
+ * @param string $hash Assumed hash of the message
+ * @param null|string &$newHash Reference will contain the updated hash if necessary. Update the existing hash with this one.
+ * @return bool Whether $hash is a valid hash of $message
+ */
+ public function verify($message, $hash, &$newHash = null) {
+ $splittedHash = $this->splitHash($hash);
+
+ if(isset($splittedHash['version'])) {
+ switch ($splittedHash['version']) {
+ case 1:
+ return $this->verifyHashV1($message, $splittedHash['hash'], $newHash);
+ }
+ } else {
+ return $this->legacyHashVerify($message, $hash, $newHash);
+ }
+
+
+ return false;
+ }
+
+}
diff --git a/lib/private/Security/SecureRandom.php b/lib/private/Security/SecureRandom.php
new file mode 100644
index 00000000000..45cb3f17ee4
--- /dev/null
+++ b/lib/private/Security/SecureRandom.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+
+use RandomLib;
+use Sabre\DAV\Exception;
+use OCP\Security\ISecureRandom;
+
+/**
+ * Class SecureRandom provides a wrapper around the random_int function to generate
+ * secure random strings. For PHP 7 the native CSPRNG is used, older versions do
+ * use a fallback.
+ *
+ * Usage:
+ * \OC::$server->getSecureRandom()->generate(10);
+ * @package OC\Security
+ */
+class SecureRandom implements ISecureRandom {
+ /**
+ * Convenience method to get a low strength random number generator.
+ *
+ * Low Strength should be used anywhere that random strings are needed
+ * in a non-cryptographical setting. They are not strong enough to be
+ * used as keys or salts. They are however useful for one-time use tokens.
+ *
+ * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int()
+ * @return $this
+ */
+ public function getLowStrengthGenerator() {
+ return $this;
+ }
+
+ /**
+ * Convenience method to get a medium strength random number generator.
+ *
+ * Medium Strength should be used for most needs of a cryptographic nature.
+ * They are strong enough to be used as keys and salts. However, they do
+ * take some time and resources to generate, so they should not be over-used
+ *
+ * @deprecated 9.0.0 Use \OC\Security\SecureRandom::generate directly or random_bytes() / random_int()
+ * @return $this
+ */
+ public function getMediumStrengthGenerator() {
+ return $this;
+ }
+
+ /**
+ * Generate a random string of specified length.
+ * @param int $length The length of the generated string
+ * @param string $characters An optional list of characters to use if no character list is
+ * specified all valid base64 characters are used.
+ * @return string
+ */
+ public function generate($length,
+ $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') {
+ $maxCharIndex = strlen($characters) - 1;
+ $randomString = '';
+
+ while($length > 0) {
+ $randomNumber = \random_int(0, $maxCharIndex);
+ $randomString .= $characters[$randomNumber];
+ $length--;
+ }
+ return $randomString;
+ }
+}
diff --git a/lib/private/Security/TrustedDomainHelper.php b/lib/private/Security/TrustedDomainHelper.php
new file mode 100644
index 00000000000..409628677a7
--- /dev/null
+++ b/lib/private/Security/TrustedDomainHelper.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, 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\Security;
+use OC\AppFramework\Http\Request;
+use OCP\IConfig;
+
+/**
+ * Class TrustedDomain
+ *
+ * @package OC\Security
+ */
+class TrustedDomainHelper {
+ /** @var IConfig */
+ private $config;
+
+ /**
+ * @param IConfig $config
+ */
+ function __construct(IConfig $config) {
+ $this->config = $config;
+ }
+
+ /**
+ * Strips a potential port from a domain (in format domain:port)
+ * @param string $host
+ * @return string $host without appended port
+ */
+ private function getDomainWithoutPort($host) {
+ $pos = strrpos($host, ':');
+ if ($pos !== false) {
+ $port = substr($host, $pos + 1);
+ if (is_numeric($port)) {
+ $host = substr($host, 0, $pos);
+ }
+ }
+ return $host;
+ }
+
+ /**
+ * Checks whether a domain is considered as trusted from the list
+ * of trusted domains. If no trusted domains have been configured, returns
+ * true.
+ * This is used to prevent Host Header Poisoning.
+ * @param string $domainWithPort
+ * @return bool true if the given domain is trusted or if no trusted domains
+ * have been configured
+ */
+ public function isTrustedDomain($domainWithPort) {
+ $domain = $this->getDomainWithoutPort($domainWithPort);
+
+ // Read trusted domains from config
+ $trustedList = $this->config->getSystemValue('trusted_domains', []);
+ if(!is_array($trustedList)) {
+ return false;
+ }
+
+ // Always allow access from localhost
+ if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) {
+ return true;
+ }
+
+ // Compare with port appended
+ if(in_array($domainWithPort, $trustedList, true)) {
+ return true;
+ }
+
+ return in_array($domain, $trustedList, true);
+ }
+
+}