diff options
author | Joas Schilling <coding@schilljs.com> | 2020-03-24 14:19:57 +0100 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2020-04-14 18:56:06 +0200 |
commit | 5e402f8aaeacf05f956c6a73d7300e7849bc4bae (patch) | |
tree | dd78e7b20ac19ed521ac147ec5236ac14a449130 /lib/private/Http | |
parent | d7a74d0e35798364fcf62ea6f89d38c0f53184ea (diff) | |
download | nextcloud-server-5e402f8aaeacf05f956c6a73d7300e7849bc4bae.tar.gz nextcloud-server-5e402f8aaeacf05f956c6a73d7300e7849bc4bae.zip |
Check all remotes for local access
Signed-off-by: Joas Schilling <coding@schilljs.com>
Diffstat (limited to 'lib/private/Http')
-rw-r--r-- | lib/private/Http/Client/Client.php | 65 | ||||
-rw-r--r-- | lib/private/Http/Client/ClientService.php | 11 |
2 files changed, 66 insertions, 10 deletions
diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index 19d5877f9fb..d21ce413f1a 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -34,8 +34,10 @@ use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\RequestOptions; use OCP\Http\Client\IClient; use OCP\Http\Client\IResponse; +use OCP\Http\Client\LocalServerException; use OCP\ICertificateManager; use OCP\IConfig; +use OCP\ILogger; /** * Class Client @@ -47,20 +49,19 @@ class Client implements IClient { private $client; /** @var IConfig */ private $config; + /** @var ILogger */ + private $logger; /** @var ICertificateManager */ private $certificateManager; - /** - * @param IConfig $config - * @param ICertificateManager $certificateManager - * @param GuzzleClient $client - */ public function __construct( IConfig $config, + ILogger $logger, ICertificateManager $certificateManager, GuzzleClient $client ) { $this->config = $config; + $this->logger = $logger; $this->client = $client; $this->certificateManager = $certificateManager; } @@ -144,6 +145,53 @@ class Client implements IClient { return $proxy; } + protected function preventLocalAddress(string $uri, array $options): void { + if (($options['nextcloud']['allow_local_address'] ?? false) || + $this->config->getSystemValueBool('allow_local_remote_servers', false)) { + return; + } + + $host = parse_url($uri, PHP_URL_HOST); + if ($host === false) { + $this->logger->warning("Could not detect any host in $uri"); + throw new LocalServerException('Could not detect any host'); + } + + $host = strtolower($host); + // remove brackets from IPv6 addresses + if (strpos($host, '[') === 0 && substr($host, -1) === ']') { + $host = substr($host, 1, -1); + } + + // Disallow localhost and local network + if ($host === 'localhost' || substr($host, -6) === '.local' || substr($host, -10) === '.localhost') { + $this->logger->warning("Host $host was not connected to because it violates local access rules"); + throw new LocalServerException('Host violates local access rules'); + } + + // Disallow hostname only + if (substr_count($host, '.') === 0) { + $this->logger->warning("Host $host was not connected to because it violates local access rules"); + throw new LocalServerException('Host violates local access rules'); + } + + if ((bool)filter_var($host, FILTER_VALIDATE_IP) && !filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + $this->logger->warning("Host $host was not connected to because it violates local access rules"); + throw new LocalServerException('Host violates local access rules'); + } + + // Also check for IPv6 IPv4 nesting, because that's not covered by filter_var + if ((bool)filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && substr_count($host, '.') > 0) { + $delimiter = strrpos($host, ':'); // Get last colon + $ipv4Address = substr($host, $delimiter + 1); + + if (!filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + $this->logger->warning("Host $host was not connected to because it violates local access rules"); + throw new LocalServerException('Host violates local access rules'); + } + } + } + /** * Sends a GET request * @@ -174,6 +222,7 @@ class Client implements IClient { * @throws \Exception If the request could not get completed */ public function get(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); $response = $this->client->request('get', $uri, $this->buildRequestOptions($options)); $isStream = isset($options['stream']) && $options['stream']; return new Response($response, $isStream); @@ -204,6 +253,7 @@ class Client implements IClient { * @throws \Exception If the request could not get completed */ public function head(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); $response = $this->client->request('head', $uri, $this->buildRequestOptions($options)); return new Response($response); } @@ -238,6 +288,8 @@ class Client implements IClient { * @throws \Exception If the request could not get completed */ public function post(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); + if (isset($options['body']) && is_array($options['body'])) { $options['form_params'] = $options['body']; unset($options['body']); @@ -276,6 +328,7 @@ class Client implements IClient { * @throws \Exception If the request could not get completed */ public function put(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); $response = $this->client->request('put', $uri, $this->buildRequestOptions($options)); return new Response($response); } @@ -310,6 +363,7 @@ class Client implements IClient { * @throws \Exception If the request could not get completed */ public function delete(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); $response = $this->client->request('delete', $uri, $this->buildRequestOptions($options)); return new Response($response); } @@ -344,6 +398,7 @@ class Client implements IClient { * @throws \Exception If the request could not get completed */ public function options(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); $response = $this->client->request('options', $uri, $this->buildRequestOptions($options)); return new Response($response); } diff --git a/lib/private/Http/Client/ClientService.php b/lib/private/Http/Client/ClientService.php index 2b18daaf737..55f03f30399 100644 --- a/lib/private/Http/Client/ClientService.php +++ b/lib/private/Http/Client/ClientService.php @@ -32,6 +32,7 @@ use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\ICertificateManager; use OCP\IConfig; +use OCP\ILogger; /** * Class ClientService @@ -41,16 +42,16 @@ use OCP\IConfig; class ClientService implements IClientService { /** @var IConfig */ private $config; + /** @var ILogger */ + private $logger; /** @var ICertificateManager */ private $certificateManager; - /** - * @param IConfig $config - * @param ICertificateManager $certificateManager - */ public function __construct(IConfig $config, + ILogger $logger, ICertificateManager $certificateManager) { $this->config = $config; + $this->logger = $logger; $this->certificateManager = $certificateManager; } @@ -58,6 +59,6 @@ class ClientService implements IClientService { * @return Client */ public function newClient(): IClient { - return new Client($this->config, $this->certificateManager, new GuzzleClient()); + return new Client($this->config, $this->logger, $this->certificateManager, new GuzzleClient()); } } |