Page MenuHomePhabricator

D10401.id25025.diff
No OneTemporary

D10401.id25025.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -10,6 +10,7 @@
'__library_version__' => 2,
'class' => array(
'AlmanacAuthorizedHost' => 'applications/almanac/storage/AlmanacAuthorizedHost.php',
+ 'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
'AlmanacDAO' => 'applications/almanac/storage/AlmanacDAO.php',
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
@@ -2784,6 +2785,7 @@
),
'xmap' => array(
'AlmanacAuthorizedHost' => 'AlmanacDAO',
+ 'AlmanacConduitUtil' => 'Phobject',
'AlmanacDAO' => 'PhabricatorLiskDAO',
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
diff --git a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
--- a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
+++ b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
@@ -13,7 +13,7 @@
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
- if (Filesystem::pathExists($this->getHostPrivateKeyPath())) {
+ if (Filesystem::pathExists(AlmanacConduitUtil::getHostPrivateKeyPath())) {
throw new Exception(
'This host already has a private key for Conduit access.');
}
@@ -26,26 +26,14 @@
->save();
Filesystem::writeFile(
- $this->getHostPrivateKeyPath(),
+ AlmanacConduitUtil::getHostPrivateKeyPath(),
$private_key);
Filesystem::writeFile(
- $this->getHostIDPath(),
+ AlmanacConduitUtil::getHostIDPath(),
$host->getID());
$console->writeOut("Registered as authorized host %d.\n", $host->getID());
}
- private function getHostPrivateKeyPath() {
- $root = dirname(phutil_get_library_root('phabricator'));
- $path = $root.'/conf/local/HOSTKEY';
- return $path;
- }
-
- private function getHostIDPath() {
- $root = dirname(phutil_get_library_root('phabricator'));
- $path = $root.'/conf/local/HOSTID';
- return $path;
- }
-
}
diff --git a/src/applications/almanac/util/AlmanacConduitUtil.php b/src/applications/almanac/util/AlmanacConduitUtil.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/util/AlmanacConduitUtil.php
@@ -0,0 +1,68 @@
+<?php
+
+final class AlmanacConduitUtil extends Phobject {
+
+ public static function verifySignature(
+ $host_id,
+ $signature,
+ ConduitAPIRequest $api_request) {
+
+ $authorized_host = id(new AlmanacAuthorizedHost())
+ ->load($host_id);
+
+ if ($authorized_host === null) {
+ return false;
+ }
+
+ // We have to convert the SSH public key to the PEM format so we can
+ // verify the signature with OpenSSH.
+ $ssh_public_key = new TempFile();
+ Filesystem::writeFile($ssh_public_key, $authorized_host->getPublicKey());
+
+ list($public_key, $stderr) = id(new ExecFuture(
+ 'ssh-keygen -e -f %s -m pkcs8',
+ $ssh_public_key))->resolvex();
+
+ unset($ssh_public_key);
+
+ return openssl_verify(
+ self::getEncodedParameters($api_request),
+ base64_decode($signature),
+ $public_key) === 1;
+ }
+
+ public static function signSignatureForRequest(
+ ConduitAPIRequest $api_request) {
+
+ $signature = '';
+ $result = openssl_sign(
+ self::getEncodedParameters($api_request),
+ $signature,
+ Filesystem::readFile(self::getHostPrivateKeyPath()));
+ if (!$result) {
+ throw new Exception('Unable to sign Conduit request with server key.');
+ }
+
+ $host_id = Filesystem::readFile(self::getHostIDPath());
+
+ return array((int)$host_id, base64_encode($signature));
+ }
+
+ public static function getHostPrivateKeyPath() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ $path = $root.'/conf/local/HOSTKEY';
+ return $path;
+ }
+
+ public static function getHostIDPath() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ $path = $root.'/conf/local/HOSTID';
+ return $path;
+ }
+
+ private static function getEncodedParameters(ConduitAPIRequest $api_request) {
+ $params = $api_request->getAllParameters();
+ return json_encode($params);
+ }
+
+}
diff --git a/src/applications/conduit/call/ConduitCall.php b/src/applications/conduit/call/ConduitCall.php
--- a/src/applications/conduit/call/ConduitCall.php
+++ b/src/applications/conduit/call/ConduitCall.php
@@ -145,15 +145,22 @@
$params['__conduit__']['isProxied'] = true;
if ($this->handler->shouldRequireAuthentication()) {
- $client->callMethodSynchronous(
- 'conduit.connect',
- array(
- 'client' => 'PhabricatorConduit',
- 'clientVersion' => '1.0',
- 'user' => $this->getUser()->getUserName(),
- 'certificate' => $this->getUser()->getConduitCertificate(),
- '__conduit__' => $params['__conduit__'],
- ));
+ if ($user->isOmnipotent()) {
+ list($host_id, $signature) =
+ AlmanacConduitUtil::signSignatureForRequest($this->request);
+
+ $client->setServerSignature($host_id, $signature);
+ } else {
+ $client->callMethodSynchronous(
+ 'conduit.connect',
+ array(
+ 'client' => 'PhabricatorConduit',
+ 'clientVersion' => '1.0',
+ 'user' => $this->getUser()->getUserName(),
+ 'certificate' => $this->getUser()->getConduitCertificate(),
+ '__conduit__' => $params['__conduit__'],
+ ));
+ }
}
return $client->callMethodSynchronous(
diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php
--- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php
@@ -209,6 +209,25 @@
$request->getUser());
}
+ // handle cross-host auth
+ if (isset($metadata['hostID']) && isset($metadata['signature'])) {
+ if (AlmanacConduitUtil::verifySignature(
+ $metadata['hostID'],
+ $metadata['signature'],
+ $api_request)) {
+
+ // Authenticated using server signature; therefore the user is
+ // the omnipotent user.
+ $api_request->setUser(PhabricatorUser::getOmnipotentUser());
+ return null;
+ } else {
+ return array(
+ 'ERR-INVALID-AUTH',
+ 'Server signature is invalid.',
+ );
+ }
+ }
+
// handle oauth
$access_token = $request->getStr('access_token');
$method_scope = $metadata['scope'];

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 14, 11:05 PM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7675357
Default Alt Text
D10401.id25025.diff (6 KB)

Event Timeline