Browse Source

Merge pull request #20005 from joeried/occ-remove-bruteforce-attempts-by-ip

Implement occ command to reset bruteforce attemps from a given IP address
Morris Jobke 4 years ago
parent
commit
e57bca31ad

+ 62 - 0
core/Command/Security/ResetBruteforceAttempts.php

@@ -0,0 +1,62 @@
+<?php
+/**
+ * @copyright Copyright (c) 2020, Johannes Riedel (johannes@johannes-riedel.de)
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\Core\Command\Security;
+
+use OC\Core\Command\Base;
+use OC\Security\Bruteforce\Throttler;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ResetBruteforceAttempts extends Base {
+
+	/** @var Throttler */
+	protected $throttler;
+
+	public function __construct(Throttler $throttler) {
+		$this->throttler = $throttler;
+		parent::__construct();
+	}
+
+	protected function configure() {
+		$this
+			->setName('security:bruteforce:reset')
+			->setDescription('resets bruteforce attemps for given IP address')
+			->addArgument(
+				'ipaddress',
+				InputArgument::REQUIRED,
+				'IP address for which the attempts are to be reset'
+			);
+	}
+
+	protected function execute(InputInterface $input, OutputInterface $output) {
+		$ip = $input->getArgument('ipaddress');
+
+		if (!filter_var($ip, FILTER_VALIDATE_IP)) {
+			$output->writeln('<error>"' . $ip . '" is not a valid IP address</error>');
+			return 1;
+		}
+
+		$this->throttler->resetDelayForIP($ip);
+	}
+}

+ 1 - 0
core/register_command.php

@@ -175,6 +175,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
 	$application->add(new OC\Core\Command\Security\ListCertificates(\OC::$server->getCertificateManager(null), \OC::$server->getL10N('core')));
 	$application->add(new OC\Core\Command\Security\ImportCertificate(\OC::$server->getCertificateManager(null)));
 	$application->add(new OC\Core\Command\Security\RemoveCertificate(\OC::$server->getCertificateManager(null)));
+	$application->add(new OC\Core\Command\Security\ResetBruteforceAttempts(\OC::$server->getBruteForceThrottler()));
 } else {
 	$application->add(new OC\Core\Command\Maintenance\Install(\OC::$server->getSystemConfig()));
 }

+ 1 - 0
lib/composer/composer/autoload_classmap.php

@@ -789,6 +789,7 @@ return array(
     'OC\\Core\\Command\\Security\\ImportCertificate' => $baseDir . '/core/Command/Security/ImportCertificate.php',
     'OC\\Core\\Command\\Security\\ListCertificates' => $baseDir . '/core/Command/Security/ListCertificates.php',
     'OC\\Core\\Command\\Security\\RemoveCertificate' => $baseDir . '/core/Command/Security/RemoveCertificate.php',
+    'OC\\Core\\Command\\Security\\ResetBruteforceAttempts' => $baseDir . '/core/Command/Security/ResetBruteforceAttempts.php',
     'OC\\Core\\Command\\Status' => $baseDir . '/core/Command/Status.php',
     'OC\\Core\\Command\\TwoFactorAuth\\Base' => $baseDir . '/core/Command/TwoFactorAuth/Base.php',
     'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => $baseDir . '/core/Command/TwoFactorAuth/Cleanup.php',

+ 1 - 0
lib/composer/composer/autoload_static.php

@@ -818,6 +818,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OC\\Core\\Command\\Security\\ImportCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/ImportCertificate.php',
         'OC\\Core\\Command\\Security\\ListCertificates' => __DIR__ . '/../../..' . '/core/Command/Security/ListCertificates.php',
         'OC\\Core\\Command\\Security\\RemoveCertificate' => __DIR__ . '/../../..' . '/core/Command/Security/RemoveCertificate.php',
+        'OC\\Core\\Command\\Security\\ResetBruteforceAttempts' => __DIR__ . '/../../..' . '/core/Command/Security/ResetBruteforceAttempts.php',
         'OC\\Core\\Command\\Status' => __DIR__ . '/../../..' . '/core/Command/Status.php',
         'OC\\Core\\Command\\TwoFactorAuth\\Base' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Base.php',
         'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Cleanup.php',

+ 29 - 6
lib/private/Security/Bruteforce/Throttler.php

@@ -89,6 +89,17 @@ class Throttler {
 		return $d2->diff($d1);
 	}
 
+	/**
+	 *  Calculate the cut off timestamp
+	 *
+	 * @return int
+	 */
+	private function getCutoffTimestamp(): int {
+		return (new \DateTime())
+			->sub($this->getCutoff(43200))
+			->getTimestamp();
+	}
+
 	/**
 	 * Register a failed attempt to bruteforce a security control
 	 *
@@ -212,9 +223,7 @@ class Throttler {
 			return 0;
 		}
 
-		$cutoffTime = (new \DateTime())
-			->sub($this->getCutoff(43200))
-			->getTimestamp();
+		$cutoffTime = $this->getCutoffTimestamp();
 
 		$qb = $this->db->getQueryBuilder();
 		$qb->select('*')
@@ -259,9 +268,7 @@ class Throttler {
 			return;
 		}
 
-		$cutoffTime = (new \DateTime())
-			->sub($this->getCutoff(43200))
-			->getTimestamp();
+		$cutoffTime = $this->getCutoffTimestamp();
 
 		$qb = $this->db->getQueryBuilder();
 		$qb->delete('bruteforce_attempts')
@@ -273,6 +280,22 @@ class Throttler {
 		$qb->execute();
 	}
 
+	/**
+	 * Reset the throttling delay for an IP address
+	 *
+	 * @param string $ip
+	 */
+	public function resetDelayForIP($ip) {
+		$cutoffTime = $this->getCutoffTimestamp();
+
+		$qb = $this->db->getQueryBuilder();
+		$qb->delete('bruteforce_attempts')
+			->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime)))
+			->andWhere($qb->expr()->eq('ip', $qb->createNamedParameter($ip)));
+
+		$qb->execute();
+	}
+
 	/**
 	 * Will sleep for the defined amount of time
 	 *