1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-27 07:50:57 +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:
epriestley 2014-02-18 12:18:04 -08:00
parent 5c84aac908
commit b96ab5aadf
7 changed files with 119 additions and 29 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository_vcspassword
CHANGE passwordHash passwordHash VARCHAR(128) COLLATE utf8_bin NOT NULL;

View 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";

View file

@ -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;
}

View file

@ -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)

View file

@ -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);
}
}

View file

@ -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'))

View file

@ -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');
}
}
}