From 75689e47e66de30b11ca26d8018c678c05a028a0 Mon Sep 17 00:00:00 2001 From: Lee Rowlands Date: Wed, 18 Dec 2019 19:07:45 +1000 Subject: [PATCH] SA-CORE-2019-012 by samuel.mortenson, larowlan, pwolanin, Sam152, Jasu_M, David_Rothstein, michieltcs, Ayesh, alexpott, xjm, vijaycs85, mcdruid --- modules/system/system.archiver.inc | 4 +- modules/system/system.tar.inc | 153 ++++++++++++++++++----------- 2 files changed, 98 insertions(+), 59 deletions(-) diff --git a/modules/system/system.archiver.inc b/modules/system/system.archiver.inc index c37f07daa10..cc4eddaf732 100644 --- a/modules/system/system.archiver.inc +++ b/modules/system/system.archiver.inc @@ -38,10 +38,10 @@ class ArchiverTar implements ArchiverInterface { public function extract($path, Array $files = array()) { if ($files) { - $this->tar->extractList($files, $path); + $this->tar->extractList($files, $path, '', FALSE, FALSE); } else { - $this->tar->extract($path); + $this->tar->extract($path, FALSE, FALSE); } return $this; diff --git a/modules/system/system.tar.inc b/modules/system/system.tar.inc index 008d6da59b3..6e3ae4238f2 100644 --- a/modules/system/system.tar.inc +++ b/modules/system/system.tar.inc @@ -40,35 +40,23 @@ */ /** - * Note on Drupal 8 porting. - * This file origin is Tar.php, release 1.4.5 (stable) with some code - * from PEAR.php, release 1.10.5 (stable) both at http://pear.php.net. + * Note on Drupal 7 porting. + * This file origin is Tar.php, release 1.4.9 (stable) with some code + * from PEAR.php, release 1.10.10 (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. * The following changes have been done: - * Added namespace Drupal\Core\Archiver. * Removed require_once 'PEAR.php'. * Added defintion of OS_WINDOWS taken from PEAR.php. - * Renamed class to ArchiveTar. * Removed extends PEAR from class. * Removed call parent:: __construct(). * Changed PEAR::loadExtension($extname) to this->loadExtension($extname). * Added function loadExtension() taken from PEAR.php. * Changed all calls of unlink() to drupal_unlink(). * Changed $this->error_object = &$this->raiseError($p_message) - * to throw new \Exception($p_message). + * to throw new Exception($p_message). */ - /** - * Note on Drupal 7 backporting from Drupal 8. - * File origin is core/lib/Drupal/Core/Archiver/ArchiveTar.php from Drupal 8. - * The following changes have been done: - * Removed namespace Drupal\Core\Archiver. - * Renamed class to Archive_Tar. - * Changed \Exception to Exception. - */ - - // Drupal removal require_once 'PEAR.php'. // Drupal addition OS_WINDOWS as defined in PEAR.php. @@ -153,6 +141,18 @@ class Archive_Tar */ public $error_object = null; + /** + * Format for data extraction + * + * @var string + */ + public $_fmt = ''; + + /** + * @var int Length of the read buffer in bytes + */ + protected $buffer_length; + /** * Archive_Tar Class constructor. This flavour of the constructor only * declare a new Archive_Tar object, identifying it by the name of the @@ -165,10 +165,11 @@ class Archive_Tar * parameter indicates if gzip, bz2 or lzma2 compression * is required. For compatibility reason the * boolean value 'true' means 'gz'. + * @param int $buffer_length Length of the read buffer in bytes * * @return bool */ - public function __construct($p_tarname, $p_compress = null) + public function __construct($p_tarname, $p_compress = null, $buffer_length = 512) { // Drupal removal parent::__construct(). @@ -263,15 +264,16 @@ class Archive_Tar 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"; + "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"; + "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . + "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; } + $this->buffer_length = $buffer_length; } public function __destruct() @@ -371,11 +373,12 @@ class Archive_Tar /** * @param string $p_path * @param bool $p_preserve + * @param bool $p_symlinks * @return bool */ - public function extract($p_path = '', $p_preserve = false) + public function extract($p_path = '', $p_preserve = false, $p_symlinks = true) { - return $this->extractModify($p_path, '', $p_preserve); + return $this->extractModify($p_path, '', $p_preserve, $p_symlinks); } /** @@ -616,11 +619,12 @@ class Archive_Tar * removed if present at the beginning of * the file/dir path. * @param boolean $p_preserve Preserve user/group ownership of files + * @param boolean $p_symlinks Allow symlinks. * * @return boolean true on success, false on error. * @see extractList() */ - public function extractModify($p_path, $p_remove_path, $p_preserve = false) + public function extractModify($p_path, $p_remove_path, $p_preserve = false, $p_symlinks = true) { $v_result = true; $v_list_detail = array(); @@ -632,7 +636,8 @@ class Archive_Tar "complete", 0, $p_remove_path, - $p_preserve + $p_preserve, + $p_symlinks ); $this->_close(); } @@ -676,11 +681,12 @@ class Archive_Tar * removed if present at the beginning of * the file/dir path. * @param boolean $p_preserve Preserve user/group ownership of files + * @param boolean $p_symlinks Allow symlinks. * * @return true on success, false on error. * @see extractModify() */ - public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false) + public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true) { $v_result = true; $v_list_detail = array(); @@ -701,7 +707,8 @@ class Archive_Tar "partial", $v_list, $p_remove_path, - $p_preserve + $p_preserve, + $p_symlinks ); $this->_close(); } @@ -1326,8 +1333,15 @@ class Archive_Tar return false; } - while (($v_buffer = fread($v_file, 512)) != '') { - $v_binary_data = pack("a512", "$v_buffer"); + while (($v_buffer = fread($v_file, $this->buffer_length)) != '') { + $buffer_length = strlen("$v_buffer"); + if ($buffer_length != $this->buffer_length) { + $pack_size = ((int)($buffer_length / 512) + 1) * 512; + $pack_format = sprintf('a%d', $pack_size); + } else { + $pack_format = sprintf('a%d', $this->buffer_length); + } + $v_binary_data = pack($pack_format, "$v_buffer"); $this->_writeBlock($v_binary_data); } @@ -1532,7 +1546,8 @@ class Archive_Tar $p_type = '', $p_uid = 0, $p_gid = 0 - ) { + ) + { $p_filename = $this->_pathReduction($p_filename); if (strlen($p_filename) > 99) { @@ -1745,7 +1760,16 @@ class Archive_Tar } // ----- Extract the checksum - $v_header['checksum'] = OctDec(trim($v_data['checksum'])); + $v_data_checksum = trim($v_data['checksum']); + if (!preg_match('/^[0-7]*$/', $v_data_checksum)) { + $this->_error( + 'Invalid checksum for file "' . $v_data['filename'] + . '" : ' . $v_data_checksum . ' extracted' + ); + return false; + } + + $v_header['checksum'] = OctDec($v_data_checksum); if ($v_header['checksum'] != $v_checksum) { $v_header['filename'] = ''; @@ -1839,10 +1863,7 @@ class Archive_Tar if (strpos($file, 'phar://') === 0) { return true; } - if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) { - return true; - } - if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) { + if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) { return true; } return false; @@ -1908,19 +1929,23 @@ class Archive_Tar } switch ($v_header['typeflag']) { - case 'L': { - if (!$this->_readLongHeader($v_header)) { - return null; + case 'L': + { + if (!$this->_readLongHeader($v_header)) { + return null; + } } - } break; + break; - case 'K': { - $v_link_header = $v_header; - if (!$this->_readLongHeader($v_link_header)) { - return null; + case 'K': + { + $v_link_header = $v_header; + if (!$this->_readLongHeader($v_link_header)) { + return null; + } + $v_header['link'] = $v_link_header['filename']; } - $v_header['link'] = $v_link_header['filename']; - } break; + break; } if ($v_header['filename'] == $p_filename) { @@ -1960,6 +1985,7 @@ class Archive_Tar * @param string $p_file_list * @param string $p_remove_path * @param bool $p_preserve + * @param bool $p_symlinks * @return bool */ public function _extractList( @@ -1968,8 +1994,10 @@ class Archive_Tar $p_mode, $p_file_list, $p_remove_path, - $p_preserve = false - ) { + $p_preserve = false, + $p_symlinks = true + ) + { $v_result = true; $v_nb = 0; $v_extract_all = true; @@ -2022,19 +2050,23 @@ class Archive_Tar } switch ($v_header['typeflag']) { - case 'L': { - if (!$this->_readLongHeader($v_header)) { - return null; + case 'L': + { + if (!$this->_readLongHeader($v_header)) { + return null; + } } - } break; + break; - case 'K': { - $v_link_header = $v_header; - if (!$this->_readLongHeader($v_link_header)) { - return null; + case 'K': + { + $v_link_header = $v_header; + if (!$this->_readLongHeader($v_link_header)) { + return null; + } + $v_header['link'] = $v_link_header['filename']; } - $v_header['link'] = $v_link_header['filename']; - } break; + break; } // ignore extended / pax headers @@ -2146,6 +2178,13 @@ class Archive_Tar } } } elseif ($v_header['typeflag'] == "2") { + if (!$p_symlinks) { + $this->_warning('Symbolic links are not allowed. ' + . 'Unable to extract {' + . $v_header['filename'] . '}' + ); + return false; + } if (@file_exists($v_header['filename'])) { @drupal_unlink($v_header['filename']); }