]> source.dussan.org Git - nextcloud-server.git/commitdiff
Respect bruteforce settings in the Throttler
authorRoeland Jago Douma <roeland@famdouma.nl>
Mon, 14 Nov 2016 13:05:01 +0000 (14:05 +0100)
committerRoeland Jago Douma <roeland@famdouma.nl>
Sun, 2 Apr 2017 19:13:50 +0000 (21:13 +0200)
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
lib/private/Security/Bruteforce/Throttler.php
tests/lib/Security/Bruteforce/ThrottlerTest.php

index 765f109fdb3b1ea7a4f6b72eb4777a52fd63641e..73a27b677b08a215c61a824bf3479a48d5f89d8b 100644 (file)
@@ -185,6 +185,67 @@ class Throttler {
                $qb->execute();
        }
 
+       /**
+        * Check if the IP is whitelisted
+        *
+        * @param string $ip
+        * @return bool
+        */
+       private function isIPWhitelisted($ip) {
+               $keys = $this->config->getAppKeys('bruteForce');
+               $keys = array_filter($keys, function($key) {
+                       $regex = '/^whitelist_/S';
+                       return preg_match($regex, $key) === 1;
+               });
+
+               if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+                       $type = 4;
+               } else if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+                       $type = 6;
+               } else {
+                       return false;
+               }
+
+               $ip = inet_pton($ip);
+
+               foreach ($keys as $key) {
+                       $cidr = $this->config->getAppValue('bruteForce', $key, null);
+
+                       $cx = explode('/', $cidr);
+                       $addr = $cx[0];
+                       $mask = (int)$cx[1];
+
+                       // Do not compare ipv4 to ipv6
+                       if (($type === 4 && !filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) ||
+                               ($type === 6 && !filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))) {
+                               continue;
+                       }
+
+                       $addr = inet_pton($addr);
+
+                       $valid = true;
+                       for($i = 0; $i < $mask; $i++) {
+                               $part = ord($addr[(int)($i/8)]);
+                               $orig = ord($ip[(int)($i/8)]);
+
+                               $part = $part & (15 << (1 - ($i % 2)));
+                               $orig = $orig & (15 << (1 - ($i % 2)));
+
+                               if ($part !== $orig) {
+                                       $valid = false;
+                                       break;
+                               }
+                       }
+
+                       if ($valid === true) {
+                               return true;
+                       }
+               }
+
+               return false;
+
+       }
+
        /**
         * Get the throttling delay (in milliseconds)
         *
@@ -193,6 +254,10 @@ class Throttler {
         * @return int
         */
        public function getDelay($ip, $action = '') {
+               if ($this->isIPWhitelisted($ip)) {
+                       return 0;
+               }
+
                $cutoffTime = (new \DateTime())
                        ->sub($this->getCutoff(43200))
                        ->getTimestamp();
index 604aecd3a65b1aa6d9887bd24d1068d006f47c38..02d5b701679b455b5be885a5cc9d41424c028684 100644 (file)
@@ -40,7 +40,7 @@ class ThrottlerTest extends TestCase {
        private $dbConnection;
        /** @var ILogger */
        private $logger;
-       /** @var IConfig */
+       /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
        private $config;
 
        public function setUp() {
@@ -120,4 +120,92 @@ class ThrottlerTest extends TestCase {
                        $this->invokePrivate($this->throttler, 'getIPv6Subnet', ['2001:0db8:85a3:0000:0000:8a2e:0370:7334', 40])
                );
        }
+
+       public function dataIsIPWhitelisted() {
+               return [
+                       [
+                               '10.10.10.10',
+                               [
+                                       'whitelist_0' => '10.10.10.0/24',
+                               ],
+                               true,
+                       ],
+                       [
+                               '10.10.10.10',
+                               [
+                                       'whitelist_0' => '192.168.0.0/16',
+                               ],
+                               false,
+                       ],
+                       [
+                               '10.10.10.10',
+                               [
+                                       'whitelist_0' => '192.168.0.0/16',
+                                       'whitelist_1' => '10.10.10.0/24',
+                               ],
+                               true,
+                       ],
+                       [
+                               'dead:beef:cafe::1',
+                               [
+                                       'whitelist_0' => '192.168.0.0/16',
+                                       'whitelist_1' => '10.10.10.0/24',
+                                       'whitelist_2' => 'deaf:beef:cafe:1234::/64'
+                               ],
+                               false,
+                       ],
+                       [
+                               'dead:beef:cafe::1',
+                               [
+                                       'whitelist_0' => '192.168.0.0/16',
+                                       'whitelist_1' => '10.10.10.0/24',
+                                       'whitelist_2' => 'deaf:beef::/64'
+                               ],
+                               false,
+                       ],
+                       [
+                               'dead:beef:cafe::1',
+                               [
+                                       'whitelist_0' => '192.168.0.0/16',
+                                       'whitelist_1' => '10.10.10.0/24',
+                                       'whitelist_2' => 'deaf:cafe::/8'
+                               ],
+                               true,
+                       ],
+                       [
+                               'invalid',
+                               [],
+                               false,
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider dataIsIPWhitelisted
+        *
+        * @param string $ip
+        * @param string[] $whitelists
+        * @param bool $isWhiteListed
+        */
+       public function testIsIPWhitelisted($ip, $whitelists, $isWhiteListed) {
+               $this->config->method('getAppKeys')
+                       ->with($this->equalTo('bruteForce'))
+                       ->willReturn(array_keys($whitelists));
+
+               $this->config->method('getAppValue')
+                       ->will($this->returnCallback(function($app, $key, $default) use ($whitelists) {
+                               if ($app !== 'bruteForce') {
+                                       return $default;
+                               }
+                               if (isset($whitelists[$key])) {
+                                       return $whitelists[$key];
+                               }
+                               return $default;
+                       }));
+
+               $this->assertSame(
+                       $isWhiteListed,
+                       $this->invokePrivate($this->throttler, 'isIPWhitelisted', [$ip])
+               );
+       }
 }