diff options
Diffstat (limited to 'vendor/phpseclib')
-rw-r--r-- | vendor/phpseclib/phpseclib/BACKERS.md | 6 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/README.md | 10 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php | 118 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php | 8 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php | 2 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php | 745 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php | 366 | ||||
-rw-r--r-- | vendor/phpseclib/phpseclib/phpseclib/bootstrap.php | 3 |
8 files changed, 1056 insertions, 202 deletions
diff --git a/vendor/phpseclib/phpseclib/BACKERS.md b/vendor/phpseclib/phpseclib/BACKERS.md index e03152ca1..87e3fd2bd 100644 --- a/vendor/phpseclib/phpseclib/BACKERS.md +++ b/vendor/phpseclib/phpseclib/BACKERS.md @@ -4,5 +4,9 @@ phpseclib ongoing development is made possible by [Tidelift](https://tidelift.co ## Backers +- Allan Simon +- Raghu Veer Dendukuri - Zane Hooper -- [Setasign](https://www.setasign.com/)
\ No newline at end of file +- [Setasign](https://www.setasign.com/) +- [Charles Severance](https://github.com/csev) +- [Rachel Fish](https://github.com/itsrachelfish)
\ No newline at end of file diff --git a/vendor/phpseclib/phpseclib/README.md b/vendor/phpseclib/phpseclib/README.md index 099486dc9..61cc09907 100644 --- a/vendor/phpseclib/phpseclib/README.md +++ b/vendor/phpseclib/phpseclib/README.md @@ -1,6 +1,6 @@ # phpseclib - PHP Secure Communications Library -[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/phpseclib/phpseclib) +[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/github/phpseclib/phpseclib) ## Supporting phpseclib @@ -52,7 +52,7 @@ SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / * Composer compatible (PSR-0 autoloading) * Install using Composer: `composer require phpseclib/phpseclib:~1.0` * Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm) -* [Download 1.0.19 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download) +* [Download 1.0.20 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.20.zip/download) ## Security contact information @@ -66,6 +66,12 @@ Need Support? * [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use) +## Special Thanks + +Special Thanks to our Patreon sponsors!: + +- Allan Simon + ## Contributing 1. Fork the Project diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php index 8822b9b88..6335a2484 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php @@ -79,7 +79,11 @@ abstract class Base /** * Encrypt / decrypt using the Cipher Feedback mode (8bit) */ - const MODE_CFB8 = 38; + const MODE_CFB8 = 6; + /** + * Encrypt / decrypt using the Output Feedback mode (8bit) + */ + const MODE_OFB8 = 7; /** * Encrypt / decrypt using the Output Feedback mode. * @@ -484,6 +488,7 @@ abstract class Base case self::MODE_CTR: case self::MODE_CFB: case self::MODE_CFB8: + case self::MODE_OFB8: case self::MODE_OFB: case self::MODE_STREAM: $this->mode = $mode; @@ -773,8 +778,25 @@ abstract class Base } } return $ciphertext; + case self::MODE_OFB8: + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); + $ciphertext.= $plaintext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; case self::MODE_OFB: return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); + case self::MODE_OFB8: + // OpenSSL has built in support for cfb8 but not ofb8 } } @@ -959,12 +981,14 @@ abstract class Base } break; case self::MODE_CFB8: + // compared to regular CFB, which encrypts a block at a time, + // here, we're encrypting a byte at a time $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { - $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); + $ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); $iv = substr($iv, 1) . $c; } @@ -976,6 +1000,21 @@ abstract class Base } } break; + case self::MODE_OFB8: + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = $this->_encryptBlock($iv); + $ciphertext.= $plaintext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { @@ -1116,6 +1155,21 @@ abstract class Base } } break; + case self::MODE_OFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); + $plaintext.= $ciphertext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + break; case self::MODE_OFB: $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); } @@ -1290,7 +1344,7 @@ abstract class Base $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { - $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv); + $plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv); $iv = substr($iv, 1) . $ciphertext[$i]; } @@ -1302,6 +1356,21 @@ abstract class Base } } break; + case self::MODE_OFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = $this->_encryptBlock($iv); + $plaintext.= $ciphertext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + break; case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { @@ -1864,6 +1933,7 @@ abstract class Base self::MODE_CFB => 'ncfb', self::MODE_CFB8 => MCRYPT_MODE_CFB, self::MODE_OFB => MCRYPT_MODE_NOFB, + self::MODE_OFB8 => MCRYPT_MODE_OFB, self::MODE_STREAM => MCRYPT_MODE_STREAM, ); @@ -2446,7 +2516,7 @@ abstract class Base for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; '.$encrypt_block.' - $_ciphertext .= ($_c = $_text[$_i] ^ $in); + $_ciphertext.= ($_c = $_text[$_i] ^ $in); $_iv = substr($_iv, 1) . $_c; } @@ -2468,7 +2538,7 @@ abstract class Base for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; '.$encrypt_block.' - $_plaintext .= $_text[$_i] ^ $in; + $_plaintext.= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $_text[$_i]; } @@ -2483,6 +2553,44 @@ abstract class Base return $_plaintext; '; break; + case self::MODE_OFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $self->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_ciphertext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $in[0]; + } + + if ($self->continuousBuffer) { + $self->encryptIV = $_iv; + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_len = strlen($_text); + $_iv = $self->decryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_plaintext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $in[0]; + } + + if ($self->continuousBuffer) { + $self->decryptIV = $_iv; + } + + return $_plaintext; + '; + break; case self::MODE_OFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php index e26fe41dd..59999d706 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -470,7 +470,7 @@ class RSA case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): define('CRYPT_RSA_MODE', self::MODE_INTERNAL); break; - case extension_loaded('openssl') && file_exists($this->configFile): + case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile): // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work $versions = array(); @@ -878,9 +878,9 @@ class RSA ); $key = "openssh-key-v1\0$key"; - return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($key), 70) . - "-----END OPENSSH PRIVATE KEY-----"; + return "-----BEGIN OPENSSH PRIVATE KEY-----\n" . + chunk_split(base64_encode($key), 70, "\n") . + "-----END OPENSSH PRIVATE KEY-----\n"; default: // eg. self::PRIVATE_FORMAT_PKCS1 $components = array(); foreach ($raw as $name => $value) { diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php index fc24b9145..52adcd450 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -263,7 +263,7 @@ class BigInteger } } - if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + if (function_exists('phpinfo') && extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work $versions = array(); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php index 1e6fba9fc..0c06c35f4 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -5,9 +5,7 @@ * * PHP version 5 * - * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version, - * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access - * to an SFTPv4/5/6 server. + * Supports SFTPv2/3/4/5/6. Defaults to v3. * * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}. * @@ -155,6 +153,24 @@ class SFTP extends SSH2 var $version; /** + * Default Server SFTP version + * + * @var int + * @see self::_initChannel() + * @access private + */ + var $defaultVersion; + + /** + * Preferred SFTP version + * + * @var int + * @see self::_initChannel() + * @access private + */ + var $preferredVersion = 3; + + /** * Current working directory * * @var string @@ -270,6 +286,21 @@ class SFTP extends SSH2 var $preserveTime = false; /** + * Arbitrary Length Packets Flag + * + * Determines whether or not packets of any length should be allowed, + * in cases where the server chooses the packet length (such as + * directory listings). By default, packets are only allowed to be + * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h) + * + * @see self::enableArbitraryLengthPackets() + * @see self::_get_sftp_packet() + * @var bool + * @access private + */ + var $allow_arbitrary_length_packets = false; + + /** * Was the last packet due to the channels being closed or not? * * @see self::get() @@ -280,6 +311,14 @@ class SFTP extends SSH2 var $channel_close = false; /** + * Has the SFTP channel been partially negotiated? + * + * @var bool + * @access private + */ + var $partial_init = false; + + /** * Default Constructor. * * Connects to an SFTP server @@ -299,15 +338,13 @@ class SFTP extends SSH2 $this->packet_types = array( 1 => 'NET_SFTP_INIT', 2 => 'NET_SFTP_VERSION', - /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+: - SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1 - pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */ 3 => 'NET_SFTP_OPEN', 4 => 'NET_SFTP_CLOSE', 5 => 'NET_SFTP_READ', 6 => 'NET_SFTP_WRITE', 7 => 'NET_SFTP_LSTAT', 9 => 'NET_SFTP_SETSTAT', + 10 => 'NET_SFTP_FSETSTAT', 11 => 'NET_SFTP_OPENDIR', 12 => 'NET_SFTP_READDIR', 13 => 'NET_SFTP_REMOVE', @@ -315,18 +352,13 @@ class SFTP extends SSH2 15 => 'NET_SFTP_RMDIR', 16 => 'NET_SFTP_REALPATH', 17 => 'NET_SFTP_STAT', - /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+: - SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 - pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */ 18 => 'NET_SFTP_RENAME', 19 => 'NET_SFTP_READLINK', 20 => 'NET_SFTP_SYMLINK', + 21 => 'NET_SFTP_LINK', 101=> 'NET_SFTP_STATUS', 102=> 'NET_SFTP_HANDLE', - /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+: - SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4 - pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */ 103=> 'NET_SFTP_DATA', 104=> 'NET_SFTP_NAME', 105=> 'NET_SFTP_ATTRS', @@ -371,25 +403,59 @@ class SFTP extends SSH2 // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why $this->attributes = array( 0x00000001 => 'NET_SFTP_ATTR_SIZE', - 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ + 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ + 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', + 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ + 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', + 0x00000040 => 'NET_SFTP_ATTR_ACL', + 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', + 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ + 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ + 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', + 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', + 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', + 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', + 0x00008000 => 'NET_SFTP_ATTR_CTIME', // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED' ); - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 - // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name - // the array for that $this->open5_flags and similarly alter the constant names. $this->open_flags = array( 0x00000001 => 'NET_SFTP_OPEN_READ', 0x00000002 => 'NET_SFTP_OPEN_WRITE', 0x00000004 => 'NET_SFTP_OPEN_APPEND', 0x00000008 => 'NET_SFTP_OPEN_CREATE', 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', - 0x00000020 => 'NET_SFTP_OPEN_EXCL' + 0x00000020 => 'NET_SFTP_OPEN_EXCL', + 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 + ); + // SFTPv5+ changed the flags up: + // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 + $this->open_flags5 = array( + // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened + 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', + 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', + 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', + 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', + 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', + // the rest of the flags are not supported + 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" + 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', + 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', + 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', + 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', + 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', + 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', + 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', + 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', + 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', + 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', + 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', + 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', ); // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 // see \phpseclib\Net\SFTP::_parseLongname() for an explanation @@ -411,6 +477,7 @@ class SFTP extends SSH2 $this->status_codes, $this->attributes, $this->open_flags, + $this->open_flags5, $this->file_types ); @@ -423,28 +490,31 @@ class SFTP extends SSH2 } /** - * Login + * Check a few things before SFTP functions are called * - * @param string $username * @return bool * @access public */ - function login($username) + function _precheck() { - if (!call_user_func_array('parent::login', func_get_args())) { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; } - return $this->_init_sftp_connection(); + if ($this->pwd === false) { + return $this->_init_sftp_connection(); + } + + return true; } /** - * (Re)initializes the SFTP channel + * Partially initialize an SFTP connection * * @return bool - * @access private + * @access public */ - function _init_sftp_connection() + function _partial_init_sftp_connection() { $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; @@ -531,11 +601,13 @@ class SFTP extends SSH2 return false; } + $this->use_request_id = true; + if (strlen($response) < 4) { return false; } extract(unpack('Nversion', $this->_string_shift($response, 4))); - $this->version = $version; + $this->defaultVersion = $version; while (!empty($response)) { if (strlen($response) < 4) { return false; @@ -550,21 +622,22 @@ class SFTP extends SSH2 $this->extensions[$key] = $value; } - /* - SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', - however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's - not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for - one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that - 'newline@vandyke.com' would. - */ - /* - if (isset($this->extensions['newline@vandyke.com'])) { - $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; - unset($this->extensions['newline@vandyke.com']); - } - */ + $this->partial_init = true; - $this->use_request_id = true; + return true; + } + + /** + * (Re)initializes the SFTP channel + * + * @return bool + * @access private + */ + function _init_sftp_connection() + { + if (!$this->partial_init && !$this->_partial_init_sftp_connection()) { + return false; + } /* A Note on SFTPv4/5/6 support: @@ -589,12 +662,60 @@ class SFTP extends SSH2 in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the channel and reopen it with a new and updated SSH_FXP_INIT packet. */ - switch ($this->version) { - case 2: - case 3: - break; - default: - return false; + $this->version = $this->defaultVersion; + if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) { + $versions = explode(',', $this->extensions['versions']); + $supported = array(6, 5, 4); + if ($this->preferredVersion) { + $supported = array_diff($supported, array($this->preferredVersion)); + array_unshift($supported, $this->preferredVersion); + } + foreach ($supported as $ver) { + if (in_array($ver, $versions)) { + if ($ver === $this->version) { + break; + } + $this->version = (int) $ver; + $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver); + if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) { + return false; + } + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + break; + } + } + } + + /* + SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', + however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's + not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for + one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that + 'newline@vandyke.com' would. + */ + /* + if (isset($this->extensions['newline@vandyke.com'])) { + $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; + unset($this->extensions['newline@vandyke.com']); + } + */ + + if ($this->version < 2 || $this->version > 6) { + return false; } $this->pwd = $this->_realpath('.'); @@ -655,6 +776,26 @@ class SFTP extends SSH2 } /** + * Enable arbitrary length packets + * + * @access public + */ + function enableArbitraryLengthPackets() + { + $this->allow_arbitrary_length_packets = true; + } + + /** + * Disable arbitrary length packets + * + * @access public + */ + function disableArbitraryLengthPackets() + { + $this->allow_arbitrary_length_packets = false; + } + + /** * Returns the current directory name * * @return mixed @@ -662,6 +803,10 @@ class SFTP extends SSH2 */ function pwd() { + if (!$this->_precheck()) { + return false; + } + return $this->pwd; } @@ -703,6 +848,10 @@ class SFTP extends SSH2 */ function realpath($path) { + if (!$this->_precheck()) { + return false; + } + return $this->_realpath($path); } @@ -785,7 +934,7 @@ class SFTP extends SSH2 */ function chdir($dir) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -942,7 +1091,7 @@ class SFTP extends SSH2 */ function _list($dir, $raw = true) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -997,13 +1146,17 @@ class SFTP extends SSH2 } extract(unpack('Nlength', $this->_string_shift($response, 4))); $shortname = $this->_string_shift($response, $length); - if (strlen($response) < 4) { - return false; + // SFTPv4 "removed the long filename from the names structure-- it can now be + // built from information available in the attrs structure." + if ($this->version < 4) { + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $longname = $this->_string_shift($response, $length); } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $longname = $this->_string_shift($response, $length); $attributes = $this->_parseAttributes($response); - if (!isset($attributes['type'])) { + if (!isset($attributes['type']) && $this->version < 4) { $fileType = $this->_parseLongname($longname); if ($fileType) { $attributes['type'] = $fileType; @@ -1163,10 +1316,6 @@ class SFTP extends SSH2 */ function size($filename) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - $result = $this->stat($filename); if ($result === false) { return false; @@ -1283,7 +1432,7 @@ class SFTP extends SSH2 */ function stat($filename) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1340,7 +1489,7 @@ class SFTP extends SSH2 */ function lstat($filename) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1454,7 +1603,7 @@ class SFTP extends SSH2 */ function touch($filename, $time = null, $atime = null) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1470,9 +1619,25 @@ class SFTP extends SSH2 $atime = $time; } - $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL; - $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime); - $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr); + if ($this->version < 4) { + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time); + } else { + $attr = pack( + 'N5', + NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, + $atime / 4294967296, + $atime, + $time / 4294967296, + $time + ); + } + + $packet = pack('Na*', strlen($filename), $filename); + $packet.= $this->version >= 5 ? + pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) : + pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL); + $packet.= $attr; + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } @@ -1495,19 +1660,47 @@ class SFTP extends SSH2 /** * Changes file or directory owner * + * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string + * would be of the form "user@dns_domain" but it does not need to be. + * `$sftp->getSupportedVersions()['version']` will return the specific version + * that's being used. + * * Returns true on success or false on error. * * @param string $filename - * @param int $uid + * @param int|string $uid * @param bool $recursive * @return bool * @access public */ function chown($filename, $uid, $recursive = false) { - // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>, - // "if the owner or group is specified as -1, then that ID is not changed" - $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1); + /* + quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>, + + "To avoid a representation that is tied to a particular underlying + implementation at the client or server, the use of UTF-8 strings has + been chosen. The string should be of the form "user@dns_domain". + This will allow for a client and server that do not use the same + local representation the ability to translate to a common syntax that + can be interpreted by both. In the case where there is no + translation available to the client or server, the attribute value + must be constructed without the "@"." + + phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't + have one? phpseclib would have no way of knowing so rather than guess phpseclib + will just use whatever value the user provided + */ + + $attr = $this->version < 4 ? + // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>, + // "if the owner or group is specified as -1, then that ID is not changed" + pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) : + // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>, + // "If either the owner or group field is zero length, the field should be + // considered absent, and no change should be made to that specific field + // during a modification operation" + pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, ''); return $this->_setstat($filename, $attr, $recursive); } @@ -1515,17 +1708,24 @@ class SFTP extends SSH2 /** * Changes file or directory group * + * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string + * would be of the form "user@dns_domain" but it does not need to be. + * `$sftp->getSupportedVersions()['version']` will return the specific version + * that's being used. + * * Returns true on success or false on error. * * @param string $filename - * @param int $gid + * @param int|string $gid * @param bool $recursive * @return bool * @access public */ function chgrp($filename, $gid, $recursive = false) { - $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid); + $attr = $this->version < 4 ? + pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) : + pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid); return $this->_setstat($filename, $attr, $recursive); } @@ -1592,7 +1792,7 @@ class SFTP extends SSH2 */ function _setstat($filename, $attr, $recursive) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1610,9 +1810,10 @@ class SFTP extends SSH2 return $result; } - // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to - // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT. - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { + $packet = $this->version >= 4 ? + pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) : + pack('Na*a*', strlen($filename), $filename, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { return false; } @@ -1682,7 +1883,10 @@ class SFTP extends SSH2 return false; } } else { - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) { + $packet = $this->version >= 4 ? + pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) : + pack('Na*a*', strlen($temp), $temp, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { return false; } @@ -1697,7 +1901,10 @@ class SFTP extends SSH2 } } - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) { + $packet = $this->version >= 4 ? + pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) : + pack('Na*a*', strlen($temp), $temp, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { return false; } @@ -1722,7 +1929,7 @@ class SFTP extends SSH2 */ function readlink($link) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1772,15 +1979,44 @@ class SFTP extends SSH2 */ function symlink($target, $link) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } //$target = $this->_realpath($target); $link = $this->_realpath($link); - $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link); - if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) { + /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 : + + Changed the SYMLINK packet to be LINK and give it the ability to + create hard links. Also change it's packet number because many + implementation implemented SYMLINK with the arguments reversed. + Hopefully the new argument names make it clear which way is which. + */ + if ($this->version == 6) { + $type = NET_SFTP_LINK; + $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1); + } else { + $type = NET_SFTP_SYMLINK; + /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 : + + 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK + + When OpenSSH's sftp-server was implemented, the order of the arguments + to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, + the reversal was not noticed until the server was widely deployed. Since + fixing this to follow the specification would cause incompatibility, the + current order was retained. For correct operation, clients should send + SSH_FXP_SYMLINK as follows: + + uint32 id + string targetpath + string linkpath */ + $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ? + pack('Na*Na*', strlen($target), $target, strlen($link), $link) : + pack('Na*Na*', strlen($link), $link, strlen($target), $target); + } + if (!$this->_send_sftp_packet($type, $packet)) { return false; } @@ -1813,7 +2049,7 @@ class SFTP extends SSH2 */ function mkdir($dir, $mode = -1, $recursive = false) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1882,7 +2118,7 @@ class SFTP extends SSH2 */ function rmdir($dir) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1931,7 +2167,8 @@ class SFTP extends SSH2 * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how * large $remote_file will be, as well. * - * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number of bytes to return, and returns a string if there is some data or null if there is no more data + * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number + * of bytes to return, and returns a string if there is some data or null if there is no more data * * If $data is a resource then it'll be used as a resource instead. * @@ -1967,7 +2204,7 @@ class SFTP extends SSH2 */ function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1978,10 +2215,14 @@ class SFTP extends SSH2 $this->_remove_from_stat_cache($remote_file); - $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; - // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." - // in practice, it doesn't seem to do that. - //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; + if ($this->version >= 5) { + $flags = NET_SFTP_OPEN_OPEN_OR_CREATE; + } else { + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; + // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." + // in practice, it doesn't seem to do that. + //$flags|= ($mode & SFTP::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; + } if ($start >= 0) { $offset = $start; @@ -1991,10 +2232,17 @@ class SFTP extends SSH2 $offset = $size !== false ? $size : 0; } else { $offset = 0; - $flags|= NET_SFTP_OPEN_TRUNCATE; + if ($this->version >= 5) { + $flags = NET_SFTP_OPEN_CREATE_TRUNCATE; + } else { + $flags|= NET_SFTP_OPEN_TRUNCATE; + } } - $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); + $packet = pack('Na*', strlen($remote_file), $remote_file); + $packet.= $this->version >= 5 ? + pack('N3', 0, $flags, 0) : + pack('N2', $flags, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } @@ -2103,6 +2351,8 @@ class SFTP extends SSH2 } } + $result = $this->_close_handle($handle); + if (!$this->_read_put_responses($i)) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); @@ -2111,18 +2361,33 @@ class SFTP extends SSH2 return false; } - if ($mode & self::SOURCE_LOCAL_FILE) { - if ($this->preserveTime) { - $stat = fstat($fp); - $this->touch($remote_file, $stat['mtime'], $stat['atime']); - } - + if ($mode & SFTP::SOURCE_LOCAL_FILE) { if (isset($fp) && is_resource($fp)) { fclose($fp); } + + if ($this->preserveTime) { + $stat = stat($data); + if ($this->version < 4) { + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']); + } else { + $attr = pack( + 'N5', + NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, + $stat['atime'] / 4294967296, + $stat['atime'], + $stat['mtime'] / 4294967296, + $stat['mtime'] + ); + } + + if (!$this->_setstat($remote_file, $attr, false)) { + user_error('Error setting file time'); + } + } } - return $this->_close_handle($handle); + return $result; } /** @@ -2209,7 +2474,7 @@ class SFTP extends SSH2 */ function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -2218,7 +2483,10 @@ class SFTP extends SSH2 return false; } - $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); + $packet = pack('Na*', strlen($remote_file), $remote_file); + $packet.= $this->version >= 5 ? + pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) : + pack('N2', NET_SFTP_OPEN_READ, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } @@ -2320,6 +2588,7 @@ class SFTP extends SSH2 } // maybe the file was successfully transferred, maybe it wasn't if ($this->channel_close) { + $this->partial_init = false; $this->_init_sftp_connection(); return false; } else { @@ -2369,7 +2638,7 @@ class SFTP extends SSH2 */ function delete($path, $recursive = true) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -2498,6 +2767,10 @@ class SFTP extends SSH2 function file_exists($path) { if ($this->use_stat_cache) { + if (!$this->_precheck()) { + return false; + } + $path = $this->_realpath($path); $result = $this->_query_stat_cache($path); @@ -2568,6 +2841,10 @@ class SFTP extends SSH2 */ function is_readable($path) { + if (!$this->_precheck()) { + return false; + } + $path = $this->_realpath($path); $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0); @@ -2596,6 +2873,10 @@ class SFTP extends SSH2 */ function is_writable($path) { + if (!$this->_precheck()) { + return false; + } + $path = $this->_realpath($path); $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0); @@ -2776,6 +3057,10 @@ class SFTP extends SSH2 */ function _get_xstat_cache_prop($path, $prop, $type) { + if (!$this->_precheck()) { + return false; + } + if ($this->use_stat_cache) { $path = $this->_realpath($path); @@ -2796,7 +3081,9 @@ class SFTP extends SSH2 } /** - * Renames a file or a directory on the SFTP server + * Renames a file or a directory on the SFTP server. + * + * If the file already exists this will return false * * @param string $oldname * @param string $newname @@ -2805,7 +3092,7 @@ class SFTP extends SSH2 */ function rename($oldname, $newname) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -2817,6 +3104,18 @@ class SFTP extends SSH2 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); + if ($this->version >= 5) { + /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 , + + 'flags' is 0 or a combination of: + + SSH_FXP_RENAME_OVERWRITE 0x00000001 + SSH_FXP_RENAME_ATOMIC 0x00000002 + SSH_FXP_RENAME_NATIVE 0x00000004 + + (none of these are currently supported) */ + $packet.= "\0\0\0\0"; + } if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { return false; } @@ -2847,6 +3146,31 @@ class SFTP extends SSH2 } /** + * Parse Time + * + * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param string $key + * @param int $flags + * @param string $response + * @return array + * @access private + */ + function _parseTime($key, $flags, &$response) + { + if (strlen($response) < 8) { + user_error('Malformed file attributes'); + return array(); + } + $attr = array(); + $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8))); + if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) { + $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4))); + } + return $attr; + } + + /** * Parse Attributes * * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. @@ -2857,16 +3181,56 @@ class SFTP extends SSH2 */ function _parseAttributes(&$response) { + if ($this->version >= 4) { + $length = 5; + $format = 'Nflags/Ctype'; + } else { + $length = 4; + $format = 'Nflags'; + } + $attr = array(); - if (strlen($response) < 4) { + if (strlen($response) < $length) { user_error('Malformed file attributes'); return array(); } - extract(unpack('Nflags', $this->_string_shift($response, 4))); - // SFTPv4+ have a type field (a byte) that follows the above flag field + extract(unpack($format, $this->_string_shift($response, $length))); + if (isset($type)) { + $attr['type'] = $type; + } foreach ($this->attributes as $key => $value) { switch ($flags & $key) { - case NET_SFTP_ATTR_SIZE: // 0x00000001 + case NET_SFTP_ATTR_UIDGID: + if ($this->version > 3) { + continue 2; + } + break; + case NET_SFTP_ATTR_CREATETIME: + case NET_SFTP_ATTR_MODIFYTIME: + case NET_SFTP_ATTR_ACL: + case NET_SFTP_ATTR_OWNERGROUP: + case NET_SFTP_ATTR_SUBSECOND_TIMES: + if ($this->version < 4) { + continue 2; + } + break; + case NET_SFTP_ATTR_BITS: + if ($this->version < 5) { + continue 2; + } + break; + case NET_SFTP_ATTR_ALLOCATION_SIZE: + case NET_SFTP_ATTR_TEXT_HINT: + case NET_SFTP_ATTR_MIME_TYPE: + case NET_SFTP_ATTR_LINK_COUNT: + case NET_SFTP_ATTR_UNTRANSLATED_NAME: + case NET_SFTP_ATTR_CTIME: + if ($this->version < 6) { + continue 2; + } + } + switch ($flags & $key) { + case NET_SFTP_ATTR_SIZE: // 0x00000001 // The size attribute is defined as an unsigned 64-bit integer. // The following will use floats on 32-bit platforms, if necessary. // As can be seen in the BigInteger class, floats are generally @@ -2875,14 +3239,14 @@ class SFTP extends SSH2 // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8))); break; - case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) + case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 or earlier) if (strlen($response) < 8) { user_error('Malformed file attributes'); return $attr; } $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); break; - case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 + case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; @@ -2896,14 +3260,134 @@ class SFTP extends SSH2 $attr+= array('type' => $fileType); } break; - case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 + case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 + if ($this->version >= 4) { + $attr+= $this->_parseTime('atime', $flags, $response); + break; + } if (strlen($response) < 8) { user_error('Malformed file attributes'); return $attr; } $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); break; - case NET_SFTP_ATTR_EXTENDED: // 0x80000000 + case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+) + $attr+= $this->_parseTime('createtime', $flags, $response); + break; + case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020 + $attr+= $this->_parseTime('mtime', $flags, $response); + break; + case NET_SFTP_ATTR_ACL: // 0x00000040 + // access control list + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7 + // currently unsupported + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + if (strlen($response) < 16) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $this->_string_shift($response, $length); // who + } + break; + case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['owner'] = $this->_string_shift($response, $length); + + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['group'] = $this->_string_shift($response, $length); + break; + case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100 + break; + case NET_SFTP_ATTR_BITS: // 0x00000200 (SFTPv5+) + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8 + // currently unsupported + // tells if you file is: + // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse + // append only, immutable, sync + if (strlen($response) < 8) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8))); + break; + case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+) + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4 + // represents the number of bytes htat the file consumes on the disk. will + // usually be larger than the 'size' field + $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8))); + break; + case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800 + // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10 + // currently unsupported + // tells if file is "known text", "guessed text", "known binary", "guessed binary" + extract(unpack('Ctext-hint', $this->_string_shift($response))); + break; + case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['mime-type'] = $this->_string_shift($response, $length); + break; + case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + $attr+= unpack('Nlink-count', $this->_string_shift($response, 4)); + break; + case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['untranslated-name'] = $this->_string_shift($response, $length); + break; + case NET_SFTP_ATTR_CTIME: // 0x00008000 + // 'ctime' contains the last time the file attributes were changed. The + // exact meaning of this field depends on the server. + $attr+= $this->_parseTime('ctime', $flags, $response); + break; + case NET_SFTP_ATTR_EXTENDED: // 0x80000000 if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; @@ -3124,9 +3608,8 @@ class SFTP extends SSH2 $tempLength = $length; $tempLength-= strlen($this->packet_buffer); - // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h - if (!$this->use_request_id && $tempLength > 256 * 1024) { + if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) { user_error('Invalid SFTP packet size'); return false; } @@ -3244,7 +3727,15 @@ class SFTP extends SSH2 */ function getSupportedVersions() { - $temp = array('version' => $this->version); + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + if (!$this->partial_init) { + $this->_partial_init_sftp_connection(); + } + + $temp = array('version' => $this->defaultVersion); if (isset($this->extensions['versions'])) { $temp['extensions'] = $this->extensions['versions']; } @@ -3252,6 +3743,36 @@ class SFTP extends SSH2 } /** + * Get supported SFTP versions + * + * @return array + * @access public + */ + function getNegotiatedVersion() + { + if (!$this->_precheck()) { + return false; + } + + return $this->version; + } + + /** + * Set preferred version + * + * If you're preferred version isn't supported then the highest supported + * version of SFTP will be utilized. Set to null or false or int(0) to + * unset the preferred version + * + * @param int $version + * @access public + */ + function setPreferredVersion($version) + { + $this->preferredVersion = $version; + } + + /** * Disconnect * * @param int $reason diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php index e449d987a..7ec4a1e36 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -71,6 +71,25 @@ use phpseclib\System\SSH\Agent; class SSH2 { /**#@+ + * Compression Types + * + * @access private + */ + /** + * No compression + */ + const NET_SSH2_COMPRESSION_NONE = 1; + /** + * zlib compression + */ + const NET_SSH2_COMPRESSION_ZLIB = 2; + /** + * zlib@openssh.com + */ + const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3; + /**#@-*/ + + /**#@+ * Execution Bitmap Masks * * @see \phpseclib\Net\SSH2::bitmap @@ -975,8 +994,65 @@ class SSH2 * * @see https://tools.ietf.org/html/rfc4252#section-5.1 * @var array|null + * @access private + */ + var $auth_methods_to_continue = null; + + /** + * Compression method + * + * @var int + * @access private + */ + var $compress = self::NET_SSH2_COMPRESSION_NONE; + + /** + * Decompression method + * + * @var resource|object + * @access private + */ + var $decompress = self::NET_SSH2_COMPRESSION_NONE; + + /** + * Compression context + * + * @var int + * @access private + */ + var $compress_context; + + /** + * Decompression context + * + * @var resource|object + * @access private + */ + var $decompress_context; + + /** + * Regenerate Compression Context + * + * @var bool + * @access private */ - private $auth_methods_to_continue = null; + var $regenerate_compression_context = false; + + /** + * Regenerate Decompression Context + * + * @var bool + * @access private + */ + var $regenerate_decompression_context = false; + + /** + * Smart multi-factor authentication flag + * + * @var bool + * @access private + */ + var $smartMFA = true; /** * Default Constructor. @@ -1218,8 +1294,8 @@ class SSH2 $read = array($this->fsock); $write = $except = null; $start = microtime(true); - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); + $sec = (int) floor($this->curTimeout); + $usec = (int) (1000000 * ($this->curTimeout - $sec)); // on windows this returns a "Warning: Invalid CRT parameters detected" error // the !count() is done as a workaround for <https://bugs.php.net/42682> if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { @@ -1234,6 +1310,7 @@ class SSH2 if (strlen($temp) == 255) { continue; } + if ($temp === false) { return false; } @@ -1569,19 +1646,25 @@ class SSH2 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } + $compression_map = array( + 'none' => self::NET_SSH2_COMPRESSION_NONE, + 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB, + 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH + ); + $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); if ($compression_algorithm_out === false) { user_error('No compatible client to server compression algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } - //$this->decompress = $compression_algorithm_out == 'zlib'; + $this->compress = $compression_map[$compression_algorithm_out]; - $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server); + $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); if ($compression_algorithm_in === false) { user_error('No compatible server to client compression algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } - //$this->compress = $compression_algorithm_in == 'zlib'; + $this->decompress = $compression_map[$compression_algorithm_in]; // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. $exchange_hash_rfc4419 = ''; @@ -2014,6 +2097,8 @@ class SSH2 } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); + $this->regenerate_compression_context = $this->regenerate_decompression_context = true; + return true; } @@ -2142,7 +2227,7 @@ class SSH2 // try logging with 'none' as an authentication method first since that's what // PuTTY does - if (substr($this->server_identifier, 0, 13) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { + if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { if ($this->_login($username)) { return true; } @@ -2174,9 +2259,61 @@ class SSH2 return $this->_login_helper($username); } - foreach ($args as $arg) { - if ($this->_login_helper($username, $arg)) { - return true; + while (count($args)) { + if (!$this->auth_methods_to_continue || !$this->smartMFA) { + $newargs = $args; + $args = array(); + } else { + $newargs = array(); + foreach ($this->auth_methods_to_continue as $method) { + switch ($method) { + case 'publickey': + foreach ($args as $key => $arg) { + if (is_object($arg)) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + break; + case 'keyboard-interactive': + $hasArray = $hasString = false; + foreach ($args as $arg) { + if ($hasArray || is_array($arg)) { + $hasArray = true; + break; + } + if ($hasString || is_string($arg)) { + $hasString = true; + break; + } + } + if ($hasArray && $hasString) { + foreach ($args as $key => $arg) { + if (is_array($arg)) { + $newargs[] = $arg; + break 2; + } + } + } + case 'password': + foreach ($args as $key => $arg) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + } + } + + if (!count($newargs)) { + return false; + } + + foreach ($newargs as $arg) { + if ($this->_login_helper($username, $arg)) { + return true; + } } } return false; @@ -2814,26 +2951,12 @@ class SSH2 return false; } - $response = $this->_get_binary_packet(); - if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } - - if (!strlen($response)) { - return false; + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) { + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - break; - case NET_SSH2_MSG_CHANNEL_FAILURE: - default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - } $this->in_request_pty_exec = true; } @@ -2954,6 +3077,13 @@ class SSH2 return false; } + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) { + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + $packet = pack( 'CNNa*C', NET_SSH2_MSG_CHANNEL_REQUEST, @@ -2966,7 +3096,12 @@ class SSH2 return false; } - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_IGNORE; + $response = $this->_get_channel_packet(self::CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; $this->bitmap |= self::MASK_SHELL; @@ -3369,8 +3504,8 @@ class SSH2 $this->curTimeout-= $elapsed; } - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); + $sec = (int)floor($this->curTimeout); + $usec = (int)(1000000 * ($this->curTimeout - $sec)); // on windows this returns a "Warning: Invalid CRT parameters detected" error if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { @@ -3384,7 +3519,7 @@ class SSH2 if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; - user_error('Connection closed prematurely'); + user_error('Connection closed (by server) prematurely ' . $elapsed . 's'); return false; } @@ -3392,7 +3527,8 @@ class SSH2 $raw = stream_get_contents($this->fsock, $this->decrypt_block_size); if (!strlen($raw)) { - return ''; + user_error('No data received from server'); + return false; } if ($this->decrypt !== false) { @@ -3455,9 +3591,41 @@ class SSH2 } } - //if ($this->decompress) { - // $payload = gzinflate(substr($payload, 2)); - //} + switch ($this->decompress) { + case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case self::NET_SSH2_COMPRESSION_ZLIB: + if ($this->regenerate_decompression_context) { + $this->regenerate_decompression_context = false; + + $cmf = ord($payload[0]); + $cm = $cmf & 0x0F; + if ($cm != 8) { // deflate + user_error("Only CM = 8 ('deflate') is supported ($cm)"); + } + $cinfo = ($cmf & 0xF0) >> 4; + if ($cinfo > 7) { + user_error("CINFO above 7 is not allowed ($cinfo)"); + } + $windowSize = 1 << ($cinfo + 8); + + $flg = ord($payload[1]); + //$fcheck = $flg && 0x0F; + if ((($cmf << 8) | $flg) % 31) { + user_error('fcheck failed'); + } + $fdict = boolval($flg & 0x20); + $flevel = ($flg & 0xC0) >> 6; + + $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8)); + $payload = substr($payload, 2); + } + if ($this->decompress_context) { + $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH); + } + } $this->get_seq_no++; @@ -3737,7 +3905,20 @@ class SSH2 function _get_channel_packet($client_channel, $skip_extended = false) { if (!empty($this->channel_buffers[$client_channel])) { - return array_shift($this->channel_buffers[$client_channel]); + switch ($this->channel_status[$client_channel]) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + foreach ($this->channel_buffers[$client_channel] as $i => $packet) { + switch (ord($packet[0])) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + case NET_SSH2_MSG_CHANNEL_FAILURE: + unset($this->channel_buffers[$client_channel][$i]); + return substr($packet, 1); + } + } + break; + default: + return substr(array_shift($this->channel_buffers[$client_channel]), 1); + } } while (true) { @@ -3811,10 +3992,7 @@ class SSH2 if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { return $data; } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; + $this->channel_buffers[$channel][] = chr($type) . $data; continue 2; case NET_SSH2_MSG_CHANNEL_REQUEST: @@ -3893,20 +4071,15 @@ class SSH2 $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); $this->_on_channel_open(); return $result; - //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: - default: + case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: user_error('Unable to open channel'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - } - break; - case NET_SSH2_MSG_IGNORE: - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - //$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA; - continue 3; - case NET_SSH2_MSG_CHANNEL_FAILURE: - user_error('Error opening channel'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + default: + if ($client_channel == $channel) { + user_error('Unexpected response to open request'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + return $this->_get_channel_packet($client_channel, $skip_extended); } break; case NET_SSH2_MSG_CHANNEL_REQUEST: @@ -3915,6 +4088,14 @@ class SSH2 return true; case NET_SSH2_MSG_CHANNEL_FAILURE: return false; + case NET_SSH2_MSG_CHANNEL_DATA: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $data = $this->_string_shift($response, $length); + $this->channel_buffers[$channel][] = chr($type) . $data; + return $this->_get_channel_packet($client_channel, $skip_extended); default: user_error('Unable to fulfill channel request'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); @@ -3928,10 +4109,6 @@ class SSH2 switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: - //if ($this->channel_status[$channel] == NET_SSH2_MSG_IGNORE) { - // $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA; - //} - /* if ($channel == self::CHANNEL_EXEC) { // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server @@ -3958,10 +4135,7 @@ class SSH2 if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; + $this->channel_buffers[$channel][] = chr($type) . $data; break; case NET_SSH2_MSG_CHANNEL_CLOSE: $this->curTimeout = 5; @@ -3980,7 +4154,7 @@ class SSH2 case NET_SSH2_MSG_CHANNEL_EOF: break; default: - user_error('Error reading channel data'); + user_error("Error reading channel data ($type)"); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } } @@ -4005,11 +4179,27 @@ class SSH2 return false; } - //if ($this->compress) { - // // the -4 removes the checksum: - // // http://php.net/function.gzcompress#57710 - // $data = substr(gzcompress($data), 0, -4); - //} + if (!isset($logged)) { + $logged = $data; + } + + switch ($this->compress) { + case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case self::NET_SSH2_COMPRESSION_ZLIB: + if (!$this->regenerate_compression_context) { + $header = ''; + } else { + $this->regenerate_compression_context = false; + $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15)); + $header = "\x78\x9C"; + } + if ($this->compress_context) { + $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH); + } + } // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 $packet_length = strlen($data) + 9; @@ -4037,10 +4227,10 @@ class SSH2 if (defined('NET_SSH2_LOGGING')) { $current = microtime(true); - $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')'; + $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; $message_number = '-> ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; - $this->_append_log($message_number, isset($logged) ? $logged : $data); + $this->_append_log($message_number, $logged); $this->last_packet = $current; } @@ -4735,10 +4925,12 @@ class SSH2 */ function getSupportedCompressionAlgorithms() { - return array( - 'none' // REQUIRED no compression - //'zlib' // OPTIONAL ZLIB (LZ77) compression - ); + $algos = array('none'); // REQUIRED no compression + if (function_exists('deflate_init')) { + $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed + $algos[] = 'zlib'; + } + return $algos; } /** @@ -4753,18 +4945,24 @@ class SSH2 { $this->_connect(); + $compression_map = array( + self::NET_SSH2_COMPRESSION_NONE => 'none', + self::NET_SSH2_COMPRESSION_ZLIB => 'zlib', + self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com' + ); + return array( 'kex' => $this->kex_algorithm, 'hostkey' => $this->signature_format, 'client_to_server' => array( 'crypt' => $this->encrypt->name, 'mac' => $this->hmac_create->name, - 'comp' => 'none', + 'comp' => $compression_map[$this->compress], ), 'server_to_client' => array( 'crypt' => $this->decrypt->name, 'mac' => $this->hmac_check->name, - 'comp' => 'none', + 'comp' => $compression_map[$this->decompress], ) ); } @@ -5173,8 +5371,24 @@ class SSH2 * @see https://tools.ietf.org/html/rfc4252#section-5.1 * @return array|null */ - public function getAuthMethodsToContinue() + function getAuthMethodsToContinue() { return $this->auth_methods_to_continue; } + + /** + * Enables "smart" multi-factor authentication (MFA) + */ + function enableSmartMFA() + { + $this->smartMFA = true; + } + + /** + * Disables "smart" multi-factor authentication (MFA) + */ + function disableSmartMFA() + { + $this->smartMFA = false; + } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php b/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php index 0da0999fd..547688f9f 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php +++ b/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php @@ -7,7 +7,8 @@ if (extension_loaded('mbstring')) { // 2 - MB_OVERLOAD_STRING - if (ini_get('mbstring.func_overload') & 2) { + // mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0. + if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) { throw new \UnexpectedValueException( 'Overloading of string functions using mbstring.func_overload ' . 'is not supported by phpseclib.' |