diff --git a/core/Command/Security/ResetBruteforceAttempts.php b/core/Command/Security/ResetBruteforceAttempts.php new file mode 100644 index 0000000000..dcb827f8dd --- /dev/null +++ b/core/Command/Security/ResetBruteforceAttempts.php @@ -0,0 +1,62 @@ +. + * + */ + + +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('"' . $ip . '" is not a valid IP address'); + return 1; + } + + $this->throttler->resetDelayForIP($ip); + } +} diff --git a/core/register_command.php b/core/register_command.php index e355d1429c..d818423d1a 100644 --- a/core/register_command.php +++ b/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())); } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index c7b966fd59..2c701643c1 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/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', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 8212d3dafc..3046aea4a9 100644 --- a/lib/composer/composer/autoload_static.php +++ b/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', diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php index 1bece6a05d..63c6361b9c 100644 --- a/lib/private/Security/Bruteforce/Throttler.php +++ b/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 *