1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-22 05:20:56 +01:00

Work around lack of PKCS8 support in OSX ssh-keygen

Summary:
Ref T4209. Ref T6240. Ref T6238. See D10401 for original discussion.

On OSX, `ssh-keygen` doesn't support PKCS8:

  - When we hit an issue with this, raise a more tailored message about it.
  - Allow the user to work around the problem with `auth cache-pkcs8 ...`, providing reasonable guidance / warnings.

In practice, this only really matters very much for one key, which I'm just going to make the services extension cache automatically. So it's sort of moot, but good to have around for weird cases and to make testing easier.

Test Plan: Hit error, cached key, got clean asymmetric auth.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4209, T6240, T6238

Differential Revision: https://secure.phabricator.com/D11021
This commit is contained in:
epriestley 2014-12-19 16:36:40 -08:00
parent ab4207a2c5
commit 0ce08b4d27
3 changed files with 134 additions and 5 deletions

View file

@ -1316,6 +1316,7 @@ phutil_register_library_map(array(
'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php',
'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php',
'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php',
'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php',
'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php',
'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php',
@ -4429,6 +4430,7 @@ phutil_register_library_map(array(
'PhabricatorAuthLinkController' => 'PhabricatorAuthController',
'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController',
'PhabricatorAuthLoginController' => 'PhabricatorAuthController',
'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow',

View file

@ -0,0 +1,96 @@
<?php
final class PhabricatorAuthManagementCachePKCS8Workflow
extends PhabricatorAuthManagementWorkflow {
protected function didConstruct() {
$this
->setName('cache-pkcs8')
->setExamples('**cache-pkcs8** --public __keyfile__ --pkcs8 __keyfile__')
->setSynopsis(
pht(
'Cache the PKCS8 format of a public key. When developing on OSX, '.
'this can be used to work around issues with ssh-keygen. Use '.
'`ssh-keygen -e -m PKCS8 -f key.pub` to generate a PKCS8 key to '.
'feed to this command.'))
->setArguments(
array(
array(
'name' => 'public',
'param' => 'keyfile',
'help' => pht('Path to public keyfile.'),
),
array(
'name' => 'pkcs8',
'param' => 'keyfile',
'help' => pht('Path to corresponding PKCS8 key.'),
),
));
}
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 --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 --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 unautorized 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;
}
}

View file

@ -108,17 +108,48 @@ final class PhabricatorAuthSSHPublicKey extends Phobject {
}
public function toPKCS8() {
$entire_key = $this->getEntireKey();
$cache_key = $this->getPKCS8CacheKey($entire_key);
// TODO: Put a cache in front of this.
$cache = PhabricatorCaches::getImmutableCache();
$pkcs8_key = $cache->getKey($cache_key);
if ($pkcs8_key) {
return $pkcs8_key;
}
$tmp = new TempFile();
Filesystem::writeFile($tmp, $this->getEntireKey());
list($pem_key) = execx(
'ssh-keygen -e -m PKCS8 -f %s',
$tmp);
try {
list($pkcs8_key) = execx(
'ssh-keygen -e -m PKCS8 -f %s',
$tmp);
} catch (CommandException $ex) {
unset($tmp);
throw new PhutilProxyException(
pht(
'Failed to convert public key into PKCS8 format. If you are '.
'developing on OSX, you may be able to use `bin/auth cache-pkcs8` '.
'to work around this issue. %s',
$ex->getMessage()),
$ex);
}
unset($tmp);
return $pem_key;
$cache->setKey($cache_key, $pkcs8_key);
return $pkcs8_key;
}
public function forcePopulatePKCS8Cache($pkcs8_key) {
$entire_key = $this->getEntireKey();
$cache_key = $this->getPKCS8CacheKey($entire_key);
$cache = PhabricatorCaches::getImmutableCache();
$cache->setKey($cache_key, $pkcs8_key);
}
private function getPKCS8CacheKey($entire_key) {
return 'pkcs8:'.PhabricatorHash::digestForIndex($entire_key);
}
}