New options for filesystme stream-wrapper:

- all:   false (default) =3D ignore files starting with a dot '.',
  true =3D show all files (. and .. are always ignored!)
- exec: false (default) =3D do NOT allow to upload or modify scripts, =

  true =3D allow it (if docroot is mounted, this allows to run scripts!)
--> deny_script method was added to egw_vfs and calls to it from
filemanager
Other fixes:
- missing write rights of the webserver were not removed from perms
  (causing warnings to be displayed in the ui)
- rename was not working due to typos
--> should be backported for obvious reasons to 1.6
This commit is contained in:
Ralf Becker 2009-03-31 11:35:46 +00:00
parent d006b7a69f
commit aa26af977d
5 changed files with 108 additions and 15 deletions

View File

@ -5,7 +5,7 @@
* @link http://www.egroupware.org
* @package filemanager
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -259,7 +259,12 @@ class filemanager_ui
// strip '?', '/' and '#' from filenames, as they are forbidden for sqlfs / make problems
$path = egw_vfs::concat($dir,str_replace(array('?','/','#'),'',$name));
if (egw_vfs::stat($path))
if(egw_vfs::deny_script($path))
{
$response->addAlert(lang('You are NOT allowed to upload a script!'));
$response->addScript("document.getElementById('$id').value='';");
}
elseif (egw_vfs::stat($path))
{
if (egw_vfs::is_dir($path))
{
@ -640,6 +645,10 @@ class filemanager_ui
else
{
$msg .= lang('Rename of %1 to %2 failed!',$path,$to).' ';
if (egw_vfs::deny_script($to))
{
$msg .= lang('You are NOT allowed to upload a script!').' ';
}
}
}
elseif ($name[0] == '#' || $name == 'comment')

View File

@ -158,6 +158,7 @@ updated comment for %1 filemanager de Kommentar für %1 aktualisiert
used space filemanager de Benutzter Platz
users and groups filemanager de Benutzer und Gruppen
wrong username or password! filemanager de Falscher Benutzername oder Passwort!
you are not allowed to upload a script! filemanager de Sie dürfen KEINE Skripte hochladen!
you do not have access to %1 filemanager de Sie besitzen keine Zugriffsrechte für %1
you need to select an owner! filemanager de Sie müssen einen Eigentümer auswählen!
you need to select some files first! filemanager de Sie müssen zuerst die Dateien auswählen!

View File

@ -158,6 +158,7 @@ updated comment for %1 filemanager en Updated comment for %1
used space filemanager en Used space
users and groups filemanager en Users and groups
wrong username or password! filemanager en Wrong username or password!
you are not allowed to upload a script! filemanager en You are NOT allowed to upload a script!
you do not have access to %1 filemanager en You do not have access to %1
you need to select an owner! filemanager en You need to select an owner!
you need to select some files first! filemanager en You need to select some files first!

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -666,6 +666,17 @@ class egw_vfs extends vfs_stream_wrapper
return self::is_readable($path,1);
}
/**
* Check if path is a script and write access would be denied by backend
*
* @param string $path
* @return boolean true if $path is a script AND exec mount-option is NOT set, false otherwise
*/
static function deny_script($path)
{
return self::_call_on_backend('deny_script',array($path),true);
}
/**
* Set or delete extended acl for a given path and owner (or delete them if is_null($rights)
*

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -21,6 +21,9 @@
* - user: uid or user-name owning the path, default root
* - group: gid or group-name owning the path, default root
* - mode: mode bit for the path, default 0005 (read and execute for nobody)
* - all: false (default) = ignore files starting with a dot '.', true = show all files (. and .. are always ignored!)
* - exec: false (default) = do NOT allow to upload or modify scripts, true = allow it (if docroot is mounted, this allows to run scripts!)
* scripts are considered every file having a script-extension (eg. .php, .pl, .py), defined with SCRIPT_EXTENSION_PREG constant
*
* @link http://www.php.net/manual/en/function.stream-wrapper-register.php
*/
@ -79,6 +82,13 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
*/
private $opened_dir_url;
/**
* Should dir show all files, or only the ones NOT starting with a dot (. and .. are never shown)
*
* @var boolean
*/
private $dir_show_all = false;
/**
* How much should be logged to the apache error-log
*
@ -88,6 +98,11 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
*/
const LOG_LEVEL = 1;
/**
* Regular expression identifying scripts, to NOT allow updating them if exec mount option is NOT set
*/
const SCRIPT_EXTENSIONS_PREG = '/\.(php[0-9]*|pl|py)$/';
/**
* This method is called immediately after your stream object is created.
*
@ -129,10 +144,20 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
}
return false;
}
if ($mode != 'r' && self::deny_script($url))
{
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) permission denied, file is a script!");
if (!($options & STREAM_URL_STAT_QUIET))
{
trigger_error(__METHOD__."($url,$mode,$options) permission denied, file is a script!",E_USER_WARNING);
}
return false;
}
// open the "real" file
if (!($this->opened_stream = fopen(parse_url($url,PHP_URL_PATH),$mode,$options)))
if (!($this->opened_stream = fopen($path=parse_url($url,PHP_URL_PATH),$mode,$options)))
{
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) fopen('$path','$mode',$options) returned false!");
return false;
}
$this->opened_stream_url = $url;
@ -303,21 +328,27 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
// check access rights
if (!($from_stat = self::url_stat($url_from,0)) || !egw_vfs::check_access(egw_vfs::dirname($url_from),egw_vfs::WRITABLE))
{
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_from permission denied!");
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $from[path] permission denied!");
return false; // no permission or file does not exist
}
$to_dir = egw_vfs::dirname($url_to);
if (!egw_vfs::check_access($to_dir,egw_vfs::WRITABLE,$to_dir_stat = self::url_stat($to_dir,0)))
{
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_to permission denied!");
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $to_dir permission denied!");
return false; // no permission or parent-dir does not exist
}
if (self::deny_script($url_to))
{
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to) permission denied, file is a script!");
return false;
}
// the filesystem stream-wrapper does NOT allow to rename files to directories, as this makes problems
// for our vfs too, we abort here with an error, like the filesystem one does
if (($to_stat = self::url_stat($path_to,0)) &&
if (($to_stat = self::url_stat($to['path'],0)) &&
($to_stat['mime'] === self::DIR_MIME_TYPE) !== ($from_stat['mime'] === self::DIR_MIME_TYPE))
{
$is_dir = $to_stat['mime'] === self::DIR_MIME_TYPE ? 'a' : 'no';
if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) $path_to is $is_dir directory!");
if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) $to[path] is $is_dir directory!");
return false; // no permission or file does not exist
}
// if destination file already exists, delete it
@ -459,16 +490,19 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
*/
function dir_opendir ( $url, $options )
{
error_log(__METHOD__."($url,$options)");
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$options)");
$this->opened_dir = null;
$parts = parse_url($this->opened_dir_url = $url);
parse_str($parts['query'],$get);
$this->dir_show_all = (bool)$get['all'];
// ToDo: check access rights
if (!($this->opened_dir = opendir($parts['path'])))
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__."($url,$options) opendir('$parts[path]') failed!");
return false;
}
return true;
@ -504,7 +538,6 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
{
$parts = parse_url($url);
//error_log(__METHOD__."('$path',$flags) calling stat($url)");
$stat = @stat($parts['path']); // suppressed the stat failed warnings
if ($stat)
@ -513,12 +546,18 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
if (!self::parse_query($parts['query'],$uid,$gid,$mode))
{
return false;
if (self::LOG_LEVEL > 0) error_log(__METHOD__."($url,$flags) can NOT self::parse_query('$parts[query]')!");
}
$stat['uid'] = $stat[4] = $uid;
$stat['gid'] = $stat[5] = $gid;
$stat['mode'] = $stat[2] = $stat['mode'] & self::MODE_DIR ? self::MODE_DIR | $mode : self::MODE_FILE | ($mode & ~0111);
// write rights also depend on the write rights of the webserver
if (!is_writable($parts['path']))
{
$stat['mode'] = $stat[2] = $stat['mode'] & ~0222;
}
}
//echo __METHOD__."($url,$flags) path=$parts[path], mount_mode=".sprintf('0%o',$mode).", mode=".sprintf('0%o',$stat['mode']).'='.egw_vfs::int2mode($stat['mode'])."\n"; //print_r($stat);
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$flags) path=$parts[path], mount_mode=".sprintf('0%o',$mode).", mode=".sprintf('0%o',$stat['mode']).'='.egw_vfs::int2mode($stat['mode']));
return $stat;
}
@ -534,7 +573,19 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
*/
function dir_readdir ( )
{
return readdir($this->opened_dir);
do {
$file = readdir($this->opened_dir);
$ignore = !($file === false || // stop if no more dirs or
$file[0] != '.' || // file does NOT start with a dot '.' or
($this->dir_show_all && $file != '.' && $file != '..' )); // file not . or .. or dir_show_all set
if (self::LOG_LEVEL > 1 && $ignore) error_log(__METHOD__.'() ignoring '.array2string($file));
}
while ($ignore);
if (self::LOG_LEVEL > 1) error_log(__METHOD__.'() returning '.array2string($file));
return $file;
}
/**
@ -559,11 +610,11 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
*/
function dir_closedir ( )
{
$ret = closedir($this->opened_dir);
closedir($this->opened_dir);
$this->opened_dir = $this->extra_dirs = null;
return $ret;
return true;
}
/**
@ -643,6 +694,26 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
//echo __METHOD__.'('.print_r($query,true).") uid=$uid, gid=$gid, mode=".sprintf('0%o',$mode)."\n";
return true;
}
/**
* Check if url is a script (self::$script_extentions) and exec mount option is NOT set
*
* @param string $url
* @return boolean true if $url is a script AND exec is NOT set, false otherwise
*/
static function deny_script($url)
{
$parts = parse_url($url);
parse_str($parts['query'],$get);
$deny = !$get['exec'] && preg_match(self::SCRIPT_EXTENSIONS_PREG,$parts['path']);
if (self::LOG_LEVEL > 1 || self::LOG_LEVEL > 0 && $deny)
{
error_log(__METHOD__."($url) returning ".array2string($deny));
}
return $deny;
}
}
stream_register_wrapper(filesystem_stream_wrapper::SCHEME ,'filesystem_stream_wrapper');