Skip to content

Commit bf17b12

Browse files
author
epriestley
committed
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
1 parent a17a368 commit bf17b12

13 files changed

+136
-33
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
2+
CHANGE userPHID objectPHID VARBINARY(64) NOT NULL;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
2+
DROP COLUMN keyHash;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
2+
ADD COLUMN keyIndex BINARY(12);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
$table = new PhabricatorAuthSSHKey();
4+
$conn_w = $table->establishConnection('w');
5+
6+
echo "Updating SSH public key indexes...\n";
7+
8+
$keys = new LiskMigrationIterator($table);
9+
foreach ($keys as $key) {
10+
$id = $key->getID();
11+
12+
echo "Updating key {$id}...\n";
13+
14+
try {
15+
$hash = $key->toPublicKey()->getHash();
16+
} catch (Exception $ex) {
17+
echo "Key has bad format! Removing key.\n";
18+
queryfx(
19+
$conn_w,
20+
'DELETE FROM %T WHERE id = %d',
21+
$table->getTableName(),
22+
$id);
23+
continue;
24+
}
25+
26+
$collision = queryfx_all(
27+
$conn_w,
28+
'SELECT * FROM %T WHERE keyIndex = %s AND id < %d',
29+
$table->getTableName(),
30+
$hash,
31+
$key->getID());
32+
if ($collision) {
33+
echo "Key is a duplicate! Removing key.\n";
34+
queryfx(
35+
$conn_w,
36+
'DELETE FROM %T WHERE id = %d',
37+
$table->getTableName(),
38+
$id);
39+
continue;
40+
}
41+
42+
queryfx(
43+
$conn_w,
44+
'UPDATE %T SET keyIndex = %s WHERE id = %d',
45+
$table->getTableName(),
46+
$hash,
47+
$key->getID());
48+
}
49+
50+
echo "Done.\n";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
2+
CHANGE keyIndex keyIndex BINARY(12) NOT NULL;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
2+
ADD UNIQUE KEY `key_unique` (keyIndex);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
UPDATE {$NAMESPACE}_auth.auth_sshkey
2+
SET name = '' WHERE name IS NULL;
3+
4+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
5+
CHANGE name name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
6+
7+
UPDATE {$NAMESPACE}_auth.auth_sshkey
8+
SET keyType = '' WHERE keyType IS NULL;
9+
10+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
11+
CHANGE keyType keyType VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;
12+
13+
UPDATE {$NAMESPACE}_auth.auth_sshkey
14+
SET keyBody = '' WHERE keyBody IS NULL;
15+
16+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
17+
CHANGE keyBody keyBody LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL;
18+
19+
UPDATE {$NAMESPACE}_auth.auth_sshkey
20+
SET keyComment = '' WHERE keyComment IS NULL;
21+
22+
ALTER TABLE {$NAMESPACE}_auth.auth_sshkey
23+
CHANGE keyComment keyComment VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL;

scripts/ssh/ssh-auth.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,15 @@
3434

3535
$type = $ssh_key->getKeyType();
3636
$type = preg_replace('@[\x00-\x20]+@', '', $type);
37+
if (!strlen($type)) {
38+
continue;
39+
}
3740

3841
$key = $ssh_key->getKeyBody();
3942
$key = preg_replace('@[\x00-\x20]+@', '', $key);
43+
if (!strlen($key)) {
44+
continue;
45+
}
4046

4147
$options = array(
4248
'command="'.$cmd.'"',

src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,18 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
7373
if ($this->objectPHIDs !== null) {
7474
$where[] = qsprintf(
7575
$conn_r,
76-
'userPHID IN (%Ls)',
76+
'objectPHID IN (%Ls)',
7777
$this->objectPHIDs);
7878
}
7979

8080
if ($this->keys !== null) {
81-
// TODO: This could take advantage of a better key, and the hashing
82-
// scheme for this table is a bit nonstandard and questionable.
83-
8481
$sql = array();
8582
foreach ($this->keys as $key) {
8683
$sql[] = qsprintf(
8784
$conn_r,
88-
'(keyType = %s AND keyBody = %s)',
85+
'(keyType = %s AND keyIndex = %s)',
8986
$key->getType(),
90-
$key->getBody());
87+
$key->getHash());
9188
}
9289
$where[] = implode(' OR ', $sql);
9390
}

src/applications/auth/storage/PhabricatorAuthSSHKey.php

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,45 @@ final class PhabricatorAuthSSHKey
44
extends PhabricatorAuthDAO
55
implements PhabricatorPolicyInterface {
66

7-
protected $userPHID;
7+
protected $objectPHID;
88
protected $name;
99
protected $keyType;
10+
protected $keyIndex;
1011
protected $keyBody;
11-
protected $keyHash;
12-
protected $keyComment;
12+
protected $keyComment = '';
1313

1414
private $object = self::ATTACHABLE;
1515

16-
public function getObjectPHID() {
17-
return $this->getUserPHID();
18-
}
19-
2016
public function getConfiguration() {
2117
return array(
2218
self::CONFIG_COLUMN_SCHEMA => array(
23-
'keyHash' => 'bytes32',
24-
'keyComment' => 'text255?',
25-
26-
// T6203/NULLABILITY
27-
// These seem like they should not be nullable.
28-
'name' => 'text255?',
29-
'keyType' => 'text255?',
30-
'keyBody' => 'text?',
19+
'name' => 'text255',
20+
'keyType' => 'text255',
21+
'keyIndex' => 'bytes12',
22+
'keyBody' => 'text',
23+
'keyComment' => 'text255',
3124
),
3225
self::CONFIG_KEY_SCHEMA => array(
33-
'userPHID' => array(
34-
'columns' => array('userPHID'),
26+
'key_object' => array(
27+
'columns' => array('objectPHID'),
3528
),
36-
'keyHash' => array(
37-
'columns' => array('keyHash'),
29+
'key_unique' => array(
30+
'columns' => array('keyIndex'),
3831
'unique' => true,
3932
),
4033
),
4134
) + parent::getConfiguration();
4235
}
4336

37+
public function save() {
38+
$this->setKeyIndex($this->toPublicKey()->getHash());
39+
return parent::save();
40+
}
41+
42+
public function toPublicKey() {
43+
return PhabricatorAuthSSHPublicKey::newFromStoredKey($this);
44+
}
45+
4446
public function getEntireKey() {
4547
$parts = array(
4648
$this->getKeyType(),
@@ -60,6 +62,8 @@ public function attachObject($object) {
6062
}
6163

6264

65+
66+
6367
/* -( PhabricatorPolicyInterface )----------------------------------------- */
6468

6569

src/applications/auth/storage/PhabricatorAuthSSHPublicKey.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ private function __construct() {
1313
// <internal>
1414
}
1515

16+
public static function newFromStoredKey(PhabricatorAuthSSHKey $key) {
17+
$public_key = new PhabricatorAuthSSHPublicKey();
18+
$public_key->type = $key->getKeyType();
19+
$public_key->body = $key->getKeyBody();
20+
$public_key->comment = $key->getKeyComment();
21+
22+
return $public_key;
23+
}
24+
1625
public static function newFromRawKey($entire_key) {
1726
$entire_key = trim($entire_key);
1827
if (!strlen($entire_key)) {
@@ -83,4 +92,11 @@ public function getComment() {
8392
return $this->comment;
8493
}
8594

95+
public function getHash() {
96+
$body = $this->getBody();
97+
$body = trim($body);
98+
$body = rtrim($body, '=');
99+
return PhabricatorHash::digestForIndex($body);
100+
}
101+
86102
}

src/applications/people/storage/PhabricatorUser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,7 @@ public function destroyObjectPermanently(
897897
}
898898

899899
$keys = id(new PhabricatorAuthSSHKey())->loadAllWhere(
900-
'userPHID = %s',
900+
'objectPHID = %s',
901901
$this->getPHID());
902902
foreach ($keys as $key) {
903903
$key->delete();

src/applications/settings/panel/PhabricatorSettingsPanelSSHKeys.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ public function processRequest(AphrontRequest $request) {
4444
$this->getPanelURI());
4545

4646
$id = nonempty($edit, $delete);
47-
48-
if ($id) {
47+
if ($id && (int)$id) {
4948
$key = id(new PhabricatorAuthSSHKeyQuery())
5049
->setViewer($viewer)
5150
->withIDs(array($id))
@@ -59,8 +58,8 @@ public function processRequest(AphrontRequest $request) {
5958
return new Aphront404Response();
6059
}
6160
} else {
62-
$key = new PhabricatorAuthSSHKey();
63-
$key->setUserPHID($user->getPHID());
61+
$key = id(new PhabricatorAuthSSHKey())
62+
->setObjectPHID($user->getPHID());
6463
}
6564

6665
if ($delete) {
@@ -89,7 +88,6 @@ public function processRequest(AphrontRequest $request) {
8988

9089
$key->setKeyType($type);
9190
$key->setKeyBody($body);
92-
$key->setKeyHash(md5($body));
9391
$key->setKeyComment($comment);
9492

9593
$e_key = null;
@@ -309,11 +307,10 @@ private function processGenerate(AphrontRequest $request) {
309307
$body = $public_key->getBody();
310308

311309
$key = id(new PhabricatorAuthSSHKey())
312-
->setUserPHID($user->getPHID())
310+
->setObjectPHID($user->getPHID())
313311
->setName('id_rsa_phabricator')
314312
->setKeyType($type)
315313
->setKeyBody($body)
316-
->setKeyHash(md5($body))
317314
->setKeyComment(pht('Generated'))
318315
->save();
319316

0 commit comments

Comments
 (0)