Skip to content

Commit c4996d2

Browse files
committed
SA-CORE-2019-001 by Ayesh, alexpott, larowlan, xjm, michieltcs, farisv
1 parent b66c933 commit c4996d2

File tree

1 file changed

+124
-67
lines changed

1 file changed

+124
-67
lines changed

lib/Drupal/Core/Archiver/ArchiveTar.php

Lines changed: 124 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
/**
4444
* Note on Drupal 8 porting.
45-
* This file origin is Tar.php, release 1.4.0 (stable) with some code
45+
* This file origin is Tar.php, release 1.4.5 (stable) with some code
4646
* from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
4747
* To simplify future porting from pear of this file, you should not
4848
* do cosmetic or other non significant changes to this file.
@@ -151,6 +151,13 @@ class ArchiveTar
151151
*/
152152
public $error_object = null;
153153

154+
/**
155+
* Format for data extraction
156+
*
157+
* @var string
158+
*/
159+
public $_fmt ='';
160+
154161
/**
155162
* Archive_Tar Class constructor. This flavour of the constructor only
156163
* 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)
257264
return false;
258265
}
259266
}
267+
268+
if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
269+
$this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
270+
"a8checksum/a1typeflag/a100link/a6magic/a2version/" .
271+
"a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
272+
} else {
273+
$this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
274+
"Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
275+
"Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
276+
}
277+
278+
260279
}
261280

262281
public function __destruct()
@@ -712,7 +731,7 @@ public function setAttribute()
712731
}
713732

714733
// ----- Get the arguments
715-
$v_att_list = & func_get_args();
734+
$v_att_list = func_get_args();
716735

717736
// ----- Read the attributes
718737
$i = 0;
@@ -1392,10 +1411,20 @@ public function _writeHeader($p_filename, $p_stored_filename)
13921411
if ($p_stored_filename == '') {
13931412
$p_stored_filename = $p_filename;
13941413
}
1395-
$v_reduce_filename = $this->_pathReduction($p_stored_filename);
1414+
$v_reduced_filename = $this->_pathReduction($p_stored_filename);
13961415

1397-
if (strlen($v_reduce_filename) > 99) {
1398-
if (!$this->_writeLongHeader($v_reduce_filename)) {
1416+
if (strlen($v_reduced_filename) > 99) {
1417+
if (!$this->_writeLongHeader($v_reduced_filename, false)) {
1418+
return false;
1419+
}
1420+
}
1421+
1422+
$v_linkname = '';
1423+
if (@is_link($p_filename)) {
1424+
$v_linkname = readlink($p_filename);
1425+
}
1426+
if (strlen($v_linkname) > 99) {
1427+
if (!$this->_writeLongHeader($v_linkname, true)) {
13991428
return false;
14001429
}
14011430
}
@@ -1404,14 +1433,10 @@ public function _writeHeader($p_filename, $p_stored_filename)
14041433
$v_uid = sprintf("%07s", DecOct($v_info[4]));
14051434
$v_gid = sprintf("%07s", DecOct($v_info[5]));
14061435
$v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
1407-
14081436
$v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
14091437

1410-
$v_linkname = '';
1411-
14121438
if (@is_link($p_filename)) {
14131439
$v_typeflag = '2';
1414-
$v_linkname = readlink($p_filename);
14151440
$v_size = sprintf("%011s", DecOct(0));
14161441
} elseif (@is_dir($p_filename)) {
14171442
$v_typeflag = "5";
@@ -1423,7 +1448,6 @@ public function _writeHeader($p_filename, $p_stored_filename)
14231448
}
14241449

14251450
$v_magic = 'ustar ';
1426-
14271451
$v_version = ' ';
14281452

14291453
if (function_exists('posix_getpwuid')) {
@@ -1438,14 +1462,12 @@ public function _writeHeader($p_filename, $p_stored_filename)
14381462
}
14391463

14401464
$v_devmajor = '';
1441-
14421465
$v_devminor = '';
14431466

14441467
$v_prefix = '';
14451468

14461469
$v_binary_data_first = pack(
14471470
"a100a8a8a8a12a12",
1448-
$v_reduce_filename,
14491471
$v_perms,
14501472
$v_uid,
14511473
$v_gid,
@@ -1485,7 +1507,7 @@ public function _writeHeader($p_filename, $p_stored_filename)
14851507
$this->_writeBlock($v_binary_data_first, 148);
14861508

14871509
// ----- Write the calculated checksum
1488-
$v_checksum = sprintf("%06s ", DecOct($v_checksum));
1510+
$v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
14891511
$v_binary_data = pack("a8", $v_checksum);
14901512
$this->_writeBlock($v_binary_data, 8);
14911513

@@ -1517,7 +1539,7 @@ public function _writeHeaderBlock(
15171539
$p_filename = $this->_pathReduction($p_filename);
15181540

15191541
if (strlen($p_filename) > 99) {
1520-
if (!$this->_writeLongHeader($p_filename)) {
1542+
if (!$this->_writeLongHeader($p_filename, false)) {
15211543
return false;
15221544
}
15231545
}
@@ -1613,36 +1635,31 @@ public function _writeHeaderBlock(
16131635
* @param string $p_filename
16141636
* @return bool
16151637
*/
1616-
public function _writeLongHeader($p_filename)
1638+
public function _writeLongHeader($p_filename, $is_link = false)
16171639
{
1618-
$v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
1619-
1620-
$v_typeflag = 'L';
1621-
1640+
$v_uid = sprintf("%07s", 0);
1641+
$v_gid = sprintf("%07s", 0);
1642+
$v_perms = sprintf("%07s", 0);
1643+
$v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
1644+
$v_mtime = sprintf("%011s", 0);
1645+
$v_typeflag = ($is_link ? 'K' : 'L');
16221646
$v_linkname = '';
1623-
1624-
$v_magic = '';
1625-
1626-
$v_version = '';
1627-
1647+
$v_magic = 'ustar ';
1648+
$v_version = ' ';
16281649
$v_uname = '';
1629-
16301650
$v_gname = '';
1631-
16321651
$v_devmajor = '';
1633-
16341652
$v_devminor = '';
1635-
16361653
$v_prefix = '';
16371654

16381655
$v_binary_data_first = pack(
16391656
"a100a8a8a8a12a12",
16401657
'././@LongLink',
1641-
0,
1642-
0,
1643-
0,
1658+
$v_perms,
1659+
$v_uid,
1660+
$v_gid,
16441661
$v_size,
1645-
0
1662+
$v_mtime
16461663
);
16471664
$v_binary_data_last = pack(
16481665
"a1a100a6a2a32a32a8a8a155a12",
@@ -1677,7 +1694,7 @@ public function _writeLongHeader($p_filename)
16771694
$this->_writeBlock($v_binary_data_first, 148);
16781695

16791696
// ----- Write the calculated checksum
1680-
$v_checksum = sprintf("%06s ", DecOct($v_checksum));
1697+
$v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
16811698
$v_binary_data = pack("a8", $v_checksum);
16821699
$this->_writeBlock($v_binary_data, 8);
16831700

@@ -1718,28 +1735,12 @@ public function _readHeader($v_binary_data, &$v_header)
17181735
// ----- Calculate the checksum
17191736
$v_checksum = 0;
17201737
// ..... First part of the header
1721-
for ($i = 0; $i < 148; $i++) {
1722-
$v_checksum += ord(substr($v_binary_data, $i, 1));
1723-
}
1724-
// ..... Ignore the checksum value and replace it by ' ' (space)
1725-
for ($i = 148; $i < 156; $i++) {
1726-
$v_checksum += ord(' ');
1727-
}
1728-
// ..... Last part of the header
1729-
for ($i = 156; $i < 512; $i++) {
1730-
$v_checksum += ord(substr($v_binary_data, $i, 1));
1731-
}
1738+
$v_binary_split = str_split($v_binary_data);
1739+
$v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
1740+
$v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
1741+
$v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
17321742

1733-
if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
1734-
$fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
1735-
"a8checksum/a1typeflag/a100link/a6magic/a2version/" .
1736-
"a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
1737-
} else {
1738-
$fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
1739-
"Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
1740-
"Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
1741-
}
1742-
$v_data = unpack($fmt, $v_binary_data);
1743+
$v_data = unpack($this->_fmt, $v_binary_data);
17431744

17441745
if (strlen($v_data["prefix"]) > 0) {
17451746
$v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
@@ -1775,7 +1776,7 @@ public function _readHeader($v_binary_data, &$v_header)
17751776
$v_header['mode'] = OctDec(trim($v_data['mode']));
17761777
$v_header['uid'] = OctDec(trim($v_data['uid']));
17771778
$v_header['gid'] = OctDec(trim($v_data['gid']));
1778-
$v_header['size'] = OctDec(trim($v_data['size']));
1779+
$v_header['size'] = $this->_tarRecToSize($v_data['size']);
17791780
$v_header['mtime'] = OctDec(trim($v_data['mtime']));
17801781
if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
17811782
$v_header['size'] = 0;
@@ -1794,6 +1795,41 @@ public function _readHeader($v_binary_data, &$v_header)
17941795
return true;
17951796
}
17961797

1798+
/**
1799+
* Convert Tar record size to actual size
1800+
*
1801+
* @param string $tar_size
1802+
* @return size of tar record in bytes
1803+
*/
1804+
private function _tarRecToSize($tar_size)
1805+
{
1806+
/*
1807+
* First byte of size has a special meaning if bit 7 is set.
1808+
*
1809+
* Bit 7 indicates base-256 encoding if set.
1810+
* Bit 6 is the sign bit.
1811+
* Bits 5:0 are most significant value bits.
1812+
*/
1813+
$ch = ord($tar_size[0]);
1814+
if ($ch & 0x80) {
1815+
// Full 12-bytes record is required.
1816+
$rec_str = $tar_size . "\x00";
1817+
1818+
$size = ($ch & 0x40) ? -1 : 0;
1819+
$size = ($size << 6) | ($ch & 0x3f);
1820+
1821+
for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
1822+
$size = ($size * 256) + ord($rec_str[$num_ch]);
1823+
}
1824+
1825+
return $size;
1826+
1827+
} else {
1828+
return OctDec(trim($tar_size));
1829+
}
1830+
}
1831+
1832+
17971833
/**
17981834
* Detect and report a malicious file name
17991835
*
@@ -1803,10 +1839,13 @@ public function _readHeader($v_binary_data, &$v_header)
18031839
*/
18041840
private function _maliciousFilename($file)
18051841
{
1806-
if (strpos($file, '/../') !== false) {
1842+
if (strpos($file, 'phar://') === 0) {
18071843
return true;
18081844
}
1809-
if (strpos($file, '../') === 0) {
1845+
if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
1846+
return true;
1847+
}
1848+
if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
18101849
return true;
18111850
}
18121851
return false;
@@ -1871,11 +1910,20 @@ private function _extractInString($p_filename)
18711910
continue;
18721911
}
18731912

1874-
// ----- Look for long filename
1875-
if ($v_header['typeflag'] == 'L') {
1876-
if (!$this->_readLongHeader($v_header)) {
1877-
return null;
1878-
}
1913+
switch ($v_header['typeflag']) {
1914+
case 'L': {
1915+
if (!$this->_readLongHeader($v_header)) {
1916+
return null;
1917+
}
1918+
} break;
1919+
1920+
case 'K': {
1921+
$v_link_header = $v_header;
1922+
if (!$this->_readLongHeader($v_link_header)) {
1923+
return null;
1924+
}
1925+
$v_header['link'] = $v_link_header['filename'];
1926+
} break;
18791927
}
18801928

18811929
if ($v_header['filename'] == $p_filename) {
@@ -1976,11 +2024,20 @@ public function _extractList(
19762024
continue;
19772025
}
19782026

1979-
// ----- Look for long filename
1980-
if ($v_header['typeflag'] == 'L') {
1981-
if (!$this->_readLongHeader($v_header)) {
1982-
return false;
1983-
}
2027+
switch ($v_header['typeflag']) {
2028+
case 'L': {
2029+
if (!$this->_readLongHeader($v_header)) {
2030+
return null;
2031+
}
2032+
} break;
2033+
2034+
case 'K': {
2035+
$v_link_header = $v_header;
2036+
if (!$this->_readLongHeader($v_link_header)) {
2037+
return null;
2038+
}
2039+
$v_header['link'] = $v_link_header['filename'];
2040+
} break;
19842041
}
19852042

19862043
// ignore extended / pax headers

0 commit comments

Comments
 (0)