mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Modernize VCS password storage to use shared hash infrastructure
Summary: Fixes T4443. Plug VCS passwords into the shared key stretching. They don't use any real stretching now (I anticipated doing something like T4443 eventually) so we can just migrate them into stretching all at once. Test Plan: - Viewed VCS settings. - Used VCS password after migration. - Set VCS password. - Upgraded VCS password by using it. - Used VCS password some more. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4443 Differential Revision: https://secure.phabricator.com/D8272
This commit is contained in:
parent
5c84aac908
commit
b96ab5aadf
7 changed files with 119 additions and 29 deletions
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_repository.repository_vcspassword
|
||||
CHANGE passwordHash passwordHash VARCHAR(128) COLLATE utf8_bin NOT NULL;
|
27
resources/sql/autopatches/20140218.passwords.4.vcs.php
Normal file
27
resources/sql/autopatches/20140218.passwords.4.vcs.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
$table = new PhabricatorRepositoryVCSPassword();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
echo "Upgrading password hashing for VCS passwords.\n";
|
||||
|
||||
$best_hasher = PhabricatorPasswordHasher::getBestHasher();
|
||||
foreach (new LiskMigrationIterator($table) as $password) {
|
||||
$id = $password->getID();
|
||||
|
||||
echo "Migrating VCS password {$id}...\n";
|
||||
|
||||
$input_hash = $password->getPasswordHash();
|
||||
$input_envelope = new PhutilOpaqueEnvelope($input_hash);
|
||||
|
||||
$storage_hash = $best_hasher->getPasswordHashForStorage($input_envelope);
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'UPDATE %T SET passwordHash = %s WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$storage_hash->openEnvelope(),
|
||||
$id);
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
|
@ -426,6 +426,18 @@ final class DiffusionServeController extends DiffusionController {
|
|||
return null;
|
||||
}
|
||||
|
||||
// If the user's password is stored using a less-than-optimal hash, upgrade
|
||||
// them to the strongest available hash.
|
||||
|
||||
$hash_envelope = new PhutilOpaqueEnvelope(
|
||||
$password_entry->getPasswordHash());
|
||||
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
|
||||
$password_entry->setPassword($password, $user);
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$password_entry->save();
|
||||
unset($unguarded);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,26 @@ final class DiffusionSetPasswordPanel extends PhabricatorSettingsPanel {
|
|||
}
|
||||
}
|
||||
|
||||
$hash_envelope = new PhutilOpaqueEnvelope($vcspassword->getPasswordHash());
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel(pht('Current Algorithm'))
|
||||
->setValue(
|
||||
PhabricatorPasswordHasher::getCurrentAlgorithmName($hash_envelope)));
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel(pht('Best Available Algorithm'))
|
||||
->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
|
||||
|
||||
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
|
||||
$errors[] = pht(
|
||||
'The strength of your stored VCS password hash can be upgraded. '.
|
||||
'To upgrade, either: use the password to authenticate with a '.
|
||||
'repository; or change your password.');
|
||||
}
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($title)
|
||||
->setForm($form)
|
||||
|
|
|
@ -9,26 +9,38 @@ final class PhabricatorRepositoryVCSPassword extends PhabricatorRepositoryDAO {
|
|||
public function setPassword(
|
||||
PhutilOpaqueEnvelope $password,
|
||||
PhabricatorUser $user) {
|
||||
return $this->setPasswordHash($this->hashPassword($password, $user));
|
||||
$hash_envelope = $this->hashPassword($password, $user);
|
||||
return $this->setPasswordHash($hash_envelope->openEnvelope());
|
||||
}
|
||||
|
||||
public function comparePassword(
|
||||
PhutilOpaqueEnvelope $password,
|
||||
PhabricatorUser $user) {
|
||||
|
||||
$hash = $this->hashPassword($password, $user);
|
||||
return ($hash == $this->getPasswordHash());
|
||||
return PhabricatorPasswordHasher::comparePassword(
|
||||
$this->getPasswordHashInput($password, $user),
|
||||
new PhutilOpaqueEnvelope($this->getPasswordHash()));
|
||||
}
|
||||
|
||||
private function getPasswordHashInput(
|
||||
PhutilOpaqueEnvelope $password,
|
||||
PhabricatorUser $user) {
|
||||
if ($user->getPHID() != $this->getUserPHID()) {
|
||||
throw new Exception("User does not match password user PHID!");
|
||||
}
|
||||
|
||||
$raw_input = PhabricatorHash::digestPassword($password, $user->getPHID());
|
||||
return new PhutilOpaqueEnvelope($raw_input);
|
||||
}
|
||||
|
||||
private function hashPassword(
|
||||
PhutilOpaqueEnvelope $password,
|
||||
PhabricatorUser $user) {
|
||||
|
||||
if ($user->getPHID() != $this->getUserPHID()) {
|
||||
throw new Exception("User does not match password user PHID!");
|
||||
}
|
||||
$input_envelope = $this->getPasswordHashInput($password, $user);
|
||||
|
||||
return PhabricatorHash::digestPassword($password, $user->getPHID());
|
||||
$best_hasher = PhabricatorPasswordHasher::getBestHasher();
|
||||
return $best_hasher->getPasswordHashForStorage($input_envelope);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,7 +114,6 @@ final class PhabricatorSettingsPanelPassword
|
|||
|
||||
$hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash());
|
||||
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
|
||||
$best_hash = PhabricatorPasswordHasher::getBestHasher();
|
||||
$errors[] = pht(
|
||||
'The strength of your stored password hash can be upgraded. '.
|
||||
'To upgrade, either: log out and log in using your password; or '.
|
||||
|
@ -157,34 +156,16 @@ final class PhabricatorSettingsPanelPassword
|
|||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Change Password')));
|
||||
|
||||
if (!strlen($user->getPasswordHash())) {
|
||||
$current_name = pht('None');
|
||||
} else {
|
||||
try {
|
||||
$current_hasher = PhabricatorPasswordHasher::getHasherForHash(
|
||||
new PhutilOpaqueEnvelope($user->getPasswordHash()));
|
||||
$current_name = $current_hasher->getHumanReadableName();
|
||||
} catch (Exception $ex) {
|
||||
$current_name = pht('Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel(pht('Current Algorithm'))
|
||||
->setValue($current_name));
|
||||
|
||||
try {
|
||||
$best_hasher = PhabricatorPasswordHasher::getBestHasher();
|
||||
$best_name = $best_hasher->getHumanReadableName();
|
||||
} catch (Exception $ex) {
|
||||
$best_name = pht('Unknown');
|
||||
}
|
||||
->setValue(PhabricatorPasswordHasher::getCurrentAlgorithmName(
|
||||
new PhutilOpaqueEnvelope($user->getPasswordHash()))));
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormStaticControl())
|
||||
->setLabel(pht('Best Available Algorithm'))
|
||||
->setValue($best_name));
|
||||
->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Change Password'))
|
||||
|
|
|
@ -385,4 +385,40 @@ abstract class PhabricatorPasswordHasher extends Phobject {
|
|||
return $hasher->verifyPassword($password, $parts['hash']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the human-readable algorithm name for a given hash.
|
||||
*
|
||||
* @param PhutilOpaqueEnvelope Storage hash.
|
||||
* @return string Human-readable algorithm name.
|
||||
*/
|
||||
public static function getCurrentAlgorithmName(PhutilOpaqueEnvelope $hash) {
|
||||
$raw_hash = $hash->openEnvelope();
|
||||
if (!strlen($raw_hash)) {
|
||||
return pht('None');
|
||||
}
|
||||
|
||||
try {
|
||||
$current_hasher = PhabricatorPasswordHasher::getHasherForHash($hash);
|
||||
return $current_hasher->getHumanReadableName();
|
||||
} catch (Exception $ex) {
|
||||
return pht('Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the human-readable algorithm name for the best available hash.
|
||||
*
|
||||
* @return string Human-readable name for best hash.
|
||||
*/
|
||||
public static function getBestAlgorithmName() {
|
||||
try {
|
||||
$best_hasher = PhabricatorPasswordHasher::getBestHasher();
|
||||
return $best_hasher->getHumanReadableName();
|
||||
} catch (Exception $ex) {
|
||||
return pht('Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue