Differential D20904 Diff 49824 src/applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php
Show All 24 Lines | public function shouldShowPasswordField() { | ||||
return true; | return true; | ||||
} | } | ||||
public function getPasswordLabel() { | public function getPasswordLabel() { | ||||
return pht('Password for Key'); | return pht('Password for Key'); | ||||
} | } | ||||
public function requiresPassword(PhutilOpaqueEnvelope $secret) { | public function requiresPassword(PhutilOpaqueEnvelope $secret) { | ||||
// According to the internet, this is the canonical test for an SSH private | // Some older SSH keys have the text "ENCRYPTED" in the key content. As | ||||
// key with a password. | // a coarse initial test, just look for this as an indicator. | ||||
return preg_match('/ENCRYPTED/', $secret->openEnvelope()); | if (preg_match('/ENCRYPTED/', $secret->openEnvelope())) { | ||||
return true; | |||||
} | |||||
// Otherwise, try to change the password from the empty string to the | |||||
// empty string. If this works, the file has no passphrase. If it does | |||||
// not work, assume it has a passphrase. | |||||
$this->requireSSHKeygen(); | |||||
$tmp = $this->newTemporaryPrivateKeyFile($secret); | |||||
list($err, $stdout, $stderr) = exec_manual( | |||||
'ssh-keygen -p -P %s -N %s -f %s', | |||||
'', | |||||
'', | |||||
(string)$tmp); | |||||
if (!$err) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | } | ||||
public function decryptSecret( | public function decryptSecret( | ||||
PhutilOpaqueEnvelope $secret, | PhutilOpaqueEnvelope $secret, | ||||
PhutilOpaqueEnvelope $password) { | PhutilOpaqueEnvelope $password) { | ||||
$tmp = new TempFile(); | $this->requireSSHKeygen(); | ||||
Filesystem::writeFile($tmp, $secret->openEnvelope()); | $tmp = $this->newTemporaryPrivateKeyFile($secret); | ||||
if (!Filesystem::binaryExists('ssh-keygen')) { | |||||
throw new Exception( | |||||
pht( | |||||
'Decrypting SSH keys requires the `%s` binary, but it '. | |||||
'is not available in %s. Either make it available or strip the '. | |||||
'password from this SSH key manually before uploading it.', | |||||
'ssh-keygen', | |||||
'$PATH')); | |||||
} | |||||
list($err, $stdout, $stderr) = exec_manual( | list($err, $stdout, $stderr) = exec_manual( | ||||
'ssh-keygen -p -P %P -N %s -f %s', | 'ssh-keygen -p -P %P -N %s -f %s', | ||||
$password, | $password, | ||||
'', | '', | ||||
(string)$tmp); | (string)$tmp); | ||||
if ($err) { | if ($err) { | ||||
return null; | return null; | ||||
} else { | } else { | ||||
return new PhutilOpaqueEnvelope(Filesystem::readFile($tmp)); | return new PhutilOpaqueEnvelope(Filesystem::readFile($tmp)); | ||||
} | } | ||||
} | } | ||||
private function requireSSHKeygen() { | |||||
if (Filesystem::binaryExists('ssh-keygen')) { | |||||
return; | |||||
} | |||||
throw new Exception( | |||||
pht( | |||||
'Decrypting SSH keys requires the "ssh-keygen" binary, but it '. | |||||
'is not available in "%s". Either make it available or strip the '. | |||||
'password from this SSH key manually before uploading it.', | |||||
'$PATH')); | |||||
} | |||||
private function newTemporaryPrivateKeyFile(PhutilOpaqueEnvelope $secret) { | |||||
$tmp = new TempFile(); | |||||
Filesystem::writeFile($tmp, $secret->openEnvelope()); | |||||
return $tmp; | |||||
} | |||||
} | } |