diff --git a/scripts/ssh/ssh-connect.php b/scripts/ssh/ssh-connect.php index 9757f3cbbe..0d87f16973 100755 --- a/scripts/ssh/ssh-connect.php +++ b/scripts/ssh/ssh-connect.php @@ -1,132 +1,159 @@ #!/usr/bin/env php parsePartial( array( array( 'name' => 'port', 'short' => 'p', 'param' => pht('port'), 'help' => pht('Port number to connect to.'), ), + array( + 'name' => 'options', + 'short' => 'o', + 'param' => pht('options'), + 'repeat' => true, + 'help' => pht('SSH options.'), + ), )); + $unconsumed_argv = $args->getUnconsumedArgumentVector(); if (function_exists('pcntl_signal')) { pcntl_signal(SIGTERM, 'ssh_connect_signal'); } function ssh_connect_signal($signo) { // This is just letting destructors fire. In particular, we want to clean // up any temporary files we wrote. See T10547. exit(128 + $signo); } $pattern = array(); $arguments = array(); $pattern[] = 'ssh'; $pattern[] = '-o'; $pattern[] = 'StrictHostKeyChecking=no'; // This prevents "known host" failures, and covers for issues where HOME is set // to something unusual. $pattern[] = '-o'; $pattern[] = 'UserKnownHostsFile=/dev/null'; $as_device = getenv('PHABRICATOR_AS_DEVICE'); $credential_phid = getenv('PHABRICATOR_CREDENTIAL'); if ($as_device) { $device = AlmanacKeys::getLiveDevice(); if (!$device) { throw new Exception( pht( 'Attempting to create an SSH connection that authenticates with '. 'the current device, but this host is not configured as a cluster '. 'device.')); } if ($credential_phid) { throw new Exception( pht( 'Attempting to proxy an SSH connection that authenticates with '. 'both the current device and a specific credential. These options '. 'are mutually exclusive.')); } } if ($credential_phid) { $viewer = PhabricatorUser::getOmnipotentUser(); $key = PassphraseSSHKey::loadFromPHID($credential_phid, $viewer); $pattern[] = '-l %P'; $arguments[] = $key->getUsernameEnvelope(); $pattern[] = '-i %P'; $arguments[] = $key->getKeyfileEnvelope(); } if ($as_device) { $pattern[] = '-l %R'; $arguments[] = AlmanacKeys::getClusterSSHUser(); $pattern[] = '-i %R'; $arguments[] = AlmanacKeys::getKeyPath('device.key'); } // Subversion passes us a host in the form "domain.com:port", which is not // valid for normal SSH but which we can parse into a valid "-p" flag. $passthru_args = $unconsumed_argv; $host = array_shift($passthru_args); $parts = explode(':', $host, 2); $host = $parts[0]; $port = $args->getArg('port'); if (!$port) { if (count($parts) == 2) { $port = $parts[1]; } } if ($port) { $pattern[] = '-p %d'; $arguments[] = $port; } +$options = $args->getArg('options'); +$allowed_ssh_options = array('SendEnv=GIT_PROTOCOL'); + +if (!empty($options)) { + foreach ($options as $option) { + if (array_search($option, $allowed_ssh_options) !== false) { + $pattern[] = '-o %s'; + $arguments[] = $option; + } else { + throw new Exception( + pht( + 'Disallowed ssh option "%s" given with "-o". '. + 'Allowed options are: %s.', + $option, + implode(', ', $allowed_ssh_options))); + } + } +} + $pattern[] = '--'; $pattern[] = '%s'; $arguments[] = $host; foreach ($passthru_args as $passthru_arg) { $pattern[] = '%s'; $arguments[] = $passthru_arg; } $pattern = implode(' ', $pattern); array_unshift($arguments, $pattern); $err = newv('PhutilExecPassthru', $arguments) ->execute(); exit($err);