1
0
mirror of https://github.com/danog/tgseclib.git synced 2025-01-22 14:01:20 +01:00

Merge branch 'send-kex-and-id-first-or-last-2.0'

This commit is contained in:
terrafrost 2017-08-07 20:09:26 -05:00
commit 520c56a797

View File

@ -881,6 +881,22 @@ class SSH2
*/ */
private static $connections; private static $connections;
/**
* Send the identification string first?
*
* @var bool
* @access private
*/
private $send_id_string_first = true;
/**
* Send the key exchange initiation packet first?
*
* @var bool
* @access private
*/
private $send_kex_first = true;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -995,13 +1011,69 @@ class SSH2
* OpenSSL, mcrypt, Eval, PHP * OpenSSL, mcrypt, Eval, PHP
* *
* @param int $engine * @param int $engine
* @access private * @access public
*/ */
public function setCryptoEngine($engine) public function setCryptoEngine($engine)
{ {
$this->crypto_engine = $engine; $this->crypto_engine = $engine;
} }
/**
* Send Identification String First
*
* https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
* both sides MUST send an identification string". It does not say which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
* @access public
*/
function sendIdentificationStringFirst()
{
$this->send_id_string_first = true;
}
/**
* Send Identification String Last
*
* https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
* both sides MUST send an identification string". It does not say which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
* @access public
*/
function sendIdentificationStringLast()
{
$this->send_id_string_first = false;
}
/**
* Send SSH_MSG_KEXINIT First
*
* https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
* sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
* @access public
*/
function sendKEXINITFirst()
{
$this->send_kex_first = true;
}
/**
* Send SSH_MSG_KEXINIT Last
*
* https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
* sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
* @access public
*/
function sendKEXINITLast()
{
$this->send_kex_first = false;
}
/** /**
* Connect to an SSHv2 server * Connect to an SSHv2 server
* *
@ -1044,7 +1116,9 @@ class SSH2
$this->identifier = $this->generate_identifier(); $this->identifier = $this->generate_identifier();
if ($this->send_id_string_first) {
fputs($this->fsock, $this->identifier . "\r\n"); fputs($this->fsock, $this->identifier . "\r\n");
}
/* According to the SSH2 specs, /* According to the SSH2 specs,
@ -1120,6 +1194,11 @@ class SSH2
throw new \RuntimeException("Cannot connect to SSH $matches[1] servers"); throw new \RuntimeException("Cannot connect to SSH $matches[1] servers");
} }
if (!$this->send_id_string_first) {
fputs($this->fsock, $this->identifier . "\r\n");
}
if (!$this->send_kex_first) {
$response = $this->get_binary_packet(); $response = $this->get_binary_packet();
if ($response === false) { if ($response === false) {
throw new \RuntimeException('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
@ -1132,6 +1211,11 @@ class SSH2
if (!$this->key_exchange($response)) { if (!$this->key_exchange($response)) {
return false; return false;
} }
}
if ($this->send_kex_first && !$this->key_exchange()) {
return false;
}
$this->bitmap|= self::MASK_CONNECTED; $this->bitmap|= self::MASK_CONNECTED;
@ -1177,13 +1261,13 @@ class SSH2
/** /**
* Key Exchange * Key Exchange
* *
* @param string $kexinit_payload_server * @param string $kexinit_payload_server optional
* @throws \UnexpectedValueException on receipt of unexpected packets * @throws \UnexpectedValueException on receipt of unexpected packets
* @throws \RuntimeException on other errors * @throws \RuntimeException on other errors
* @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
* @access private * @access private
*/ */
private function key_exchange($kexinit_payload_server) private function key_exchange($kexinit_payload_server = false)
{ {
$kex_algorithms = [ $kex_algorithms = [
// Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
@ -1321,6 +1405,49 @@ class SSH2
$client_cookie = Random::string(16); $client_cookie = Random::string(16);
$kexinit_payload_client = pack(
'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
NET_SSH2_MSG_KEXINIT,
$client_cookie,
strlen($str_kex_algorithms),
$str_kex_algorithms,
strlen($str_server_host_key_algorithms),
$str_server_host_key_algorithms,
strlen($encryption_algorithms_client_to_server),
$encryption_algorithms_client_to_server,
strlen($encryption_algorithms_server_to_client),
$encryption_algorithms_server_to_client,
strlen($mac_algorithms_client_to_server),
$mac_algorithms_client_to_server,
strlen($mac_algorithms_server_to_client),
$mac_algorithms_server_to_client,
strlen($compression_algorithms_client_to_server),
$compression_algorithms_client_to_server,
strlen($compression_algorithms_server_to_client),
$compression_algorithms_server_to_client,
0,
'',
0,
'',
0,
0
);
if ($this->send_kex_first) {
if (!$this->send_binary_packet($kexinit_payload_client)) {
return false;
}
$kexinit_payload_server = $this->get_binary_packet();
if ($kexinit_payload_server === false) {
throw new \RuntimeException('Connection closed by server');
}
if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
}
}
$response = $kexinit_payload_server; $response = $kexinit_payload_server;
Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
$server_cookie = Strings::shift($response, 16); $server_cookie = Strings::shift($response, 16);
@ -1392,39 +1519,9 @@ class SSH2
$first_kex_packet_follows = $first_kex_packet_follows != 0; $first_kex_packet_follows = $first_kex_packet_follows != 0;
// the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. if (!$this->send_kex_first && !$this->send_binary_packet($kexinit_payload_client)) {
$kexinit_payload_client = pack(
'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
NET_SSH2_MSG_KEXINIT,
$client_cookie,
strlen($str_kex_algorithms),
$str_kex_algorithms,
strlen($str_server_host_key_algorithms),
$str_server_host_key_algorithms,
strlen($encryption_algorithms_client_to_server),
$encryption_algorithms_client_to_server,
strlen($encryption_algorithms_server_to_client),
$encryption_algorithms_server_to_client,
strlen($mac_algorithms_client_to_server),
$mac_algorithms_client_to_server,
strlen($mac_algorithms_server_to_client),
$mac_algorithms_server_to_client,
strlen($compression_algorithms_client_to_server),
$compression_algorithms_client_to_server,
strlen($compression_algorithms_server_to_client),
$compression_algorithms_server_to_client,
0,
'',
0,
'',
0,
0
);
if (!$this->send_binary_packet($kexinit_payload_client)) {
return false; return false;
} }
// here ends the second place.
// we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange