* Filemanager/WebDAV: generally deny user to delete directories /, /home, /apps, /templates (last 2 incl. subdirectories)

This commit is contained in:
Ralf Becker 2016-08-18 10:03:53 +02:00
parent 73dbaa0dfb
commit f1f8c4e98d
5 changed files with 64 additions and 9 deletions

View File

@ -723,23 +723,42 @@ class Vfs
return $value > (int) substr($argument,1); return $value > (int) substr($argument,1);
} }
/**
* Check if given directory is protected (user not allowed to remove or rename)
*
* Following directorys are protected:
* - /
* - /apps incl. subdirectories
* - /home
* - /templates incl. subdirectories
*
* @param string $dir path or url
* @return boolean true for protected dirs, false otherwise
*/
static function isProtectedDir($dir)
{
if ($dir[0] != '/') $dir = self::parse_url($dir, PHP_URL_PATH);
return preg_match('#^/(apps(/[^/]+)?|home|templates(/[^/]+)?)?/*$#', $dir) > 0;
}
/** /**
* Recursiv remove all given url's, including it's content if they are files * Recursiv remove all given url's, including it's content if they are files
* *
* @param string|array $urls url or array of url's * @param string|array $urls url or array of url's
* @param boolean $allow_urls =false allow to use url's, default no only pathes (to stay within the vfs) * @param boolean $allow_urls =false allow to use url's, default no only pathes (to stay within the vfs)
* @throws Exception\AssertionFailed when trainig to remove /, /apps or /home * @throws Vfs\Exception\ProtectedDirectory if trying to delete a protected directory, see Vfs::isProtected()
* @return array * @return array
*/ */
static function remove($urls,$allow_urls=false) static function remove($urls,$allow_urls=false)
{ {
//error_log(__METHOD__.'('.array2string($urls).')'); //error_log(__METHOD__.'('.array2string($urls).')');
// some precaution to never allow to (recursivly) remove /, /apps or /home
foreach((array)$urls as $url) foreach((array)$urls as $url)
{ {
if (preg_match('/^\/?(home|apps|)\/*$/',self::parse_url($url,PHP_URL_PATH))) // some precaution to never allow to (recursivly) remove /, /apps or /home, see Vfs::isProtected()
if (self::isProtectedDir($url))
{ {
throw new Exception\AssertionFailed(__METHOD__.'('.array2string($urls).") Cautiously rejecting to remove folder '$url'!"); throw new Vfs\Exception\ProtectedDirectory("Deleting protected directory '$url' rejected!");
} }
} }
return self::find($urls, array('depth'=>true,'url'=>$allow_urls,'hidden'=>true), __CLASS__.'::_rm_rmdir'); return self::find($urls, array('depth'=>true,'url'=>$allow_urls,'hidden'=>true), __CLASS__.'::_rm_rmdir');

View File

@ -0,0 +1,21 @@
<?php
/**
* EGroupware API - Exceptions
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package vfs
* @subpackage exception
* @access public
* @version $Id$
*/
namespace EGroupware\Api\Vfs\Exception;
/**
* User or code tried to delete or rename a protected directory, see Vfs::isProtectedDir
*
* This exception extends \Exception to not catch it accidently.
*/
class ProtectedDirectory extends \Exception { }

View File

@ -571,9 +571,14 @@ class StreamWrapper implements StreamWrapperIface
* @param string $path_from * @param string $path_from
* @param string $path_to * @param string $path_to
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
* @throws Exception\ProtectedDirectory if trying to delete a protected directory, see Vfs::isProtected()
*/ */
function rename ( $path_from, $path_to ) function rename ( $path_from, $path_to )
{ {
if (Vfs::isProtectedDir($path_from))
{
throw new Exception\ProtectedDirectory("Renaming protected directory '$path_from' rejected!");
}
if (!($url_from = $this->resolve_url_symlinks($path_from,true,false)) || if (!($url_from = $this->resolve_url_symlinks($path_from,true,false)) ||
!($url_to = $this->resolve_url_symlinks($path_to,false))) !($url_to = $this->resolve_url_symlinks($path_to,false)))
{ {
@ -665,9 +670,14 @@ class StreamWrapper implements StreamWrapperIface
* @param string $path * @param string $path
* @param int $options Possible values include STREAM_REPORT_ERRORS. * @param int $options Possible values include STREAM_REPORT_ERRORS.
* @return boolean TRUE on success or FALSE on failure. * @return boolean TRUE on success or FALSE on failure.
* @throws Exception\ProtectedDirectory if trying to delete a protected directory, see Vfs::isProtected()
*/ */
function rmdir ( $path, $options ) function rmdir ( $path, $options )
{ {
if (Vfs::isProtectedDir($path))
{
throw new Exception\ProtectedDirectory("Deleting protected directory '$path' rejected!");
}
unset($options); // not uses but required by function signature unset($options); // not uses but required by function signature
if (!($url = $this->resolve_url_symlinks($path))) if (!($url = $this->resolve_url_symlinks($path)))
{ {

View File

@ -95,7 +95,7 @@ class WebDAV extends HTTP_WebDAV_Server_Filesystem
$ret = !empty($deleted[$options['path']]); $ret = !empty($deleted[$options['path']]);
//error_log(__METHOD__."() Vfs::remove($options[path]) returned ".array2string($deleted)." --> ".array2string($ret)); //error_log(__METHOD__."() Vfs::remove($options[path]) returned ".array2string($deleted)." --> ".array2string($ret));
} }
catch (\Exception $e) { catch (Exception\ProtectedDirectory $e) {
return '403 Forbidden: '.$e->getMessage(); return '403 Forbidden: '.$e->getMessage();
} }
} }
@ -221,9 +221,14 @@ class WebDAV extends HTTP_WebDAV_Server_Filesystem
} }
if ($del) { if ($del) {
if (!rename($source, $dest)) { try {
return "500 Internal server error"; if (!rename($source, $dest)) {
} return "500 Internal server error";
}
}
catch (Exception\ProtectedDirectory $e) {
return "403 Forbidden: ".$e->getMessage();
}
} else { } else {
if (is_dir($source) && $options['depth'] == 'infinity') { if (is_dir($source) && $options['depth'] == 'infinity') {
$files = Vfs::find($source,array('depth' => true,'url' => true)); // depth=true: return dirs first, url=true: allow urls! $files = Vfs::find($source,array('depth' => true,'url' => true)); // depth=true: return dirs first, url=true: allow urls!

View File

@ -751,7 +751,7 @@ class filemanager_ui
// some precaution to never allow to (recursivly) remove /, /apps or /home // some precaution to never allow to (recursivly) remove /, /apps or /home
foreach((array)$selected as $path) foreach((array)$selected as $path)
{ {
if (preg_match('/^\/?(home|apps|)\/*$/',$path)) if (Vfs::isProtectedDir($path))
{ {
$errs++; $errs++;
return lang("Cautiously rejecting to remove folder '%1'!",Vfs::decodePath($path)); return lang("Cautiously rejecting to remove folder '%1'!",Vfs::decodePath($path));