Merge remote-tracking branch 'upstream/master'

This commit is contained in:
terrafrost 2012-11-11 04:22:34 -06:00
commit f76ab5946c
5 changed files with 1677 additions and 388 deletions

View File

@ -856,6 +856,7 @@ class Crypt_RSA {
);
if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) {
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
$rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
@ -943,7 +944,7 @@ class Crypt_RSA {
$iv = pack('H*', trim($matches[2]));
$symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
$ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key);
$ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-| #s', '', $key);
$ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
if ($ciphertext === false) {
$ciphertext = $key;
@ -981,7 +982,7 @@ class Crypt_RSA {
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
} else {
$decoded = preg_replace('#-.+-|[\r\n]#', '', $key);
$decoded = preg_replace('#-.+-|[\r\n]| #', '', $key);
$decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
}
@ -1045,10 +1046,10 @@ class Crypt_RSA {
$length = $this->_decodeLength($key);
$temp = $this->_string_shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) {
$components['modulus'] = new Math_BigInteger($temp, -256);
$components['modulus'] = new Math_BigInteger($temp, 256);
$this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
$length = $this->_decodeLength($key);
$components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
return $components;
}
@ -1056,28 +1057,28 @@ class Crypt_RSA {
return false;
}
$length = $this->_decodeLength($key);
$components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
$components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
$components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256));
$components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));
if (!empty($key)) {
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
@ -1091,13 +1092,13 @@ class Crypt_RSA {
$this->_decodeLength($key);
$key = substr($key, 1);
$length = $this->_decodeLength($key);
$components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
}
}
@ -1228,6 +1229,19 @@ class Crypt_RSA {
}
}
/**
* Returns the key size
*
* More specifically, this returns the size of the modulo in bits.
*
* @access public
* @return Integer
*/
function getSize()
{
return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
}
/**
* Start Element Handler
*
@ -1875,7 +1889,7 @@ class Crypt_RSA {
$result = 0;
for ($i = 0; $i < strlen($x); $i++) {
$result |= $x[$i] ^ $y[$i];
$result |= ord($x[$i]) ^ ord($y[$i]);
}
return $result == 0;
@ -2623,4 +2637,4 @@ class Crypt_RSA {
return $this->_rsassa_pss_verify($message, $signature);
}
}
}
}

View File

@ -167,11 +167,11 @@ class File_ASN1 {
/**
* Default date format
*
* @var Array
* @var String
* @access private
* @link http://php.net/class.datetime
*/
var $format = 'M j H:i:s Y T';
var $format = 'D, d M y H:i:s O';
/**
* Default date format
@ -195,6 +195,60 @@ class File_ASN1 {
*/
var $filters;
/**
* Type mapping table for the ANY type.
*
* Structured or unknown types are mapped to a FILE_ASN1_Element.
* Unambiguous types get the direct mapping (int/real/bool).
* Others are mapped as a choice, with an extra indexing level.
*
* @var Array
* @access public
*/
var $ANYmap = array(
FILE_ASN1_TYPE_BOOLEAN => true,
FILE_ASN1_TYPE_INTEGER => true,
FILE_ASN1_TYPE_BIT_STRING => 'bitString',
FILE_ASN1_TYPE_OCTET_STRING => 'octetString',
FILE_ASN1_TYPE_NULL => 'null',
FILE_ASN1_TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
FILE_ASN1_TYPE_REAL => true,
FILE_ASN1_TYPE_ENUMERATED => 'enumerated',
FILE_ASN1_TYPE_UTF8_STRING => 'utf8String',
FILE_ASN1_TYPE_NUMERIC_STRING => 'numericString',
FILE_ASN1_TYPE_PRINTABLE_STRING => 'printableString',
FILE_ASN1_TYPE_TELETEX_STRING => 'teletexString',
FILE_ASN1_TYPE_VIDEOTEX_STRING => 'videotexString',
FILE_ASN1_TYPE_IA5_STRING => 'ia5String',
FILE_ASN1_TYPE_UTC_TIME => 'utcTime',
FILE_ASN1_TYPE_GENERALIZED_TIME => 'generalTime',
FILE_ASN1_TYPE_GRAPHIC_STRING => 'graphicString',
FILE_ASN1_TYPE_VISIBLE_STRING => 'visibleString',
FILE_ASN1_TYPE_GENERAL_STRING => 'generalString',
FILE_ASN1_TYPE_UNIVERSAL_STRING => 'universalString',
//FILE_ASN1_TYPE_CHARACTER_STRING => 'characterString',
FILE_ASN1_TYPE_BMP_STRING => 'bmpString'
);
/**
* String type to character size mapping table.
*
* Non-convertable types are absent from this table.
* size == 0 indicates variable length encoding.
*
* @var Array
* @access public
*/
var $stringTypeSize = array(
FILE_ASN1_TYPE_UTF8_STRING => 0,
FILE_ASN1_TYPE_BMP_STRING => 2,
FILE_ASN1_TYPE_UNIVERSAL_STRING => 4,
FILE_ASN1_TYPE_PRINTABLE_STRING => 1,
FILE_ASN1_TYPE_TELETEX_STRING => 1,
FILE_ASN1_TYPE_IA5_STRING => 1,
FILE_ASN1_TYPE_VISIBLE_STRING => 1,
);
/**
* Parse BER-encoding
*
@ -206,6 +260,11 @@ class File_ASN1 {
*/
function decodeBER($encoded)
{
if (is_object($encoded) && strtolower(get_class($encoded)) == 'file_asn1_element') {
$encoded = $encoded->element;
}
$this->encoded = $encoded;
return $this->_decode_ber($encoded);
}
@ -225,10 +284,6 @@ class File_ASN1 {
{
$decoded = array();
if ($start == 0) {
$this->encoded = $encoded;
}
while ( strlen($encoded) ) {
$current = array('start' => $start);
@ -309,10 +364,9 @@ class File_ASN1 {
$current['content'] = (bool) ord($content[0]);
break;
case FILE_ASN1_TYPE_INTEGER:
case FILE_ASN1_TYPE_ENUMERATED:
$current['content'] = new Math_BigInteger($content, -256);
break;
case FILE_ASN1_TYPE_ENUMERATED: // not currently supported
return false;
case FILE_ASN1_TYPE_REAL: // not currently supported
return false;
case FILE_ASN1_TYPE_BIT_STRING:
@ -433,18 +487,28 @@ class File_ASN1 {
*
* Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
*
* @param String $encoded
* @param Integer $start
* @param Array $decoded
* @param Array $mapping
* @return Array
* @access public
*/
function asn1map($decoded, $mapping, $idx = NULL)
function asn1map($decoded, $mapping)
{
if (isset($mapping['explicit'])) {
$decoded = $decoded['content'][0];
}
switch (true) {
case $mapping['type'] == FILE_ASN1_TYPE_ANY:
$intype = $decoded['type'];
if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) {
return new File_ASN1_Element(substr($this->encoded, $decoded['start'], $decoded['length']));
}
$inmap = $this->ANYmap[$intype];
if (is_string($inmap)) {
return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping));
}
break;
case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
foreach ($mapping['children'] as $key => $option) {
switch (true) {
@ -460,7 +524,6 @@ class File_ASN1 {
case isset($mapping['implicit']):
case isset($mapping['explicit']):
case $decoded['type'] == $mapping['type']:
case $mapping['type'] == FILE_ASN1_TYPE_ANY:
break;
default:
return NULL;
@ -470,14 +533,6 @@ class File_ASN1 {
$decoded['type'] = $mapping['type'];
}
if ($mapping['type'] == FILE_ASN1_TYPE_ANY) {
if ($decoded['type'] == FILE_ASN1_TYPE_SEQUENCE || $decoded['type'] == FILE_ASN1_TYPE_SET) {
// return $this->encode_der($decoded['content']);
//return serialize($decoded['content']);
return substr($this->encoded, $decoded['start'], $decoded['length']);
}
}
switch ($decoded['type']) {
case FILE_ASN1_TYPE_SEQUENCE:
$map = array();
@ -507,13 +562,18 @@ class File_ASN1 {
continue;
}
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
$constant = NULL;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$childClass = isset($child['class']) ? $child['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = isset($temp['class']) ? $child['cast'] : $child['constant'];
} else {
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
$constant = NULL;
}
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
}
elseif (isset($child['constant'])) {
$childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($child['optional'])) {
@ -527,14 +587,9 @@ class File_ASN1 {
$temp = $decoded['content'][$i];
}
} elseif (!isset($child['constant'])) {
if ($child['type'] == FILE_ASN1_TYPE_CHOICE) {
$map[$key] = $this->asn1map($temp, $child);
$i++;
continue;
}
// we could do this, as well:
// $buffer = $this->asn1map($temp, $child); if (isset($buffer)) { $map[$key] = $buffer; }
if ($child['type'] == $temp['type']) {
if ($child['type'] == $temp['type'] || $child['type'] == FILE_ASN1_TYPE_ANY) {
$map[$key] = $this->asn1map($temp, $child);
$i++;
if (count($decoded['content']) == $i) {
@ -591,14 +646,20 @@ class File_ASN1 {
continue;
}
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
$constant = NULL;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$childClass = isset($child['class']) ? $child['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = isset($temp['class']) ? $child['cast'] : $child['constant'];
} else {
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
$constant = NULL;
}
if (isset($child['class'])) {
$childClass = $child['class'];
$constant = $child['cast'];
}
elseif (isset($child['constant'])) {
$childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($constant) && isset($temp['constant'])) {
if (($constant == $temp['constant']) && ($childClass == $tempClass)) {
$map[$key] = $this->asn1map($temp['content'], $child);
@ -658,6 +719,8 @@ class File_ASN1 {
}
case FILE_ASN1_TYPE_OCTET_STRING:
return base64_encode($decoded['content']);
case FILE_ASN1_TYPE_NULL:
return '';
case FILE_ASN1_TYPE_BOOLEAN:
return $decoded['content'];
case FILE_ASN1_TYPE_NUMERIC_STRING:
@ -673,6 +736,7 @@ class File_ASN1 {
case FILE_ASN1_TYPE_BMP_STRING:
return $decoded['content'];
case FILE_ASN1_TYPE_INTEGER:
case FILE_ASN1_TYPE_ENUMERATED:
$temp = $decoded['content'];
if (isset($mapping['implicit'])) {
$temp = new Math_BigInteger($decoded['content'], -256);
@ -720,6 +784,11 @@ class File_ASN1 {
return $source->element;
}
// do not encode (implicitly optional) fields with value set to default
if (isset($mapping['default']) && $source === $mapping['default']) {
return '';
}
if (isset($idx)) {
$this->location[] = $idx;
}
@ -727,6 +796,7 @@ class File_ASN1 {
$tag = $mapping['type'];
switch ($tag) {
case FILE_ASN1_TYPE_SET: // Children order is not important, thus process in sequence.
case FILE_ASN1_TYPE_SEQUENCE:
$tag|= 0x20; // set the constructed bit
$value = '';
@ -753,13 +823,19 @@ class File_ASN1 {
continue;
}
$temp = $this->_encode_der($source[$key], $child, $key);
if ($temp === false) {
return false;
}
// An empty child encoding means it has been optimized out.
// Else we should have at least one tag byte.
if ($temp === '') {
continue;
}
// if isset($child['constant']) is true then isset($child['optional']) should be true as well
if (isset($child['constant'])) {
$temp = $this->_encode_der($source[$key], $child, $key);
if ($temp === false) {
return false;
}
/*
From X.680-0207.pdf#page=58 (30.6):
@ -776,65 +852,10 @@ class File_ASN1 {
$subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
} else {
$temp = $this->_encode_der($source[$key], $child, $key);
if ($temp === false) {
return false;
}
}
$value.= $temp;
}
break;
// the main diff between sets and sequences is the encapsulation of the foreach in another for loop
case FILE_ASN1_TYPE_SET:
$tag|= 0x20;
$value = '';
// ignore the min and max
if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($source as $content) {
$temp = $this->_encode_der($content, $child);
if ($temp === false) {
return false;
}
$value.= $temp;
}
break;
}
for ($i = 0; $i < count($source[$key]); $i++) {
foreach ($mapping['children'] as $key => $child) {
if (!isset($source[$key])) {
if (!isset($child['optional'])) {
return false;
}
continue;
}
// if isset($child['constant']) is true then isset($child['optional']) should be true as well
if (isset($child['constant'])) {
$temp = $this->_encode_der($source[$key][$i], $child, $key);
if ($temp === false) {
return false;
}
if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) {
$subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
$temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
} else {
$temp = $this->_encode_der($source[$key][$i], $child, $key);
if ($temp === false) {
return false;
}
}
$value.= $temp;
}
}
break;
case FILE_ASN1_TYPE_CHOICE:
$temp = false;
@ -844,13 +865,20 @@ class File_ASN1 {
}
$temp = $this->_encode_der($source[$key], $child, $key);
if ($temp === false) {
return false;
}
// An empty child encoding means it has been optimized out.
// Else we should have at least one tag byte.
if ($temp === '') {
continue;
}
$tag = ord($temp[0]);
// if isset($child['constant']) is true then isset($child['optional']) should be true as well
if (isset($child['constant'])) {
if ($temp === false) {
return false;
}
if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) {
$subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
$temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
@ -858,10 +886,6 @@ class File_ASN1 {
$subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
} else {
if ($temp === false) {
return false;
}
}
}
@ -869,12 +893,13 @@ class File_ASN1 {
array_pop($this->location);
}
if (isset($mapping['cast'])) {
if ($temp && isset($mapping['cast'])) {
$temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
}
return $temp;
case FILE_ASN1_TYPE_INTEGER:
case FILE_ASN1_TYPE_ENUMERATED:
if (!isset($mapping['mapping'])) {
$value = $source->toBytes(true);
} else {
@ -951,14 +976,31 @@ class File_ASN1 {
}
break;
case FILE_ASN1_TYPE_ANY:
if (!isset($source)) {
if (isset($idx)) {
array_pop($this->location);
}
return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL));
$loc = $this->location;
if (isset($idx)) {
array_pop($this->location);
}
switch (true) {
case !isset($source):
return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping);
case is_int($source):
case is_object($source) && strtolower(get_class($source)) == 'math_biginteger':
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping);
case is_float($source):
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping);
case is_bool($source):
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping);
case is_array($source) && count($source) == 1:
$typename = implode('', array_keys($source));
$outtype = array_search($typename, $this->ANYmap, true);
if ($outtype !== false) {
return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping);
}
}
$filters = $this->filters;
foreach ($this->location as $part) {
foreach ($loc as $part) {
if (!isset($filters[$part])) {
$filters = false;
break;
@ -966,13 +1008,10 @@ class File_ASN1 {
$filters = $filters[$part];
}
if ($filters === false) {
user_error('No filters defined for ' . implode('/', $this->location), E_USER_NOTICE);
user_error('No filters defined for ' . implode('/', $loc), E_USER_NOTICE);
return false;
}
if (isset($idx)) {
array_pop($this->location);
}
return $this->_encode_der($source, $filters);
return $this->_encode_der($source, $filters + $mapping);
case FILE_ASN1_TYPE_NULL:
$value = '';
break;
@ -984,6 +1023,9 @@ class File_ASN1 {
case FILE_ASN1_TYPE_BMP_STRING:
case FILE_ASN1_TYPE_IA5_STRING:
case FILE_ASN1_TYPE_VISIBLE_STRING:
case FILE_ASN1_TYPE_VIDEOTEX_STRING:
case FILE_ASN1_TYPE_GRAPHIC_STRING:
case FILE_ASN1_TYPE_GENERAL_STRING:
$value = $source;
break;
case FILE_ASN1_TYPE_BOOLEAN:
@ -1129,4 +1171,104 @@ class File_ASN1 {
$string = substr($string, $index);
return $substr;
}
/**
* String type conversion
*
* This is a lazy conversion, dealing only with character size.
* No real conversion table is used.
*
* @param String $in
* @param optional Integer $from
* @param optional Integer $to
* @return String
* @access public
*/
function convert($in, $from = FILE_ASN1_TYPE_UTF8_STRING, $to = FILE_ASN1_TYPE_UTF8_STRING)
{
if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
return false;
}
$insize = $this->stringTypeSize[$from];
$outsize = $this->stringTypeSize[$to];
$inlength = strlen($in);
$out = '';
for ($i = 0; $i < $inlength;) {
if ($inlength - $i < $insize) {
return false;
}
// Get an input character as a 32-bit value.
$c = ord($in[$i++]);
switch (true) {
case $insize == 4:
$c = ($c << 8) | ord($in[$i++]);
$c = ($c << 8) | ord($in[$i++]);
case $insize == 2:
$c = ($c << 8) | ord($in[$i++]);
case $insize == 1:
break;
case ($c & 0x80) == 0x00:
break;
case ($c & 0x40) == 0x00:
return false;
default:
$bit = 6;
do {
if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
return false;
}
$c = ($c << 6) | (ord($in[$i++]) & 0x3F);
$bit += 5;
$mask = 1 << $bit;
} while ($c & $bit);
$c &= $mask - 1;
break;
}
// Convert and append the character to output string.
$v = '';
switch (true) {
case $outsize == 4:
$v .= chr($c & 0xFF);
$c >>= 8;
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 2:
$v .= chr($c & 0xFF);
$c >>= 8;
case $outsize == 1:
$v .= chr($c & 0xFF);
$c >>= 8;
if ($c) {
return false;
}
break;
case ($c & 0x80000000) != 0:
return false;
case $c >= 0x04000000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x04000000;
case $c >= 0x00200000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00200000;
case $c >= 0x00010000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00010000;
case $c >= 0x00000800:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00000800;
case $c >= 0x00000080:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x000000C0;
default:
$v .= chr($c);
break;
}
$out .= strrev($v);
}
return $out;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
*
* PHP versions 4 and 5
*
* Currently only supports SFTPv3, which, according to wikipedia.org, "is the most widely used version,
* Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
* implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
* to an SFTPv4/5/6 server.
*
@ -452,8 +452,12 @@ class Net_SFTP extends Net_SSH2 {
in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
channel and reopen it with a new and updated SSH_FXP_INIT packet.
*/
if ($this->version != 3) {
return false;
switch ($this->version) {
case 2:
case 3:
break;
default:
return false;
}
$this->pwd = $this->_realpath('.', false);
@ -474,16 +478,36 @@ class Net_SFTP extends Net_SSH2 {
return $this->pwd;
}
/**
* Logs errors
*
* @param String $response
* @param optional Integer $status
* @access public
*/
function _logError($response, $status = -1) {
if ($status == -1) {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
}
$error = $this->status_codes[$status];
if ($this->version > 2) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
} else {
$this->sftp_errors[] = $error;
}
}
/**
* Canonicalize the Server-Side Path Name
*
* SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns
* the absolute (canonicalized) path. If $mode is set to NET_SFTP_CONFIRM_DIR (as opposed to NET_SFTP_CONFIRM_NONE,
* which is what it is set to by default), false is returned if $dir is not a valid directory.
* the absolute (canonicalized) path.
*
* @see Net_SFTP::chdir()
* @param String $dir
* @param optional Integer $mode
* @return Mixed
* @access private
*/
@ -518,7 +542,11 @@ class Net_SFTP extends Net_SSH2 {
$dir = $dir[0] == '/' ? '/' . rtrim(substr($dir, 1), '/') : rtrim($dir, '/');
if ($dir == '.' || $dir == $this->pwd) {
return $this->pwd . $file;
$temp = $this->pwd;
if (!empty($file)) {
$temp.= '/' . $file;
}
return $temp;
}
if ($dir[0] != '/') {
@ -557,8 +585,7 @@ class Net_SFTP extends Net_SSH2 {
$this->fileType = $this->_parseLongname($this->_string_shift($response, $length));
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
@ -567,7 +594,11 @@ class Net_SFTP extends Net_SSH2 {
// if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to
// be a bonafide directory
return $realpath . '/' . $file;
if (!empty($file)) {
$realpath.= '/' . $file;
}
return $realpath;
}
/**
@ -611,8 +642,7 @@ class Net_SFTP extends Net_SSH2 {
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -631,8 +661,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}
@ -699,8 +728,7 @@ class Net_SFTP extends Net_SSH2 {
break;
case NET_SFTP_STATUS:
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -747,8 +775,7 @@ class Net_SFTP extends Net_SSH2 {
case NET_SFTP_STATUS:
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}
break 2;
@ -772,8 +799,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}
@ -966,8 +992,7 @@ class Net_SFTP extends Net_SSH2 {
}
return $attributes;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
}
@ -1070,8 +1095,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
}
// rather than return what the permissions *should* be, we'll return what they actually are. this will also
@ -1088,8 +1112,7 @@ class Net_SFTP extends Net_SSH2 {
$attrs = $this->_parseAttributes($response);
return $attrs['permissions'];
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
}
@ -1119,7 +1142,11 @@ class Net_SFTP extends Net_SSH2 {
return $this->chmod($mode, $path);
}
// presumably $entries will never be empty because it'll always have . and ..
// normally $entries would have at least . and .. but it might not if the directories
// permissions didn't allow reading
if (empty($entries)) {
return false;
}
foreach ($entries as $filename=>$props) {
if ($filename == '.' || $filename == '..') {
@ -1182,11 +1209,38 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
$dir = $this->_realpath(rtrim($dir, '/'));
if ($dir === false) {
return false;
if ($dir[0] != '/') {
$dir = $this->_realpath(rtrim($dir, '/'));
if ($dir === false) {
return false;
}
if (!$this->_mkdir_helper($dir)) {
return false;
}
} else {
$dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir));
$temp = '';
foreach ($dirs as $dir) {
$temp.= '/' . $dir;
$result = $this->_mkdir_helper($temp);
}
if (!$result) {
return false;
}
}
return true;
}
/**
* Helper function for directory creation
*
* @param String $dir
* @return Boolean
* @access private
*/
function _mkdir_helper($dir)
{
// by not providing any permissions, hopefully the server will use the logged in users umask - their
// default permissions.
if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) {
@ -1201,8 +1255,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}
@ -1242,8 +1295,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}
@ -1310,8 +1362,7 @@ class Net_SFTP extends Net_SSH2 {
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -1380,8 +1431,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}
@ -1409,8 +1459,7 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
break;
}
}
@ -1452,8 +1501,7 @@ class Net_SFTP extends Net_SSH2 {
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
return false;
default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
@ -1491,8 +1539,7 @@ class Net_SFTP extends Net_SSH2 {
}
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
break 2;
default:
user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS', E_USER_NOTICE);
@ -1517,8 +1564,7 @@ class Net_SFTP extends Net_SSH2 {
return false;
}
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response);
// check the status from the NET_SFTP_STATUS case in the above switch after the file has been closed
if ($status != NET_SFTP_STATUS_OK) {
@ -1565,8 +1611,7 @@ class Net_SFTP extends Net_SSH2 {
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
if (!$recursive) {
return false;
}
@ -1597,7 +1642,11 @@ class Net_SFTP extends Net_SSH2 {
$i = 0;
$entries = $this->_list($path, true, false);
// presumably $entries will never be empty because it'll always have . and ..
// normally $entries would have at least . and .. but it might not if the directories
// permissions didn't allow reading
if (empty($entries)) {
return false;
}
foreach ($entries as $filename=>$props) {
if ($filename == '.' || $filename == '..') {
@ -1681,8 +1730,7 @@ class Net_SFTP extends Net_SSH2 {
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
$this->_logError($response, $status);
return false;
}

View File

@ -851,7 +851,7 @@ class Net_SSH2 {
$this->message_number_log[] = '->';
if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
$this->message_log[] = $temp;
$this->message_log[] = $extra . $temp;
$this->message_log[] = $this->identifier . "\r\n";
}
}