protected function execute(ConduitAPIRequest $request)
 {
     $viewer = $request->getUser();
     $query = id(new PhabricatorAuthSSHKeyQuery())->setViewer($viewer);
     $ids = $request->getValue('ids');
     if ($ids !== null) {
         $query->withIDs($ids);
     }
     $phids = $request->getValue('phids');
     if ($phids !== null) {
         $query->withPHIDs($phids);
     }
     $object_phids = $request->getValue('objectPHIDs');
     if ($object_phids !== null) {
         $query->withObjectPHIDs($object_phids);
     }
     $keys = $request->getValue('keys');
     if ($keys !== null) {
         $key_objects = array();
         foreach ($keys as $key) {
             $key_objects[] = PhabricatorAuthSSHPublicKey::newFromRawKey($key);
         }
         $query->withKeys($key_objects);
     }
     $pager = $this->newPager($request);
     $public_keys = $query->executeWithCursorPager($pager);
     $data = array();
     foreach ($public_keys as $public_key) {
         $data[] = array('id' => $public_key->getID(), 'name' => $public_key->getName(), 'phid' => $public_key->getPHID(), 'objectPHID' => $public_key->getObjectPHID(), 'isTrusted' => (bool) $public_key->getIsTrusted(), 'key' => $public_key->getEntireKey());
     }
     $results = array('data' => $data);
     return $this->addPagerResults($results, $pager);
 }
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $public_keyfile = $args->getArg('public');
     if (!strlen($public_keyfile)) {
         throw new PhutilArgumentUsageException(pht('You must specify the path to a public keyfile with %s.', '--public'));
     }
     if (!Filesystem::pathExists($public_keyfile)) {
         throw new PhutilArgumentUsageException(pht('Specified public keyfile "%s" does not exist!', $public_keyfile));
     }
     $public_key = Filesystem::readFile($public_keyfile);
     $pkcs8_keyfile = $args->getArg('pkcs8');
     if (!strlen($pkcs8_keyfile)) {
         throw new PhutilArgumentUsageException(pht('You must specify the path to a pkcs8 keyfile with %s.', '--pkc8s'));
     }
     if (!Filesystem::pathExists($pkcs8_keyfile)) {
         throw new PhutilArgumentUsageException(pht('Specified pkcs8 keyfile "%s" does not exist!', $pkcs8_keyfile));
     }
     $pkcs8_key = Filesystem::readFile($pkcs8_keyfile);
     $warning = pht('Adding a PKCS8 keyfile to the cache can be very dangerous. If the ' . 'PKCS8 file really encodes a different public key than the one ' . 'specified, an attacker could use it to gain unauthorized access.' . "\n\n" . 'Generally, you should use this option only in a development ' . 'environment where ssh-keygen is broken and it is inconvenient to ' . 'fix it, and only if you are certain you understand the risks. You ' . 'should never cache a PKCS8 file you did not generate yourself.');
     $console->writeOut("%s\n", phutil_console_wrap($warning));
     $prompt = pht('Really trust this PKCS8 keyfile?');
     if (!phutil_console_confirm($prompt)) {
         throw new PhutilArgumentUsageException(pht('Aborted workflow.'));
     }
     $key = PhabricatorAuthSSHPublicKey::newFromRawKey($public_key);
     $key->forcePopulatePKCS8Cache($pkcs8_key);
     $console->writeOut("%s\n", pht('Cached PKCS8 key for public key.'));
     return 0;
 }
コード例 #3
0
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $key = $this->newKeyForObjectPHID($request->getStr('objectPHID'));
     if (!$key) {
         return new Aphront404Response();
     }
     $cancel_uri = $key->getObject()->getSSHPublicKeyManagementURI($viewer);
     $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($viewer, $request, $cancel_uri);
     if ($request->isFormPost()) {
         $default_name = $key->getObject()->getSSHKeyDefaultName();
         $keys = PhabricatorSSHKeyGenerator::generateKeypair();
         list($public_key, $private_key) = $keys;
         $file = PhabricatorFile::buildFromFileDataOrHash($private_key, array('name' => $default_name . '.key', 'ttl' => time() + 60 * 10, 'viewPolicy' => $viewer->getPHID()));
         $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($public_key);
         $type = $public_key->getType();
         $body = $public_key->getBody();
         $key->setName($default_name)->setKeyType($type)->setKeyBody($body)->setKeyComment(pht('Generated'))->save();
         // NOTE: We're disabling workflow on submit so the download works. We're
         // disabling workflow on cancel so the page reloads, showing the new
         // key.
         return $this->newDialog()->setTitle(pht('Download Private Key'))->setDisableWorkflowOnCancel(true)->setDisableWorkflowOnSubmit(true)->setSubmitURI($file->getDownloadURI())->appendParagraph(pht('A keypair has been generated, and the public key has been ' . 'added as a recognized key. Use the button below to download ' . 'the private key.'))->appendParagraph(pht('After you download the private key, it will be destroyed. ' . 'You will not be able to retrieve it if you lose your copy.'))->addSubmitButton(pht('Download Private Key'))->addCancelButton($cancel_uri, pht('Done'));
     }
     try {
         PhabricatorSSHKeyGenerator::assertCanGenerateKeypair();
         return $this->newDialog()->setTitle(pht('Generate New Keypair'))->addHiddenInput('objectPHID', $key->getObject()->getPHID())->appendParagraph(pht('This workflow will generate a new SSH keypair, add the public ' . 'key, and let you download the private key.'))->appendParagraph(pht('Phabricator will not retain a copy of the private key.'))->addSubmitButton(pht('Generate New Keypair'))->addCancelButton($cancel_uri);
     } catch (Exception $ex) {
         return $this->newDialog()->setTitle(pht('Unable to Generate Keys'))->appendParagraph($ex->getMessage())->addCancelButton($cancel_uri);
     }
 }
コード例 #4
0
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $id = $request->getURIData('id');
     if ($id) {
         $key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($viewer)->withIDs(array($id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
         if (!$key) {
             return new Aphront404Response();
         }
         $is_new = false;
     } else {
         $key = $this->newKeyForObjectPHID($request->getStr('objectPHID'));
         if (!$key) {
             return new Aphront404Response();
         }
         $is_new = true;
     }
     $cancel_uri = $key->getObject()->getSSHPublicKeyManagementURI($viewer);
     if ($key->getIsTrusted()) {
         $id = $key->getID();
         return $this->newDialog()->setTitle(pht('Can Not Edit Trusted Key'))->appendParagraph(pht('This key is trusted. Trusted keys can not be edited. ' . 'Use %s to revoke trust before editing the key.', phutil_tag('tt', array(), "bin/almanac untrust-key --id {$id}")))->addCancelButton($cancel_uri, pht('Okay'));
     }
     $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($viewer, $request, $cancel_uri);
     $v_name = $key->getName();
     $e_name = strlen($v_name) ? null : true;
     $v_key = $key->getEntireKey();
     $e_key = strlen($v_key) ? null : true;
     $errors = array();
     if ($request->isFormPost()) {
         $v_name = $request->getStr('name');
         $v_key = $request->getStr('key');
         if (!strlen($v_name)) {
             $errors[] = pht('You must provide a name for this public key.');
             $e_name = pht('Required');
         } else {
             $key->setName($v_name);
         }
         if (!strlen($v_key)) {
             $errors[] = pht('You must provide a public key.');
             $e_key = pht('Required');
         } else {
             try {
                 $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($v_key);
                 $type = $public_key->getType();
                 $body = $public_key->getBody();
                 $comment = $public_key->getComment();
                 $key->setKeyType($type);
                 $key->setKeyBody($body);
                 $key->setKeyComment($comment);
                 $e_key = null;
             } catch (Exception $ex) {
                 $e_key = pht('Invalid');
                 $errors[] = $ex->getMessage();
             }
         }
         if (!$errors) {
             try {
                 $key->save();
                 return id(new AphrontRedirectResponse())->setURI($cancel_uri);
             } catch (Exception $ex) {
                 $e_key = pht('Duplicate');
                 $errors[] = pht('This public key is already associated with another user or ' . 'device. Each key must unambiguously identify a single unique ' . 'owner.');
             }
         }
     }
     $form = id(new AphrontFormView())->setUser($viewer)->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Name'))->setName('name')->setError($e_name)->setValue($v_name))->appendChild(id(new AphrontFormTextAreaControl())->setLabel(pht('Public Key'))->setName('key')->setValue($v_key)->setError($e_key));
     if ($is_new) {
         $title = pht('Upload SSH Public Key');
         $save_button = pht('Upload Public Key');
         $form->addHiddenInput('objectPHID', $key->getObject()->getPHID());
     } else {
         $title = pht('Edit SSH Public Key');
         $save_button = pht('Save Changes');
     }
     return $this->newDialog()->setTitle($title)->setWidth(AphrontDialogView::WIDTH_FORM)->setErrors($errors)->appendForm($form)->addSubmitButton($save_button)->addCancelButton($cancel_uri);
 }
コード例 #5
0
 /**
  * Authenticate the client making the request to a Phabricator user account.
  *
  * @param   ConduitAPIRequest Request being executed.
  * @param   dict              Request metadata.
  * @return  null|pair         Null to indicate successful authentication, or
  *                            an error code and error message pair.
  */
 private function authenticateUser(ConduitAPIRequest $api_request, array $metadata)
 {
     $request = $this->getRequest();
     if ($request->getUser()->getPHID()) {
         $request->validateCSRF();
         return $this->validateAuthenticatedUser($api_request, $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 "%s" parameter.', 'auth.host'));
         }
         // TODO: Validate that we are the host!
         $raw_key = idx($metadata, 'auth.key');
         $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key);
         $ssl_public_key = $public_key->toPKCS8();
         // 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 {
             if (!$stored_key->getIsTrusted()) {
                 return array('ERR-INVALID-AUTH', pht('The key which signed this request is not trusted. Only ' . 'trusted keys can be used to sign API calls.'));
             }
             if (!PhabricatorEnv::isClusterRemoteAddress()) {
                 return array('ERR-INVALID-AUTH', pht('This request originates from outside of the Phabricator ' . 'cluster address range. Requests signed with trusted ' . 'device keys must originate from within the cluster.'));
             }
             $user = PhabricatorUser::getOmnipotentUser();
             // Flag this as an intracluster request.
             $api_request->setIsClusterRequest(true);
         }
         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 "%s" ("%s") is not recognized.', 'auth.type', $auth_type));
         }
     }
     $token_string = idx($metadata, 'token');
     if (strlen($token_string)) {
         if (strlen($token_string) != 32) {
             return array('ERR-INVALID-AUTH', pht('API token "%s" has the wrong length. API tokens should be ' . '32 characters long.', $token_string));
         }
         $type = head(explode('-', $token_string));
         $valid_types = PhabricatorConduitToken::getAllTokenTypes();
         $valid_types = array_fuse($valid_types);
         if (empty($valid_types[$type])) {
             return array('ERR-INVALID-AUTH', pht('API token "%s" has the wrong format. API tokens should be ' . '32 characters long and begin with one of these prefixes: %s.', $token_string, implode(', ', $valid_types)));
         }
         $token = id(new PhabricatorConduitTokenQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withTokens(array($token_string))->withExpired(false)->executeOne();
         if (!$token) {
             $token = id(new PhabricatorConduitTokenQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withTokens(array($token_string))->withExpired(true)->executeOne();
             if ($token) {
                 return array('ERR-INVALID-AUTH', pht('API token "%s" was previously valid, but has expired.', $token_string));
             } else {
                 return array('ERR-INVALID-AUTH', pht('API token "%s" is not valid.', $token_string));
             }
         }
         // If this is a "cli-" token, it expires shortly after it is generated
         // by default. Once it is actually used, we extend its lifetime and make
         // it permanent. This allows stray tokens to get cleaned up automatically
         // if they aren't being used.
         if ($token->getTokenType() == PhabricatorConduitToken::TYPE_COMMANDLINE) {
             if ($token->getExpires()) {
                 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
                 $token->setExpires(null);
                 $token->save();
                 unset($unguarded);
             }
         }
         // If this is a "clr-" token, Phabricator must be configured in cluster
         // mode and the remote address must be a cluster node.
         if ($token->getTokenType() == PhabricatorConduitToken::TYPE_CLUSTER) {
             if (!PhabricatorEnv::isClusterRemoteAddress()) {
                 return array('ERR-INVALID-AUTH', pht('This request originates from outside of the Phabricator ' . 'cluster address range. Requests signed with cluster API ' . 'tokens must originate from within the cluster.'));
             }
             // Flag this as an intracluster request.
             $api_request->setIsClusterRequest(true);
         }
         $user = $token->getObject();
         if (!$user instanceof PhabricatorUser) {
             return array('ERR-INVALID-AUTH', pht('API token is not associated with a valid user.'));
         }
         return $this->validateAuthenticatedUser($api_request, $user);
     }
     // handle oauth
     $access_token = idx($metadata, 'access_token');
     $method_scope = idx($metadata, 'scope');
     if ($access_token && $method_scope != PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE) {
         $token = id(new PhabricatorOAuthServerAccessToken())->loadOneWhere('token = %s', $access_token);
         if (!$token) {
             return array('ERR-INVALID-AUTH', pht('Access token does not exist.'));
         }
         $oauth_server = new PhabricatorOAuthServer();
         $valid = $oauth_server->validateAccessToken($token, $method_scope);
         if (!$valid) {
             return array('ERR-INVALID-AUTH', pht('Access token is invalid.'));
         }
         // valid token, so let's log in the user!
         $user_phid = $token->getUserPHID();
         $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $user_phid);
         if (!$user) {
             return array('ERR-INVALID-AUTH', pht('Access token is for invalid user.'));
         }
         return $this->validateAuthenticatedUser($api_request, $user);
     }
     // Handle sessionless auth.
     // TODO: This is super messy.
     // TODO: Remove this in favor of token-based auth.
     if (isset($metadata['authUser'])) {
         $user = id(new PhabricatorUser())->loadOneWhere('userName = %s', $metadata['authUser']);
         if (!$user) {
             return array('ERR-INVALID-AUTH', pht('Authentication is invalid.'));
         }
         $token = idx($metadata, 'authToken');
         $signature = idx($metadata, 'authSignature');
         $certificate = $user->getConduitCertificate();
         $hash = sha1($token . $certificate);
         if (!phutil_hashes_are_identical($hash, $signature)) {
             return array('ERR-INVALID-AUTH', pht('Authentication is invalid.'));
         }
         return $this->validateAuthenticatedUser($api_request, $user);
     }
     // Handle session-based auth.
     // TODO: Remove this in favor of token-based auth.
     $session_key = idx($metadata, 'sessionKey');
     if (!$session_key) {
         return array('ERR-INVALID-SESSION', pht('Session key is not present.'));
     }
     $user = id(new PhabricatorAuthSessionEngine())->loadUserForSession(PhabricatorAuthSession::TYPE_CONDUIT, $session_key);
     if (!$user) {
         return array('ERR-INVALID-SESSION', pht('Session key is invalid.'));
     }
     return $this->validateAuthenticatedUser($api_request, $user);
 }
コード例 #6
0
 protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions)
 {
     $errors = parent::validateTransaction($object, $type, $xactions);
     switch ($type) {
         case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:
             $missing = $this->validateIsEmptyTextField($object->getName(), $xactions);
             if ($missing) {
                 $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('SSH key name is required.'), nonempty(last($xactions), null));
                 $error->setIsMissingFieldError(true);
                 $errors[] = $error;
             }
             break;
         case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:
             $missing = $this->validateIsEmptyTextField($object->getName(), $xactions);
             if ($missing) {
                 $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('SSH key material is required.'), nonempty(last($xactions), null));
                 $error->setIsMissingFieldError(true);
                 $errors[] = $error;
             } else {
                 foreach ($xactions as $xaction) {
                     $new = $xaction->getNewValue();
                     try {
                         $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($new);
                     } catch (Exception $ex) {
                         $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction);
                     }
                 }
             }
             break;
         case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:
             foreach ($xactions as $xaction) {
                 if (!$xaction->getNewValue()) {
                     $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('SSH keys can not be reactivated.'), $xaction);
                 }
             }
             break;
     }
     return $errors;
 }
コード例 #7
0
 public function execute(PhutilArgumentParser $args)
 {
     $viewer = $this->getViewer();
     $device_name = $args->getArg('device');
     if (!strlen($device_name)) {
         throw new PhutilArgumentUsageException(pht('Specify a device with --device.'));
     }
     $device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($device_name))->executeOne();
     if (!$device) {
         throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $device_name));
     }
     $identify_as = $args->getArg('identify-as');
     $raw_device = $device_name;
     if (strlen($identify_as)) {
         $raw_device = $identify_as;
     }
     $identity_device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($raw_device))->executeOne();
     if (!$identity_device) {
         throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $raw_device));
     }
     $private_key_path = $args->getArg('private-key');
     if (!strlen($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('Specify a private key with --private-key.'));
     }
     if (!Filesystem::pathExists($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('No private key exists at path "%s"!', $private_key_path));
     }
     $raw_private_key = Filesystem::readFile($private_key_path);
     $phd_user = PhabricatorEnv::getEnvConfig('phd.user');
     if (!$phd_user) {
         throw new PhutilArgumentUsageException(pht('Config option "phd.user" is not set. You must set this option ' . 'so the private key can be stored with the correct permissions.'));
     }
     $tmp = new TempFile();
     list($err) = exec_manual('chown %s %s', $phd_user, $tmp);
     if ($err) {
         throw new PhutilArgumentUsageException(pht('Unable to change ownership of an identity file to daemon user ' . '"%s". Run this command as %s or root.', $phd_user, $phd_user));
     }
     $stored_public_path = AlmanacKeys::getKeyPath('device.pub');
     $stored_private_path = AlmanacKeys::getKeyPath('device.key');
     $stored_device_path = AlmanacKeys::getKeyPath('device.id');
     if (!$args->getArg('force')) {
         if (Filesystem::pathExists($stored_public_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered public key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_public_path)));
         }
         if (Filesystem::pathExists($stored_private_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered private key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_private_path)));
         }
     }
     // NOTE: We're writing the private key here so we can change permissions
     // on it without causing weird side effects to the file specified with
     // the `--private-key` flag. The file needs to have restrictive permissions
     // before `ssh-keygen` will willingly operate on it.
     $tmp_private = new TempFile();
     Filesystem::changePermissions($tmp_private, 0600);
     execx('chown %s %s', $phd_user, $tmp_private);
     Filesystem::writeFile($tmp_private, $raw_private_key);
     list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private);
     $key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);
     $public_key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($this->getViewer())->withKeys(array($key_object))->withIsActive(true)->executeOne();
     if (!$public_key) {
         throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is not ' . 'yet known to Phabricator. Associate the public key with an ' . 'Almanac device in the web interface before registering hosts ' . 'with it.'));
     }
     if ($public_key->getObjectPHID() !== $device->getPHID()) {
         $public_phid = $public_key->getObjectPHID();
         $public_handles = $viewer->loadHandles(array($public_phid));
         $public_handle = $public_handles[$public_phid];
         throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is already ' . 'associated with an object ("%s") other than the specified ' . 'device ("%s"). You can not use a single private key to identify ' . 'multiple devices or users.', $public_handle->getFullName(), $device->getName()));
     }
     if (!$public_key->getIsTrusted()) {
         throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'properly associated with the device, but is not yet trusted. ' . 'Trust this key before registering devices with it.'));
     }
     echo tsprintf("%s\n", pht('Installing public key...'));
     $tmp_public = new TempFile();
     Filesystem::changePermissions($tmp_public, 0600);
     execx('chown %s %s', $phd_user, $tmp_public);
     Filesystem::writeFile($tmp_public, $raw_public_key);
     execx('mv -f %s %s', $tmp_public, $stored_public_path);
     echo tsprintf("%s\n", pht('Installing private key...'));
     execx('mv -f %s %s', $tmp_private, $stored_private_path);
     echo tsprintf("%s\n", pht('Installing device %s...', $raw_device));
     // The permissions on this file are more open because the webserver also
     // needs to read it.
     $tmp_device = new TempFile();
     Filesystem::changePermissions($tmp_device, 0644);
     execx('chown %s %s', $phd_user, $tmp_device);
     Filesystem::writeFile($tmp_device, $raw_device);
     execx('mv -f %s %s', $tmp_device, $stored_device_path);
     echo tsprintf("**<bg:green> %s </bg>** %s\n", pht('HOST REGISTERED'), pht('This host has been registered as "%s" and a trusted keypair ' . 'has been installed.', $raw_device));
 }
コード例 #8
0
 public function toPublicKey()
 {
     return PhabricatorAuthSSHPublicKey::newFromStoredKey($this);
 }
コード例 #9
0
ファイル: ssh-auth-key.php プロジェクト: pugong/phabricator
#!/usr/bin/env php
<?php 
$root = dirname(dirname(dirname(__FILE__)));
require_once $root . '/scripts/__init_script__.php';
try {
    $cert = file_get_contents('php://stdin');
    $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($cert);
} catch (Exception $ex) {
    exit(1);
}
$key = id(new PhabricatorAuthSSHKeyQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withKeys(array($public_key))->executeOne();
if (!$key) {
    exit(1);
}
$object = $key->getObject();
if (!$object instanceof PhabricatorUser) {
    exit(1);
}
$bin = $root . '/bin/ssh-exec';
$cmd = csprintf('%s --phabricator-ssh-user %s', $bin, $object->getUsername());
// This is additional escaping for the SSH 'command="..."' string.
$cmd = addcslashes($cmd, '"\\');
$options = array('command="' . $cmd . '"', 'no-port-forwarding', 'no-X11-forwarding', 'no-agent-forwarding', 'no-pty');
echo implode(',', $options);
exit(0);
コード例 #10
0
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $device_name = $args->getArg('device');
     if (!strlen($device_name)) {
         throw new PhutilArgumentUsageException(pht('Specify a device with --device.'));
     }
     $device = id(new AlmanacDeviceQuery())->setViewer($this->getViewer())->withNames(array($device_name))->executeOne();
     if (!$device) {
         throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $device_name));
     }
     $private_key_path = $args->getArg('private-key');
     if (!strlen($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('Specify a private key with --private-key.'));
     }
     if (!Filesystem::pathExists($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('Private key "%s" does not exist!', $private_key_path));
     }
     $raw_private_key = Filesystem::readFile($private_key_path);
     $phd_user = PhabricatorEnv::getEnvConfig('phd.user');
     if (!$phd_user) {
         throw new PhutilArgumentUsageException(pht('Config option "phd.user" is not set. You must set this option ' . 'so the private key can be stored with the correct permissions.'));
     }
     $tmp = new TempFile();
     list($err) = exec_manual('chown %s %s', $phd_user, $tmp);
     if ($err) {
         throw new PhutilArgumentUsageException(pht('Unable to change ownership of a file to daemon user "%s". Run ' . 'this command as %s or root.', $phd_user, $phd_user));
     }
     $stored_public_path = AlmanacKeys::getKeyPath('device.pub');
     $stored_private_path = AlmanacKeys::getKeyPath('device.key');
     $stored_device_path = AlmanacKeys::getKeyPath('device.id');
     if (!$args->getArg('force')) {
         if (Filesystem::pathExists($stored_public_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered public key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_public_path)));
         }
         if (Filesystem::pathExists($stored_private_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered private key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_private_path)));
         }
     }
     // NOTE: We're writing the private key here so we can change permissions
     // on it without causing weird side effects to the file specified with
     // the `--private-key` flag. The file needs to have restrictive permissions
     // before `ssh-keygen` will willingly operate on it.
     $tmp_private = new TempFile();
     Filesystem::changePermissions($tmp_private, 0600);
     execx('chown %s %s', $phd_user, $tmp_private);
     Filesystem::writeFile($tmp_private, $raw_private_key);
     list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private);
     $key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);
     $public_key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($this->getViewer())->withKeys(array($key_object))->executeOne();
     if ($public_key) {
         if ($public_key->getObjectPHID() !== $device->getPHID()) {
             throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'already associated with an object other than the specified ' . 'device. You can not use a single private key to identify ' . 'multiple devices or users.'));
         } else {
             if (!$public_key->getIsTrusted()) {
                 throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'already associated with the device, but is not trusted. ' . 'Registering this key would trust the other entities which ' . 'hold it. Use a unique key, or explicitly enable trust for the ' . 'current key.'));
             } else {
                 if (!$args->getArg('allow-key-reuse')) {
                     throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'already associated with the device. If you do not want to ' . 'use a unique key, use --allow-key-reuse to permit ' . 'reassociation.'));
                 }
             }
         }
     } else {
         $public_key = id(new PhabricatorAuthSSHKey())->setObjectPHID($device->getPHID())->attachObject($device)->setName($device->getSSHKeyDefaultName())->setKeyType($key_object->getType())->setKeyBody($key_object->getBody())->setKeyComment(pht('Registered'))->setIsTrusted(1);
     }
     $console->writeOut("%s\n", pht('Installing public key...'));
     $tmp_public = new TempFile();
     Filesystem::changePermissions($tmp_public, 0600);
     execx('chown %s %s', $phd_user, $tmp_public);
     Filesystem::writeFile($tmp_public, $raw_public_key);
     execx('mv -f %s %s', $tmp_public, $stored_public_path);
     $console->writeOut("%s\n", pht('Installing private key...'));
     execx('mv -f %s %s', $tmp_private, $stored_private_path);
     $raw_device = $device_name;
     $identify_as = $args->getArg('identify-as');
     if (strlen($identify_as)) {
         $raw_device = $identify_as;
     }
     $console->writeOut("%s\n", pht('Installing device %s...', $raw_device));
     // The permissions on this file are more open because the webserver also
     // needs to read it.
     $tmp_device = new TempFile();
     Filesystem::changePermissions($tmp_device, 0644);
     execx('chown %s %s', $phd_user, $tmp_device);
     Filesystem::writeFile($tmp_device, $raw_device);
     execx('mv -f %s %s', $tmp_device, $stored_device_path);
     if (!$public_key->getID()) {
         $console->writeOut("%s\n", pht('Registering device key...'));
         $public_key->save();
     }
     $console->writeOut("**<bg:green> %s </bg>** %s\n", pht('HOST REGISTERED'), pht('This host has been registered as "%s" and a trusted keypair ' . 'has been installed.', $raw_device));
 }