From e0abab9bb42727fe8cdcd03ca5dd922f2351f1fd Mon Sep 17 00:00:00 2001 From: terrafrost Date: Sat, 20 Apr 2013 14:35:08 -0500 Subject: [PATCH] SSH2: keyboard-interactive changes Let's say your SSH server had a two-part keyboard-interactive auth. One prompt is for "Password" and the other is for "Verification code". Previously you'd have to do this: $ssh->login($username, 'pass1', 'code1'); It'd try password authentication with pass1, fail, then do keyboard-interactive with pass1 and then keyboard-interacitve with code1. ie. the order in which it tried stuff was dependent on the order it was past to the Net_SSH2 object. And it'd always try password auth first. Now you can go straight to keyboard-interactive and mix the order as follows: $ssh->login($username, array('Password' => 'pass1'), array('Verification code' => 'code1')); --- phpseclib/Net/SSH2.php | 50 +++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 1fed7fc7..fc0a858f 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -744,6 +744,14 @@ class Net_SSH2 { */ var $last_interactive_response = ''; + /** + * Keyboard Interactive Request / Responses + * + * @see Net_SSH2::_keyboard_interactive_process() + * @access private + */ + var $keyboard_requests_responses = array(); + /** * Default Constructor. * @@ -1476,6 +1484,7 @@ class Net_SSH2 { if (empty($args)) { return $this->_login_helper($username); } + foreach ($args as $arg) { if ($this->_login_helper($username, $arg)) { return true; @@ -1525,7 +1534,7 @@ class Net_SSH2 { } if (strlen($this->last_interactive_response)) { - return !is_string($password) ? false : $this->_keyboard_interactive_process($password); + return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); } // although PHP5's get_class() preserves the case, PHP4's does not @@ -1533,6 +1542,14 @@ class Net_SSH2 { return $this->_privatekey_login($username, $password); } + if (is_array($password)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + } + return false; + } + if (!isset($password)) { $packet = pack('CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', @@ -1675,14 +1692,31 @@ class Net_SSH2 { extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // language tag; may be empty extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); - /* - for ($i = 0; $i < $num_prompts; $i++) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - // prompt - ie. "Password: "; must not be empty - $this->_string_shift($response, $length); - $echo = $this->_string_shift($response) != chr(0); + + for ($i = 0; $i < count($responses); $i++) { + if (is_array($responses[$i])) { + foreach ($responses[$i] as $key => $value) { + $this->keyboard_requests_responses[$key] = $value; + } + unset($responses[$i]); + } + } + $responses = array_values($responses); + + if (isset($this->keyboard_requests_responses)) { + for ($i = 0; $i < $num_prompts; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + // prompt - ie. "Password: "; must not be empty + $prompt = $this->_string_shift($response, $length); + //$echo = $this->_string_shift($response) != chr(0); + foreach ($this->keyboard_requests_responses as $key => $value) { + if (substr($prompt, 0, strlen($key)) == $key) { + $responses[] = $value; + break; + } + } + } } - */ // see http://tools.ietf.org/html/rfc4256#section-3.2 if (strlen($this->last_interactive_response)) {