diff --git a/composer.json b/composer.json index 2600d0a25..a96395443 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "pear/pear-core-minimal": "^1.10", "php-http/guzzle7-adapter": "^1.0.0", "php-opencloud/openstack": "^3.1", - "phpseclib/phpseclib": "^2.0.45", + "phpseclib/phpseclib": "^2.0.48", "pimple/pimple": "^3.5.0", "psr/clock": "^1.0", "psr/container": "^2.0.2", diff --git a/composer.lock b/composer.lock index 8e4627904..1d2848932 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "98082464b9dd0c38d14cadb463a0e05c", + "content-hash": "0d8da5dc1140ab43c5a4a7dc8eb7971b", "packages": [ { "name": "aws/aws-crt-php", @@ -2771,16 +2771,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.47", + "version": "2.0.48", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "b7d7d90ee7df7f33a664b4aea32d50a305d35adb" + "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/b7d7d90ee7df7f33a664b4aea32d50a305d35adb", - "reference": "b7d7d90ee7df7f33a664b4aea32d50a305d35adb", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/eaa7be704b8b93a6913b69eb7f645a59d7731b61", + "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61", "shasum": "" }, "require": { @@ -2861,7 +2861,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.47" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.48" }, "funding": [ { @@ -2877,7 +2877,7 @@ "type": "tidelift" } ], - "time": "2024-02-26T04:55:38+00:00" + "time": "2024-12-14T21:03:54+00:00" }, { "name": "pimple/pimple", diff --git a/composer/installed.json b/composer/installed.json index cc86d5bdc..8617898b9 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -2888,17 +2888,17 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.47", - "version_normalized": "2.0.47.0", + "version": "2.0.48", + "version_normalized": "2.0.48.0", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "b7d7d90ee7df7f33a664b4aea32d50a305d35adb" + "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/b7d7d90ee7df7f33a664b4aea32d50a305d35adb", - "reference": "b7d7d90ee7df7f33a664b4aea32d50a305d35adb", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/eaa7be704b8b93a6913b69eb7f645a59d7731b61", + "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61", "shasum": "" }, "require": { @@ -2916,7 +2916,7 @@ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.", "ext-xml": "Install the XML extension to load XML formatted public keys." }, - "time": "2024-02-26T04:55:38+00:00", + "time": "2024-12-14T21:03:54+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2981,7 +2981,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.47" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.48" }, "funding": [ { diff --git a/composer/installed.php b/composer/installed.php index 39eff4631..e7b85c649 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -401,9 +401,9 @@ 'dev_requirement' => false, ), 'phpseclib/phpseclib' => array( - 'pretty_version' => '2.0.47', - 'version' => '2.0.47.0', - 'reference' => 'b7d7d90ee7df7f33a664b4aea32d50a305d35adb', + 'pretty_version' => '2.0.48', + 'version' => '2.0.48.0', + 'reference' => 'eaa7be704b8b93a6913b69eb7f645a59d7731b61', 'type' => 'library', 'install_path' => __DIR__ . '/../phpseclib/phpseclib', 'aliases' => array(), diff --git a/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/phpseclib/phpseclib/phpseclib/Crypt/Base.php index ab5944cde..2d4225a3f 100644 --- a/phpseclib/phpseclib/phpseclib/Crypt/Base.php +++ b/phpseclib/phpseclib/phpseclib/Crypt/Base.php @@ -611,7 +611,6 @@ function getBlockLength() * * @access public * @param string $key - * @internal Could, but not must, extend by the child Crypt_* class */ function setKey($key) { diff --git a/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/phpseclib/phpseclib/phpseclib/Crypt/RSA.php index fec689585..7f5df1d5f 100644 --- a/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +++ b/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -1396,9 +1396,14 @@ function. As is, the definitive authority on this encoding scheme isn't the IET $this->components = array(); $xml = xml_parser_create('UTF-8'); - xml_set_object($xml, $this); - xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); - xml_set_character_data_handler($xml, '_data_handler'); + if (version_compare(PHP_VERSION, '8.4.0', '>=')) { + xml_set_element_handler($xml, array($this, '_start_element_handler'), array($this, '_stop_element_handler')); + xml_set_character_data_handler($xml, array($this, '_data_handler')); + } else { + xml_set_object($xml, $this); + xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); + xml_set_character_data_handler($xml, '_data_handler'); + } // add to account for "dangling" tags like ... that are sometimes added if (!xml_parse($xml, '' . $key . '')) { xml_parser_free($xml); diff --git a/phpseclib/phpseclib/phpseclib/File/X509.php b/phpseclib/phpseclib/phpseclib/File/X509.php index 7b8d96e29..64e22655c 100644 --- a/phpseclib/phpseclib/phpseclib/File/X509.php +++ b/phpseclib/phpseclib/phpseclib/File/X509.php @@ -3847,7 +3847,8 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') array( 'version' => 'v1', 'subject' => $this->dn, - 'subjectPKInfo' => $publicKey + 'subjectPKInfo' => $publicKey, + 'attributes' => array() ), 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), 'signature' => false // this is going to be overwritten later @@ -4003,11 +4004,11 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; if (!$version) { if (!empty($tbsCertList['crlExtensions'])) { - $version = 1; // v2. + $version = 'v2'; // v2. } elseif (!empty($tbsCertList['revokedCertificates'])) { foreach ($tbsCertList['revokedCertificates'] as $cert) { if (!empty($cert['crlEntryExtensions'])) { - $version = 1; // v2. + $version = 'v2'; // v2. } } } diff --git a/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/phpseclib/phpseclib/phpseclib/Math/BigInteger.php index 7747a95b6..5f2283678 100644 --- a/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +++ b/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -251,8 +251,17 @@ class BigInteger function __construct($x = 0, $base = 10) { if (!defined('MATH_BIGINTEGER_MODE')) { + + // https://github.com/php/php-src/commit/e0a0e216a909dc4ee4ea7c113a5f41d49525f02e broke GMP + // https://github.com/php/php-src/commit/424ba0f2ff9677d16b4e339e90885bd4bc49fcf1 fixed it + // see https://github.com/php/php-src/issues/16870 for more info + if (version_compare(PHP_VERSION, '8.2.26', '<')) { + $gmpOK = true; + } else { + $gmpOK = !in_array(PHP_VERSION_ID, array(80226, 80314, 80400, 80401)); + } switch (true) { - case extension_loaded('gmp'): + case extension_loaded('gmp') && $gmpOK: define('MATH_BIGINTEGER_MODE', self::MODE_GMP); break; case extension_loaded('bcmath'): diff --git a/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/phpseclib/phpseclib/phpseclib/Net/SFTP.php index 28b568062..1c6ef7f9a 100644 --- a/phpseclib/phpseclib/phpseclib/Net/SFTP.php +++ b/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -758,7 +758,8 @@ function _init_sftp_connection() return false; } $this->canonicalize_paths = false; - $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); + $this->_reset_sftp(); + return $this->_init_sftp_connection(); } $this->_update_stat_cache($this->pwd, array()); @@ -3629,20 +3630,30 @@ function _send_sftp_packet($type, $data, $request_id = 1) } /** - * Resets a connection for re-use + * Resets the SFTP channel for re-use * - * @param int $reason * @access private */ - function _reset_connection($reason) + function _reset_sftp() { - parent::_reset_connection($reason); $this->use_request_id = false; $this->pwd = false; $this->requestBuffer = array(); $this->partial_init = false; } + /** + * Resets a connection for re-use + * + * @param int $reason + * @access private + */ + function _reset_connection($reason) + { + parent::_reset_connection($reason); + $this->_reset_sftp(); + } + /** * Receives SFTP Packets * diff --git a/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/phpseclib/phpseclib/phpseclib/Net/SSH2.php index 607cc2145..775d894fb 100644 --- a/phpseclib/phpseclib/phpseclib/Net/SSH2.php +++ b/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -146,6 +146,10 @@ class SSH2 * Dumps the content real-time to a file */ const LOG_REALTIME_FILE = 4; + /** + * Dumps the message numbers real-time + */ + const LOG_REALTIME_SIMPLE = 5; /** * Make sure that the log never gets larger than this */ @@ -1005,7 +1009,7 @@ class SSH2 * @var bool * @access private */ - var $retry_connect = false; + var $login_credentials_finalized = false; /** * Binary Packet Buffer @@ -1097,12 +1101,57 @@ class SSH2 var $smartMFA = true; /** - * Extra packets counter + * Bytes Transferred Since Last Key Exchange + * + * Includes outbound and inbound totals * + * @var int + * @access private + */ + var $bytesTransferredSinceLastKEX = 0; + + /** + * After how many transferred byte should phpseclib initiate a key re-exchange? + * + * @var int + * @access private + */ + var $doKeyReexchangeAfterXBytes = 1073741824; + + /** + * Has a key re-exchange been initialized? + * * @var bool * @access private */ - var $extra_packets; + var $keyExchangeInProgress = false; + + /** + * KEX Buffer + * + * If we're in the middle of a key exchange we want to buffer any additional packets we get until + * the key exchange is over + * + * @see self::_get_binary_packet() + * @see self::_key_exchange() + * @see self::exec() + * @var array + * @access private + */ + var $kex_buffer = array(); + + /** + * Strict KEX Flag + * + * If kex-strict-s-v00@openssh.com is present in the first KEX packet it need not + * be present in subsequent packet + * + * @see self::_key_exchange() + * @see self::exec() + * @var array + * @access private + */ + var $strict_kex_flag = false; /** * Default Constructor. @@ -1361,25 +1410,17 @@ function _connect() } $temp = stream_get_line($this->fsock, 255, "\n"); - if (strlen($temp) == 255) { - continue; - } if ($temp === false) { return false; } - $line.= "$temp\n"; - - // quoting RFC4253, "Implementers who wish to maintain - // compatibility with older, undocumented versions of this protocol may - // want to process the identification string without expecting the - // presence of the carriage return character for reasons described in - // Section 5 of this document." + $line .= $temp; + if (strlen($temp) == 255) { + continue; + } - //if (substr($line, -2) == "\r\n") { - // break; - //} + $line .= "\n"; break; } @@ -1400,7 +1441,8 @@ function _connect() $this->_append_log('->', $this->identifier . "\r\n"); } - $this->server_identifier = trim($temp, "\r\n"); + $this->server_identifier = trim($data, "\r\n"); + if (strlen($extra)) { $this->errors[] = $data; } @@ -1485,8 +1527,13 @@ function _generate_identifier() */ function _key_exchange($kexinit_payload_server = false) { + $this->bytesTransferredSinceLastKEX = 0; + $preferred = $this->preferred; - $send_kex = true; + // for the initial key exchange $send_kex is true (no key re-exchange has been started) + // for phpseclib initiated key exchanges $send_kex is false + $send_kex = !$this->keyExchangeInProgress; + $this->keyExchangeInProgress = true; $kex_algorithms = isset($preferred['kex']) ? $preferred['kex'] : @@ -1572,22 +1619,29 @@ function _key_exchange($kexinit_payload_server = false) 0 ); - if ($kexinit_payload_server === false) { + if ($kexinit_payload_server === false && $send_kex) { if (!$this->_send_binary_packet($kexinit_payload_client)) { return false; } - $this->extra_packets = 0; - $kexinit_payload_server = $this->_get_binary_packet(); - if ($kexinit_payload_server === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } + while (true) { + $kexinit_payload_server = $this->_get_binary_packet(); + if ($kexinit_payload_server === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (strlen($kexinit_payload_server)) { + switch (ord($kexinit_payload_server[0])) { + case NET_SSH2_MSG_KEXINIT: + break 2; + case NET_SSH2_MSG_DISCONNECT: + return $this->_handleDisconnect($kexinit_payload_server); + } + } - if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) { - user_error('Expected SSH_MSG_KEXINIT'); - return false; + $this->kex_buffer[] = $kexinit_payload_server; } $send_kex = false; @@ -1603,9 +1657,14 @@ function _key_exchange($kexinit_payload_server = false) $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 ($this->session_id === false) { + // [kex-strict-s-v00@openssh.com is] only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored + // if [it is] present in subsequent SSH2_MSG_KEXINIT packets + $this->strict_kex_flag = true; + if (count($this->kex_buffer)) { + user_error('Possible Terrapin Attack detected'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } } } @@ -2014,7 +2073,9 @@ function _key_exchange($kexinit_payload_server = false) return false; } - if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) { + $this->keyExchangeInProgress = false; + + if ($this->strict_kex_flag) { $this->get_seq_no = $this->send_seq_no = 0; } @@ -2292,7 +2353,7 @@ function _bad_algorithm_candidate($algorithm) function login($username) { $args = func_get_args(); - if (!$this->retry_connect) { + if (!$this->login_credentials_finalized) { $this->auth[] = $args; } @@ -2383,6 +2444,7 @@ function _login($username) foreach ($newargs as $arg) { if ($this->_login_helper($username, $arg)) { + $this->login_credentials_finalized = true; return true; } } @@ -2418,10 +2480,14 @@ function _login_helper($username, $password = null) return false; } + $bad_key_size_fix = $this->bad_key_size_fix; $response = $this->_get_binary_packet(); if ($response === false) { - if ($this->retry_connect) { - $this->retry_connect = false; + // bad_key_size_fix is only ever re-assigned to true + // under certain conditions. when it's newly set we'll + // retry the connection with that new setting but we'll + // only try it once. + if ($bad_key_size_fix != $this->bad_key_size_fix) { if (!$this->_connect()) { return false; } @@ -2790,10 +2856,12 @@ function _ssh_agent_login($username, $agent) { $this->agent = $agent; $keys = $agent->requestIdentities(); + $orig_algorithms = $this->supported_private_key_algorithms; foreach ($keys as $key) { if ($this->_privatekey_login($username, $key)) { return true; } + $this->supported_private_key_algorithms = $orig_algorithms; } return false; @@ -3551,7 +3619,6 @@ function ping() function _reconnect() { $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); - $this->retry_connect = true; if (!$this->_connect()) { return false; } @@ -3575,7 +3642,6 @@ function _reset_connection($reason) $this->hmac_check = $this->hmac_create = false; $this->hmac_size = false; $this->session_id = false; - $this->retry_connect = true; $this->get_seq_no = $this->send_seq_no = 0; } @@ -3590,6 +3656,10 @@ function _reset_connection($reason) */ function _get_binary_packet($skip_channel_filter = false) { + if (!$this->keyExchangeInProgress && count($this->kex_buffer)) { + return $this->_filter(array_shift($this->kex_buffer), $skip_channel_filter); + } + if ($skip_channel_filter) { $read = array($this->fsock); $write = $except = null; @@ -3674,9 +3744,13 @@ function _get_binary_packet($skip_channel_filter = false) $remaining_length = $packet_length + 4 - $this->decrypt_block_size; + if (!$this->keyExchangeInProgress) { + $this->bytesTransferredSinceLastKEX+= $packet_length + $padding_length + 5; + } + // quoting , // "implementations SHOULD check that the packet length is reasonable" - // PuTTY uses 0x9000 as the actual max packet size and so to shall we + // PuTTY uses 0x9000 as the actual max packet size and so, too, 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->decryptName) && !($this->bitmap & SSH2::MASK_LOGIN)) { $this->bad_key_size_fix = true; @@ -3766,7 +3840,34 @@ function _get_binary_packet($skip_channel_filter = false) $this->last_packet = $current; } - return $this->_filter($payload, $skip_channel_filter); + if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) { + $this->_key_exchange(); + } + + // don't filter if we're in the middle of a key exchange (since _filter might send out packets) + return $this->keyExchangeInProgress ? $payload : $this->_filter($payload, $skip_channel_filter); + } + + /** + * Handle Disconnect + * + * Because some binary packets need to be ignored... + * + * @see self::_filter() + * @see self::_key_exchange + * @return boolean + * @access private + */ + function _handleDisconnect($payload) + { + $this->_string_shift($payload, 1); + if (strlen($payload) < 8) { + return false; + } + extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); + $this->bitmap = 0; + return false; } /** @@ -3782,20 +3883,11 @@ function _filter($payload, $skip_channel_filter) { switch (ord($payload[0])) { case NET_SSH2_MSG_DISCONNECT: - $this->_string_shift($payload, 1); - if (strlen($payload) < 8) { - return false; - } - extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); - $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); - $this->bitmap = 0; - return false; + return $this->_handleDisconnect($payload); 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; @@ -3807,7 +3899,7 @@ function _filter($payload, $skip_channel_filter) case NET_SSH2_MSG_UNIMPLEMENTED: return false; case NET_SSH2_MSG_KEXINIT: - // this is here for key re-exchanges after the initial key exchange + // this is here for server initiated key re-exchanges after the initial key exchange if ($this->session_id !== false) { $this->send_kex_first = false; if (!$this->_key_exchange($payload)) { @@ -3816,6 +3908,28 @@ function _filter($payload, $skip_channel_filter) } $payload = $this->_get_binary_packet($skip_channel_filter); } + break; + case NET_SSH2_MSG_EXT_INFO: + $this->_string_shift($payload, 1); + if (strlen($payload) < 4) { + return false; + } + $nr_extensions = unpack('Nlength', $this->_string_shift($payload, 4)); + for ($i = 0; $i < $nr_extensions['length']; $i++) { + if (strlen($payload) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($payload, 4)); + $extension_name = $this->_string_shift($payload, $temp['length']); + if ($extension_name == 'server-sig-algs') { + if (strlen($payload) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($payload, 4)); + $this->supported_private_key_algorithms = explode(',', $this->_string_shift($payload, $temp['length'])); + } + } + $payload = $this->_get_binary_packet($skip_channel_filter); } // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in @@ -4059,9 +4173,6 @@ function _get_channel_packet($client_channel, $skip_extended = false) } else { $response = $this->_get_binary_packet(true); if ($response === true && $this->is_timeout) { - if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { - $this->_close_channel($client_channel); - } return true; } if ($response === false) { @@ -4352,6 +4463,10 @@ function _send_binary_packet($data, $logged = null) $packet.= $hmac; + if (!$this->keyExchangeInProgress) { + $this->bytesTransferredSinceLastKEX+= strlen($packet); + } + $start = microtime(true); $result = strlen($packet) == @fputs($this->fsock, $packet); $stop = microtime(true); @@ -4365,6 +4480,10 @@ function _send_binary_packet($data, $logged = null) $this->last_packet = $current; } + if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) { + $this->_key_exchange(); + } + return $result; } @@ -4442,6 +4561,10 @@ function _append_log($message_number, $message) $this->realtime_log_wrap = true; } fputs($this->realtime_log_file, $entry); + break; + case NET_SSH2_LOG_REALTIME_SIMPLE: + echo $message_number; + echo PHP_SAPI == 'cli' ? "\r\n" : '
'; } } @@ -5111,42 +5234,66 @@ function getAlgorithmsNegotiated() */ function setPreferredAlgorithms($methods) { + $keys = array('client_to_server', 'server_to_client'); + + if (isset($methods['kex']) && is_string($methods['kex'])) { + $methods['kex'] = explode(',', $methods['kex']); + } + + if (isset($methods['hostkey']) && is_string($methods['hostkey'])) { + $methods['hostkey'] = explode(',', $methods['hostkey']); + } + + foreach ($keys as $key) { + if (isset($methods[$key])) { + $a = &$methods[$key]; + if (isset($a['crypt']) && is_string($a['crypt'])) { + $a['crypt'] = explode(',', $a['crypt']); + } + if (isset($a['comp']) && is_string($a['comp'])) { + $a['comp'] = explode(',', $a['comp']); + } + if (isset($a['mac']) && is_string($a['mac'])) { + $a['mac'] = explode(',', $a['mac']); + } + } + } + $preferred = $methods; if (isset($preferred['kex'])) { $preferred['kex'] = array_intersect( $preferred['kex'], - $this->getSupportedKEXAlgorithms() + static::getSupportedKEXAlgorithms() ); } if (isset($preferred['hostkey'])) { $preferred['hostkey'] = array_intersect( $preferred['hostkey'], - $this->getSupportedHostKeyAlgorithms() + static::getSupportedHostKeyAlgorithms() ); } - $keys = array('client_to_server', 'server_to_client'); foreach ($keys as $key) { if (isset($preferred[$key])) { $a = &$preferred[$key]; if (isset($a['crypt'])) { $a['crypt'] = array_intersect( $a['crypt'], - $this->getSupportedEncryptionAlgorithms() + static::getSupportedEncryptionAlgorithms() ); } if (isset($a['comp'])) { $a['comp'] = array_intersect( $a['comp'], - $this->getSupportedCompressionAlgorithms() + static::getSupportedCompressionAlgorithms() ); } if (isset($a['mac'])) { $a['mac'] = array_intersect( $a['mac'], - $this->getSupportedMACAlgorithms() + static::getSupportedMACAlgorithms() ); } } @@ -5526,4 +5673,12 @@ function disableSmartMFA() { $this->smartMFA = false; } + + /** + * How many bytes until the next key re-exchange? + */ + function bytesUntilKeyReexchange($bytes) + { + $this->doKeyReexchangeAfterXBytes = $bytes; + } }