1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-28 08:20:57 +01:00
phorge-phorge/scripts/ssh/ssh-auth.php
epriestley 4a53fc339e Don't use "phutil_hashes_are_identical()" to compare public keys
Summary:
Ref T13436. There's no real security value to doing this comparison, it just wards off evil "security researchers" who get upset if you ever compare two strings with a non-constant-time algorithm.

In practice, SSH public keys are pretty long, pretty public, and have pretty similar lengths. This leads to a relatively large amount of work to do constant-time comparisons on them (we frequently can't abort early after identifying differing string length).

Test Plan: Ran `bin/ssh-auth --sshd-key ...` on `secure` with ~1K keys, saw runtime drop by ~50% (~400ms to ~200ms) with `===`.

Maniphest Tasks: T13436

Differential Revision: https://secure.phabricator.com/D20875
2019-10-28 18:34:30 -07:00

156 lines
4 KiB
PHP
Executable file

#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/init/init-script.php';
// TODO: For now, this is using "parseParital()", not "parse()". This allows
// the script to accept (and ignore) additional arguments. This preserves
// backward compatibility until installs have time to migrate to the new
// syntax.
$args = id(new PhutilArgumentParser($argv))
->parsePartial(
array(
array(
'name' => 'sshd-key',
'param' => 'k',
'help' => pht(
'Accepts the "%%k" parameter from "AuthorizedKeysCommand".'),
),
));
$sshd_key = $args->getArg('sshd-key');
// NOTE: We are caching a datastructure rather than the flat key file because
// the path on disk to "ssh-exec" is arbitrarily mutable at runtime. See T12397.
$cache = PhabricatorCaches::getMutableCache();
$authstruct_key = PhabricatorAuthSSHKeyQuery::AUTHSTRUCT_CACHEKEY;
$authstruct_raw = $cache->getKey($authstruct_key);
$authstruct = null;
if (strlen($authstruct_raw)) {
try {
$authstruct = phutil_json_decode($authstruct_raw);
} catch (Exception $ex) {
// Ignore any issues with the cached data; we'll just rebuild the
// structure below.
}
}
if ($authstruct === null) {
$keys = id(new PhabricatorAuthSSHKeyQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIsActive(true)
->execute();
if (!$keys) {
echo pht('No keys found.')."\n";
exit(1);
}
$key_list = array();
foreach ($keys as $ssh_key) {
$key_argv = array();
$object = $ssh_key->getObject();
if ($object instanceof PhabricatorUser) {
$key_argv[] = '--phabricator-ssh-user';
$key_argv[] = $object->getUsername();
} else if ($object instanceof AlmanacDevice) {
if (!$ssh_key->getIsTrusted()) {
// If this key is not a trusted device key, don't allow SSH
// authentication.
continue;
}
$key_argv[] = '--phabricator-ssh-device';
$key_argv[] = $object->getName();
} else {
// We don't know what sort of key this is; don't permit SSH auth.
continue;
}
$key_argv[] = '--phabricator-ssh-key';
$key_argv[] = $ssh_key->getID();
// Strip out newlines and other nonsense from the key type and key body.
$type = $ssh_key->getKeyType();
$type = preg_replace('@[\x00-\x20]+@', '', $type);
if (!strlen($type)) {
continue;
}
$key = $ssh_key->getKeyBody();
$key = preg_replace('@[\x00-\x20]+@', '', $key);
if (!strlen($key)) {
continue;
}
$key_list[] = array(
'argv' => $key_argv,
'type' => $type,
'key' => $key,
);
}
$authstruct = array(
'keys' => $key_list,
);
$authstruct_raw = phutil_json_encode($authstruct);
$ttl = phutil_units('24 hours in seconds');
$cache->setKey($authstruct_key, $authstruct_raw, $ttl);
}
// If we've received an "--sshd-key" argument and it matches some known key,
// only emit that key. (For now, if the key doesn't match, we'll fall back to
// emitting all keys.)
if ($sshd_key !== null) {
$matches = array();
foreach ($authstruct['keys'] as $key => $key_struct) {
if ($key_struct['key'] === $sshd_key) {
$matches[$key] = $key_struct;
}
}
if ($matches) {
$authstruct['keys'] = $matches;
}
}
$bin = $root.'/bin/ssh-exec';
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
$lines = array();
foreach ($authstruct['keys'] as $key_struct) {
$key_argv = $key_struct['argv'];
$key = $key_struct['key'];
$type = $key_struct['type'];
$cmd = csprintf('%s %Ls', $bin, $key_argv);
if (strlen($instance)) {
$cmd = csprintf('PHABRICATOR_INSTANCE=%s %C', $instance, $cmd);
}
// 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',
);
$options = implode(',', $options);
$lines[] = $options.' '.$type.' '.$key."\n";
}
$authfile = implode('', $lines);
echo $authfile;
exit(0);