Browse Source

PublickKeyTokenProvider: Fix password update routine with password hash

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
tags/v26.0.0beta1
Marcel Klehr 1 year ago
parent
commit
adfe367106
No account linked to committer's email address

+ 57
- 0
core/Migrations/Version25000Date20220905140840.php View File

@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2022 Marcel Klehr <mklehr@gmx.net>
*
* @author Marcel Klehr <mklehr@gmx.net>
*
* @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\Migrations;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version25000Date20220905140840 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

$authTokenTable = $schema->getTable('authtoken');
if (!$authTokenTable->hasColumn('password_hash')) {
$authTokenTable->addColumn('password_hash', Types::STRING, [
'notnull' => false,
'length' => 255,
]);
return $schema;
}
return null;
}
}

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

@@ -1075,6 +1075,7 @@ return array(
'OC\\Core\\Migrations\\Version24000Date20220425072957' => $baseDir . '/core/Migrations/Version24000Date20220425072957.php',
'OC\\Core\\Migrations\\Version25000Date20220515204012' => $baseDir . '/core/Migrations/Version25000Date20220515204012.php',
'OC\\Core\\Migrations\\Version25000Date20220602190540' => $baseDir . '/core/Migrations/Version25000Date20220602190540.php',
'OC\\Core\\Migrations\\Version25000Date20220905140840' => $baseDir . '/core/Migrations/Version25000Date20220905140840.php',
'OC\\Core\\Migrations\\Version25000Date20221007010957' => $baseDir . '/core/Migrations/Version25000Date20221007010957.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',

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

@@ -1108,6 +1108,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Migrations\\Version24000Date20220425072957' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220425072957.php',
'OC\\Core\\Migrations\\Version25000Date20220515204012' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220515204012.php',
'OC\\Core\\Migrations\\Version25000Date20220602190540' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220602190540.php',
'OC\\Core\\Migrations\\Version25000Date20220905140840' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220905140840.php',
'OC\\Core\\Migrations\\Version25000Date20221007010957' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20221007010957.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',

+ 6
- 0
lib/private/Authentication/Token/PublicKeyToken.php View File

@@ -45,6 +45,8 @@ use OCP\AppFramework\Db\Entity;
* @method void setPublicKey(string $key)
* @method void setVersion(int $version)
* @method bool getPasswordInvalid()
* @method string getPasswordHash()
* @method setPasswordHash(string $hash)
*/
class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
public const VERSION = 2;
@@ -58,6 +60,9 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
/** @var string encrypted user password */
protected $password;

/** @var string hashed user password */
protected $passwordHash;

/** @var string token name (e.g. browser/OS) */
protected $name;

@@ -98,6 +103,7 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
$this->addType('uid', 'string');
$this->addType('loginName', 'string');
$this->addType('password', 'string');
$this->addType('passwordHash', 'string');
$this->addType('name', 'string');
$this->addType('token', 'string');
$this->addType('type', 'int');

+ 16
- 4
lib/private/Authentication/Token/PublicKeyTokenProvider.php View File

@@ -41,6 +41,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
use OCP\Security\IHasher;
use Psr\Log\LoggerInterface;

class PublicKeyTokenProvider implements IProvider {
@@ -66,12 +67,15 @@ class PublicKeyTokenProvider implements IProvider {
/** @var CappedMemoryCache */
private $cache;

private IHasher $hasher;

public function __construct(PublicKeyTokenMapper $mapper,
ICrypto $crypto,
IConfig $config,
IDBConnection $db,
LoggerInterface $logger,
ITimeFactory $time) {
ITimeFactory $time,
IHasher $hasher) {
$this->mapper = $mapper;
$this->crypto = $crypto;
$this->config = $config;
@@ -80,6 +84,7 @@ class PublicKeyTokenProvider implements IProvider {
$this->time = $time;

$this->cache = new CappedMemoryCache();
$this->hasher = $hasher;
}

/**
@@ -286,10 +291,15 @@ class PublicKeyTokenProvider implements IProvider {
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($this->hashPassword($password));
$this->updateToken($t);
}
}

private function hashPassword(string $password): string {
return $this->hasher->hash(sha1($password) . $password);
}

public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
$this->cache->clear();

@@ -401,6 +411,7 @@ class PublicKeyTokenProvider implements IProvider {
throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
}
$dbToken->setPassword($this->encryptPassword($password, $publicKey));
$dbToken->setPasswordHash($this->hashPassword($password));
}

$dbToken->setName($name);
@@ -435,11 +446,12 @@ class PublicKeyTokenProvider implements IProvider {

// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($uid);
$passwordHash = $this->hashPassword($password);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
$encryptedPassword = $this->encryptPassword($password, $publicKey);
if ($t->getPassword() !== $encryptedPassword) {
$t->setPassword($encryptedPassword);
if ($t->getPasswordHash() === null || $this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($passwordHash);
$t->setPasswordInvalid(false);
$this->updateToken($t);
}

+ 2
- 0
tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php View File

@@ -64,6 +64,7 @@ class PublicKeyTokenProviderTest extends TestCase {
parent::setUp();

$this->mapper = $this->createMock(PublicKeyTokenMapper::class);
$this->hasher = \OC::$server->getHasher();
$this->crypto = \OC::$server->getCrypto();
$this->config = $this->createMock(IConfig::class);
$this->config->method('getSystemValue')
@@ -87,6 +88,7 @@ class PublicKeyTokenProviderTest extends TestCase {
$this->db,
$this->logger,
$this->timeFactory,
$this->hasher,
);
}


Loading…
Cancel
Save