Forráskód Böngészése

Fix #yogosha19665

Laurent Destailleur 1 éve
szülő
commit
fbf91cc211

+ 2 - 2
htdocs/core/ajax/fileupload.php

@@ -18,7 +18,7 @@
 
 /**
  *       \file       htdocs/core/ajax/fileupload.php
- *       \brief      File to return Ajax response on file upload
+ *       \brief      File to return Ajax response on common file upload. For large files, see flowjs-server.php
  */
 
 if (!defined('NOREQUIREMENU')) {
@@ -36,7 +36,7 @@ if (!defined('NOREQUIRESOC')) {
 
 // Load Dolibarr environment
 require '../../main.inc.php';
-require_once DOL_DOCUMENT_ROOT.'/core/class/fileupload.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/fileupload.class.php';	// Class to upload common files
 require_once DOL_DOCUMENT_ROOT.'/core/class/genericobject.class.php';
 
 $id = GETPOST('fk_element', 'int');

+ 28 - 26
htdocs/core/class/fileupload.class.php

@@ -18,7 +18,7 @@
 
 /**
  *       \file       htdocs/core/class/fileupload.class.php
- *       \brief      File to return Ajax response on file upload
+ *       \brief      File to return Ajax response on common file upload. For large files, see flowjs-server.php
  */
 
 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
@@ -44,7 +44,7 @@ class FileUpload
 	 */
 	public function __construct($options = null, $fk_element = null, $element = null)
 	{
-		global $db, $conf;
+		global $db;
 		global $hookmanager;
 
 		$hookmanager->initHooks(array('fileupload'));
@@ -56,8 +56,8 @@ class FileUpload
 		$this->element = $element;
 
 		$pathname = str_replace('/class', '', $element_prop['classpath']);
-		$filename = $element_prop['classfile'];
-		$dir_output = $element_prop['dir_output'];
+		$filename = dol_sanitizeFileName($element_prop['classfile']);
+		$dir_output = dol_sanitizePathName($element_prop['dir_output']);
 
 		//print 'fileupload.class.php: element='.$element.' pathname='.$pathname.' filename='.$filename.' dir_output='.$dir_output."\n";
 
@@ -131,7 +131,7 @@ class FileUpload
 				*/
 				'thumbnail' => array(
 					'upload_dir' => $dir_output.'/'.$object_ref.'/thumbs/',
-					'upload_url' => DOL_URL_ROOT.'/document.php?modulepart='.urlencode($element).'&attachment=1&file=/'.$object_ref.'/thumbs/',
+					'upload_url' => DOL_URL_ROOT.'/document.php?modulepart='.urlencode($element).'&attachment=1&file='.urlencode('/'.$object_ref.'/thumbs/'),
 					'max_width' => 80,
 					'max_height' => 80
 				)
@@ -211,20 +211,20 @@ class FileUpload
 	 */
 	protected function getFileObject($file_name)
 	{
-		$file_path = $this->options['upload_dir'].$file_name;
-		if (is_file($file_path) && $file_name[0] !== '.') {
+		$file_path = $this->options['upload_dir'].dol_sanitizeFileName($file_name);
+		if (dol_is_file($file_path) && $file_name[0] !== '.') {
 			$file = new stdClass();
 			$file->name = $file_name;
 			$file->mime = dol_mimetype($file_name, '', 2);
 			$file->size = filesize($file_path);
-			$file->url = $this->options['upload_url'].rawurlencode($file->name);
+			$file->url = $this->options['upload_url'].urlencode($file->name);
 			foreach ($this->options['image_versions'] as $version => $options) {
-				if (is_file($options['upload_dir'].$file_name)) {
+				if (dol_is_file($options['upload_dir'].$file_name)) {
 					$tmp = explode('.', $file->name);
 
 					// We save the path of mini file into file->... (seems not used)
 					$keyforfile = $version.'_url';
-					$file->$keyforfile = $options['upload_url'].rawurlencode($tmp[0].'_mini.'.$tmp[1]);
+					$file->$keyforfile = $options['upload_url'].urlencode($tmp[0].'_mini.'.$tmp[1]);
 				}
 			}
 			$this->setFileDeleteUrl($file);
@@ -280,7 +280,7 @@ class FileUpload
 	}
 
 	/**
-	 * Enter description here ...
+	 * Make validation on an uploaded file
 	 *
 	 * @param 	string	$uploaded_file		Uploade file
 	 * @param 	object	$file				File
@@ -303,7 +303,7 @@ class FileUpload
 			return false;
 		}
 		if ($uploaded_file && is_uploaded_file($uploaded_file)) {
-			$file_size = filesize($uploaded_file);
+			$file_size = dol_filesize($uploaded_file);
 		} else {
 			$file_size = $_SERVER['CONTENT_LENGTH'];
 		}
@@ -379,15 +379,14 @@ class FileUpload
 	{
 		// Remove path information and dots around the filename, to prevent uploading
 		// into different directories or replacing hidden system files.
-		// Also remove control characters and spaces (\x00..\x20) around the filename:
-		$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
+		$file_name = basename(dol_sanitizeFileName($name));
 		// Add missing file extension for known image types:
 		$matches = array();
 		if (strpos($file_name, '.') === false && preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
 			$file_name .= '.'.$matches[1];
 		}
 		if ($this->options['discard_aborted_uploads']) {
-			while (is_file($this->options['upload_dir'].$file_name)) {
+			while (dol_is_file($this->options['upload_dir'].$file_name)) {
 				$file_name = $this->upcountName($file_name);
 			}
 		}
@@ -414,12 +413,16 @@ class FileUpload
 		$file->size = intval($size);
 		$file->type = $type;
 
+		// Sanitize to avoid stream execution when calling file_size(). Not that this is a second security because
+		// most streams are already disabled by stream_wrapper_unregister() in filefunc.inc.php
+		$uploaded_file = preg_replace('/\s*(http|ftp)s?:/i', '', $uploaded_file);
+
 		$validate = $this->validate($uploaded_file, $file, $error, $index);
 
 		if ($validate) {
 			if (dol_mkdir($this->options['upload_dir']) >= 0) {
-				$file_path = $this->options['upload_dir'].$file->name;
-				$append_file = !$this->options['discard_aborted_uploads'] && is_file($file_path) && $file->size > filesize($file_path);
+				$file_path = dol_sanitizePathName($this->options['upload_dir']).dol_sanitizeFileName($file->name);
+				$append_file = !$this->options['discard_aborted_uploads'] && dol_is_file($file_path) && $file->size > dol_filesize($file_path);
 
 				clearstatcache();
 
@@ -434,16 +437,16 @@ class FileUpload
 					// Non-multipart uploads (PUT method support)
 					file_put_contents($file_path, fopen('php://input', 'r'), $append_file ? FILE_APPEND : 0);
 				}
-				$file_size = filesize($file_path);
+				$file_size = dol_filesize($file_path);
 				if ($file_size === $file->size) {
-					$file->url = $this->options['upload_url'].rawurlencode($file->name);
+					$file->url = $this->options['upload_url'].urlencode($file->name);
 					foreach ($this->options['image_versions'] as $version => $options) {
 						if ($this->createScaledImage($file->name, $options)) {	// Creation of thumbs mini and small is ok
 							$tmp = explode('.', $file->name);
 
 							// We save the path of mini file into file->... (seems not used)
 							$keyforfile = $version.'_url';
-							$file->$keyforfile = $options['upload_url'].rawurlencode($tmp[0].'_mini.'.$tmp[1]);
+							$file->$keyforfile = $options['upload_url'].urlencode($tmp[0].'_mini.'.$tmp[1]);
 						}
 					}
 				} elseif ($this->options['discard_aborted_uploads']) {
@@ -537,7 +540,7 @@ class FileUpload
 		/* disabled. Param redirect seems not used
 		$redirect = isset($_REQUEST['redirect']) ? stripslashes($_REQUEST['redirect']) : null;
 		if ($redirect) {
-			header('Location: '.sprintf($redirect, rawurlencode($json)));
+			header('Location: '.sprintf($redirect, urlencode($json)));
 			return;
 		}
 		*/
@@ -559,14 +562,13 @@ class FileUpload
 	 */
 	public function delete()
 	{
-		$file_name = isset($_REQUEST['file']) ?
-		basename(stripslashes($_REQUEST['file'])) : null;
-		$file_path = $this->options['upload_dir'].$file_name;
-		$success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
+		$file_name = GETPOST('file') ? basename(GETPOST('file')) : null;
+		$file_path = $this->options['upload_dir'].dol_sanitizeFileName($file_name);
+		$success = dol_is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
 		if ($success) {
 			foreach ($this->options['image_versions'] as $version => $options) {
 				$file = $options['upload_dir'].$file_name;
-				if (is_file($file)) {
+				if (dol_is_file($file)) {
 					unlink($file);
 				}
 			}

+ 1 - 0
htdocs/filefunc.inc.php

@@ -81,6 +81,7 @@ $result = @include_once $conffile; // Keep @ because with some error reporting t
 // Disable some not used PHP stream
 $listofwrappers = stream_get_wrappers();
 // We need '.phar' for geoip2. TODO Replace phar in geoip with exploded files so we can disable phar by default.
+// phar stream does not auto unserialize content (possible code execution) since PHP 8.1
 $arrayofstreamtodisable = array('compress.zlib', 'compress.bzip2', 'ftp', 'ftps', 'glob', 'data', 'expect', 'ogg', 'rar', 'zip', 'zlib');
 if (!empty($dolibarr_main_stream_to_disable) && is_array($dolibarr_main_stream_to_disable)) {
 	$arrayofstreamtodisable = $dolibarr_main_stream_to_disable;

+ 2 - 2
htdocs/ftp/index.php

@@ -111,8 +111,8 @@ if ($action == 'uploadfile') {
 		$nbfile = count($_FILES['userfile']['name']);
 		for ($i = 0; $i < $nbfile; $i++) {
 			$newsection = $newsectioniso;
-			$fileupload = $_FILES['userfile']['name'][$i];
-			$fileuploadpath = $_FILES['userfile']['tmp_name'][$i];
+			$fileupload = dol_sanitizeFileName($_FILES['userfile']['name'][$i]);
+			$fileuploadpath = dol_sanitizePathName($_FILES['userfile']['tmp_name'][$i]);
 			$result = dol_ftp_put($conn_id, $fileupload, $fileuploadpath, $newsection);
 
 			if ($result) {