diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 491e5212..101f7e99 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -343,6 +343,14 @@ class SSH2 */ private $languages_client_to_server = false; + /** + * Server Signature Algorithms + * + * @link https://www.rfc-editor.org/rfc/rfc8308.html#section-3.1 + * @var array|false + */ + private $server_sig_algs = false; + /** * Preferred Algorithms * @@ -1122,6 +1130,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', @@ -1535,6 +1544,8 @@ class SSH2 $preferred['client_to_server']['comp'] : SSH2::getSupportedCompressionAlgorithms(); + $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c')); + // some SSH servers have buggy implementations of some of the above algorithms switch (true) { case $this->server_identifier == 'SSH-2.0-SSHD': @@ -2332,7 +2343,23 @@ class SSH2 throw $e; } - list($type, $service) = Strings::unpackSSH2('Cs', $response); + list($type) = Strings::unpackSSH2('C', $response); + + if ($type == NET_SSH2_MSG_EXT_INFO) { + list($nr_extensions) = Strings::unpackSSH2('N', $response); + for ($i = 0; $i < $nr_extensions; $i++) { + list($extension_name, $extension_value) = Strings::unpackSSH2('ss', $response); + if ($extension_name == 'server-sig-algs') { + $this->server_sig_algs = explode(',', $extension_value); + } + } + + $response = $this->get_binary_packet(); + list($type) = Strings::unpackSSH2('C', $response); + } + + list($service) = Strings::unpackSSH2('s', $response); + if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); @@ -2607,7 +2634,9 @@ class SSH2 if ($publickey instanceof RSA) { $privatekey = $privatekey->withPadding(RSA::SIGNATURE_PKCS1); $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa']; - if (isset($this->preferred['hostkey'])) { + if ($this->server_sig_algs) { + $algos = array_intersect($this->server_sig_algs, $algos); + } elseif (isset($this->preferred['hostkey'])) { $algos = array_intersect($this->preferred['hostkey'], $algos); } $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms);