SSH2: add support for multi-factor authentication

This commit is contained in:
terrafrost 2013-04-19 22:23:06 -05:00
parent 7ff96b3946
commit ccd4ce1d19

View File

@ -119,8 +119,9 @@ if (!class_exists('Crypt_AES')) {
* @access private * @access private
*/ */
define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
define('NET_SSH2_MASK_LOGIN', 0x00000002); define('NET_SSH2_MASK_LOGIN_REQ', 0x00000002);
define('NET_SSH2_MASK_SHELL', 0x00000004); define('NET_SSH2_MASK_LOGIN', 0x00000004);
define('NET_SSH2_MASK_SHELL', 0x00000008);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -730,10 +731,19 @@ class Net_SSH2 {
/** /**
* Contents of stdError * Contents of stdError
*
* @access private * @access private
*/ */
var $stdErrorLog; var $stdErrorLog;
/**
* The Last Interactive Response
*
* @see Net_SSH2::_keyboard_interactive_process()
* @access private
*/
var $last_interactive_response = '';
/** /**
* Default Constructor. * Default Constructor.
* *
@ -1466,6 +1476,7 @@ class Net_SSH2 {
return false; return false;
} }
if (!($this->bitmap & NET_SSH2_MASK_LOGIN_REQ)) {
$packet = pack('CNa*', $packet = pack('CNa*',
NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth' NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth'
); );
@ -1486,6 +1497,12 @@ class Net_SSH2 {
user_error('Expected SSH_MSG_SERVICE_ACCEPT'); user_error('Expected SSH_MSG_SERVICE_ACCEPT');
return false; return false;
} }
$this->bitmap |= NET_SSH2_MASK_LOGIN_REQ;
}
if (strlen($this->last_interactive_response)) {
return !is_string($password) ? false : $this->_keyboard_interactive_process($password);
}
// although PHP5's get_class() preserves the case, PHP4's does not // although PHP5's get_class() preserves the case, PHP4's does not
if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') { if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
@ -1559,7 +1576,10 @@ class Net_SSH2 {
// multi-factor authentication // multi-factor authentication
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$auth_methods = explode(',', $this->_string_shift($response, $length)); $auth_methods = explode(',', $this->_string_shift($response, $length));
if (in_array('keyboard-interactive', $auth_methods)) { extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
$partial_success = $partial_success != 0;
if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
if ($this->_keyboard_interactive_login($username, $password)) { if ($this->_keyboard_interactive_login($username, $password)) {
$this->bitmap |= NET_SSH2_MASK_LOGIN; $this->bitmap |= NET_SSH2_MASK_LOGIN;
return true; return true;
@ -1610,25 +1630,20 @@ class Net_SSH2 {
{ {
$responses = func_get_args(); $responses = func_get_args();
$response = $this->_get_binary_packet(); if (strlen($this->last_interactive_response)) {
$response = $this->last_interactive_response;
} else {
$orig = $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); user_error('Connection closed by server');
return false; return false;
} }
}
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
switch ($type) { switch ($type) {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
// see http://tools.ietf.org/html/rfc4256#section-3.2
if (defined('NET_SSH2_LOGGING')) {
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
$this->message_number_log[count($this->message_number_log) - 1]
);
}
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // name; may be empty $this->_string_shift($response, $length); // name; may be empty
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
@ -1645,6 +1660,23 @@ class Net_SSH2 {
} }
*/ */
// see http://tools.ietf.org/html/rfc4256#section-3.2
if (strlen($this->last_interactive_response)) {
$this->last_interactive_response = '';
} else {
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
$this->message_number_log[count($this->message_number_log) - 1]
);
}
if (!count($responses) && $num_prompts) {
$this->last_interactive_response = $orig;
$this->bitmap |= NET_SSH_MASK_LOGIN_INTERACTIVE;
return false;
}
/* /*
After obtaining the requested information from the user, the client After obtaining the requested information from the user, the client
MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
@ -1735,7 +1767,7 @@ class Net_SSH2 {
case NET_SSH2_MSG_USERAUTH_FAILURE: case NET_SSH2_MSG_USERAUTH_FAILURE:
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); return false;
case NET_SSH2_MSG_USERAUTH_PK_OK: case NET_SSH2_MSG_USERAUTH_PK_OK:
// we'll just take it on faith that the public key blob and the public key algorithm name are as // we'll just take it on faith that the public key blob and the public key algorithm name are as
// they should be // they should be