aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php')
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php169
1 files changed, 152 insertions, 17 deletions
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
index 7ec4a1e36..607cc2145 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
@@ -274,6 +274,18 @@ class SSH2
var $server_host_key_algorithms = false;
/**
+ * Supported Private Key Algorithms
+ *
+ * In theory this should be the same as the Server Host Key Algorithms but, in practice,
+ * some servers (eg. Azure) will support rsa-sha2-512 as a server host key algorithm but
+ * not a private key algorithm
+ *
+ * @see self::privatekey_login()
+ * @var array|false
+ */
+ var $supported_private_key_algorithms = false;
+
+ /**
* Encryption Algorithms: Client to Server
*
* @see self::getEncryptionAlgorithmsClient2Server()
@@ -391,6 +403,14 @@ class SSH2
var $decrypt = false;
/**
+ * Decryption Algorithm Name
+ *
+ * @var string|null
+ * @access private
+ */
+ var $decryptName;
+
+ /**
* Client to Server Encryption Object
*
* @see self::_send_binary_packet()
@@ -400,6 +420,14 @@ class SSH2
var $encrypt = false;
/**
+ * Encryption Algorithm Name
+ *
+ * @var string|null
+ * @access private
+ */
+ var $encryptName;
+
+ /**
* Client to Server HMAC Object
*
* @see self::_send_binary_packet()
@@ -409,6 +437,13 @@ class SSH2
var $hmac_create = false;
/**
+ * Client to Server HMAC Name
+ *
+ * @var string|false
+ */
+ private $hmac_create_name;
+
+ /**
* Server to Client HMAC Object
*
* @see self::_get_binary_packet()
@@ -418,6 +453,13 @@ class SSH2
var $hmac_check = false;
/**
+ * Server to Client HMAC Name
+ *
+ * @var string|false
+ */
+ var $hmac_check_name;
+
+ /**
* Size of server to client HMAC
*
* We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
@@ -1055,9 +1097,19 @@ class SSH2
var $smartMFA = true;
/**
+ * Extra packets counter
+ *
+ * @var bool
+ * @access private
+ */
+ var $extra_packets;
+
+ /**
* Default Constructor.
*
* $host can either be a string, representing the host, or a stream resource.
+ * If $host is a stream resource then $port doesn't do anything, altho $timeout
+ * still will be used
*
* @param mixed $host
* @param int $port
@@ -1075,6 +1127,7 @@ class SSH2
4 => 'NET_SSH2_MSG_DEBUG',
5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
+ 7 => 'NET_SSH2_MSG_EXT_INFO', // RFC 8308
20 => 'NET_SSH2_MSG_KEXINIT',
21 => 'NET_SSH2_MSG_NEWKEYS',
30 => 'NET_SSH2_MSG_KEXDH_INIT',
@@ -1147,6 +1200,8 @@ class SSH2
31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
);
+ $this->timeout = $timeout;
+
if (is_resource($host)) {
$this->fsock = $host;
return;
@@ -1155,7 +1210,6 @@ class SSH2
if (is_string($host)) {
$this->host = $host;
$this->port = $port;
- $this->timeout = $timeout;
}
}
@@ -1459,6 +1513,8 @@ class SSH2
$preferred['client_to_server']['comp'] :
$this->getSupportedCompressionAlgorithms();
+ $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c', 'kex-strict-c-v00@openssh.com'));
+
// some SSH servers have buggy implementations of some of the above algorithms
switch (true) {
case $this->server_identifier == 'SSH-2.0-SSHD':
@@ -1521,6 +1577,7 @@ class SSH2
return false;
}
+ $this->extra_packets = 0;
$kexinit_payload_server = $this->_get_binary_packet();
if ($kexinit_payload_server === false) {
$this->bitmap = 0;
@@ -1545,6 +1602,12 @@ class SSH2
}
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
+ if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) {
+ if ($this->session_id === false && $this->extra_packets) {
+ user_error('Possible Terrapin Attack detected');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ }
+ }
if (strlen($response) < 4) {
return false;
@@ -1552,6 +1615,8 @@ class SSH2
$temp = unpack('Nlength', $this->_string_shift($response, 4));
$this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
+ $this->supported_private_key_algorithms = $this->server_host_key_algorithms;
+
if (strlen($response) < 4) {
return false;
}
@@ -1949,6 +2014,10 @@ class SSH2
return false;
}
+ if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) {
+ $this->get_seq_no = $this->send_seq_no = 0;
+ }
+
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
$this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
@@ -1978,7 +2047,7 @@ class SSH2
}
$this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
- $this->encrypt->name = $decrypt;
+ $this->encryptName = $encrypt;
}
$this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
@@ -2008,7 +2077,7 @@ class SSH2
}
$this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
- $this->decrypt->name = $decrypt;
+ $this->decryptName = $decrypt;
}
/* The "arcfour128" algorithm is the RC4 cipher, as described in
@@ -2053,7 +2122,7 @@ class SSH2
$this->hmac_create = new Hash('md5-96');
$createKeyLength = 16;
}
- $this->hmac_create->name = $mac_algorithm_out;
+ $this->hmac_create_name = $mac_algorithm_out;
$checkKeyLength = 0;
$this->hmac_size = 0;
@@ -2083,7 +2152,7 @@ class SSH2
$checkKeyLength = 16;
$this->hmac_size = 12;
}
- $this->hmac_check->name = $mac_algorithm_in;
+ $this->hmac_check_name = $mac_algorithm_in;
$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
while ($createKeyLength > strlen($key)) {
@@ -2223,7 +2292,9 @@ class SSH2
function login($username)
{
$args = func_get_args();
- $this->auth[] = $args;
+ if (!$this->retry_connect) {
+ $this->auth[] = $args;
+ }
// try logging with 'none' as an authentication method first since that's what
// PuTTY does
@@ -2366,6 +2437,35 @@ class SSH2
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ if ($type == NET_SSH2_MSG_EXT_INFO) {
+ if (strlen($response) < 4) {
+ return false;
+ }
+ $nr_extensions = unpack('Nlength', $this->_string_shift($response, 4));
+ for ($i = 0; $i < $nr_extensions['length']; $i++) {
+ if (strlen($response) < 4) {
+ return false;
+ }
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $extension_name = $this->_string_shift($response, $temp['length']);
+ if ($extension_name == 'server-sig-algs') {
+ if (strlen($response) < 4) {
+ return false;
+ }
+ $temp = unpack('Nlength', $this->_string_shift($response, 4));
+ $this->supported_private_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
+ }
+ }
+
+ $response = $this->_get_binary_packet();
+ if ($response === false) {
+ $this->bitmap = 0;
+ user_error('Connection closed by server');
+ return false;
+ }
+ extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ }
+
if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
user_error('Expected SSH_MSG_SERVICE_ACCEPT');
return false;
@@ -2731,7 +2831,13 @@ class SSH2
$publickey['n']
);
- switch ($this->signature_format) {
+ $algos = array('rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa');
+ if (isset($this->preferred['hostkey'])) {
+ $algos = array_intersect($algos, $this->preferred['hostkey']);
+ }
+ $algo = $this->_array_intersect_first($algos, $this->supported_private_key_algorithms);
+
+ switch ($algo) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
@@ -2781,7 +2887,12 @@ class SSH2
return false;
}
extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
- $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
+ $auth_methods = explode(',', $this->_string_shift($response, $methodlistlen));
+ if (in_array('publickey', $auth_methods) && substr($signatureType, 0, 9) == 'rsa-sha2-') {
+ $this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, array('rsa-sha2-256', 'rsa-sha2-512'));
+ return $this->_privatekey_login($username, $privatekey);
+ }
+ $this->auth_methods_to_continue = $auth_methods;
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
return false;
case NET_SSH2_MSG_USERAUTH_PK_OK:
@@ -2836,6 +2947,16 @@ class SSH2
}
/**
+ * Return the currently configured timeout
+ *
+ * @return int
+ */
+ function getTimeout()
+ {
+ return $this->timeout;
+ }
+
+ /**
* Set Timeout
*
* $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
@@ -3363,7 +3484,7 @@ class SSH2
*/
function isConnected()
{
- return (bool) ($this->bitmap & self::MASK_CONNECTED);
+ return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock);
}
/**
@@ -3519,11 +3640,18 @@ class SSH2
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
- user_error('Connection closed (by server) prematurely ' . $elapsed . 's');
+ $str = 'Connection closed (by server) prematurely';
+ if (isset($elapsed)) {
+ $str.= ' ' . $elapsed . 's';
+ }
+ user_error($str);
return false;
}
$start = microtime(true);
+ $sec = (int) floor($this->curTimeout);
+ $usec = (int) (1000000 * ($this->curTimeout - $sec));
+ stream_set_timeout($this->fsock, $sec, $usec);
$raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
if (!strlen($raw)) {
@@ -3550,7 +3678,7 @@ class SSH2
// "implementations SHOULD check that the packet length is reasonable"
// PuTTY uses 0x9000 as the actual max packet size and so to shall we
if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
- if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decryptName) && !($this->bitmap & SSH2::MASK_LOGIN)) {
$this->bad_key_size_fix = true;
$this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return false;
@@ -3663,9 +3791,11 @@ class SSH2
$this->bitmap = 0;
return false;
case NET_SSH2_MSG_IGNORE:
+ $this->extra_packets++;
$payload = $this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_DEBUG:
+ $this->extra_packets++;
$this->_string_shift($payload, 2);
if (strlen($payload) < 4) {
return false;
@@ -3677,6 +3807,7 @@ class SSH2
case NET_SSH2_MSG_UNIMPLEMENTED:
return false;
case NET_SSH2_MSG_KEXINIT:
+ // this is here for key re-exchanges after the initial key exchange
if ($this->session_id !== false) {
$this->send_kex_first = false;
if (!$this->_key_exchange($payload)) {
@@ -4573,7 +4704,9 @@ class SSH2
}
/**
- * Returns all errors
+ * Returns all errors / debug messages on the SSH layer
+ *
+ * If you are looking for messages from the SFTP layer, please see SFTP::getSFTPErrors()
*
* @return string[]
* @access public
@@ -4584,7 +4717,9 @@ class SSH2
}
/**
- * Returns the last error
+ * Returns the last error received on the SSH layer
+ *
+ * If you are looking for messages from the SFTP layer, please see SFTP::getLastSFTPError()
*
* @return string
* @access public
@@ -4955,13 +5090,13 @@ class SSH2
'kex' => $this->kex_algorithm,
'hostkey' => $this->signature_format,
'client_to_server' => array(
- 'crypt' => $this->encrypt->name,
- 'mac' => $this->hmac_create->name,
+ 'crypt' => $this->encryptName,
+ 'mac' => $this->hmac_create_name,
'comp' => $compression_map[$this->compress],
),
'server_to_client' => array(
- 'crypt' => $this->decrypt->name,
- 'mac' => $this->hmac_check->name,
+ 'crypt' => $this->decryptName,
+ 'mac' => $this->hmac_check_name,
'comp' => $compression_map[$this->decompress],
)
);