diff --git a/core/lib/Drupal/Core/Archiver/ArchiveTar.php b/core/lib/Drupal/Core/Archiver/ArchiveTar.php index 716511edee29c85dfe38595c5489a45ee7fbab14..abee870c0bf8c2acf8b26deeddd6572c4c5af25c 100644 --- a/core/lib/Drupal/Core/Archiver/ArchiveTar.php +++ b/core/lib/Drupal/Core/Archiver/ArchiveTar.php @@ -42,7 +42,7 @@ /** * Note on Drupal 8 porting. - * This file origin is Tar.php, release 1.4.0 (stable) with some code + * This file origin is Tar.php, release 1.4.5 (stable) with some code * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net. * To simplify future porting from pear of this file, you should not * do cosmetic or other non significant changes to this file. @@ -151,6 +151,13 @@ class ArchiveTar */ public $error_object = null; + /** + * Format for data extraction + * + * @var string + */ + public $_fmt =''; + /** * Archive_Tar Class constructor. This flavour of the constructor only * declare a new Archive_Tar object, identifying it by the name of the @@ -257,6 +264,18 @@ public function __construct($p_tarname, $p_compress = null) return false; } } + + if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) { + $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" . + "a8checksum/a1typeflag/a100link/a6magic/a2version/" . + "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; + } else { + $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" . + "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . + "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; + } + + } public function __destruct() @@ -712,7 +731,7 @@ public function setAttribute() } // ----- Get the arguments - $v_att_list = & func_get_args(); + $v_att_list = func_get_args(); // ----- Read the attributes $i = 0; @@ -1392,10 +1411,20 @@ public function _writeHeader($p_filename, $p_stored_filename) if ($p_stored_filename == '') { $p_stored_filename = $p_filename; } - $v_reduce_filename = $this->_pathReduction($p_stored_filename); + $v_reduced_filename = $this->_pathReduction($p_stored_filename); - if (strlen($v_reduce_filename) > 99) { - if (!$this->_writeLongHeader($v_reduce_filename)) { + if (strlen($v_reduced_filename) > 99) { + if (!$this->_writeLongHeader($v_reduced_filename, false)) { + return false; + } + } + + $v_linkname = ''; + if (@is_link($p_filename)) { + $v_linkname = readlink($p_filename); + } + if (strlen($v_linkname) > 99) { + if (!$this->_writeLongHeader($v_linkname, true)) { return false; } } @@ -1404,14 +1433,10 @@ public function _writeHeader($p_filename, $p_stored_filename) $v_uid = sprintf("%07s", DecOct($v_info[4])); $v_gid = sprintf("%07s", DecOct($v_info[5])); $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); - $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); - $v_linkname = ''; - if (@is_link($p_filename)) { $v_typeflag = '2'; - $v_linkname = readlink($p_filename); $v_size = sprintf("%011s", DecOct(0)); } elseif (@is_dir($p_filename)) { $v_typeflag = "5"; @@ -1423,7 +1448,6 @@ public function _writeHeader($p_filename, $p_stored_filename) } $v_magic = 'ustar '; - $v_version = ' '; if (function_exists('posix_getpwuid')) { @@ -1438,14 +1462,12 @@ public function _writeHeader($p_filename, $p_stored_filename) } $v_devmajor = ''; - $v_devminor = ''; $v_prefix = ''; $v_binary_data_first = pack( "a100a8a8a8a12a12", - $v_reduce_filename, $v_perms, $v_uid, $v_gid, @@ -1485,7 +1507,7 @@ public function _writeHeader($p_filename, $p_stored_filename) $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); + $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); @@ -1517,7 +1539,7 @@ public function _writeHeaderBlock( $p_filename = $this->_pathReduction($p_filename); if (strlen($p_filename) > 99) { - if (!$this->_writeLongHeader($p_filename)) { + if (!$this->_writeLongHeader($p_filename, false)) { return false; } } @@ -1613,36 +1635,31 @@ public function _writeHeaderBlock( * @param string $p_filename * @return bool */ - public function _writeLongHeader($p_filename) + public function _writeLongHeader($p_filename, $is_link = false) { - $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); - - $v_typeflag = 'L'; - + $v_uid = sprintf("%07s", 0); + $v_gid = sprintf("%07s", 0); + $v_perms = sprintf("%07s", 0); + $v_size = sprintf("%'011s", DecOct(strlen($p_filename))); + $v_mtime = sprintf("%011s", 0); + $v_typeflag = ($is_link ? 'K' : 'L'); $v_linkname = ''; - - $v_magic = ''; - - $v_version = ''; - + $v_magic = 'ustar '; + $v_version = ' '; $v_uname = ''; - $v_gname = ''; - $v_devmajor = ''; - $v_devminor = ''; - $v_prefix = ''; $v_binary_data_first = pack( "a100a8a8a8a12a12", '././@LongLink', - 0, - 0, - 0, + $v_perms, + $v_uid, + $v_gid, $v_size, - 0 + $v_mtime ); $v_binary_data_last = pack( "a1a100a6a2a32a32a8a8a155a12", @@ -1677,7 +1694,7 @@ public function _writeLongHeader($p_filename) $this->_writeBlock($v_binary_data_first, 148); // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); + $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); $this->_writeBlock($v_binary_data, 8); @@ -1718,28 +1735,12 @@ public function _readHeader($v_binary_data, &$v_header) // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header - for ($i = 0; $i < 148; $i++) { - $v_checksum += ord(substr($v_binary_data, $i, 1)); - } - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i = 148; $i < 156; $i++) { - $v_checksum += ord(' '); - } - // ..... Last part of the header - for ($i = 156; $i < 512; $i++) { - $v_checksum += ord(substr($v_binary_data, $i, 1)); - } + $v_binary_split = str_split($v_binary_data); + $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148))); + $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',))); + $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512))); - if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) { - $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" . - "a8checksum/a1typeflag/a100link/a6magic/a2version/" . - "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; - } else { - $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" . - "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . - "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; - } - $v_data = unpack($fmt, $v_binary_data); + $v_data = unpack($this->_fmt, $v_binary_data); if (strlen($v_data["prefix"]) > 0) { $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; @@ -1775,7 +1776,7 @@ public function _readHeader($v_binary_data, &$v_header) $v_header['mode'] = OctDec(trim($v_data['mode'])); $v_header['uid'] = OctDec(trim($v_data['uid'])); $v_header['gid'] = OctDec(trim($v_data['gid'])); - $v_header['size'] = OctDec(trim($v_data['size'])); + $v_header['size'] = $this->_tarRecToSize($v_data['size']); $v_header['mtime'] = OctDec(trim($v_data['mtime'])); if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { $v_header['size'] = 0; @@ -1794,6 +1795,41 @@ public function _readHeader($v_binary_data, &$v_header) return true; } + /** + * Convert Tar record size to actual size + * + * @param string $tar_size + * @return size of tar record in bytes + */ + private function _tarRecToSize($tar_size) + { + /* + * First byte of size has a special meaning if bit 7 is set. + * + * Bit 7 indicates base-256 encoding if set. + * Bit 6 is the sign bit. + * Bits 5:0 are most significant value bits. + */ + $ch = ord($tar_size[0]); + if ($ch & 0x80) { + // Full 12-bytes record is required. + $rec_str = $tar_size . "\x00"; + + $size = ($ch & 0x40) ? -1 : 0; + $size = ($size << 6) | ($ch & 0x3f); + + for ($num_ch = 1; $num_ch < 12; ++$num_ch) { + $size = ($size * 256) + ord($rec_str[$num_ch]); + } + + return $size; + + } else { + return OctDec(trim($tar_size)); + } + } + + /** * Detect and report a malicious file name * @@ -1803,10 +1839,13 @@ public function _readHeader($v_binary_data, &$v_header) */ private function _maliciousFilename($file) { - if (strpos($file, '/../') !== false) { + if (strpos($file, 'phar://') === 0) { return true; } - if (strpos($file, '../') === 0) { + if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) { + return true; + } + if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) { return true; } return false; @@ -1871,11 +1910,20 @@ private function _extractInString($p_filename) continue; } - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) { - return null; - } + switch ($v_header['typeflag']) { + case 'L': { + if (!$this->_readLongHeader($v_header)) { + return null; + } + } break; + + case 'K': { + $v_link_header = $v_header; + if (!$this->_readLongHeader($v_link_header)) { + return null; + } + $v_header['link'] = $v_link_header['filename']; + } break; } if ($v_header['filename'] == $p_filename) { @@ -1976,11 +2024,20 @@ public function _extractList( continue; } - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) { - return false; - } + switch ($v_header['typeflag']) { + case 'L': { + if (!$this->_readLongHeader($v_header)) { + return null; + } + } break; + + case 'K': { + $v_link_header = $v_header; + if (!$this->_readLongHeader($v_link_header)) { + return null; + } + $v_header['link'] = $v_link_header['filename']; + } break; } // ignore extended / pax headers