fix filesystem stream-wrapper mounted inside other users home-directory would allow access

This commit is contained in:
Ralf Becker 2020-09-22 21:54:23 +02:00
parent fd178ddd73
commit 7cc46f04e1
3 changed files with 23 additions and 6 deletions

View File

@ -182,9 +182,9 @@ class Base
* @param string $fullurl full url returned by resolve_url * @param string $fullurl full url returned by resolve_url
* @return string|NULL mount url or null if not found * @return string|NULL mount url or null if not found
*/ */
static function mount_url($fullurl) static function mount_url($fullurl, &$mounted=null)
{ {
foreach(array_reverse(self::$fstab) as $url) foreach(array_reverse(self::$fstab) as $mounted => $url)
{ {
list($url_no_query) = explode('?',$url); list($url_no_query) = explode('?',$url);
if (substr($fullurl,0,1+strlen($url_no_query)) === $url_no_query.'/') if (substr($fullurl,0,1+strlen($url_no_query)) === $url_no_query.'/')
@ -212,9 +212,10 @@ class Base
* @param boolean $use_symlinkcache =true * @param boolean $use_symlinkcache =true
* @param boolean $replace_user_pass_host =true replace $user,$pass,$host in url, default true, if false result is not cached * @param boolean $replace_user_pass_host =true replace $user,$pass,$host in url, default true, if false result is not cached
* @param boolean $fix_url_query =false true append relativ path to url query parameter, default not * @param boolean $fix_url_query =false true append relativ path to url query parameter, default not
* @param ?string &$mounted =null on return mount-point of resolved url, IF $_path is a path or vfs-url, other urls return NULL!
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry * @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
*/ */
static function resolve_url($_path,$do_symlink=true,$use_symlinkcache=true,$replace_user_pass_host=true,$fix_url_query=false) static function resolve_url($_path,$do_symlink=true,$use_symlinkcache=true,$replace_user_pass_host=true,$fix_url_query=false, &$mounted=null)
{ {
$path = self::get_path($_path); $path = self::get_path($_path);
@ -222,7 +223,8 @@ class Base
if (isset(self::$resolve_url_cache[$path]) && $replace_user_pass_host) if (isset(self::$resolve_url_cache[$path]) && $replace_user_pass_host)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = '".self::$resolve_url_cache[$path]."' (from cache)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = '".self::$resolve_url_cache[$path]."' (from cache)");
return self::$resolve_url_cache[$path]; $mounted = self::$resolve_url_cache[$path]['mounted'];
return self::$resolve_url_cache[$path]['url'];
} }
// check if we can already resolve path (or a part of it) with a known symlinks // check if we can already resolve path (or a part of it) with a known symlinks
if ($use_symlinkcache) if ($use_symlinkcache)
@ -283,7 +285,7 @@ class Base
} }
$url = $replace; $url = $replace;
} }
if ($replace_user_pass_host) self::$resolve_url_cache[$path] = $url; if ($replace_user_pass_host) self::$resolve_url_cache[$path] = ['url' => $url, 'mounted' => $mounted];
return $url; return $url;
} }

View File

@ -695,12 +695,22 @@ class StreamWrapper extends Base implements StreamWrapperIface
// we have no context, but $path is a URL with a valid user --> set it // we have no context, but $path is a URL with a valid user --> set it
$this->check_set_context($path, true); $this->check_set_context($path, true);
if (!($url = static::resolve_url($path,!($flags & STREAM_URL_STAT_LINK), $check_symlink_components))) if (!($url = static::resolve_url($path, !($flags & STREAM_URL_STAT_LINK), $check_symlink_components, true, false, $mount_point)))
{ {
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$path',$flags) can NOT resolve path!"); if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$path',$flags) can NOT resolve path!");
return false; return false;
} }
// we need to make sure the mount-point is readable eg. if something is mounted into an other users home-directory
if (!isset($mount_point)) Vfs::mount_url($url, $mount_point); // resolve_url only returns mount-point for pathes or vfs urls
if (!in_array($mount_point, ['/', '/apps', '/home']) && // they all are public readable
($class = self::scheme2class(Vfs::parse_url($url, PHP_URL_SCHEME))) &&
!is_a($class, Vfs\Sqlfs\StreamWrapper::class) && // decendents of SqlFS stream-wrapper always check traversal right to /
!$this->check_access(Vfs::dirname($mount_point), Vfs::READABLE))
{
return false; // mount-point is not reachable
}
if (empty(parse_url($url, PHP_URL_USER))) if (empty(parse_url($url, PHP_URL_USER)))
{ {
$url = str_replace('://', '://'.Api\Accounts::id2name($this->context ? stream_context_get_options($this->context)[self::SCHEME]['user'] : Vfs::$user).'@', $url); $url = str_replace('://', '://'.Api\Accounts::id2name($this->context ? stream_context_get_options($this->context)[self::SCHEME]['user'] : Vfs::$user).'@', $url);

View File

@ -25,6 +25,11 @@ var_dump(Vfs::scandir('/home'));
var_dump(Vfs::find('/home', ['maxdepth' => 1])); var_dump(Vfs::find('/home', ['maxdepth' => 1]));
//var_dump(Vfs::scandir("/home/$sysop")); //var_dump(Vfs::scandir("/home/$sysop"));
Vfs::$is_root = true;
Vfs::mount('filesystem://default/var/lib/egroupware', "/home/$other/something", false, false);
Vfs::$is_root = false;
var_dump(Vfs::stat("/home/$other/something"));
var_dump(file_put_contents("vfs://default/home/$sysop/test.txt", "Just a test ;)\n")); var_dump(file_put_contents("vfs://default/home/$sysop/test.txt", "Just a test ;)\n"));
var_dump("Vfs::proppatch('/home/$sysop/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])=".array2string(Vfs::proppatch("/home/$sysop/test.txt", [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])), var_dump("Vfs::proppatch('/home/$sysop/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])=".array2string(Vfs::proppatch("/home/$sysop/test.txt", [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])),
"Vfs::propfind('/home/$sysop/test.txt')=".json_encode(Vfs::propfind("/home/$sysop/test.txt"), JSON_UNESCAPED_SLASHES)); "Vfs::propfind('/home/$sysop/test.txt')=".json_encode(Vfs::propfind("/home/$sysop/test.txt"), JSON_UNESCAPED_SLASHES));