1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 10:12:41 +01:00

Standardize SSH key storage

Summary:
Ref T5833. This fixes a few weird things with this table:

  - A bunch of columns were nullable for no reason.
  - We stored an MD5 hash of the key (unusual) but never used it and callers were responsible for manually populating it.
  - We didn't perform known-key-text lookups by using an index.

Test Plan:
  - Ran migrations.
  - Faked duplicate keys, saw them clean up correctly.
  - Added new keys.
  - Generated new keys.
  - Used `bin/auth-ssh` and `bin/auth-ssh-key`.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5833

Differential Revision: https://secure.phabricator.com/D10805
This commit is contained in:
epriestley 2014-11-07 15:34:44 -08:00
parent a17a368692
commit bf17b12daf
13 changed files with 136 additions and 33 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
CHANGE userPHID objectPHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
DROP COLUMN keyHash;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
ADD COLUMN keyIndex BINARY(12);

View file

@ -0,0 +1,50 @@
<?php
$table = new PhabricatorAuthSSHKey();
$conn_w = $table->establishConnection('w');
echo "Updating SSH public key indexes...\n";
$keys = new LiskMigrationIterator($table);
foreach ($keys as $key) {
$id = $key->getID();
echo "Updating key {$id}...\n";
try {
$hash = $key->toPublicKey()->getHash();
} catch (Exception $ex) {
echo "Key has bad format! Removing key.\n";
queryfx(
$conn_w,
'DELETE FROM %T WHERE id = %d',
$table->getTableName(),
$id);
continue;
}
$collision = queryfx_all(
$conn_w,
'SELECT * FROM %T WHERE keyIndex = %s AND id < %d',
$table->getTableName(),
$hash,
$key->getID());
if ($collision) {
echo "Key is a duplicate! Removing key.\n";
queryfx(
$conn_w,
'DELETE FROM %T WHERE id = %d',
$table->getTableName(),
$id);
continue;
}
queryfx(
$conn_w,
'UPDATE %T SET keyIndex = %s WHERE id = %d',
$table->getTableName(),
$hash,
$key->getID());
}
echo "Done.\n";

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
CHANGE keyIndex keyIndex BINARY(12) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
ADD UNIQUE KEY `key_unique` (keyIndex);

View file

@ -0,0 +1,23 @@
UPDATE {$NAMESPACE}_auth.auth_sshkey
SET name = '' WHERE name IS NULL;
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
CHANGE name name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
UPDATE {$NAMESPACE}_auth.auth_sshkey
SET keyType = '' WHERE keyType IS NULL;
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
CHANGE keyType keyType VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
UPDATE {$NAMESPACE}_auth.auth_sshkey
SET keyBody = '' WHERE keyBody IS NULL;
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
CHANGE keyBody keyBody LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL;
UPDATE {$NAMESPACE}_auth.auth_sshkey
SET keyComment = '' WHERE keyComment IS NULL;
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
CHANGE keyComment keyComment VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;

View file

@ -34,9 +34,15 @@ foreach ($keys as $ssh_key) {
$type = $ssh_key->getKeyType(); $type = $ssh_key->getKeyType();
$type = preg_replace('@[\x00-\x20]+@', '', $type); $type = preg_replace('@[\x00-\x20]+@', '', $type);
if (!strlen($type)) {
continue;
}
$key = $ssh_key->getKeyBody(); $key = $ssh_key->getKeyBody();
$key = preg_replace('@[\x00-\x20]+@', '', $key); $key = preg_replace('@[\x00-\x20]+@', '', $key);
if (!strlen($key)) {
continue;
}
$options = array( $options = array(
'command="'.$cmd.'"', 'command="'.$cmd.'"',

View file

@ -73,21 +73,18 @@ final class PhabricatorAuthSSHKeyQuery
if ($this->objectPHIDs !== null) { if ($this->objectPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,
'userPHID IN (%Ls)', 'objectPHID IN (%Ls)',
$this->objectPHIDs); $this->objectPHIDs);
} }
if ($this->keys !== null) { if ($this->keys !== null) {
// TODO: This could take advantage of a better key, and the hashing
// scheme for this table is a bit nonstandard and questionable.
$sql = array(); $sql = array();
foreach ($this->keys as $key) { foreach ($this->keys as $key) {
$sql[] = qsprintf( $sql[] = qsprintf(
$conn_r, $conn_r,
'(keyType = %s AND keyBody = %s)', '(keyType = %s AND keyIndex = %s)',
$key->getType(), $key->getType(),
$key->getBody()); $key->getHash());
} }
$where[] = implode(' OR ', $sql); $where[] = implode(' OR ', $sql);
} }

View file

@ -4,43 +4,45 @@ final class PhabricatorAuthSSHKey
extends PhabricatorAuthDAO extends PhabricatorAuthDAO
implements PhabricatorPolicyInterface { implements PhabricatorPolicyInterface {
protected $userPHID; protected $objectPHID;
protected $name; protected $name;
protected $keyType; protected $keyType;
protected $keyIndex;
protected $keyBody; protected $keyBody;
protected $keyHash; protected $keyComment = '';
protected $keyComment;
private $object = self::ATTACHABLE; private $object = self::ATTACHABLE;
public function getObjectPHID() {
return $this->getUserPHID();
}
public function getConfiguration() { public function getConfiguration() {
return array( return array(
self::CONFIG_COLUMN_SCHEMA => array( self::CONFIG_COLUMN_SCHEMA => array(
'keyHash' => 'bytes32', 'name' => 'text255',
'keyComment' => 'text255?', 'keyType' => 'text255',
'keyIndex' => 'bytes12',
// T6203/NULLABILITY 'keyBody' => 'text',
// These seem like they should not be nullable. 'keyComment' => 'text255',
'name' => 'text255?',
'keyType' => 'text255?',
'keyBody' => 'text?',
), ),
self::CONFIG_KEY_SCHEMA => array( self::CONFIG_KEY_SCHEMA => array(
'userPHID' => array( 'key_object' => array(
'columns' => array('userPHID'), 'columns' => array('objectPHID'),
), ),
'keyHash' => array( 'key_unique' => array(
'columns' => array('keyHash'), 'columns' => array('keyIndex'),
'unique' => true, 'unique' => true,
), ),
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }
public function save() {
$this->setKeyIndex($this->toPublicKey()->getHash());
return parent::save();
}
public function toPublicKey() {
return PhabricatorAuthSSHPublicKey::newFromStoredKey($this);
}
public function getEntireKey() { public function getEntireKey() {
$parts = array( $parts = array(
$this->getKeyType(), $this->getKeyType(),
@ -60,6 +62,8 @@ final class PhabricatorAuthSSHKey
} }
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -13,6 +13,15 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
// <internal> // <internal>
} }
public static function newFromStoredKey(PhabricatorAuthSSHKey $key) {
$public_key = new PhabricatorAuthSSHPublicKey();
$public_key->type = $key->getKeyType();
$public_key->body = $key->getKeyBody();
$public_key->comment = $key->getKeyComment();
return $public_key;
}
public static function newFromRawKey($entire_key) { public static function newFromRawKey($entire_key) {
$entire_key = trim($entire_key); $entire_key = trim($entire_key);
if (!strlen($entire_key)) { if (!strlen($entire_key)) {
@ -83,4 +92,11 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
return $this->comment; return $this->comment;
} }
public function getHash() {
$body = $this->getBody();
$body = trim($body);
$body = rtrim($body, '=');
return PhabricatorHash::digestForIndex($body);
}
} }

View file

@ -897,7 +897,7 @@ EOBODY;
} }
$keys = id(new PhabricatorAuthSSHKey())->loadAllWhere( $keys = id(new PhabricatorAuthSSHKey())->loadAllWhere(
'userPHID = %s', 'objectPHID = %s',
$this->getPHID()); $this->getPHID());
foreach ($keys as $key) { foreach ($keys as $key) {
$key->delete(); $key->delete();

View file

@ -44,8 +44,7 @@ final class PhabricatorSettingsPanelSSHKeys
$this->getPanelURI()); $this->getPanelURI());
$id = nonempty($edit, $delete); $id = nonempty($edit, $delete);
if ($id && (int)$id) {
if ($id) {
$key = id(new PhabricatorAuthSSHKeyQuery()) $key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer($viewer) ->setViewer($viewer)
->withIDs(array($id)) ->withIDs(array($id))
@ -59,8 +58,8 @@ final class PhabricatorSettingsPanelSSHKeys
return new Aphront404Response(); return new Aphront404Response();
} }
} else { } else {
$key = new PhabricatorAuthSSHKey(); $key = id(new PhabricatorAuthSSHKey())
$key->setUserPHID($user->getPHID()); ->setObjectPHID($user->getPHID());
} }
if ($delete) { if ($delete) {
@ -89,7 +88,6 @@ final class PhabricatorSettingsPanelSSHKeys
$key->setKeyType($type); $key->setKeyType($type);
$key->setKeyBody($body); $key->setKeyBody($body);
$key->setKeyHash(md5($body));
$key->setKeyComment($comment); $key->setKeyComment($comment);
$e_key = null; $e_key = null;
@ -309,11 +307,10 @@ final class PhabricatorSettingsPanelSSHKeys
$body = $public_key->getBody(); $body = $public_key->getBody();
$key = id(new PhabricatorAuthSSHKey()) $key = id(new PhabricatorAuthSSHKey())
->setUserPHID($user->getPHID()) ->setObjectPHID($user->getPHID())
->setName('id_rsa_phabricator') ->setName('id_rsa_phabricator')
->setKeyType($type) ->setKeyType($type)
->setKeyBody($body) ->setKeyBody($body)
->setKeyHash(md5($body))
->setKeyComment(pht('Generated')) ->setKeyComment(pht('Generated'))
->save(); ->save();