Commit 657b36dd authored by epriestley's avatar epriestley
Browse files

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
parent 2ac98771
......@@ -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;
}
}
......@@ -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'];
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment