From 5a270c271567d3c6ef9d0f1f78814b5b249ca2fe Mon Sep 17 00:00:00 2001
From: Morris Jobke <hey@morrisjobke.de>
Date: Thu, 23 Nov 2017 13:37:50 +0100
Subject: [PATCH] Reset bruteforce attempt table on successful login

* only clear the entries that come from the same subnet, same action and same metadata

Signed-off-by: Morris Jobke <hey@morrisjobke.de>
---
 lib/base.php                                  | 16 ++++++++---
 lib/private/Security/Bruteforce/Throttler.php | 27 +++++++++++++++++++
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/lib/base.php b/lib/base.php
index dc09d0f533d..6193b591ab5 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -730,7 +730,7 @@ class OC {
 			OC_User::setIncognitoMode(true);
 		}
 
-		self::registerCacheHooks();
+		self::registerCleanupHooks();
 		self::registerFilesystemHooks();
 		self::registerShareHooks();
 		self::registerEncryptionWrapper();
@@ -802,15 +802,23 @@ class OC {
 	}
 
 	/**
-	 * register hooks for the cache
+	 * register hooks for the cleanup of cache and bruteforce protection
 	 */
-	public static function registerCacheHooks() {
+	public static function registerCleanupHooks() {
 		//don't try to do this before we are properly setup
 		if (\OC::$server->getSystemConfig()->getValue('installed', false) && !self::checkUpgrade(false)) {
 
 			// NOTE: This will be replaced to use OCP
 			$userSession = self::$server->getUserSession();
-			$userSession->listen('\OC\User', 'postLogin', function () {
+			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
+				if (!defined('PHPUNIT_RUN')) {
+					// reset brute force delay for this IP address and username
+					$uid = \OC::$server->getUserSession()->getUser()->getUID();
+					$request = \OC::$server->getRequest();
+					$throttler = \OC::$server->getBruteForceThrottler();
+					$throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
+				}
+
 				try {
 					$cache = new \OC\Cache\File();
 					$cache->gc();
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index 1626cee8cb3..f08b721d143 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -242,6 +242,33 @@ class Throttler {
 		return (int) \ceil($firstDelay * 1000);
 	}
 
+	/**
+	 * Reset the throttling delay for an IP address, action and metadata
+	 *
+	 * @param string $ip
+	 * @param string $action
+	 * @param string $metadata
+	 */
+	public function resetDelay($ip, $action, $metadata) {
+		$ipAddress = new IpAddress($ip);
+		if ($this->isIPWhitelisted((string)$ipAddress)) {
+			return;
+		}
+
+		$cutoffTime = (new \DateTime())
+			->sub($this->getCutoff(43200))
+			->getTimestamp();
+
+		$qb = $this->db->getQueryBuilder();
+		$qb->delete('bruteforce_attempts')
+			->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime)))
+			->andWhere($qb->expr()->eq('subnet', $qb->createNamedParameter($ipAddress->getSubnet())))
+			->andWhere($qb->expr()->eq('action', $qb->createNamedParameter($action)))
+			->andWhere($qb->expr()->eq('metadata', $qb->createNamedParameter(json_encode($metadata))));
+
+		$qb->execute();
+	}
+
 	/**
 	 * Will sleep for the defined amount of time
 	 *
-- 
GitLab