diff --git a/phpseclib/Crypt/Base.php b/phpseclib/Crypt/Base.php index 715ab2a5..7096b662 100644 --- a/phpseclib/Crypt/Base.php +++ b/phpseclib/Crypt/Base.php @@ -545,6 +545,7 @@ abstract class Base * @see Crypt/Hash.php * @param String $password * @param optional String $method + * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length * @return Boolean * @access public * @internal Could, but not must, extend by the child Crypt_* class @@ -579,8 +580,7 @@ abstract class Base $hashObj = new Hash(); $hashObj->setHash($hash); if ($dkLen > $hashObj->getLength()) { - user_error('Derived key too long'); - return false; + throw new \LengthException('Derived key length cannot be longer than the hash length'); } $t = $password . $salt; for ($i = 0; $i < $count; ++$i) { @@ -1768,6 +1768,7 @@ abstract class Base * * @see \phpseclib\Crypt\Base::_unpad() * @param String $text + * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size * @access private * @return String */ @@ -1779,8 +1780,7 @@ abstract class Base if ($length % $this->block_size == 0) { return $text; } else { - user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); - $this->padding = true; + throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); } } @@ -1797,6 +1797,7 @@ abstract class Base * * @see \phpseclib\Crypt\Base::_pad() * @param String $text + * @throws \LengthException if the ciphertext's length is not a multiple of the block size * @access private * @return String */ @@ -1809,7 +1810,7 @@ abstract class Base $length = ord($text[strlen($text) - 1]); if (!$length || $length > $this->block_size) { - return false; + throw new \LengthException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})"); } return substr($text, 0, -$length); diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index 78837ddf..9edb9f61 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -2057,14 +2057,14 @@ class RSA * @access private * @param \phpseclib\Math\BigInteger $x * @param Integer $xLen + * @throws \OutOfBoundsException if strlen($x) > $xLen * @return String */ function _i2osp($x, $xLen) { $x = $x->toBytes(); if (strlen($x) > $xLen) { - user_error('Integer too large'); - return false; + throw new \OutOfBoundsException('Integer too large'); } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); } @@ -2219,13 +2219,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $m + * @throws \OutOfRangeException if $m < 0 or $m > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsaep($m) { if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); - return false; + throw new \OutOfRangeException('Message representative out of range'); } return $this->_exponentiate($m); } @@ -2237,13 +2237,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $c + * @throws \OutOfRangeException if $c < 0 or $c > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsadp($c) { if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { - user_error('Ciphertext representative out of range'); - return false; + throw new \OutOfRangeException('Ciphertext representative out of range'); } return $this->_exponentiate($c); } @@ -2255,13 +2255,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $m + * @throws \OutOfRangeException if $m < 0 or $m > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsasp1($m) { if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); - return false; + throw new \OutOfRangeException('Message representative out of range'); } return $this->_exponentiate($m); } @@ -2273,13 +2273,13 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $s + * @throws \OutOfRangeException if $s < 0 or $s > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsavp1($s) { if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { - user_error('Signature representative out of range'); - return false; + throw new \OutOfRangeException('Signature representative out of range'); } return $this->_exponentiate($s); } @@ -2317,6 +2317,7 @@ class RSA * @access private * @param String $m * @param String $l + * @throws \OutOfBoundsException if strlen($m) > $this->k - 2 * $this->hLen - 2 * @return String */ function _rsaes_oaep_encrypt($m, $l = '') @@ -2329,8 +2330,7 @@ class RSA // be output. if ($mLen > $this->k - 2 * $this->hLen - 2) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-OAEP encoding @@ -2380,6 +2380,7 @@ class RSA * @access private * @param String $c * @param String $l + * @throws \RuntimeException on decryption error * @return String */ function _rsaes_oaep_decrypt($c, $l = '') @@ -2390,8 +2391,7 @@ class RSA // be output. if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // RSA decryption @@ -2399,8 +2399,7 @@ class RSA $c = $this->_os2ip($c); $m = $this->_rsadp($c); if ($m === false) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $em = $this->_i2osp($m, $this->k); @@ -2417,13 +2416,11 @@ class RSA $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); if ($lHash != $lHash2) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $m = ltrim($m, chr(0)); if (ord($m[0]) != 1) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // Output the message M @@ -2454,6 +2451,7 @@ class RSA * * @access private * @param String $m + * @throws \OutOfBoundsException if strlen($m) > $this->k - 11 * @return String */ function _rsaes_pkcs1_v1_5_encrypt($m) @@ -2463,8 +2461,7 @@ class RSA // Length checking if ($mLen > $this->k - 11) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-PKCS1-v1_5 encoding @@ -2513,6 +2510,7 @@ class RSA * * @access private * @param String $c + * @throws \RuntimeException on decryption error * @return String */ function _rsaes_pkcs1_v1_5_decrypt($c) @@ -2520,8 +2518,7 @@ class RSA // Length checking if (strlen($c) != $this->k) { // or if k < 11 - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // RSA decryption @@ -2530,24 +2527,21 @@ class RSA $m = $this->_rsadp($c); if ($m === false) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $em = $this->_i2osp($m, $this->k); // EME-PKCS1-v1_5 decoding if (ord($em[0]) != 0 || ord($em[1]) > 2) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { - user_error('Decryption error'); - return false; + throw new \RuntimeException('Decryption error'); } // Output M @@ -2562,6 +2556,7 @@ class RSA * * @access private * @param String $m + * @throws \RuntimeException on encoding error * @param Integer $emBits */ function _emsa_pss_encode($m, $emBits) @@ -2574,8 +2569,7 @@ class RSA $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { - user_error('Encoding error'); - return false; + throw new \RuntimeException('Encoding error'); } $salt = Random::string($sLen); @@ -2672,6 +2666,7 @@ class RSA * @access private * @param String $m * @param String $s + * @throws \RuntimeException on invalid signature * @return String */ function _rsassa_pss_verify($m, $s) @@ -2679,8 +2674,7 @@ class RSA // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // RSA verification @@ -2690,13 +2684,11 @@ class RSA $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); if ($m2 === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } $em = $this->_i2osp($m2, $modBits >> 3); if ($em === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // EMSA-PSS verification @@ -2712,6 +2704,7 @@ class RSA * @access private * @param String $m * @param Integer $emLen + * @throws \LengthException if the intended encoded message length is too short * @return String */ function _emsa_pkcs1_v1_5_encode($m, $emLen) @@ -2745,8 +2738,7 @@ class RSA $tLen = strlen($t); if ($emLen < $tLen + 11) { - user_error('Intended encoded message length too short'); - return false; + throw new \LengthException('Intended encoded message length too short'); } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); @@ -2763,6 +2755,7 @@ class RSA * * @access private * @param String $m + * @throws \LengthException if the RSA modulus is too short * @return String */ function _rsassa_pkcs1_v1_5_sign($m) @@ -2771,8 +2764,7 @@ class RSA $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); if ($em === false) { - user_error('RSA modulus too short'); - return false; + throw new \LengthException('RSA modulus too short'); } // RSA signature @@ -2793,6 +2785,8 @@ class RSA * * @access private * @param String $m + * @throws \RuntimeException if the signature is invalid + * @throws \LengthException if the RSA modulus is too short * @return String */ function _rsassa_pkcs1_v1_5_verify($m, $s) @@ -2800,8 +2794,7 @@ class RSA // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // RSA verification @@ -2809,21 +2802,18 @@ class RSA $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } $em = $this->_i2osp($m2, $this->k); if ($em === false) { - user_error('Invalid signature'); - return false; + throw new \RuntimeException('Invalid signature'); } // EMSA-PKCS1-v1_5 encoding $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); if ($em2 === false) { - user_error('RSA modulus too short'); - return false; + throw new \LengthException('RSA modulus too short'); } // Compare diff --git a/phpseclib/Crypt/Random.php b/phpseclib/Crypt/Random.php index 9fb1d15b..e6269035 100644 --- a/phpseclib/Crypt/Random.php +++ b/phpseclib/Crypt/Random.php @@ -49,6 +49,7 @@ class Random * eg. for RSA key generation. * * @param Integer $length + * @throws \RuntimeException if a symmetric cipher is needed but not loaded * @return String */ public static function string($length) @@ -199,8 +200,7 @@ class Random $crypto = new RC4(); break; default: - user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded'); - return false; + throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded'); } $crypto->setKey($key); diff --git a/phpseclib/Exception/BadConfigurationException.php b/phpseclib/Exception/BadConfigurationException.php new file mode 100644 index 00000000..096148a0 --- /dev/null +++ b/phpseclib/Exception/BadConfigurationException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * BadConfigurationException + * + * @package BadConfigurationException + * @author Jim Wigginton + */ +class BadConfigurationException extends \RuntimeException +{ +} diff --git a/phpseclib/Exception/FileNotFoundException.php b/phpseclib/Exception/FileNotFoundException.php new file mode 100644 index 00000000..984edfcc --- /dev/null +++ b/phpseclib/Exception/FileNotFoundException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * FileNotFoundException + * + * @package FileNotFoundException + * @author Jim Wigginton + */ +class FileNotFoundException extends \RuntimeException +{ +} diff --git a/phpseclib/Exception/NoSupportedAlgorithmsException.php b/phpseclib/Exception/NoSupportedAlgorithmsException.php new file mode 100644 index 00000000..bca9a753 --- /dev/null +++ b/phpseclib/Exception/NoSupportedAlgorithmsException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * NoSupportedAlgorithmsException + * + * @package NoSupportedAlgorithmsException + * @author Jim Wigginton + */ +class NoSupportedAlgorithmsException extends \RuntimeException +{ +} diff --git a/phpseclib/Exception/UnsupportedAlgorithmException.php b/phpseclib/Exception/UnsupportedAlgorithmException.php new file mode 100644 index 00000000..47cc41d4 --- /dev/null +++ b/phpseclib/Exception/UnsupportedAlgorithmException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * UnsupportedAlgorithmException + * + * @package UnsupportedAlgorithmException + * @author Jim Wigginton + */ +class UnsupportedAlgorithmException extends \RuntimeException +{ +} diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index 717f4f8d..fa58ec7f 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -793,6 +793,7 @@ class ASN1 * @param String $mapping * @param Integer $idx * @return String + * @throws \RuntimeException if the input has an error in it * @access private */ function _encode_der($source, $mapping, $idx = null, $special = array()) @@ -985,7 +986,7 @@ class ASN1 case self::TYPE_OBJECT_IDENTIFIER: $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); if ($oid === false) { - user_error('Invalid OID'); + throw new \RuntimeException('Invalid OID'); return false; } $value = ''; @@ -1038,7 +1039,7 @@ class ASN1 $filters = $filters[$part]; } if ($filters === false) { - user_error('No filters defined for ' . implode('/', $loc)); + throw new \RuntimeException('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); @@ -1062,7 +1063,7 @@ class ASN1 $value = $source ? "\xFF" : "\x00"; break; default: - user_error('Mapping provides no type definition for ' . implode('/', $this->location)); + throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index cfdfbe2e..cbe16bae 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -32,6 +32,7 @@ use phpseclib\Crypt\Random; use phpseclib\File\ASN1; use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; +use phpseclib\Exception\UnsupportedAlgorithmException; /** * Pure-PHP X.509 Parser @@ -1641,7 +1642,7 @@ class X509 $map = $this->_getMapping($id); if (is_bool($map)) { if (!$map) { - user_error($id . ' is not a currently supported extension'); + //user_error($id . ' is not a currently supported extension'); unset($extensions[$i]); } } else { @@ -1714,7 +1715,7 @@ class X509 $id = $attributes[$i]['type']; $map = $this->_getMapping($id); if ($map === false) { - user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); unset($attributes[$i]); } elseif (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; @@ -2107,7 +2108,8 @@ class X509 /** * Validates a signature * - * Returns true if the signature is verified, false if it is not correct or null on error + * Returns true if the signature is verified and false if it is not correct. + * If the algorithms are unsupposed an exception is thrown. * * @param String $publicKeyAlgorithm * @param String $publicKey @@ -2115,7 +2117,8 @@ class X509 * @param String $signature * @param String $signatureSubject * @access private - * @return Integer + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported + * @return Boolean */ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) { @@ -2139,11 +2142,11 @@ class X509 } break; default: - return null; + throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } break; default: - return null; + throw new UnsupportedAlgorithmException('Public key algorithm unsupported'); } return true; @@ -3628,6 +3631,7 @@ class X509 * @param \phpseclib\File\X509 $subject * @param String $signatureAlgorithm * @access public + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported * @return Mixed */ function _sign($key, $signatureAlgorithm) @@ -3646,10 +3650,12 @@ class X509 $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); return $this->currentCert; + default: + throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } } - return false; + throw new UnsupportedAlgorithmException('Unsupported public key algorithm'); } /** diff --git a/phpseclib/Net/SCP.php b/phpseclib/Net/SCP.php index 678297e0..ebfa999a 100644 --- a/phpseclib/Net/SCP.php +++ b/phpseclib/Net/SCP.php @@ -34,6 +34,7 @@ namespace phpseclib\Net; use phpseclib\Net\SSH1; use phpseclib\Net\SSH2; +use phpseclib\Exception\FileNotFoundException; /** * Pure-PHP implementations of SCP. @@ -140,6 +141,7 @@ class SCP * @param String $data * @param optional Integer $mode * @param optional Callable $callback + * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist * @return Boolean * @access public */ @@ -168,8 +170,7 @@ class SCP $size = strlen($data); } else { if (!is_file($data)) { - user_error("$data is not a valid file", E_USER_NOTICE); - return false; + throw new FileNotFoundException("$data is not a valid file"); } $fp = @fopen($data, 'rb'); @@ -289,6 +290,7 @@ class SCP * Receives a packet from an SSH server * * @return String + * @throws \UnexpectedValueException on receipt of an unexpected packet * @access private */ function _receive() @@ -314,8 +316,7 @@ class SCP $this->ssh->bitmap = 0; return false; default: - user_error('Unknown packet received', E_USER_NOTICE); - return false; + throw new \UnexpectedValueException('Unknown packet received'); } } } diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 60d8923d..8460a1c9 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -38,6 +38,7 @@ namespace phpseclib\Net; use phpseclib\Net\SSH2; +use phpseclib\Exception\FileNotFoundException; /** * Pure-PHP implementations of SFTP. @@ -383,6 +384,7 @@ class SFTP extends SSH2 * * @param String $username * @param optional String $password + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Boolean * @access public */ @@ -470,8 +472,7 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_VERSION) { - user_error('Expected SSH_FXP_VERSION'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_VERSION'); } extract(unpack('Nversion', $this->_string_shift($response, 4))); @@ -610,6 +611,7 @@ class SFTP extends SSH2 * * @see \phpseclib\Net\SFTP::chdir() * @param String $path + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Mixed * @access private */ @@ -634,8 +636,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); } } @@ -666,6 +667,7 @@ class SFTP extends SSH2 * Changes the current directory * * @param String $dir + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Boolean * @access public */ @@ -710,8 +712,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } if (!$this->_close_handle($handle)) { @@ -813,6 +814,7 @@ class SFTP extends SSH2 * @param String $dir * @param optional Boolean $raw * @return Mixed + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _list($dir, $raw = true) @@ -844,8 +846,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } $this->_update_stat_cache($dir, array()); @@ -899,8 +900,7 @@ class SFTP extends SSH2 } break 2; default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); } } @@ -1259,6 +1259,7 @@ class SFTP extends SSH2 * * @param String $filename * @param Integer $type + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Mixed * @access private */ @@ -1279,8 +1280,7 @@ class SFTP extends SSH2 return false; } - user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); } /** @@ -1306,6 +1306,7 @@ class SFTP extends SSH2 * @param String $filename * @param optional Integer $time * @param optional Integer $atime + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Boolean * @access public */ @@ -1342,8 +1343,7 @@ class SFTP extends SSH2 $this->_logError($response); break; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } return $this->_setstat($filename, $attr, false); @@ -1396,6 +1396,7 @@ class SFTP extends SSH2 * @param Integer $mode * @param String $filename * @param optional Boolean $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Mixed * @access public */ @@ -1433,8 +1434,7 @@ class SFTP extends SSH2 return false; } - user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); } /** @@ -1443,6 +1443,7 @@ class SFTP extends SSH2 * @param String $filename * @param String $attr * @param Boolean $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Boolean * @access private */ @@ -1481,8 +1482,7 @@ class SFTP extends SSH2 */ $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } extract(unpack('Nstatus', $this->_string_shift($response, 4))); @@ -1570,6 +1570,7 @@ class SFTP extends SSH2 * Return the target of a symbolic link * * @param String $link + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Mixed * @access public */ @@ -1593,8 +1594,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); } extract(unpack('Ncount', $this->_string_shift($response, 4))); @@ -1614,6 +1614,7 @@ class SFTP extends SSH2 * * @param String $target * @param String $link + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Boolean * @access public */ @@ -1633,8 +1634,7 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } extract(unpack('Nstatus', $this->_string_shift($response, 4))); @@ -1686,6 +1686,7 @@ class SFTP extends SSH2 * * @param String $dir * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _mkdir_helper($dir, $attr) @@ -1696,8 +1697,7 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } extract(unpack('Nstatus', $this->_string_shift($response, 4))); @@ -1713,6 +1713,7 @@ class SFTP extends SSH2 * Removes a directory. * * @param String $dir + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Boolean * @access public */ @@ -1733,8 +1734,7 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } extract(unpack('Nstatus', $this->_string_shift($response, 4))); @@ -1794,6 +1794,9 @@ class SFTP extends SSH2 * @param optional Integer $start * @param optional Integer $local_start * @param optional callable|null $progressCallback + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid + * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist * @return Boolean * @access public * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode(). @@ -1841,8 +1844,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 @@ -1850,7 +1852,7 @@ class SFTP extends SSH2 switch (true) { case $mode & self::SOURCE_CALLBACK: if (!is_callable($data)) { - user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); + throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); } $dataCallback = $data; // do nothing @@ -1861,8 +1863,7 @@ class SFTP extends SSH2 break; case $mode & self::SOURCE_LOCAL_FILE: if (!is_file($data)) { - user_error("$data is not a valid file"); - return false; + throw new FileNotFoundException("$data is not a valid file"); } $fp = @fopen($data, 'rb'); if (!$fp) { @@ -1950,6 +1951,7 @@ class SFTP extends SSH2 * * @param Integer $i * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _read_put_responses($i) @@ -1957,8 +1959,7 @@ class SFTP extends SSH2 while ($i--) { $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } extract(unpack('Nstatus', $this->_string_shift($response, 4))); @@ -1976,6 +1977,7 @@ class SFTP extends SSH2 * * @param String $handle * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _close_handle($handle) @@ -1988,8 +1990,7 @@ class SFTP extends SSH2 // -- 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; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } extract(unpack('Nstatus', $this->_string_shift($response, 4))); @@ -2014,6 +2015,7 @@ class SFTP extends SSH2 * @param optional String $local_file * @param optional Integer $offset * @param optional Integer $length + * @throws \UnexpectedValueException on receipt of unexpected packets * @return Mixed * @access public */ @@ -2042,8 +2044,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } if (is_resource($local_file)) { @@ -2091,11 +2092,10 @@ class SFTP extends SSH2 $this->_logError($response); break 2; default: - user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); if ($fclose_check) { fclose($fp); } - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); } if ($length > 0 && $length <= $offset - $start) { @@ -2129,6 +2129,7 @@ class SFTP extends SSH2 * @param String $path * @param Boolean $recursive * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets * @access public */ function delete($path, $recursive = true) @@ -2149,8 +2150,7 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED @@ -2482,6 +2482,7 @@ class SFTP extends SSH2 * @param String $oldname * @param String $newname * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets * @access public */ function rename($oldname, $newname) @@ -2504,8 +2505,7 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 210af478..0daf81dc 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -60,6 +60,7 @@ use phpseclib\Crypt\TripleDES; use phpseclib\Crypt\Twofish; use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. use phpseclib\System\SSH\Agent; +use phpseclib\Exception\NoSupportedAlgorithmsException; /** * Pure-PHP implementation of SSHv2. @@ -989,6 +990,8 @@ class SSH2 * Connect to an SSHv2 server * * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _connect() @@ -1008,8 +1011,7 @@ class SSH2 $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout); if (!$this->fsock) { $host = $this->host . ':' . $this->port; - user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); - return false; + throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); } $elapsed = microtime(true) - $start; @@ -1060,8 +1062,7 @@ class SSH2 } if (feof($this->fsock)) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } $this->identifier = $this->_generate_identifier(); @@ -1077,21 +1078,18 @@ class SSH2 } if ($matches[1] != '1.99' && $matches[1] != '2.0') { - user_error("Cannot connect to SSH $matches[1] servers"); - return false; + throw new \RuntimeException("Cannot connect to SSH $matches[1] servers"); } fputs($this->fsock, $this->identifier . "\r\n"); $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { - user_error('Expected SSH_MSG_KEXINIT'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } if (!$this->_key_exchange($response)) { @@ -1143,6 +1141,9 @@ class SSH2 * Key Exchange * * @param String $kexinit_payload_server + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible * @access private */ function _key_exchange($kexinit_payload_server) @@ -1354,27 +1355,28 @@ class SSH2 // here ends the second place. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange + // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the // diffie-hellman key exchange as fast as possible $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client); $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); if ($decryptKeyLength === null) { - user_error('No compatible server to client encryption algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); } $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server); $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); if ($encryptKeyLength === null) { - user_error('No compatible client to server encryption algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); } // through diffie-hellman key exchange a symmetric key is obtained $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); if ($kex_algorithm === false) { - user_error('No compatible key exchange algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); } // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. @@ -1491,20 +1493,17 @@ class SSH2 $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes); if (!$this->_send_binary_packet($data)) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != $serverKexReplyMessage) { - user_error('Expected SSH_MSG_KEXDH_REPLY'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_KEXDH_REPLY'); } $temp = unpack('Nlength', $this->_string_shift($response, 4)); @@ -1564,13 +1563,13 @@ class SSH2 $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); if ($server_host_key_algorithm === false) { - user_error('No compatible server host key algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); } if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { - user_error('Server Host Key Algorithm Mismatch'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Server Host Key Algorithm Mismatch'); } $packet = pack( @@ -1585,15 +1584,13 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_NEWKEYS) { - user_error('Expected SSH_MSG_NEWKEYS'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); } $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); @@ -1662,8 +1659,8 @@ class SSH2 $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server); if ($mac_algorithm === false) { - user_error('No compatible client to server message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); } $createKeyLength = 0; // ie. $mac_algorithm == 'none' @@ -1691,8 +1688,8 @@ class SSH2 $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client); if ($mac_algorithm === false) { - user_error('No compatible server to client message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); } $checkKeyLength = 0; @@ -1738,15 +1735,15 @@ class SSH2 $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client); if ($compression_algorithm === false) { - user_error('No compatible server to client compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); } $this->decompress = $compression_algorithm == 'zlib'; $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server); if ($compression_algorithm === false) { - user_error('No compatible client to server compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); } $this->compress = $compression_algorithm == 'zlib'; @@ -1891,6 +1888,8 @@ class SSH2 * @param String $username * @param optional String $password * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages. @@ -1915,15 +1914,13 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { - user_error('Expected SSH_MSG_SERVICE_ACCEPT'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); } $this->bitmap |= self::MASK_LOGIN_REQ; } @@ -1964,8 +1961,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); @@ -2019,8 +2015,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); @@ -2096,6 +2091,7 @@ class SSH2 * * @param String $responses... * @return Boolean + * @throws \RuntimeException on connection error * @access private */ function _keyboard_interactive_process() @@ -2107,8 +2103,7 @@ class SSH2 } else { $orig = $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } } @@ -2232,6 +2227,7 @@ class SSH2 * @param String $username * @param \phpseclib\Crypt\RSA $password * @return Boolean + * @throws \RuntimeException on connection error * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages. @@ -2277,8 +2273,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); @@ -2312,8 +2307,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); @@ -2363,6 +2357,7 @@ class SSH2 * @param String $command * @param optional Callback $callback * @return String + * @throws \RuntimeException on connection error * @access public */ function exec($command, $callback = null) @@ -2430,8 +2425,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } list(, $type) = unpack('C', $this->_string_shift($response, 1)); @@ -2441,8 +2435,8 @@ class SSH2 break; case NET_SSH2_MSG_CHANNEL_FAILURE: default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to request pseudo-terminal'); } $this->in_request_pty_exec = true; } @@ -2510,6 +2504,8 @@ class SSH2 * @see \phpseclib\Net\SSH2::read() * @see \phpseclib\Net\SSH2::write() * @return Boolean + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _initShell() @@ -2566,8 +2562,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } list(, $type) = unpack('C', $this->_string_shift($response, 1)); @@ -2578,8 +2573,8 @@ class SSH2 case NET_SSH2_MSG_CHANNEL_FAILURE: break; default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \UnexpectedValueException('Unable to request pseudo-terminal'); } $packet = pack( @@ -2656,6 +2651,7 @@ class SSH2 * @param String $expect * @param Integer $mode * @return String + * @throws \RuntimeException on connection error * @access public */ function read($expect = '', $mode = self::READ_SIMPLE) @@ -2664,13 +2660,11 @@ class SSH2 $this->is_timeout = false; if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $channel = $this->_get_interactive_channel(); @@ -2701,18 +2695,17 @@ class SSH2 * @see \phpseclib\Net\SSH2::read() * @param String $cmd * @return Boolean + * @throws \RuntimeException on connection error * @access public */ function write($cmd) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); @@ -2869,14 +2862,14 @@ class SSH2 * * @see \phpseclib\Net\SSH2::_send_binary_packet() * @return String + * @throws \RuntimeException on connection errors * @access private */ function _get_binary_packet() { if (!is_resource($this->fsock) || feof($this->fsock)) { - user_error('Connection closed prematurely'); $this->bitmap = 0; - return false; + throw new \RuntimeException('Connection closed prematurely'); } $start = microtime(true); @@ -2890,8 +2883,7 @@ class SSH2 $raw = $this->decrypt->decrypt($raw); } if ($raw === false) { - user_error('Unable to decrypt content'); - return false; + throw new \RuntimeException('Unable to decrypt content'); } extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); @@ -2902,17 +2894,15 @@ class SSH2 // "implementations SHOULD check that the packet length is reasonable" // PuTTY uses 0x9000 as the actual max packet size and so to shall we if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { - user_error('Invalid size'); - return false; + throw new \RuntimeException('Invalid size'); } $buffer = ''; while ($remaining_length > 0) { $temp = fread($this->fsock, $remaining_length); if ($temp === false || feof($this->fsock)) { - user_error('Error reading from socket'); $this->bitmap = 0; - return false; + throw new \RuntimeException('Error reading from socket'); } $buffer.= $temp; $remaining_length-= strlen($temp); @@ -2928,12 +2918,10 @@ class SSH2 if ($this->hmac_check !== false) { $hmac = fread($this->fsock, $this->hmac_size); if ($hmac === false || strlen($hmac) != $this->hmac_size) { - user_error('Error reading socket'); $this->bitmap = 0; - return false; + throw new \RuntimeException('Error reading socket'); } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { - user_error('Invalid HMAC'); - return false; + throw new \RuntimeException('Invalid HMAC'); } } @@ -3161,6 +3149,7 @@ class SSH2 * * @param $client_channel * @return Mixed + * @throws \RuntimeException on connection error * @access private */ function _get_channel_packet($client_channel, $skip_extended = false) @@ -3193,8 +3182,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } if ($client_channel == -1 && $response === true) { return true; @@ -3243,8 +3231,8 @@ class SSH2 return $result; //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: default: - user_error('Unable to open channel'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to open channel'); } break; case NET_SSH2_MSG_CHANNEL_REQUEST: @@ -3254,8 +3242,8 @@ class SSH2 case NET_SSH2_MSG_CHANNEL_FAILURE: return false; default: - user_error('Unable to fulfill channel request'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to fulfill channel request'); } case NET_SSH2_MSG_CHANNEL_CLOSE: return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); @@ -3364,8 +3352,8 @@ class SSH2 case NET_SSH2_MSG_CHANNEL_EOF: break; default: - user_error('Error reading channel data'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Error reading channel data'); } } } @@ -3384,9 +3372,8 @@ class SSH2 function _send_binary_packet($data, $logged = null) { if (!is_resource($this->fsock) || feof($this->fsock)) { - user_error('Connection closed prematurely'); $this->bitmap = 0; - return false; + throw new \RuntimeException('Connection closed prematurely'); } //if ($this->compress) { @@ -3945,6 +3932,8 @@ class SSH2 * is recommended. Returns false if the server signature is not signed correctly with the public host key. * * @return Mixed + * @throws \RuntimeException on badly formatted keys + * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format * @access public */ function getServerPublicHostKey() @@ -3990,8 +3979,8 @@ class SSH2 padding, unsigned, and in network byte order). */ $temp = unpack('Nlength', $this->_string_shift($signature, 4)); if ($temp['length'] != 40) { - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); } $r = new BigInteger($this->_string_shift($signature, 20), 256); @@ -4002,8 +3991,8 @@ class SSH2 case $r->compare($q) >= 0: case $s->equals($zero): case $s->compare($q) >= 0: - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); } $w = $s->modInverse($q); @@ -4022,7 +4011,7 @@ class SSH2 list(, $v) = $v->divide($q); if (!$v->equals($r)) { - user_error('Bad server signature'); + //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } @@ -4044,7 +4033,7 @@ class SSH2 $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); if (!$rsa->verify($this->exchange_hash, $signature)) { - user_error('Bad server signature'); + //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } */ @@ -4059,8 +4048,8 @@ class SSH2 // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); } $s = $s->modPow($e, $n); @@ -4070,13 +4059,13 @@ class SSH2 $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { - user_error('Bad server signature'); + //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; default: - user_error('Unsupported signature format'); - return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + throw new NoSupportedAlgorithmsException('Unsupported signature format'); } return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); diff --git a/phpseclib/System/SSH/Agent.php b/phpseclib/System/SSH/Agent.php index 4cb9dec0..5ec47169 100644 --- a/phpseclib/System/SSH/Agent.php +++ b/phpseclib/System/SSH/Agent.php @@ -35,6 +35,7 @@ namespace phpseclib\System\SSH; use phpseclib\Crypt\RSA; use phpseclib\System\SSH\Agent\Identity; +use phpseclib\Exception\BadConfigurationException; /** * Pure-PHP ssh-agent client identity factory @@ -115,6 +116,8 @@ class Agent * Default Constructor * * @return \phpseclib\System\SSH\Agent + * @throws \phpseclib\Exception\BadConfigurationException if SSH_AUTH_SOCK cannot be found + * @throws \RuntimeException on connection errors * @access public */ function __construct() @@ -127,13 +130,12 @@ class Agent $address = $_ENV['SSH_AUTH_SOCK']; break; default: - user_error('SSH_AUTH_SOCK not found'); - return false; + throw new \BadConfigurationException('SSH_AUTH_SOCK not found'); } $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); if (!$this->fsock) { - user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); + throw new \RuntimeException("Unable to connect to ssh-agent (Error $errno: $errstr)"); } } @@ -144,6 +146,7 @@ class Agent * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects * * @return Array + * @throws \RuntimeException on receipt of unexpected packets * @access public */ function requestIdentities() @@ -154,13 +157,13 @@ class Agent $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { - user_error('Connection closed while requesting identities'); + throw new \RuntimeException('Connection closed while requesting identities'); } $length = current(unpack('N', fread($this->fsock, 4))); $type = ord(fread($this->fsock, 1)); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { - user_error('Unable to request identities'); + throw new \RuntimeException('Unable to request identities'); } $identities = array(); @@ -271,6 +274,7 @@ class Agent * * @param String $data * @return data from SSH Agent + * @throws \RuntimeException on connection errors * @access private */ function _forward_data($data) @@ -289,7 +293,7 @@ class Agent } if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { - user_error('Connection closed attempting to forward data to SSH agent'); + throw new \RuntimeException('Connection closed attempting to forward data to SSH agent'); } $this->socket_buffer = ''; diff --git a/phpseclib/System/SSH/Agent/Identity.php b/phpseclib/System/SSH/Agent/Identity.php index 490edf6e..82fd224c 100644 --- a/phpseclib/System/SSH/Agent/Identity.php +++ b/phpseclib/System/SSH/Agent/Identity.php @@ -134,6 +134,7 @@ class Identity * * @param String $message * @return String + * @throws \RuntimeException on connection errors * @access public */ function sign($message) @@ -142,13 +143,13 @@ class Identity $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); $packet = pack('Na*', strlen($packet), $packet); if (strlen($packet) != fputs($this->fsock, $packet)) { - user_error('Connection closed during signing'); + throw new \RuntimeException('Connection closed during signing'); } $length = current(unpack('N', fread($this->fsock, 4))); $type = ord(fread($this->fsock, 1)); if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { - user_error('Unable to retreive signature'); + throw new \RuntimeException('Unable to retreive signature'); } $signature_blob = fread($this->fsock, $length - 1);