Mise à jour des librairies

This commit is contained in:
lars
2019-03-10 23:30:23 +01:00
parent 2040b7be39
commit 7df3d72953
3603 changed files with 233169 additions and 107764 deletions

View File

@@ -24,6 +24,7 @@ AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509
### 2.0
* Long term support (LTS) release
* Modernized version of 1.0
* Minimum PHP version: 5.3.3
* PSR-4 autoloading with namespace rooted at `\phpseclib`
@@ -36,7 +37,7 @@ AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509
* 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.11 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.11.zip/download)
* [Download 1.0.15 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.15.zip/download)
## Support

27
vendor/phpseclib/phpseclib/appveyor.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
build: false
shallow_clone: false
platform:
- x86
- x64
clone_folder: C:\projects\phpseclib
install:
- cinst -y OpenSSL.Light
- SET PATH=C:\Program Files\OpenSSL;%PATH%
- sc config wuauserv start= auto
- net start wuauserv
- cinst -y php --version 5.6.30
- cd c:\tools\php56
- copy php.ini-production php.ini
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- echo extension=php_gmp.dll >> php.ini
- cd C:\projects\phpseclib
- SET PATH=C:\tools\php56;%PATH%
- php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe
- php.exe composer.phar install --prefer-source --no-interaction
test_script:
- cd C:\projects\phpseclib
- vendor\bin\phpunit.bat tests/Windows32Test.php

View File

@@ -649,10 +649,10 @@ abstract class Base
case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
while (strlen($key) < $dkLen) {
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
$f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u);
@@ -707,7 +707,7 @@ abstract class Base
case self::MODE_STREAM:
return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
$result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
@@ -1040,14 +1040,14 @@ abstract class Base
break;
case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) {
$ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
$ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
}
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size);
$ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
$ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
$offset = 2 * $this->block_size;
} else {
$offset = $this->block_size;
@@ -1358,7 +1358,7 @@ abstract class Base
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result;
}
@@ -1369,7 +1369,7 @@ abstract class Base
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor);
$ciphertext.= $block ^ $otp;
@@ -1413,7 +1413,7 @@ abstract class Base
}
if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) {
$encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
}
$encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) {

View File

@@ -112,6 +112,15 @@ class Hash
*/
var $key = false;
/**
* Computed Key
*
* @see self::_computeKey()
* @var string
* @access private
*/
var $computedKey = false;
/**
* Outer XOR (Internal HMAC)
*
@@ -130,6 +139,15 @@ class Hash
*/
var $ipad;
/**
* Engine
*
* @see self::setHash()
* @var string
* @access private
*/
var $engine;
/**
* Default Constructor.
*
@@ -166,6 +184,43 @@ class Hash
function setKey($key = false)
{
$this->key = $key;
$this->_computeKey();
}
/**
* Pre-compute the key used by the HMAC
*
* Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes
* will first hash the key using H and then use the resultant L byte string as the actual key to HMAC."
*
* As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
* when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during
* every call
*
* @access private
*/
function _computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}
if (strlen($this->key) <= $this->b) {
$this->computedKey = $this->key;
return;
}
switch ($this->engine) {
case self::MODE_MHASH:
$this->computedKey = mhash($this->hash, $this->key);
break;
case self::MODE_HASH:
$this->computedKey = hash($this->hash, $this->key, true);
break;
case self::MODE_INTERNAL:
$this->computedKey = call_user_func($this->hash, $this->key);
}
}
/**
@@ -216,19 +271,38 @@ class Hash
}
switch ($hash) {
case 'md2-96':
case 'md2':
$mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
$this->b = 16;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->b = 64;
break;
default:
$this->b = 128;
}
switch ($hash) {
case 'md2':
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$mode = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
$this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$mode = CRYPT_HASH_MODE;
$this->engine = CRYPT_HASH_MODE;
}
switch ($mode) {
switch ($this->engine) {
case self::MODE_MHASH:
switch ($hash) {
case 'md5':
@@ -241,6 +315,7 @@ class Hash
default:
$this->hash = MHASH_SHA1;
}
$this->_computeKey(self::MODE_MHASH);
return;
case self::MODE_HASH:
switch ($hash) {
@@ -257,35 +332,33 @@ class Hash
default:
$this->hash = 'sha1';
}
$this->_computeKey(self::MODE_HASH);
return;
}
switch ($hash) {
case 'md2':
$this->b = 16;
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->b = 64;
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->b = 64;
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->b = 128;
$this->hash = array($this, '_sha512');
break;
case 'sha1':
default:
$this->b = 64;
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
$this->_computeKey(self::MODE_INTERNAL);
}
/**
@@ -297,33 +370,25 @@ class Hash
*/
function hash($text)
{
$mode = is_array($this->hash) ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
if (!empty($this->key) || is_string($this->key)) {
switch ($mode) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text, $this->key);
$output = mhash($this->hash, $text, $this->computedKey);
break;
case self::MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->key, true);
$output = hash_hmac($this->hash, $text, $this->computedKey, true);
break;
case self::MODE_INTERNAL:
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC."
-- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key;
$key = str_pad($key, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
$key = str_pad($this->computedKey, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
}
} else {
switch ($mode) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text);
break;

View File

@@ -1020,9 +1020,9 @@ class RSA
* @access private
* @see self::_convertPublicKey()
* @see self::_convertPrivateKey()
* @param string $key
* @param string|array $key
* @param int $type
* @return array
* @return array|bool
*/
function _parseKey($key, $type)
{
@@ -1333,9 +1333,14 @@ class RSA
xml_set_character_data_handler($xml, '_data_handler');
// add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
xml_parser_free($xml);
unset($xml);
return false;
}
xml_parser_free($xml);
unset($xml);
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
// from PuTTY's SSHPUBK.C
case self::PRIVATE_FORMAT_PUTTY:
@@ -1505,8 +1510,9 @@ class RSA
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
*
* @access public
* @param string $key
* @param int $type optional
* @param string|RSA|array $key
* @param bool|int $type optional
* @return bool
*/
function loadKey($key, $type = false)
{
@@ -2215,12 +2221,13 @@ class RSA
return false;
}
$result = 0;
$result = "\0";
$x^= $y;
for ($i = 0; $i < strlen($x); $i++) {
$result |= ord($x[$i]) ^ ord($y[$i]);
$result|= $x[$i];
}
return $result == 0;
return $result === "\0";
}
/**
@@ -2427,19 +2434,26 @@ class RSA
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
if (!$this->_equals($lHash, $lHash2)) {
user_error('Decryption error');
return false;
$hashesMatch = $this->_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
}
$m = ltrim($m, chr(0));
if (ord($m[0]) != 1) {
// we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch & !$patternMatch) {
user_error('Decryption error');
return false;
}
// Output the message M
return substr($m, 1);
return substr($m, $offset + 1);
}
/**

View File

@@ -308,6 +308,9 @@ class ASN1
$remainingLength = $length;
while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) == "\0\0") {
@@ -359,6 +362,9 @@ class ASN1
$current['content'] = substr($content, $content_pos);
} else {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {
@@ -383,6 +389,9 @@ class ASN1
$length = 0;
while (substr($content, $content_pos, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
// all subtags should be octet strings
//if ($temp['type'] != self::TYPE_OCTET_STRING) {
@@ -415,6 +424,9 @@ class ASN1
break 2;
}
$temp = $this->_decode_ber($content, $start + $offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
$offset+= $temp['length'];

View File

@@ -1928,6 +1928,9 @@ class X509
// "Certificate Transparency"
// https://tools.ietf.org/html/rfc6962
case '1.3.6.1.4.1.11129.2.4.2':
// "Qualified Certificate statements"
// https://tools.ietf.org/html/rfc3739#section-3.2.6
case '1.3.6.1.5.5.7.1.3':
return true;
// CSR attributes
@@ -2092,7 +2095,7 @@ class X509
*
* If $date isn't defined it is assumed to be the current date.
*
* @param int $date optional
* @param \DateTime|string $date optional
* @access public
*/
function validateDate($date = null)
@@ -2102,7 +2105,7 @@ class X509
}
if (!isset($date)) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
$date = new DateTime(null, new DateTimeZone(@date_default_timezone_get()));
}
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
@@ -2111,9 +2114,16 @@ class X509
$notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
if (is_string($date)) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get()));
$notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get()));
switch (true) {
case $date < new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())):
case $date > new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())):
case $date < $notBefore:
case $date > $notAfter:
return false;
}
@@ -2920,7 +2930,7 @@ class X509
}
$output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ?
array_merge((array) $dn[$prop], array($value)) :
array_merge((array) $result[$desc], array($value)) :
$value;
$start = false;
}

View File

@@ -45,7 +45,6 @@
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2006 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib\Math;

View File

@@ -144,6 +144,11 @@ class SCP
return false;
}
if (empty($remote_file)) {
user_error('remote_file cannot be blank', E_USER_NOTICE);
return false;
}
if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to
return false;
}

View File

@@ -109,11 +109,11 @@ class SFTP extends SSH2
* The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
* concurrent actions, so it's somewhat academic, here.
*
* @var int
* @var boolean
* @see self::_send_sftp_packet()
* @access private
*/
var $request_id = false;
var $use_request_id = false;
/**
* The Packet Type
@@ -250,6 +250,15 @@ class SFTP extends SSH2
*/
var $canonicalize_paths = true;
/**
* Request Buffers
*
* @see self::_get_sftp_packet()
* @var array
* @access private
*/
var $requestBuffer = array();
/**
* Default Constructor.
*
@@ -519,7 +528,7 @@ class SFTP extends SSH2
}
*/
$this->request_id = 1;
$this->use_request_id = true;
/*
A Note on SFTPv4/5/6 support:
@@ -864,7 +873,17 @@ class SFTP extends SSH2
unset($files[$key]);
continue;
}
if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) {
$is_directory = false;
if ($key != '.' && $key != '..') {
if ($this->use_stat_cache) {
$is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)));
} else {
$stat = $this->lstat($dir . '/' . $key);
$is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
}
}
if ($is_directory) {
$depth++;
$files[$key] = $this->rawlist($dir . '/' . $key, true);
$depth--;
@@ -2189,7 +2208,7 @@ class SFTP extends SSH2
$packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;
$packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) {
if ($fclose_check) {
fclose($fp);
}
@@ -2204,15 +2223,17 @@ class SFTP extends SSH2
break;
}
$packets_sent = $i - 1;
$clear_responses = false;
while ($i > 0) {
$i--;
if ($clear_responses) {
$this->_get_sftp_packet();
$this->_get_sftp_packet($packets_sent - $i);
continue;
} else {
$response = $this->_get_sftp_packet();
$response = $this->_get_sftp_packet($packets_sent - $i);
}
switch ($this->packet_type) {
@@ -2921,10 +2942,10 @@ class SFTP extends SSH2
* @return bool
* @access private
*/
function _send_sftp_packet($type, $data)
function _send_sftp_packet($type, $data, $request_id = 1)
{
$packet = $this->request_id !== false ?
pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
$packet = $this->use_request_id ?
pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) :
pack('NCa*', strlen($data) + 1, $type, $data);
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
@@ -2962,8 +2983,15 @@ class SFTP extends SSH2
* @return string
* @access private
*/
function _get_sftp_packet()
function _get_sftp_packet($request_id = null)
{
if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
$this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
$temp = $this->requestBuffer[$request_id]['packet'];
unset($this->requestBuffer[$request_id]);
return $temp;
}
$this->curTimeout = false;
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
@@ -3001,8 +3029,8 @@ class SFTP extends SSH2
$this->packet_type = ord($this->_string_shift($this->packet_buffer));
if ($this->request_id !== false) {
$this->_string_shift($this->packet_buffer, 4); // remove the request id
if ($this->use_request_id) {
extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id
$length-= 5; // account for the request id and the packet type
} else {
$length-= 1; // account for the packet type
@@ -3025,6 +3053,14 @@ class SFTP extends SSH2
}
}
if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) {
$this->requestBuffer[$packet_id] = array(
'packet_type' => $this->packet_type,
'packet' => $packet
);
return $this->_get_sftp_packet($request_id);
}
return $packet;
}

View File

@@ -104,6 +104,7 @@ class SSH2
const CHANNEL_SHELL = 2;
const CHANNEL_SUBSYSTEM = 3;
const CHANNEL_AGENT_FORWARD = 4;
const CHANNEL_KEEP_ALIVE = 5;
/**#@-*/
/**#@+
@@ -918,6 +919,22 @@ class SSH2
*/
var $binary_packet_buffer = false;
/**
* Preferred Signature Format
*
* @var string|false
* @access private
*/
var $preferred_signature_format = false;
/**
* Authentication Credentials
*
* @var array
* @access private
*/
var $auth = array();
/**
* Default Constructor.
*
@@ -1124,11 +1141,12 @@ class SSH2
}
$elapsed = microtime(true) - $start;
$this->curTimeout-= $elapsed;
if ($this->curTimeout <= 0) {
$this->is_timeout = true;
return false;
if ($this->curTimeout) {
$this->curTimeout-= $elapsed;
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return false;
}
}
}
@@ -1193,6 +1211,7 @@ class SSH2
}
if (feof($this->fsock)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -1206,7 +1225,7 @@ class SSH2
$this->server_identifier = trim($temp, "\r\n");
if (strlen($extra)) {
$this->errors[] = utf8_decode($data);
$this->errors[] = $data;
}
if (version_compare($matches[3], '1.99', '<')) {
@@ -1221,6 +1240,7 @@ class SSH2
if (!$this->send_kex_first) {
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -1309,6 +1329,8 @@ class SSH2
}
$server_host_key_algorithms = array(
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
'ssh-dss' // REQUIRED sign Raw DSS Key
);
@@ -1459,6 +1481,7 @@ class SSH2
$kexinit_payload_server = $this->_get_binary_packet();
if ($kexinit_payload_server === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -1595,6 +1618,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -1687,12 +1711,14 @@ class SSH2
$data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
if (!$this->_send_binary_packet($data)) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -1782,9 +1808,25 @@ class SSH2
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
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);
switch ($server_host_key_algorithm) {
case 'ssh-dss':
$expected_key_format = 'ssh-dss';
break;
//case 'rsa-sha2-256':
//case 'rsa-sha2-512':
//case 'ssh-rsa':
default:
$expected_key_format = 'ssh-rsa';
}
if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
switch (true) {
case $this->signature_format == $server_host_key_algorithm:
case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
case $this->signature_format != 'ssh-rsa':
user_error('Server Host Key Algorithm Mismatch');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
}
$packet = pack(
@@ -1799,6 +1841,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -1820,7 +1863,7 @@ class SSH2
$this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
if ($this->encrypt) {
if ($this->crypto_engine) {
$this->encrypt->setEngine($this->crypto_engine);
$this->encrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->encrypt->block_size) {
$this->encrypt_block_size = $this->encrypt->block_size;
@@ -1844,7 +1887,7 @@ class SSH2
$this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
if ($this->decrypt) {
if ($this->crypto_engine) {
$this->decrypt->setEngine($this->crypto_engine);
$this->decrypt->setPreferredEngine($this->crypto_engine);
}
if ($this->decrypt->block_size) {
$this->decrypt_block_size = $this->decrypt->block_size;
@@ -2095,6 +2138,7 @@ class SSH2
function login($username)
{
$args = func_get_args();
$this->auth[] = $args;
return call_user_func_array(array(&$this, '_login'), $args);
}
@@ -2166,6 +2210,7 @@ class SSH2
}
return $this->_login_helper($username, $password);
}
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2218,6 +2263,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2276,6 +2322,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2294,7 +2341,7 @@ class SSH2
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
$this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE:
// can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
@@ -2376,6 +2423,7 @@ class SSH2
} else {
$orig = $response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2545,6 +2593,21 @@ class SSH2
$publickey['n']
);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
$signatureType = 'rsa-sha2-512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
$signatureType = 'rsa-sha2-256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$signatureType = 'ssh-rsa';
}
$part1 = pack(
'CNa*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST,
@@ -2555,7 +2618,7 @@ class SSH2
strlen('publickey'),
'publickey'
);
$part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
$part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);
$packet = $part1 . chr(0) . $part2;
if (!$this->_send_binary_packet($packet)) {
@@ -2564,6 +2627,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2595,8 +2659,9 @@ class SSH2
$packet = $part1 . chr(1) . $part2;
$privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
$privatekey->setHash($hash);
$signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
$signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
$signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
$packet.= pack('Na*', strlen($signature), $signature);
if (!$this->_send_binary_packet($packet)) {
@@ -2605,6 +2670,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2731,6 +2797,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -2870,6 +2937,7 @@ class SSH2
$response = $this->_get_binary_packet();
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -3184,6 +3252,66 @@ class SSH2
return (bool) ($this->bitmap & self::MASK_LOGIN);
}
/**
* Pings a server connection, or tries to reconnect if the connection has gone down
*
* Inspired by http://php.net/manual/en/mysqli.ping.php
*
* @return bool
* @access public
*/
function ping()
{
if (!$this->isAuthenticated()) {
return false;
}
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
$packet_size = 0x4000;
$packet = pack(
'CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN,
strlen('session'),
'session',
self::CHANNEL_KEEP_ALIVE,
$this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
$packet_size
);
if (!@$this->_send_binary_packet($packet)) {
return $this->_reconnect();
}
$this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
$response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
if ($response !== false) {
$this->_close_channel(self::CHANNEL_KEEP_ALIVE);
return true;
}
return $this->_reconnect();
}
/**
* In situ reconnect method
*
* @return boolean
* @access private
*/
function _reconnect()
{
$this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
$this->retry_connect = true;
if (!$this->_connect()) {
return false;
}
foreach ($this->auth as $auth) {
$result = call_user_func_array(array(&$this, 'parent::login'), $auth);
}
return $result;
}
/**
* Resets a connection for re-use
*
@@ -3214,8 +3342,8 @@ class SSH2
function _get_binary_packet($skip_channel_filter = false)
{
if (!is_resource($this->fsock) || feof($this->fsock)) {
user_error('Connection closed prematurely');
$this->bitmap = 0;
user_error('Connection closed prematurely');
return false;
}
@@ -3258,8 +3386,8 @@ class SSH2
while ($remaining_length > 0) {
$temp = stream_get_contents($this->fsock, $remaining_length);
if ($temp === false || feof($this->fsock)) {
user_error('Error reading from socket');
$this->bitmap = 0;
user_error('Error reading from socket');
return false;
}
$buffer.= $temp;
@@ -3277,8 +3405,8 @@ class SSH2
if ($this->hmac_check !== false) {
$hmac = stream_get_contents($this->fsock, $this->hmac_size);
if ($hmac === false || strlen($hmac) != $this->hmac_size) {
user_error('Error reading socket');
$this->bitmap = 0;
user_error('Error reading socket');
return false;
} elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
user_error('Invalid HMAC');
@@ -3322,7 +3450,7 @@ class SSH2
return false;
}
extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
$this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
$this->bitmap = 0;
return false;
case NET_SSH2_MSG_IGNORE:
@@ -3334,7 +3462,7 @@ class SSH2
return false;
}
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
$this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
$payload = $this->_get_binary_packet($skip_channel_filter);
break;
case NET_SSH2_MSG_UNIMPLEMENTED:
@@ -3357,7 +3485,7 @@ class SSH2
return false;
}
extract(unpack('Nlength', $this->_string_shift($payload, 4)));
$this->banner_message = utf8_decode($this->_string_shift($payload, $length));
$this->banner_message = $this->_string_shift($payload, $length);
$payload = $this->_get_binary_packet();
}
@@ -3561,7 +3689,12 @@ class SSH2
$response = $this->binary_packet_buffer;
$this->binary_packet_buffer = false;
} else {
if ($this->curTimeout) {
$read = array($this->fsock);
$write = $except = null;
if (!$this->curTimeout) {
@stream_select($read, $write, $except, null);
} else {
if ($this->curTimeout < 0) {
$this->is_timeout = true;
return true;
@@ -3584,6 +3717,7 @@ class SSH2
$response = $this->_get_binary_packet(true);
if ($response === false) {
$this->bitmap = 0;
user_error('Connection closed by server');
return false;
}
@@ -3592,10 +3726,6 @@ class SSH2
if ($client_channel == -1 && $response === true) {
return true;
}
if (!strlen($response)) {
return '';
}
if (!strlen($response)) {
return false;
}
@@ -3818,8 +3948,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;
user_error('Connection closed prematurely');
return false;
}
@@ -4480,6 +4610,8 @@ class SSH2
break;
case 'ssh-rsa':
case 'rsa-sha2-256':
case 'rsa-sha2-512':
if (strlen($server_public_host_key) < 4) {
return false;
}
@@ -4502,8 +4634,21 @@ class SSH2
$signature = $this->_string_shift($signature, $temp['length']);
$rsa = new RSA();
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
}
$rsa->setHash($hash);
$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');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
@@ -4530,7 +4675,30 @@ class SSH2
$s = $s->modPow($e, $n);
$s = $s->toBytes();
$h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
break;
case 'rsa-sha2-256':
$hash = 'sha256';
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
}
$hashObj = new Hash($hash);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash));
break;
case 'rsa-sha2-256':
$h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash));
break;
//case 'ssh-rsa':
default:
$hash = 'sha1';
$h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
}
$h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;
if ($s != $h) {

View File

@@ -43,7 +43,7 @@ use phpseclib\System\SSH\Agent\Identity;
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @access internal
* @access public
*/
class Agent
{
@@ -117,18 +117,20 @@ class Agent
* @return \phpseclib\System\SSH\Agent
* @access public
*/
function __construct()
function __construct($address = null)
{
switch (true) {
case isset($_SERVER['SSH_AUTH_SOCK']):
$address = $_SERVER['SSH_AUTH_SOCK'];
break;
case isset($_ENV['SSH_AUTH_SOCK']):
$address = $_ENV['SSH_AUTH_SOCK'];
break;
default:
user_error('SSH_AUTH_SOCK not found');
return false;
if (!$address) {
switch (true) {
case isset($_SERVER['SSH_AUTH_SOCK']):
$address = $_SERVER['SSH_AUTH_SOCK'];
break;
case isset($_ENV['SSH_AUTH_SOCK']):
$address = $_ENV['SSH_AUTH_SOCK'];
break;
default:
user_error('SSH_AUTH_SOCK not found');
return false;
}
}
$this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
@@ -155,12 +157,14 @@ 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');
return array();
}
$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');
return array();
}
$identities = array();

View File

@@ -32,6 +32,17 @@ use phpseclib\System\SSH\Agent;
*/
class Identity
{
/**@+
* Signature Flags
*
* See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
*
* @access private
*/
const SSH_AGENT_RSA2_256 = 2;
const SSH_AGENT_RSA2_512 = 4;
/**#@-*/
/**
* Key Object
*
@@ -59,6 +70,16 @@ class Identity
*/
var $fsock;
/**
* Signature flags
*
* @var int
* @access private
* @see self::sign()
* @see self::setHash()
*/
var $flags = 0;
/**
* Default Constructor.
*
@@ -126,6 +147,31 @@ class Identity
{
}
/**
* Set Hash
*
* ssh-agent doesn't support using hashes for RSA other than SHA1
*
* @param string $hash
* @access public
*/
function setHash($hash)
{
$this->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
break;
default:
user_error('The only supported hashes for RSA are sha1, sha256 and sha512');
}
}
/**
* Create a signature
*
@@ -138,7 +184,7 @@ class Identity
function sign($message)
{
// the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0);
$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags);
$packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing');
@@ -151,8 +197,34 @@ class Identity
}
$signature_blob = fread($this->fsock, $length - 1);
// the only other signature format defined - ssh-dss - is the same length as ssh-rsa
// the + 12 is for the other various SSH added length fields
return substr($signature_blob, strlen('ssh-rsa') + 12);
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length != strlen($signature_blob)) {
user_error('Malformed signature blob');
}
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length > strlen($signature_blob) + 4) {
user_error('Malformed signature blob');
}
$type = $this->_string_shift($signature_blob, $length);
$this->_string_shift($signature_blob, 4);
return $signature_blob;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}