1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-23 15:22:41 +01:00

Allow Phabricator to accept Conduit requests signed with an SSH key

Summary:
Ref T4209.  Depends on D10402.

This updates Conduit to support authenticating calls from other servers by signing the request parameters with the sending server's private key and verifying it with the public key stored in the database.

Test Plan:
  - Made like 500 bad calls using the stuff in D10402.
  - Made a few valid calls using the stuff in D10402.

Reviewers: hach-que, btrahan, #blessed_reviewers

Reviewed By: btrahan, #blessed_reviewers

Subscribers: epriestley, Korvin

Maniphest Tasks: T6240, T4209

Differential Revision: https://secure.phabricator.com/D10401
This commit is contained in:
epriestley 2014-11-17 13:11:52 -08:00
parent 2ac987714b
commit 657b36dd06
2 changed files with 100 additions and 0 deletions

View file

@ -99,4 +99,26 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
return PhabricatorHash::digestForIndex($body);
}
public function getEntireKey() {
$key = $this->type.' '.$this->body;
if (strlen($this->comment)) {
$key = $key.' '.$this->comment;
}
return $key;
}
public function toPCKS8() {
// TODO: Put a cache in front of this.
$tmp = new TempFile();
Filesystem::writeFile($tmp, $this->getEntireKey());
list($pem_key) = execx(
'ssh-keygen -e -m pcks8 -f %s',
$tmp);
unset($tmp);
return $pem_key;
}
}

View file

@ -209,6 +209,84 @@ final class PhabricatorConduitAPIController
$request->getUser());
}
$auth_type = idx($metadata, 'auth.type');
if ($auth_type === ConduitClient::AUTH_ASYMMETRIC) {
$host = idx($metadata, 'auth.host');
if (!$host) {
return array(
'ERR-INVALID-AUTH',
pht(
'Request is missing required "auth.host" parameter.'),
);
}
// TODO: Validate that we are the host!
$raw_key = idx($metadata, 'auth.key');
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key);
$ssl_public_key = $public_key->toPCKS8();
// First, verify the signature.
try {
$protocol_data = $metadata;
// TODO: We should stop writing this into the protocol data when
// processing a request.
unset($protocol_data['scope']);
ConduitClient::verifySignature(
$this->method,
$api_request->getAllParameters(),
$protocol_data,
$ssl_public_key);
} catch (Exception $ex) {
return array(
'ERR-INVALID-AUTH',
pht(
'Signature verification failure. %s',
$ex->getMessage()),
);
}
// If the signature is valid, find the user or device which is
// associated with this public key.
$stored_key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withKeys(array($public_key))
->executeOne();
if (!$stored_key) {
return array(
'ERR-INVALID-AUTH',
pht(
'No user or device is associated with that public key.'),
);
}
$object = $stored_key->getObject();
if ($object instanceof PhabricatorUser) {
$user = $object;
} else {
throw new Exception(
pht('Not Implemented: Would authenticate Almanac device.'));
}
return $this->validateAuthenticatedUser(
$api_request,
$user);
} else if ($auth_type === null) {
// No specified authentication type, continue with other authentication
// methods below.
} else {
return array(
'ERR-INVALID-AUTH',
pht(
'Provided "auth.type" ("%s") is not recognized.',
$auth_type),
);
}
// handle oauth
$access_token = $request->getStr('access_token');
$method_scope = $metadata['scope'];