@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+100
+22
src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php
··· 99 99 return PhabricatorHash::digestForIndex($body); 100 100 } 101 101 102 + public function getEntireKey() { 103 + $key = $this->type.' '.$this->body; 104 + if (strlen($this->comment)) { 105 + $key = $key.' '.$this->comment; 106 + } 107 + return $key; 108 + } 109 + 110 + public function toPCKS8() { 111 + 112 + // TODO: Put a cache in front of this. 113 + 114 + $tmp = new TempFile(); 115 + Filesystem::writeFile($tmp, $this->getEntireKey()); 116 + list($pem_key) = execx( 117 + 'ssh-keygen -e -m pcks8 -f %s', 118 + $tmp); 119 + unset($tmp); 120 + 121 + return $pem_key; 122 + } 123 + 102 124 }
+78
src/applications/conduit/controller/PhabricatorConduitAPIController.php
··· 209 209 $request->getUser()); 210 210 } 211 211 212 + $auth_type = idx($metadata, 'auth.type'); 213 + if ($auth_type === ConduitClient::AUTH_ASYMMETRIC) { 214 + $host = idx($metadata, 'auth.host'); 215 + if (!$host) { 216 + return array( 217 + 'ERR-INVALID-AUTH', 218 + pht( 219 + 'Request is missing required "auth.host" parameter.'), 220 + ); 221 + } 222 + 223 + // TODO: Validate that we are the host! 224 + 225 + $raw_key = idx($metadata, 'auth.key'); 226 + $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key); 227 + $ssl_public_key = $public_key->toPCKS8(); 228 + 229 + // First, verify the signature. 230 + try { 231 + $protocol_data = $metadata; 232 + 233 + // TODO: We should stop writing this into the protocol data when 234 + // processing a request. 235 + unset($protocol_data['scope']); 236 + 237 + ConduitClient::verifySignature( 238 + $this->method, 239 + $api_request->getAllParameters(), 240 + $protocol_data, 241 + $ssl_public_key); 242 + } catch (Exception $ex) { 243 + return array( 244 + 'ERR-INVALID-AUTH', 245 + pht( 246 + 'Signature verification failure. %s', 247 + $ex->getMessage()), 248 + ); 249 + } 250 + 251 + // If the signature is valid, find the user or device which is 252 + // associated with this public key. 253 + 254 + $stored_key = id(new PhabricatorAuthSSHKeyQuery()) 255 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 256 + ->withKeys(array($public_key)) 257 + ->executeOne(); 258 + if (!$stored_key) { 259 + return array( 260 + 'ERR-INVALID-AUTH', 261 + pht( 262 + 'No user or device is associated with that public key.'), 263 + ); 264 + } 265 + 266 + $object = $stored_key->getObject(); 267 + 268 + if ($object instanceof PhabricatorUser) { 269 + $user = $object; 270 + } else { 271 + throw new Exception( 272 + pht('Not Implemented: Would authenticate Almanac device.')); 273 + } 274 + 275 + return $this->validateAuthenticatedUser( 276 + $api_request, 277 + $user); 278 + } else if ($auth_type === null) { 279 + // No specified authentication type, continue with other authentication 280 + // methods below. 281 + } else { 282 + return array( 283 + 'ERR-INVALID-AUTH', 284 + pht( 285 + 'Provided "auth.type" ("%s") is not recognized.', 286 + $auth_type), 287 + ); 288 + } 289 + 212 290 // handle oauth 213 291 $access_token = $request->getStr('access_token'); 214 292 $method_scope = $metadata['scope'];