diff options
author | Julius Härtl <jus@bitgrid.net> | 2023-02-04 22:35:35 +0100 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2023-02-09 13:44:00 +0100 |
commit | 580feecdbfa0dac342d133fa1482f7a4bd4539fb (patch) | |
tree | 4613481f3a24c58fde0b833fa54c27614f886fdc | |
parent | dd891e7e8128bfd888a9451514db3c1b82b1a9cc (diff) | |
download | nextcloud-server-580feecdbfa0dac342d133fa1482f7a4bd4539fb.tar.gz nextcloud-server-580feecdbfa0dac342d133fa1482f7a4bd4539fb.zip |
fix(authtoken): Store only one hash for authtokens with the current password per user
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r-- | lib/private/Authentication/Token/PublicKeyTokenMapper.php | 27 | ||||
-rw-r--r-- | lib/private/Authentication/Token/PublicKeyTokenProvider.php | 24 |
2 files changed, 50 insertions, 1 deletions
diff --git a/lib/private/Authentication/Token/PublicKeyTokenMapper.php b/lib/private/Authentication/Token/PublicKeyTokenMapper.php index 7b11ef8adf3..8feb275b3b7 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenMapper.php +++ b/lib/private/Authentication/Token/PublicKeyTokenMapper.php @@ -229,4 +229,31 @@ class PublicKeyTokenMapper extends QBMapper { ); $update->executeStatement(); } + + public function updateHashesForUser(string $userId, string $passwordHash): void { + $qb = $this->db->getQueryBuilder(); + $update = $qb->update($this->getTableName()) + ->set('password_hash', $qb->createNamedParameter($passwordHash)) + ->where( + $qb->expr()->eq('uid', $qb->createNamedParameter($userId)) + ); + $update->executeStatement(); + } + + public function getFirstTokenForUser(string $userId): ?PublicKeyToken { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($userId))) + ->setMaxResults(1) + ->orderBy('id'); + $result = $qb->executeQuery(); + + $data = $result->fetch(); + $result->closeCursor(); + if ($data === false) { + return null; + } + return PublicKeyToken::fromRow($data); + } } diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index c8adec24b31..e64abcd231f 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -102,9 +102,23 @@ class PublicKeyTokenProvider implements IProvider { $name = mb_substr($name, 0, 120) . '…'; } + // We need to check against one old token to see if there is a password + // hash that we can reuse for detecting outdated passwords + $randomOldToken = $this->mapper->getFirstTokenForUser($uid); + $oldTokenMatches = $randomOldToken && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash()); + $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember); + + if ($oldTokenMatches) { + $dbToken->setPasswordHash($randomOldToken->getPasswordHash()); + } + $this->mapper->insert($dbToken); + if (!$oldTokenMatches && $password !== null) { + $this->updatePasswords($uid, $password); + } + // Add the token to the cache $this->cache[$dbToken->getToken()] = $dbToken; @@ -289,10 +303,11 @@ class PublicKeyTokenProvider implements IProvider { // Update the password for all tokens $tokens = $this->mapper->getTokenByUser($token->getUID()); + $hashedPassword = $this->hashPassword($password); foreach ($tokens as $t) { $publicKey = $t->getPublicKey(); $t->setPassword($this->encryptPassword($password, $publicKey)); - $t->setPasswordHash($this->hashPassword($password)); + $t->setPasswordHash($hashedPassword); $this->updateToken($t); } } @@ -481,6 +496,13 @@ class PublicKeyTokenProvider implements IProvider { $this->updateToken($t); } } + + // If password hashes are different we update them all to be equal so + // that the next execution only needs to verify once + if (count($hashNeedsUpdate) > 1) { + $newPasswordHash = $this->hashPassword($password); + $this->mapper->updateHashesForUser($uid, $newPasswordHash); + } } private function logOpensslError() { |