diff options
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/app.php | 5 | ||||
-rw-r--r-- | lib/private/appframework/http/request.php | 288 | ||||
-rw-r--r-- | lib/private/connector/sabre/file.php | 30 | ||||
-rw-r--r-- | lib/private/connector/sabre/request.php | 2 | ||||
-rw-r--r-- | lib/private/installer.php | 1 | ||||
-rw-r--r-- | lib/private/log/owncloud.php | 5 | ||||
-rw-r--r-- | lib/private/request.php | 330 | ||||
-rw-r--r-- | lib/private/response.php | 11 | ||||
-rw-r--r-- | lib/private/route/router.php | 5 | ||||
-rw-r--r-- | lib/private/security/trusteddomainhelper.php | 75 | ||||
-rw-r--r-- | lib/private/server.php | 86 | ||||
-rw-r--r-- | lib/private/setup.php | 6 | ||||
-rw-r--r-- | lib/private/template.php | 5 | ||||
-rw-r--r-- | lib/private/templatelayout.php | 10 | ||||
-rw-r--r-- | lib/private/urlgenerator.php | 3 | ||||
-rw-r--r-- | lib/private/util.php | 7 |
16 files changed, 456 insertions, 413 deletions
diff --git a/lib/private/app.php b/lib/private/app.php index f41cb82c36b..1af2c36e19f 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -665,10 +665,11 @@ class OC_App { * @return string */ public static function getCurrentApp() { - $script = substr(OC_Request::scriptName(), strlen(OC::$WEBROOT) + 1); + $request = \OC::$server->getRequest(); + $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1); $topFolder = substr($script, 0, strpos($script, '/')); if (empty($topFolder)) { - $path_info = OC_Request::getPathInfo(); + $path_info = $request->getPathInfo(); if ($path_info) { $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1); } diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index 4902671d4b8..f11d189962d 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -24,6 +24,8 @@ namespace OC\AppFramework\Http; +use OC\Security\TrustedDomainHelper; +use OCP\IConfig; use OCP\IRequest; use OCP\Security\ISecureRandom; @@ -31,9 +33,14 @@ use OCP\Security\ISecureRandom; * Class for accessing variables in the request. * This class provides an immutable object with request variables. */ - class Request implements \ArrayAccess, \Countable, IRequest { + const USER_AGENT_IE = '/MSIE/'; + // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent + const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; + const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; + const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/'; + protected $inputStream; protected $content; protected $items = array(); @@ -51,6 +58,8 @@ class Request implements \ArrayAccess, \Countable, IRequest { ); /** @var ISecureRandom */ protected $secureRandom; + /** @var IConfig */ + protected $config; /** @var string */ protected $requestId = ''; @@ -66,15 +75,18 @@ class Request implements \ArrayAccess, \Countable, IRequest { * - string 'method' the request method (GET, POST etc) * - string|false 'requesttoken' the requesttoken or false when not available * @param ISecureRandom $secureRandom + * @param IConfig $config * @param string $stream * @see http://www.php.net/manual/en/reserved.variables.php */ public function __construct(array $vars=array(), ISecureRandom $secureRandom, + IConfig $config, $stream='php://input') { $this->inputStream = $stream; $this->items['params'] = array(); $this->secureRandom = $secureRandom; + $this->config = $config; if(!array_key_exists('method', $vars)) { $vars['method'] = 'GET'; @@ -115,7 +127,9 @@ class Request implements \ArrayAccess, \Countable, IRequest { ); } - + /** + * @param $parameters + */ public function setUrlParameters($parameters) { $this->items['urlParams'] = $parameters; $this->items['parameters'] = array_merge( @@ -124,7 +138,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { ); } - // Countable method. + /** + * Countable method + * @return int + */ public function count() { return count(array_keys($this->items['parameters'])); } @@ -176,7 +193,11 @@ class Request implements \ArrayAccess, \Countable, IRequest { throw new \RuntimeException('You cannot change the contents of the request object'); } - // Magic property accessors + /** + * Magic property accessors + * @param $name + * @param $value + */ public function __set($name, $value) { throw new \RuntimeException('You cannot change the contents of the request object'); } @@ -231,12 +252,17 @@ class Request implements \ArrayAccess, \Countable, IRequest { } } - + /** + * @param $name + * @return bool + */ public function __isset($name) { return isset($this->items['parameters'][$name]); } - + /** + * @param $id + */ public function __unset($id) { throw new \RunTimeException('You cannot change the contents of the request object'); } @@ -412,4 +438,254 @@ class Request implements \ArrayAccess, \Countable, IRequest { return $this->requestId; } + /** + * Returns the remote address, if the connection came from a trusted proxy + * and `forwarded_for_headers` has been configured then the IP address + * specified in this header will be returned instead. + * Do always use this instead of $_SERVER['REMOTE_ADDR'] + * @return string IP address + */ + public function getRemoteAddress() { + $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; + $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); + + if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { + $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', []); + + foreach($forwardedForHeaders as $header) { + if(isset($this->server[$header])) { + foreach(explode(',', $this->server[$header]) as $IP) { + $IP = trim($IP); + if (filter_var($IP, FILTER_VALIDATE_IP) !== false) { + return $IP; + } + } + } + } + } + + return $remoteAddress; + } + + /** + * Check overwrite condition + * @param string $type + * @return bool + */ + private function isOverwriteCondition($type = '') { + $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/'; + return $regex === '//' || preg_match($regex, $this->server['REMOTE_ADDR']) === 1 + || ($type !== 'protocol' && $this->config->getSystemValue('forcessl', false)); + } + + /** + * Returns the server protocol. It respects reverse proxy servers and load + * balancers. + * @return string Server protocol (http or https) + */ + public function getServerProtocol() { + if($this->config->getSystemValue('overwriteprotocol') !== '' + && $this->isOverwriteCondition('protocol')) { + return $this->config->getSystemValue('overwriteprotocol'); + } + + if (isset($this->server['HTTP_X_FORWARDED_PROTO'])) { + $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']); + // Verify that the protocol is always HTTP or HTTPS + // default to http if an invalid value is provided + return $proto === 'https' ? 'https' : 'http'; + } + + if (isset($this->server['HTTPS']) + && $this->server['HTTPS'] !== null + && $this->server['HTTPS'] !== 'off') { + return 'https'; + } + + return 'http'; + } + + /** + * Returns the request uri, even if the website uses one or more + * reverse proxies + * @return string + */ + public function getRequestUri() { + $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; + if($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) { + $uri = $this->getScriptName() . substr($uri, strlen($this->server['SCRIPT_NAME'])); + } + return $uri; + } + + /** + * Get raw PathInfo from request (not urldecoded) + * @throws \Exception + * @return string|false Path info or false when not found + */ + public function getRawPathInfo() { + $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; + // remove too many leading slashes - can be caused by reverse proxy configuration + if (strpos($requestUri, '/') === 0) { + $requestUri = '/' . ltrim($requestUri, '/'); + } + + $requestUri = preg_replace('%/{2,}%', '/', $requestUri); + + // Remove the query string from REQUEST_URI + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + + $scriptName = $this->server['SCRIPT_NAME']; + $pathInfo = $requestUri; + + // strip off the script name's dir and file name + // FIXME: Sabre does not really belong here + list($path, $name) = \Sabre\DAV\URLUtil::splitPath($scriptName); + if (!empty($path)) { + if($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) { + $pathInfo = substr($pathInfo, strlen($path)); + } else { + throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')"); + } + } + if (strpos($pathInfo, '/'.$name) === 0) { + $pathInfo = substr($pathInfo, strlen($name) + 1); + } + if (strpos($pathInfo, $name) === 0) { + $pathInfo = substr($pathInfo, strlen($name)); + } + if($pathInfo === '/'){ + return ''; + } else { + return $pathInfo; + } + } + + /** + * Get PathInfo from request + * @throws \Exception + * @return string|false Path info or false when not found + */ + public function getPathInfo() { + if(isset($this->server['PATH_INFO'])) { + return $this->server['PATH_INFO']; + } + + $pathInfo = $this->getRawPathInfo(); + // following is taken from \Sabre\DAV\URLUtil::decodePathSegment + $pathInfo = rawurldecode($pathInfo); + $encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']); + + switch($encoding) { + case 'ISO-8859-1' : + $pathInfo = utf8_encode($pathInfo); + } + // end copy + + return $pathInfo; + } + + /** + * Returns the script name, even if the website uses one or more + * reverse proxies + * @return string the script name + */ + public function getScriptName() { + $name = $this->server['SCRIPT_NAME']; + $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot'); + if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) { + // FIXME: This code is untestable due to ___DIR__ + $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -strlen('lib/private/'))); + $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), strlen($serverRoot))); + $name = '/' . ltrim($overwriteWebRoot . $suburi, '/'); + } + return $name; + } + + /** + * Checks whether the user agent matches a given regex + * @param array $agent array of agent names + * @return bool true if at least one of the given agent matches, false otherwise + */ + public function isUserAgent(array $agent) { + foreach ($agent as $regex) { + if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) { + return true; + } + } + return false; + } + + /** + * Returns the unverified server host from the headers without checking + * whether it is a trusted domain + * @return string Server host + */ + public function getInsecureServerHost() { + $host = null; + if (isset($this->server['HTTP_X_FORWARDED_HOST'])) { + if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) { + $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']); + $host = trim(current($parts)); + } else { + $host = $this->server['HTTP_X_FORWARDED_HOST']; + } + } else { + if (isset($this->server['HTTP_HOST'])) { + $host = $this->server['HTTP_HOST']; + } else if (isset($this->server['SERVER_NAME'])) { + $host = $this->server['SERVER_NAME']; + } + } + return $host; + } + + + /** + * Returns the server host from the headers, or the first configured + * trusted domain if the host isn't in the trusted list + * @return string Server host + */ + public function getServerHost() { + // FIXME: Ugly workaround that we need to get rid of + if (\OC::$CLI && defined('PHPUNIT_RUN')) { + return 'localhost'; + } + + // overwritehost is always trusted + $host = $this->getOverwriteHost(); + if ($host !== null) { + return $host; + } + + // get the host from the headers + $host = $this->getInsecureServerHost(); + + // Verify that the host is a trusted domain if the trusted domains + // are defined + // If no trusted domain is provided the first trusted domain is returned + $trustedDomainHelper = new TrustedDomainHelper($this->config); + if ($trustedDomainHelper->isTrustedDomain($host)) { + return $host; + } else { + $trustedList = $this->config->getSystemValue('trusted_domains', []); + return $trustedList[0]; + } + } + + /** + * Returns the overwritehost setting from the config if set and + * if the overwrite condition is met + * @return string|null overwritehost value or null if not defined or the defined condition + * isn't met + */ + private function getOverwriteHost() { + if($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) { + return $this->config->getSystemValue('overwritehost'); + } + return null; + } + } diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index e57d04f9a6e..bb672696f2b 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -149,9 +149,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } // allow sync clients to send the mtime along in a header - $mtime = OC_Request::hasModificationTime(); - if ($mtime !== false) { - if($this->fileView->touch($this->path, $mtime)) { + $request = \OC::$server->getRequest(); + if (isset($request->server['HTTP_X_OC_MTIME'])) { + if($this->fileView->touch($this->path, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } @@ -165,8 +165,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ /** * Returns the data - * * @return string|resource + * @throws \Sabre\DAV\Exception\Forbidden + * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function get() { @@ -187,9 +188,8 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ /** * Delete the current file - * - * @return void * @throws \Sabre\DAV\Exception\Forbidden + * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function delete() { if (!$this->info->isDeletable()) { @@ -251,6 +251,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ return \OC_Helper::getSecureMimeType($mimeType); } + /** + * @return array|false + */ public function getDirectDownload() { if (\OCP\App::isEnabled('encryption')) { return []; @@ -267,6 +270,10 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ /** * @param resource $data * @return null|string + * @throws \Sabre\DAV\Exception + * @throws \Sabre\DAV\Exception\BadRequest + * @throws \Sabre\DAV\Exception\NotImplemented + * @throws \Sabre\DAV\Exception\ServiceUnavailable */ private function createFileChunked($data) { @@ -319,9 +326,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } // allow sync clients to send the mtime along in a header - $mtime = OC_Request::hasModificationTime(); - if ($mtime !== false) { - if($this->fileView->touch($targetPath, $mtime)) { + $request = \OC::$server->getRequest(); + if (isset($request->server['HTTP_X_OC_MTIME'])) { + if($this->fileView->touch($targetPath, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } @@ -340,9 +347,8 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ * Returns whether a part file is needed for the given storage * or whether the file can be assembled/uploaded directly on the * target storage. - * - * @param \OCP\Files\Storage $storage storage to check - * @param bool true if the storage needs part file handling + * @param \OCP\Files\Storage $storage + * @return bool true if the storage needs part file handling */ private function needsPartFile($storage) { // TODO: in the future use ChunkHandler provided by storage diff --git a/lib/private/connector/sabre/request.php b/lib/private/connector/sabre/request.php index c98b28c4d74..2ce753d916f 100644 --- a/lib/private/connector/sabre/request.php +++ b/lib/private/connector/sabre/request.php @@ -28,7 +28,7 @@ class OC_Connector_Sabre_Request extends \Sabre\HTTP\Request { * @return string */ public function getUri() { - return OC_Request::requestUri(); + return \OC::$server->getRequest()->getRequestUri(); } /** diff --git a/lib/private/installer.php b/lib/private/installer.php index aeac3497fd7..8ed15a3a5d8 100644 --- a/lib/private/installer.php +++ b/lib/private/installer.php @@ -529,7 +529,6 @@ class OC_Installer{ * @param string $folder the folder of the app to check * @return boolean true for app is o.k. and false for app is not o.k. */ - public static function checkCode($folder) { // is the code checker enabled? if(!OC_Config::getValue('appcodechecker', false)) { diff --git a/lib/private/log/owncloud.php b/lib/private/log/owncloud.php index 8e55bf1c695..62c2da6febe 100644 --- a/lib/private/log/owncloud.php +++ b/lib/private/log/owncloud.php @@ -68,8 +68,9 @@ class OC_Log_Owncloud { $timezone = new DateTimeZone('UTC'); } $time = new DateTime(null, $timezone); - $reqId = \OC::$server->getRequest()->getId(); - $remoteAddr = \OC_Request::getRemoteAddress(); + $request = \OC::$server->getRequest(); + $reqId = $request->getId(); + $remoteAddr = $request->getRemoteAddress(); // remove username/passwords from URLs before writing the to the log file $time = $time->format($format); if($minLevel == OC_Log::DEBUG) { diff --git a/lib/private/request.php b/lib/private/request.php deleted file mode 100644 index ab011c913d9..00000000000 --- a/lib/private/request.php +++ /dev/null @@ -1,330 +0,0 @@ -<?php -/** - * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class OC_Request { - - const USER_AGENT_IE = '/MSIE/'; - // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent - const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; - const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; - const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/'; - - /** - * Returns the remote address, if the connection came from a trusted proxy and `forwarded_for_headers` has been configured - * then the IP address specified in this header will be returned instead. - * Do always use this instead of $_SERVER['REMOTE_ADDR'] - * @return string IP address - */ - public static function getRemoteAddress() { - $remoteAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; - $trustedProxies = \OC::$server->getConfig()->getSystemValue('trusted_proxies', array()); - - if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { - $forwardedForHeaders = \OC::$server->getConfig()->getSystemValue('forwarded_for_headers', array()); - - foreach($forwardedForHeaders as $header) { - if (array_key_exists($header, $_SERVER) === true) { - foreach (explode(',', $_SERVER[$header]) as $IP) { - $IP = trim($IP); - if (filter_var($IP, FILTER_VALIDATE_IP) !== false) { - return $IP; - } - } - } - } - } - - return $remoteAddress; - } - - /** - * Check overwrite condition - * @param string $type - * @return bool - */ - private static function isOverwriteCondition($type = '') { - $regex = '/' . OC_Config::getValue('overwritecondaddr', '') . '/'; - return $regex === '//' or preg_match($regex, $_SERVER['REMOTE_ADDR']) === 1 - or ($type !== 'protocol' and OC_Config::getValue('forcessl', false)); - } - - /** - * Strips a potential port from a domain (in format domain:port) - * @param $host - * @return string $host without appended port - */ - public static 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 static function isTrustedDomain($domainWithPort) { - // Extract port from domain if needed - $domain = self::getDomainWithoutPort($domainWithPort); - - // FIXME: Empty config array defaults to true for now. - Deprecate this behaviour with ownCloud 8. - $trustedList = \OC::$server->getConfig()->getSystemValue('trusted_domains', array()); - if (empty($trustedList)) { - return true; - } - - // FIXME: Workaround for older instances still with port applied. Remove for ownCloud 9. - if(in_array($domainWithPort, $trustedList)) { - return true; - } - - // Always allow access from localhost - if (preg_match(self::REGEX_LOCALHOST, $domain) === 1) { - return true; - } - - return in_array($domain, $trustedList); - } - - /** - * Returns the unverified server host from the headers without checking - * whether it is a trusted domain - * @return string the server host - * - * Returns the server host, even if the website uses one or more - * reverse proxies - */ - public static function insecureServerHost() { - $host = null; - if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { - if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) { - $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); - $host = trim(current($parts)); - } else { - $host = $_SERVER['HTTP_X_FORWARDED_HOST']; - } - } else { - if (isset($_SERVER['HTTP_HOST'])) { - $host = $_SERVER['HTTP_HOST']; - } else if (isset($_SERVER['SERVER_NAME'])) { - $host = $_SERVER['SERVER_NAME']; - } - } - return $host; - } - - /** - * Returns the overwritehost setting from the config if set and - * if the overwrite condition is met - * @return string|null overwritehost value or null if not defined or the defined condition - * isn't met - */ - public static function getOverwriteHost() { - if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) { - return OC_Config::getValue('overwritehost'); - } - return null; - } - - /** - * Returns the server host from the headers, or the first configured - * trusted domain if the host isn't in the trusted list - * @return string the server host - * - * Returns the server host, even if the website uses one or more - * reverse proxies - */ - public static function serverHost() { - if (OC::$CLI && defined('PHPUNIT_RUN')) { - return 'localhost'; - } - - // overwritehost is always trusted - $host = self::getOverwriteHost(); - if ($host !== null) { - return $host; - } - - // get the host from the headers - $host = self::insecureServerHost(); - - // Verify that the host is a trusted domain if the trusted domains - // are defined - // If no trusted domain is provided the first trusted domain is returned - if (self::isTrustedDomain($host)) { - return $host; - } else { - $trustedList = \OC_Config::getValue('trusted_domains', array('')); - return $trustedList[0]; - } - } - - /** - * Returns the server protocol - * @return string the server protocol - * - * Returns the server protocol. It respects reverse proxy servers and load balancers - */ - public static function serverProtocol() { - if(OC_Config::getValue('overwriteprotocol', '') !== '' and self::isOverwriteCondition('protocol')) { - return OC_Config::getValue('overwriteprotocol'); - } - if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { - $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); - // Verify that the protocol is always HTTP or HTTPS - // default to http if an invalid value is provided - return $proto === 'https' ? 'https' : 'http'; - } - if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') { - return 'https'; - } - return 'http'; - } - - /** - * Returns the request uri - * @return string the request uri - * - * Returns the request uri, even if the website uses one or more - * reverse proxies - * @return string - */ - public static function requestUri() { - $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - if (OC_Config::getValue('overwritewebroot', '') !== '' and self::isOverwriteCondition()) { - $uri = self::scriptName() . substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - return $uri; - } - - /** - * Returns the script name - * @return string the script name - * - * Returns the script name, even if the website uses one or more - * reverse proxies - */ - public static function scriptName() { - $name = $_SERVER['SCRIPT_NAME']; - $overwriteWebRoot = OC_Config::getValue('overwritewebroot', ''); - if ($overwriteWebRoot !== '' and self::isOverwriteCondition()) { - $serverroot = str_replace("\\", '/', substr(__DIR__, 0, -strlen('lib/private/'))); - $suburi = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen($serverroot))); - $name = '/' . ltrim($overwriteWebRoot . $suburi, '/'); - } - return $name; - } - - /** - * get Path info from request - * @return string Path info or false when not found - */ - public static function getPathInfo() { - if (array_key_exists('PATH_INFO', $_SERVER)) { - $path_info = $_SERVER['PATH_INFO']; - }else{ - $path_info = self::getRawPathInfo(); - // following is taken from \Sabre\DAV\URLUtil::decodePathSegment - $path_info = rawurldecode($path_info); - $encoding = mb_detect_encoding($path_info, array('UTF-8', 'ISO-8859-1')); - - switch($encoding) { - - case 'ISO-8859-1' : - $path_info = utf8_encode($path_info); - - } - // end copy - } - return $path_info; - } - - /** - * get Path info from request, not urldecoded - * @throws Exception - * @return string Path info or false when not found - */ - public static function getRawPathInfo() { - $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - // remove too many leading slashes - can be caused by reverse proxy configuration - if (strpos($requestUri, '/') === 0) { - $requestUri = '/' . ltrim($requestUri, '/'); - } - - $requestUri = preg_replace('%/{2,}%', '/', $requestUri); - - // Remove the query string from REQUEST_URI - if ($pos = strpos($requestUri, '?')) { - $requestUri = substr($requestUri, 0, $pos); - } - - $scriptName = $_SERVER['SCRIPT_NAME']; - $path_info = $requestUri; - - // strip off the script name's dir and file name - list($path, $name) = \Sabre\DAV\URLUtil::splitPath($scriptName); - if (!empty($path)) { - if( $path === $path_info || strpos($path_info, $path.'/') === 0) { - $path_info = substr($path_info, strlen($path)); - } else { - throw new Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')"); - } - } - if (strpos($path_info, '/'.$name) === 0) { - $path_info = substr($path_info, strlen($name) + 1); - } - if (strpos($path_info, $name) === 0) { - $path_info = substr($path_info, strlen($name)); - } - if($path_info === '/'){ - return ''; - } else { - return $path_info; - } - } - - /** - * Check if the requester sent along an mtime - * @return false or an mtime - */ - static public function hasModificationTime () { - if (isset($_SERVER['HTTP_X_OC_MTIME'])) { - return $_SERVER['HTTP_X_OC_MTIME']; - } else { - return false; - } - } - - /** - * Checks whether the user agent matches a given regex - * @param string|array $agent agent name or array of agent names - * @return boolean true if at least one of the given agent matches, - * false otherwise - */ - static public function isUserAgent($agent) { - if (!is_array($agent)) { - $agent = array($agent); - } - foreach ($agent as $regex) { - if (preg_match($regex, $_SERVER['HTTP_USER_AGENT'])) { - return true; - } - } - return false; - } -} diff --git a/lib/private/response.php b/lib/private/response.php index cf18115111a..9be5d75c314 100644 --- a/lib/private/response.php +++ b/lib/private/response.php @@ -158,11 +158,12 @@ class OC_Response { * @param string $type disposition type, either 'attachment' or 'inline' */ static public function setContentDispositionHeader( $filename, $type = 'attachment' ) { - if (OC_Request::isUserAgent(array( - OC_Request::USER_AGENT_IE, - OC_Request::USER_AGENT_ANDROID_MOBILE_CHROME, - OC_Request::USER_AGENT_FREEBOX - ))) { + if (\OC::$server->getRequest()->isUserAgent( + [ + \OC\AppFramework\Http\Request::USER_AGENT_IE, + \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, + \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, + ])) { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode( $filename ) . '"' ); } else { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode( $filename ) diff --git a/lib/private/route/router.php b/lib/private/route/router.php index 3559b841926..25e897123d1 100644 --- a/lib/private/route/router.php +++ b/lib/private/route/router.php @@ -63,8 +63,9 @@ class Router implements IRouter { } else { $method = 'GET'; } - $host = \OC_Request::serverHost(); - $schema = \OC_Request::serverProtocol(); + $request = \OC::$server->getRequest(); + $host = $request->getServerHost(); + $schema = $request->getServerProtocol(); $this->context = new RequestContext($baseUrl, $method, $host, $schema); // TODO cache $this->root = $this->getCollection('root'); diff --git a/lib/private/security/trusteddomainhelper.php b/lib/private/security/trusteddomainhelper.php new file mode 100644 index 00000000000..593263897be --- /dev/null +++ b/lib/private/security/trusteddomainhelper.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright (c) 2015 Lukas Reschke <lukas@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +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 $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; + } + + // TODO: Workaround for older instances still with port applied. Remove for ownCloud 9. + if(in_array($domainWithPort, $trustedList)) { + return true; + } + + // Always allow access from localhost + if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) { + return true; + } + return in_array($domain, $trustedList); + } + +} diff --git a/lib/private/server.php b/lib/private/server.php index 9660597b39d..9422f332eb1 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -41,45 +41,6 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('ContactsManager', function ($c) { return new ContactsManager(); }); - $this->registerService('Request', function (Server $c) { - if (isset($c['urlParams'])) { - $urlParams = $c['urlParams']; - } else { - $urlParams = array(); - } - - if ($c->getSession()->exists('requesttoken')) { - $requestToken = $c->getSession()->get('requesttoken'); - } else { - $requestToken = false; - } - - if (defined('PHPUNIT_RUN') && PHPUNIT_RUN - && in_array('fakeinput', stream_get_wrappers()) - ) { - $stream = 'fakeinput://data'; - } else { - $stream = 'php://input'; - } - - return new Request( - [ - 'get' => $_GET, - 'post' => $_POST, - 'files' => $_FILES, - 'server' => $_SERVER, - 'env' => $_ENV, - 'cookies' => $_COOKIE, - 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) - ? $_SERVER['REQUEST_METHOD'] - : null, - 'urlParams' => $urlParams, - 'requesttoken' => $requestToken, - ], - $this->getSecureRandom(), - $stream - ); - }); $this->registerService('PreviewManager', function ($c) { return new PreviewManager(); }); @@ -313,12 +274,57 @@ class Server extends SimpleContainer implements IServerContainer { * currently being processed is returned from this method. * In case the current execution was not initiated by a web request null is returned * + * FIXME: This should be queried as well. However, due to our totally awesome + * static code a lot of tests do stuff like $_SERVER['foo'] which obviously + * will not work with that approach. We even have some integration tests in our + * unit tests which setup a complete webserver. Once the code is all non-static + * or we don't have such mixed integration/unit tests setup anymore this can + * get moved out again. + * * @return \OCP\IRequest|null */ function getRequest() { - return $this->query('Request'); + if (isset($this['urlParams'])) { + $urlParams = $this['urlParams']; + } else { + $urlParams = array(); + } + + if ($this->getSession()->exists('requesttoken')) { + $requestToken = $this->getSession()->get('requesttoken'); + } else { + $requestToken = false; + } + + if (defined('PHPUNIT_RUN') && PHPUNIT_RUN + && in_array('fakeinput', stream_get_wrappers()) + ) { + $stream = 'fakeinput://data'; + } else { + $stream = 'php://input'; + } + + return new Request( + [ + 'get' => $_GET, + 'post' => $_POST, + 'files' => $_FILES, + 'server' => $_SERVER, + 'env' => $_ENV, + 'cookies' => $_COOKIE, + 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) + ? $_SERVER['REQUEST_METHOD'] + : null, + 'urlParams' => $urlParams, + 'requesttoken' => $requestToken, + ], + $this->getSecureRandom(), + $this->getConfig(), + $stream + ); } + /** * Returns the preview manager which can create preview images for a given file * diff --git a/lib/private/setup.php b/lib/private/setup.php index e3a29b6469d..a3b46c1eb4f 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -157,12 +157,14 @@ class OC_Setup { return $error; } + $request = \OC::$server->getRequest(); + //no errors, good if(isset($options['trusted_domains']) && is_array($options['trusted_domains'])) { $trustedDomains = $options['trusted_domains']; } else { - $trustedDomains = array(\OC_Request::getDomainWithoutPort(\OC_Request::serverHost())); + $trustedDomains = [\OCP\Util::getServerHostName()]; } if (OC_Util::runningOnWindows()) { @@ -185,7 +187,7 @@ class OC_Setup { 'secret' => $secret, 'trusted_domains' => $trustedDomains, 'datadirectory' => $dataDir, - 'overwrite.cli.url' => \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . OC::$WEBROOT, + 'overwrite.cli.url' => $request->getServerProtocol() . '://' . $request->getServerHost() . OC::$WEBROOT, 'dbtype' => $dbType, 'version' => implode('.', OC_Util::getVersion()), ]); diff --git a/lib/private/template.php b/lib/private/template.php index 4fa1c867d54..b0d212c6f53 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -215,6 +215,7 @@ class OC_Template extends \OC\Template\Base { * @param Exception $exception */ public static function printExceptionErrorPage(Exception $exception) { + $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorMsg', $exception->getMessage()); $content->assign('errorCode', $exception->getCode()); @@ -222,8 +223,8 @@ class OC_Template extends \OC\Template\Base { $content->assign('line', $exception->getLine()); $content->assign('trace', $exception->getTraceAsString()); $content->assign('debugMode', defined('DEBUG') && DEBUG === true); - $content->assign('remoteAddr', OC_Request::getRemoteAddress()); - $content->assign('requestID', \OC::$server->getRequest()->getId()); + $content->assign('remoteAddr', $request->getRemoteAddress()); + $content->assign('requestID', $request->getId()); $content->printPage(); die(); } diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index 1a97eb26347..1678795f524 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -34,9 +34,9 @@ class OC_TemplateLayout extends OC_Template { $this->config = \OC::$server->getConfig(); // Decide which page we show - if( $renderAs == 'user' ) { + if($renderAs == 'user') { parent::__construct( 'core', 'layout.user' ); - if(in_array(OC_APP::getCurrentApp(), array('settings','admin', 'help'))!==false) { + if(in_array(OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) { $this->assign('bodyid', 'body-settings'); }else{ $this->assign('bodyid', 'body-user'); @@ -72,9 +72,9 @@ class OC_TemplateLayout extends OC_Template { } } $userDisplayName = OC_User::getDisplayName(); - $this->assign( 'user_displayname', $userDisplayName ); - $this->assign( 'user_uid', OC_User::getUser() ); - $this->assign( 'appsmanagement_active', strpos(OC_Request::requestUri(), OC_Helper::linkToRoute('settings_apps')) === 0 ); + $this->assign('user_displayname', $userDisplayName); + $this->assign('user_uid', OC_User::getUser()); + $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), OC_Helper::linkToRoute('settings_apps')) === 0 ); $this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true)); $this->assign('userAvatarSet', \OC_Helper::userAvatarSet(OC_User::getUser())); } else if ($renderAs == 'error') { diff --git a/lib/private/urlgenerator.php b/lib/private/urlgenerator.php index 0bf8ce22998..e87a6c354fb 100644 --- a/lib/private/urlgenerator.php +++ b/lib/private/urlgenerator.php @@ -170,7 +170,8 @@ class URLGenerator implements IURLGenerator { ? '' : \OC::$WEBROOT; - return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). $webRoot . $separator . $url; + $request = \OC::$server->getRequest(); + return $request->getServerProtocol() . '://' . $request->getServerHost() . $webRoot . $separator . $url; } /** diff --git a/lib/private/util.php b/lib/private/util.php index 2be7e8eb293..1993a7c9a98 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -849,8 +849,11 @@ class OC_Util { // Check if we are a user if (!OC_User::isLoggedIn()) { header('Location: ' . OC_Helper::linkToAbsolute('', 'index.php', - array('redirect_url' => OC_Request::requestUri()) - )); + [ + 'redirect_url' => \OC::$server->getRequest()->getRequestUri() + ] + ) + ); exit(); } } |