From c724b720be6cc9c49fb964aea4d9a66abb260ca1 Mon Sep 17 00:00:00 2001 From: "Uwe.Tews" Date: Sun, 26 Apr 2009 16:56:17 +0000 Subject: [PATCH] - added trusted stream checking to security - internal changes at file dependency check for caching --- change_log.txt | 4 ++ libs/Smarty.class.php | 19 ++++++-- libs/plugins/securitypolicy.default.php | 9 ++++ libs/sysplugins/internal.compile_include.php | 18 ++----- ...ternal.compile_internal_function_call.php} | 22 ++++----- libs/sysplugins/internal.debug.php | 12 ++++- libs/sysplugins/internal.security_handler.php | 21 ++++++-- libs/sysplugins/internal.template.php | 48 +++++++++++++++---- .../internal.templatecompilerbase.php | 2 +- 9 files changed, 112 insertions(+), 43 deletions(-) rename libs/sysplugins/{internal.compile_internalfunctioncall.php => internal.compile_internal_function_call.php} (80%) diff --git a/change_log.txt b/change_log.txt index b57f7596..7e44970d 100644 --- a/change_log.txt +++ b/change_log.txt @@ -1,3 +1,7 @@ +04/26/2009 +- added trusted stream checking to security +- internal changes at file dependency check for caching + 04/24/2009 - changed name of {template} tag to {function} - added new {template} tag diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index 978b88fb..be2f9f14 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -47,6 +47,13 @@ define('SMARTY_PARENT_SCOPE', 1); define('SMARTY_ROOT_SCOPE', 2); define('SMARTY_GLOBAL_SCOPE', 3); +/** +* define caching modes +*/ +define('SMARTY_CACHING_OFF', 0); +define('SMARTY_CACHING_LIFETIME_CURRENT', 1); +define('SMARTY_CACHING_LIVETIME_SAVED', 2); + /** * load required base class for creation of the smarty object */ @@ -111,7 +118,7 @@ class Smarty extends Smarty_Internal_TemplateBase { // config var settings public $config_overwrite = true; //Controls whether variables with the same name overwrite each other. public $config_booleanize = true; //Controls whether config values of on/true/yes and off/false/no get converted to boolean - public $config_read_hidden = true; //Controls whether hidden config sections/vars are read from the file. + public $config_read_hidden = true; //Controls whether hidden config sections/vars are read from the file. // config vars public $config_vars = array(); // assigned tpl vars @@ -172,9 +179,9 @@ class Smarty extends Smarty_Internal_TemplateBase { * Class constructor, initializes basic smarty properties */ public function __construct() - { + { // set instance object - self::instance($this); + self::instance($this); if (is_callable('mb_internal_encoding')) { $this->has_mb = true; @@ -204,7 +211,11 @@ class Smarty extends Smarty_Internal_TemplateBase { $this->loadPlugin('Smarty_Internal_Run_Filter'); $this->filter_handler = new Smarty_Internal_Run_Filter; if (!$this->debugging && $this->debugging_ctrl == 'URL') { - $_query_string = $this->request_use_auto_globals ? isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING']:'' : isset($GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING']) ? $GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING']:''; + if (isset($_SERVER['QUERY_STRING'])) { + $_query_string = $_SERVER['QUERY_STRING']; + } else { + $_query_string = ''; + } if (false !== strpos($_query_string, $this->smarty_debug_id)) { if (false !== strpos($_query_string, $this->smarty_debug_id . '=on')) { // enable debugging for this browser session diff --git a/libs/plugins/securitypolicy.default.php b/libs/plugins/securitypolicy.default.php index 9eabba83..9b85d2b1 100644 --- a/libs/plugins/securitypolicy.default.php +++ b/libs/plugins/securitypolicy.default.php @@ -65,6 +65,15 @@ class Smarty_Security_Policy { * @var array */ public $modifiers = array('escape','count'); + + /** + * This is an array of trusted streams. + * + * If empty all streams are allowed. + * If set to 'none' none is allowed. + * @var array + */ + public $streams = array('file'); /** + flag if constants can be accessed from template */ diff --git a/libs/sysplugins/internal.compile_include.php b/libs/sysplugins/internal.compile_include.php index a4cc31b6..7204b334 100644 --- a/libs/sysplugins/internal.compile_include.php +++ b/libs/sysplugins/internal.compile_include.php @@ -44,7 +44,8 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase { $_parent_scope = SMARTY_GLOBAL_SCOPE; } } - + // default for included templates + $_caching = SMARTY_CACHING_OFF; /* * if the {include} tag provides individual parameter for caching * it will not be included into the common cache file and treated like @@ -56,21 +57,14 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase { } if (isset($_attr['nocache'])) { if ($_attr['nocache'] == 'true') { - $_caching = 'false'; $this->compiler->tag_nocache = true; } } if (isset($_attr['caching'])) { if ($_attr['caching'] == 'true') { - $_caching = 'true'; + $_caching = SMARTY_CACHING_LIFETIME_CURRENT; } } - -// if ($this->compiler->tag_nocache == false) { - // save file dependency -// $compiler->template->properties['file_dependency'][] = array($_template->getTemplateFilepath(), $_template->getTemplateTimestamp()); -// unset ($_template); -// } // create template object $_output = "cache_id, \$_smarty_tpl->compile_id);"; // delete {include} standard attributes @@ -90,11 +84,7 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase { if (isset($_caching_lifetime)) { $_output .= "\$_template->caching_lifetime = $_caching_lifetime;"; } - if (isset($_caching)) { - $_output .= "\$_template->caching = $_caching;"; - } elseif (isset($_caching_lifetime)) { - $_output .= "\$_template->caching = true;"; - } + $_output .= "\$_template->caching = $_caching;"; // was there an assign attribute if (isset($_assign)) { $_output .= "\$_smarty_tpl->assign($_assign,\$_smarty_tpl->smarty->fetch(\$_template)); ?>"; diff --git a/libs/sysplugins/internal.compile_internalfunctioncall.php b/libs/sysplugins/internal.compile_internal_function_call.php similarity index 80% rename from libs/sysplugins/internal.compile_internalfunctioncall.php rename to libs/sysplugins/internal.compile_internal_function_call.php index 3e435236..197e886e 100644 --- a/libs/sysplugins/internal.compile_internalfunctioncall.php +++ b/libs/sysplugins/internal.compile_internal_function_call.php @@ -1,20 +1,20 @@ $_value) { $_output .= "\$_template->assign('$_key',$_value);\n"; } - } + } if (isset($compiler->template->properties['function'][$_name]['compiled'])) { - $_compiled = str_replace(array('_%n',"'"), array("\n","\'"), $compiler->template->properties['function'][$_name]['compiled']); + $_compiled = str_replace(array('_%n', "'"), array("\n", "\'"), $compiler->template->properties['function'][$_name]['compiled']); $_output .= "\$_template->compiled_template = '$_compiled';\n \$_template->mustCompile = false;\n"; } else { -// for recursion + // for recursion $_output .= "\$_template->compiled_template = \$_smarty_tpl->compiled_template;\n \$_template->mustCompile = false;\n"; - } + } // was there an assign attribute if (isset($_assign)) { $_output .= "\$_smarty_tpl->assign($_assign,\$_smarty_tpl->smarty->fetch(\$_template)); ?>"; diff --git a/libs/sysplugins/internal.debug.php b/libs/sysplugins/internal.debug.php index 9ec38c99..2143f0ef 100644 --- a/libs/sysplugins/internal.debug.php +++ b/libs/sysplugins/internal.debug.php @@ -18,8 +18,7 @@ class Smarty_Internal_Debug extends Smarty_Internal_TemplateBase { */ public function display_debug() { - $this->smarty = Smarty::instance(); - + $this->smarty = Smarty::instance(); // get template names $i = 0; $_template_data = array(); @@ -32,6 +31,15 @@ class Smarty_Internal_Debug extends Smarty_Internal_TemplateBase { $_template_data[$i]['render_time'] = $_template_obj->render_time; $_template_data[$i]['cache_time'] = $_template_obj->cache_time; $i++; + if (false && $i == 1) { + foreach ($_template_obj->properties['file_dependency'] as $_file) { + $_template_data[$i]['name'] = $_file[0]; + $_template_data[$i]['compile_time'] = 0; + $_template_data[$i]['render_time'] = 0; + $_template_data[$i]['cache_time'] = 0; + $i++; + } + } } } } diff --git a/libs/sysplugins/internal.security_handler.php b/libs/sysplugins/internal.security_handler.php index f8556117..9151a36a 100644 --- a/libs/sysplugins/internal.security_handler.php +++ b/libs/sysplugins/internal.security_handler.php @@ -10,7 +10,6 @@ * This class contains all methods for security checking */ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base { - /** * Check if PHP function is trusted. * @@ -44,6 +43,22 @@ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base { return false; } } + /** + * Check if stream is trusted. + * + * @param string $stream_name + * @param object $compiler compiler object + * @return boolean true if stream is trusted + */ + function isTrustedStream($stream_name) + { + if (empty($this->smarty->security_policy->streams) || in_array($stream_name, $this->smarty->security_policy->streams)) { + return true; + } else { + throw new Exception ("stream \"" . $stream_name . "\" not allowed by security setting"); + return false; + } + } /** * Check if directory of file resource is trusted. @@ -70,7 +85,7 @@ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base { if ($_cd == $_rp) { return true; } elseif (strncmp($_rp, $_cd, strlen($_cd)) == 0 && - (strlen($_rp) == strlen($_cd) || substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR)) { + (strlen($_rp) == strlen($_cd) || substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR)) { return true; } } @@ -90,7 +105,7 @@ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base { function isTrustedPHPDir($filepath) { $_rp = realpath($filepath); - if (!empty($this->smarty->security_policy->trusted_dir)) { + if (!empty($this->smarty->security_policy->trusted_dir)) { foreach ((array)$this->smarty->security_policy->trusted_dir as $curr_dir) { if (($_cd = realpath($curr_dir)) !== false) { if ($_cd == $_rp) { diff --git a/libs/sysplugins/internal.template.php b/libs/sysplugins/internal.template.php index 40248173..378ac098 100644 --- a/libs/sysplugins/internal.template.php +++ b/libs/sysplugins/internal.template.php @@ -89,7 +89,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { $this->caching_type = $this->smarty->default_caching_type; $this->security = $this->smarty->security; $this->cache_resource_class = 'Smarty_Internal_CacheResource_' . ucfirst($this->caching_type); - $this->parent = $_parent; + $this->parent = $_parent; + $this->properties['file_dependency'] = array(); // Template resource $this->template_resource = $template_resource; // parse resource name @@ -286,7 +287,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { if ($this->compiler_object->compileTemplate($this)) { // compiling succeded if (!$this->isEvaluated()) { - // build file dependency string + // build template property string $this->properties_string = 'properties) . "*/ ?>\n"; // write compiled template $this->smarty->write_file_object->writeFile($this->getCompiledFilepath(), $this->properties_string . $this->dir_acc_sec_string . $this->getCompiledTemplate()); @@ -298,7 +299,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { throw new Exception("Error compiling template {$this->getTemplateFilepath ()}"); return false; } - $this->compile_time = $this->_get_time() - $_start_time; + $this->compile_time += $this->_get_time() - $_start_time; } /** @@ -347,6 +348,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { public function writeCachedContent () { // build file dependency string + $this->properties['caching_lifetime'] = $this->caching_lifetime; $this->properties_string = 'properties) . "*/ ?>\n"; $this->rendered_content = $this->properties_string . $this->dir_acc_sec_string . $this->rendered_content; return ($this->isEvaluated() || !$this->caching) ? false : $this->cacher_object->writeCachedContent($this); @@ -363,9 +365,29 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { { if ($this->isCached === null) { $this->isCached = false; - if ($this->caching && !$this->isEvaluated()) { - if (!$this->mustCompile() && $this->getTemplateTimestamp() <= $this->getCachedTimestamp() && ((time() <= $this->getCachedTimestamp() + $this->caching_lifetime) || $this->caching_lifetime < 0)) { + if ($this->caching && !$this->isEvaluated() && !$this->force_compile) { + if ($this->getCachedTimestamp() === false) { + return $this->isCached; + } + if (/*$this->getTemplateTimestamp() <= $this->getCachedTimestamp() && */ + ($this->caching == SMARTY_CACHING_LIVETIME_SAVED || ($this->caching == SMARTY_CACHING_LIFETIME_CURRENT && (time() <= ($this->getCachedTimestamp() + $this->caching_lifetime) || $this->caching_lifetime < 0)))) { $this->rendered_content = $this->cacher_object->getCachedContents($this); + $_found = preg_match('~\<\?php /\*(.*)\*/ \?\>~', $this->rendered_content, $_matches); + if ($_found) { + $this->properties = unserialize($_matches[1]); + if ($this->caching == SMARTY_CACHING_LIVETIME_SAVED && (time() > ($this->getCachedTimestamp() + $this->properties['caching_lifetime']) || $this->properties['caching_lifetime'] < 0)) { + $this->rendered_content = null; + return $this->isCached; + } + if (!empty($this->properties['file_dependency'])) { + foreach ($this->properties['file_dependency'] as $file_to_check) { + If (filemtime($file_to_check[0]) > $this->getCachedTimestamp()) { + $this->rendered_content = null; + return $this->isCached; + } + } + } + } $this->isCached = true; } } @@ -415,10 +437,17 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { // include PHP template include($this->getTemplateFilepath ()); } - $this->render_time = $this->_get_time() - $_start_time; - $this->rendered_content = ob_get_clean(); + $this->render_time += $this->_get_time() - $_start_time; + $this->rendered_content = ob_get_clean(); + if (!$this->isEvaluated) { + $this->properties['file_dependency'][] = array($this->getTemplateFilepath(), $this->getTemplateTimestamp()); + } + if ($this->parent instanceof Smarty_Template or $this->parent instanceof Smarty_Internal_Template) { + $this->parent->properties['file_dependency'] = array_merge($this->parent->properties['file_dependency'], $this->properties['file_dependency']); + } // write to cache when nessecary if (!$this->isEvaluated() && $this->caching) { + $this->properties['file_dependency'] = array_unique($this->properties['file_dependency']); // write rendered template $this->writeCachedContent($this); } @@ -452,7 +481,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { ob_start(); eval("?>" . $this->rendered_content); $this->updateParentVariables(); - $this->cache_time = $this->_get_time() - $_start_time; + $this->cache_time += $this->_get_time() - $_start_time; return (isset($this->smarty->autoload_filters['output']) || isset($this->smarty->registered_filters['output']))? $this->smarty->filter_handler->execute('output', ob_get_clean()) : ob_get_clean(); } else { @@ -502,6 +531,9 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { $_known_stream = stream_get_wrappers(); if (in_array($this->resource_type, $_known_stream)) { // is known stream + if ($this->smarty->security) { + $this->smarty->security_handler->isTrustedStream($this->resource_type); + } if (!isset($this->resource_objects['stream'])) { $this->smarty->loadPlugin('Smarty_Internal_Resource_Stream'); $this->resource_objects['stream'] = new Smarty_Internal_Resource_Stream; diff --git a/libs/sysplugins/internal.templatecompilerbase.php b/libs/sysplugins/internal.templatecompilerbase.php index d53fe609..440a06fe 100644 --- a/libs/sysplugins/internal.templatecompilerbase.php +++ b/libs/sysplugins/internal.templatecompilerbase.php @@ -115,7 +115,7 @@ class Smarty_Internal_TemplateCompilerBase extends Smarty_Internal_Base { if (isset($this->template->properties['function'][$tag])) { // template defined by {template} tag $args['name'] = $tag; - $tag = 'internalfunctioncall'; + $tag = 'internal_function_call'; } if (!($_output = $this->$tag($args, $this)) === false) { if ($_output !== true) {