diff --git a/README.md b/README.md index fbd58bd8..e90b12f4 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ MIT-licensed pure-PHP implementations of an arbitrary-precision integer arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, -AES, SSH-1, SSH-2, SFTP, and X.509 +AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 -* [Download (0.3.1)](http://sourceforge.net/projects/phpseclib/files/phpseclib0.3.1.zip/download) +* [Download (0.3.5)](http://sourceforge.net/projects/phpseclib/files/phpseclib0.3.5.zip/download) * [Browse Git](https://github.com/phpseclib/phpseclib) * [Documentation](http://phpseclib.sourceforge.net/) * [Support](http://www.frostjedi.com/phpbb/viewforum.php?f=46) diff --git a/phpseclib/Crypt/AES.php b/phpseclib/Crypt/AES.php index 9b70e98d..81fa2fea 100644 --- a/phpseclib/Crypt/AES.php +++ b/phpseclib/Crypt/AES.php @@ -59,7 +59,6 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -67,7 +66,7 @@ * Include Crypt_Rijndael */ if (!class_exists('Crypt_Rijndael')) { - require_once 'Rijndael.php'; + require_once('Rijndael.php'); } /**#@+ diff --git a/phpseclib/Crypt/DES.php b/phpseclib/Crypt/DES.php index 4e799764..7789b7d3 100644 --- a/phpseclib/Crypt/DES.php +++ b/phpseclib/Crypt/DES.php @@ -56,7 +56,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Crypt/Hash.php b/phpseclib/Crypt/Hash.php index c8323b1a..840fcd50 100644 --- a/phpseclib/Crypt/Hash.php +++ b/phpseclib/Crypt/Hash.php @@ -52,7 +52,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Crypt/RC4.php b/phpseclib/Crypt/RC4.php index c7e0a0c4..7b9de4ca 100644 --- a/phpseclib/Crypt/RC4.php +++ b/phpseclib/Crypt/RC4.php @@ -58,7 +58,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index 8eedc0bb..e919b002 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -65,7 +65,6 @@ * @author Jim Wigginton * @copyright MMIX Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: RSA.php,v 1.19 2010/09/12 21:58:54 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Crypt/Random.php b/phpseclib/Crypt/Random.php index 475ec4f2..948512da 100644 --- a/phpseclib/Crypt/Random.php +++ b/phpseclib/Crypt/Random.php @@ -38,7 +38,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Crypt/Rijndael.php b/phpseclib/Crypt/Rijndael.php index 6c6278a1..9be500e6 100644 --- a/phpseclib/Crypt/Rijndael.php +++ b/phpseclib/Crypt/Rijndael.php @@ -68,7 +68,6 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Crypt/TripleDES.php b/phpseclib/Crypt/TripleDES.php index 448225d1..4030c6c9 100644 --- a/phpseclib/Crypt/TripleDES.php +++ b/phpseclib/Crypt/TripleDES.php @@ -50,7 +50,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/File/ANSI.php b/phpseclib/File/ANSI.php index 83fc15ff..20a0312b 100644 --- a/phpseclib/File/ANSI.php +++ b/phpseclib/File/ANSI.php @@ -34,8 +34,7 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ - * @link htp://phpseclib.sourceforge.net + * @link http://phpseclib.sourceforge.net */ /** diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index b16b9e1c..eb251e77 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -37,7 +37,6 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index ee461aab..b7c68b2f 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -40,8 +40,7 @@ * @author Jim Wigginton * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id$ - * @link htp://phpseclib.sourceforge.net + * @link http://phpseclib.sourceforge.net */ /** @@ -54,8 +53,9 @@ if (!class_exists('File_ASN1')) { /** * Flag to only accept signatures signed by certificate authorities * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * * @access public - * @see File_X509::validateSignature() */ define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1); @@ -1990,13 +1990,16 @@ class File_X509 { * Works on X.509 certs, CSR's and CRL's. * Returns true if the signature is verified, false if it is not correct or NULL on error * + * By default returns false for self-signed certs. Call validateSignature(false) to make this support + * self-signed. + * * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. * - * @param Integer $options optional + * @param Boolean $caonly optional * @access public * @return Mixed */ - function validateSignature($options = 0) + function validateSignature($caonly = true) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { return 0; @@ -2037,10 +2040,10 @@ class File_X509 { } } } - if (count($this->CAs) == $i && ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + if (count($this->CAs) == $i && $caonly) { return false; } - } elseif (!isset($signingCert) || ($options & FILE_X509_VALIDATE_SIGNATURE_BY_CA)) { + } elseif (!isset($signingCert) || $caonly) { return false; } return $this->_validateSignature( diff --git a/phpseclib/Math/BigInteger.php b/phpseclib/Math/BigInteger.php index d47d61a3..3837e8b7 100644 --- a/phpseclib/Math/BigInteger.php +++ b/phpseclib/Math/BigInteger.php @@ -70,7 +70,6 @@ * @author Jim Wigginton * @copyright MMVI Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $ * @link http://pear.php.net/package/Math_BigInteger */ @@ -2712,7 +2711,6 @@ class Math_BigInteger { * * @param Integer $bits * @access public - * @return Math_BigInteger */ function setPrecision($bits) { @@ -3059,8 +3057,6 @@ class Math_BigInteger { $min = $temp; } - $generator = $this->generator; - $max = $max->subtract($min); $max = ltrim($max->toBytes(), chr(0)); $size = strlen($max) - 1; @@ -3120,10 +3116,18 @@ class Math_BigInteger { */ function randomPrime($min = false, $max = false, $timeout = false) { + if ($min === false) { + $min = new Math_BigInteger(0); + } + + if ($max === false) { + $max = new Math_BigInteger(0x7FFFFFFF); + } + $compare = $max->compare($min); if (!$compare) { - return $min; + return $min->isPrime() ? $min : false; } else if ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; @@ -3131,36 +3135,6 @@ class Math_BigInteger { $min = $temp; } - // gmp_nextprime() requires PHP 5 >= 5.2.0 per . - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { - // we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function - // does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however, - // the same $max / $min checks are not performed. - if ($min === false) { - $min = new Math_BigInteger(0); - } - - if ($max === false) { - $max = new Math_BigInteger(0x7FFFFFFF); - } - - $x = $this->random($min, $max); - - $x->value = gmp_nextprime($x->value); - - if ($x->compare($max) <= 0) { - return $x; - } - - $x->value = gmp_nextprime($min->value); - - if ($x->compare($max) <= 0) { - return $x; - } - - return false; - } - static $one, $two; if (!isset($one)) { $one = new Math_BigInteger(1); @@ -3170,6 +3144,22 @@ class Math_BigInteger { $start = time(); $x = $this->random($min, $max); + + // gmp_nextprime() requires PHP 5 >= 5.2.0 per . + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { + $p->value = gmp_nextprime($x->value); + + if ($p->compare($max) <= 0) { + return $p; + } + + if (!$min->equals($x)) { + $x = $x->subtract($one); + } + + return $x->randomPrime($min, $x); + } + if ($x->equals($two)) { return $x; } diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 0b99225f..e6e8b239 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -649,19 +649,7 @@ class Net_SFTP extends Net_SSH2 { return false; } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); + if (!$this->_close_handle($handle)) { return false; } @@ -793,21 +781,7 @@ class Net_SFTP extends Net_SSH2 { } } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - // "The client MUST release all resources associated with the handle regardless of the status." - // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); + if (!$this->_close_handle($handle)) { return false; } @@ -1087,25 +1061,7 @@ class Net_SFTP extends Net_SSH2 { $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: - $handle = substr($response, 4); - - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; + return $this->_close_handle(substr($response, 4)); case NET_SFTP_STATUS: $this->_logError($response); break; @@ -1454,19 +1410,33 @@ class Net_SFTP extends Net_SSH2 { * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take * care of that, yourself. * - * As for $start... if it's negative (which it is by default) a new file will be created or an existing - * file truncated depending on $mode | NET_SFTP_RESUME. If it's zero or positive it'll be updated at that - * spot. + * $mode can take an additional two parameters - NET_SFTP_RESUME and NET_SFTP_RESUME_START. These are bitwise AND'd with + * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: + * + * NET_SFTP_LOCAL_FILE | NET_SFTP_RESUME + * + * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace + * NET_SFTP_RESUME with NET_SFTP_RESUME start. + * + * If $mode & (NET_SFTP_RESUME | NET_SFTP_RESUME_START) then NET_SFTP_RESUME_START will be assumed. + * + * $start and $local_start give you more fine grained control over this process and take precident over NET_SFTP_RESUME* + * when they're non-negative. ie. $start could let you write at the end of a file (like NET_SFTP_RESUME) or in the middle + * of one. $local_start could let you start your reading from the end of a file (like NET_SFTP_RESUME_START) or in the + * middle of one. + * + * Setting $local_start to > 0 or $mode | NET_SFTP_RESUME_START doesn't do anything unless $mode | NET_SFTP_LOCAL_FILE. * * @param String $remote_file * @param String $data * @param optional Integer $mode * @param optional Integer $start + * @param optional Integer $local_start * @return Boolean * @access public * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode(). */ - function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1) + function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { return false; @@ -1482,19 +1452,16 @@ class Net_SFTP extends Net_SSH2 { // in practice, it doesn't seem to do that. //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; - // if NET_SFTP_OPEN_APPEND worked as it should the following (up until the -----------) wouldn't be necessary - $offset = 0; - if (($mode & NET_SFTP_RESUME) || $start >= 0) { - if ($start >= 0) { - $offset = $start; - } else { - $size = $this->_size($remote_file); - $offset = $size !== false ? $size : 0; - } + if ($start >= 0) { + $offset = $start; + } elseif ($mode & NET_SFTP_RESUME) { + // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called + $size = $this->_size($remote_file); + $offset = $size !== false ? $size : 0; } else { + $offset = 0; $flags|= NET_SFTP_OPEN_TRUNCATE; } - // -------------- $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { @@ -1527,6 +1494,14 @@ class Net_SFTP extends Net_SSH2 { return false; } $size = filesize($data); + + if ($local_start >= 0) { + fseek($fp, $local_start); + } elseif ($mode & NET_SFTP_RESUME_START) { + // do nothing + } else { + fseek($fp, $offset); + } } else { $size = strlen($data); } @@ -1558,6 +1533,10 @@ class Net_SFTP extends Net_SSH2 { } if (!$this->_read_put_responses($i)) { + if ($mode & NET_SFTP_LOCAL_FILE) { + fclose($fp); + } + $this->_close_handle($handle); return false; } @@ -1565,23 +1544,7 @@ class Net_SFTP extends Net_SSH2 { fclose($fp); } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; + return $this->_close_handle($handle); } /** @@ -1613,6 +1576,36 @@ class Net_SFTP extends Net_SSH2 { return $i < 0; } + /** + * Close handle + * + * @param String $handle + * @return Boolean + * @access private + */ + function _close_handle($handle) + { + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + // "The client MUST release all resources associated with the handle regardless of the status." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + /** * Downloads a file from the SFTP server. * @@ -1717,28 +1710,7 @@ class Net_SFTP extends Net_SSH2 { fclose($fp); } - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - // if $content isn't set that means a file was written to - if (isset($content)) { - return $content; - } - - return true; + return $this->_close_handle($handle); } /** diff --git a/phpseclib/Net/SFTP/Stream.php b/phpseclib/Net/SFTP/Stream.php index 23448570..1527b0dc 100644 --- a/phpseclib/Net/SFTP/Stream.php +++ b/phpseclib/Net/SFTP/Stream.php @@ -153,7 +153,7 @@ class Net_SFTP_Stream { */ function _parse_path($path) { - extract(parse_url($path)); + extract(parse_url($path) + array('port' => 22)); if (!isset($host)) { return false; @@ -201,7 +201,7 @@ class Net_SFTP_Stream { if (isset(self::$instances[$host][$port][$user][(string) $pass])) { $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; } else { - $this->sftp = new Net_SFTP($host, isset($port) ? $port : 22); + $this->sftp = new Net_SFTP($host, $port); if (isset($this->notification) && is_callable($this->notification)) { /* if !is_callable($this->notification) we could do this: diff --git a/phpseclib/Net/SSH1.php b/phpseclib/Net/SSH1.php index 89b5aa2c..83d5980d 100644 --- a/phpseclib/Net/SSH1.php +++ b/phpseclib/Net/SSH1.php @@ -62,7 +62,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: SSH1.php,v 1.15 2010/03/22 22:01:38 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 2a79265f..f2bde0e5 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -64,7 +64,6 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version $Id: SSH2.php,v 1.53 2010-10-24 01:24:30 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -720,6 +719,14 @@ class Net_SSH2 { */ var $banner_message = ''; + /** + * Did read() timeout or return normally? + * + * @see Net_SSH2::isTimeout + * @access private + */ + var $is_timeout = false; + /** * Default Constructor. * @@ -1163,28 +1170,29 @@ class Net_SSH2 { // see http://tools.ietf.org/html/rfc2409#section-6.2 and // http://tools.ietf.org/html/rfc2412, appendex E case 'diffie-hellman-group1-sha1': - $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . - '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . - '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . - 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'); - $keyLength = $keyLength < 160 ? $keyLength : 160; - $hash = 'sha1'; + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; break; // see http://tools.ietf.org/html/rfc3526#section-3 case 'diffie-hellman-group14-sha1': - $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . - '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . - '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . - 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . - '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . - '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . - 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . - '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'); - $keyLength = $keyLength < 160 ? $keyLength : 160; - $hash = 'sha1'; + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . + '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . + '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . + '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; + break; } - $p = new Math_BigInteger($p, 256); + // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 + // the generator field element is 2 (decimal) and the hash function is sha1. + $g = new Math_BigInteger(2); + $prime = new Math_BigInteger($prime, 16); + $kexHash = new Crypt_Hash('sha1'); //$q = $p->bitwise_rightShift(1); /* To increase the speed of the key exchange, both client and server may @@ -1194,14 +1202,12 @@ class Net_SSH2 { [VAN-OORSCHOT]. -- http://tools.ietf.org/html/rfc4419#section-6.2 */ - $q = new Math_BigInteger(1); - $q = $q->bitwise_leftShift(2 * $keyLength); - $q = $q->subtract(new Math_BigInteger(1)); + $one = new Math_BigInteger(1); + $keyLength = min($keyLength, $kexHash->getLength()); + $max = $one->bitwise_leftShift(16 * $keyLength)->subtract($one); // 2 * 8 * $keyLength - $g = new Math_BigInteger(2); - $x = new Math_BigInteger(); - $x = $x->random(new Math_BigInteger(1), $q); - $e = $g->modPow($x, $p); + $x = $one->random($one, $max); + $e = $g->modPow($x, $prime); $eBytes = $e->toBytes(true); $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes); @@ -1239,7 +1245,7 @@ class Net_SSH2 { $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); $this->signature_format = $this->_string_shift($this->signature, $temp['length']); - $key = $f->modPow($x, $p); + $key = $f->modPow($x, $prime); $keyBytes = $key->toBytes(true); $this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*', @@ -1249,7 +1255,7 @@ class Net_SSH2 { $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes ); - $this->exchange_hash = pack('H*', $hash($this->exchange_hash)); + $this->exchange_hash = $kexHash->hash($this->exchange_hash); if ($this->session_id === false) { $this->session_id = $this->exchange_hash; @@ -1448,15 +1454,15 @@ class Net_SSH2 { $this->encrypt->enableContinuousBuffer(); $this->encrypt->disablePadding(); - $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id)); + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); while ($this->encrypt_block_size > strlen($iv)) { - $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv)); + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); while ($encryptKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); } @@ -1465,15 +1471,15 @@ class Net_SSH2 { $this->decrypt->enableContinuousBuffer(); $this->decrypt->disablePadding(); - $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id)); + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); while ($this->decrypt_block_size > strlen($iv)) { - $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv)); + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); while ($decryptKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); } @@ -1547,15 +1553,15 @@ class Net_SSH2 { $this->hmac_size = 12; } - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); - $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id)); + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); while ($checkKeyLength > strlen($key)) { - $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key)); + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); @@ -2013,9 +2019,10 @@ class Net_SSH2 { * @return String * @access public */ - function exec($command, $block = true) + function exec($command, $callback = NULL) { $this->curTimeout = $this->timeout; + $this->is_timeout = false; $this->stdErrorLog = ''; if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { @@ -2097,7 +2104,7 @@ class Net_SSH2 { $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; - if (!$block || $this->in_request_pty_exec) { + if ($callback === false || $this->in_request_pty_exec) { return true; } @@ -2106,11 +2113,15 @@ class Net_SSH2 { $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); switch (true) { case $temp === true: - return $output; + return is_callable($callback) ? true : $output; case $temp === false: return false; default: - $output.= $temp; + if (is_callable($callback)) { + $callback($temp); + } else { + $output.= $temp; + } } } } @@ -2207,6 +2218,7 @@ class Net_SSH2 { function read($expect = '', $mode = NET_SSH2_READ_SIMPLE) { $this->curTimeout = $this->timeout; + $this->is_timeout = false; if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); @@ -2264,6 +2276,31 @@ class Net_SSH2 { return $this->_send_channel_packet($channel, $cmd); } + /** + * Closes a channel + * + * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call + * + * @access public + */ + function reset() + { + $channel = $this->in_request_pty_exec ? NET_SSH2_CHANNEL_EXEC : NET_SSH2_CHANNEL_SHELL; + $this->_close_channel($channel); + } + + /** + * Is timeout? + * + * Did exec() or read() return because they timed out or because they encountered the end? + * + * @access public + */ + function isTimeout() + { + return $this->is_timeout; + } + /** * Disconnect * @@ -2532,7 +2569,7 @@ class Net_SSH2 { while (true) { if ($this->curTimeout) { if ($this->curTimeout < 0) { - $this->_close_channel($client_channel); + $this->is_timeout = true; return true; } @@ -2544,7 +2581,7 @@ class Net_SSH2 { $usec = 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)) { - $this->_close_channel($client_channel); + $this->is_timeout = true; return true; } $elapsed = strtok(microtime(), ' ') + strtok('') - $start; @@ -2657,6 +2694,13 @@ class Net_SSH2 { if ($length) { $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); } + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; case 'exit-status': extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); $this->exit_status = $exit_status; @@ -2666,6 +2710,8 @@ class Net_SSH2 { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; default: // "Some systems may not implement signals, in which case they SHOULD ignore this message." // -- http://tools.ietf.org/html/rfc4254#section-6.9