From de4957a1512d327c8677c9231eb1e156e3f36eda Mon Sep 17 00:00:00 2001 From: "Uwe.Tews" Date: Mon, 28 Dec 2009 15:27:13 +0000 Subject: [PATCH] - update for security fixes - make modifier plugins always trusted --- change_log.txt | 4 ++ .../smarty_internal_compile_block.php | 4 +- .../smarty_internal_compile_include.php | 4 +- ...arty_internal_compile_private_modifier.php | 47 ++++++++--------- libs/sysplugins/smarty_internal_template.php | 51 ++++++++++++------- .../smarty_internal_templatecompilerbase.php | 6 +-- 6 files changed, 70 insertions(+), 46 deletions(-) diff --git a/change_log.txt b/change_log.txt index e5299804..86d6eedf 100644 --- a/change_log.txt +++ b/change_log.txt @@ -1,3 +1,7 @@ +12/28/2009 +- update for security fixes +- make modifier plugins always trusted + 12/27/2009 --- this is a major update with a couple of internal changes --- - new config file lexer/parser (thanks to Thue Jnaus Kristensen) diff --git a/libs/sysplugins/smarty_internal_compile_block.php b/libs/sysplugins/smarty_internal_compile_block.php index 41a775c1..668bd28f 100644 --- a/libs/sysplugins/smarty_internal_compile_block.php +++ b/libs/sysplugins/smarty_internal_compile_block.php @@ -68,7 +68,6 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_CompileBase { $_tpl->forceNocache = true; $_tpl->suppressHeader = true; $_tpl->suppressFileDependency = true; - if (strpos($this->smarty->block_data[$_name]['source'], '%%%%SMARTY_PARENT%%%%') !== false) { $_output = str_replace('%%%%SMARTY_PARENT%%%%', $compiler->template->extracted_compiled_code, $_tpl->getCompiledTemplate()); } elseif ($this->smarty->block_data[$_name]['mode'] == 'prepend') { @@ -78,7 +77,8 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_CompileBase { } elseif (!empty($this->smarty->block_data[$_name])) { $_output = $_tpl->getCompiledTemplate(); } - $compiler->template->properties = array_merge_recursive($compiler->template->properties, $_tpl->properties); + $compiler->template->properties['file_dependency'] = array_merge($compiler->template->properties['file_dependency'], $_tpl->properties['file_dependency']); + $compiler->template->properties['function'] = array_merge($compiler->template->properties['function'], $_tpl->properties['function']); unset($_tpl); } else { $_output = $compiler->template->extracted_compiled_code; diff --git a/libs/sysplugins/smarty_internal_compile_include.php b/libs/sysplugins/smarty_internal_compile_include.php index 933d719a..4e215668 100644 --- a/libs/sysplugins/smarty_internal_compile_include.php +++ b/libs/sysplugins/smarty_internal_compile_include.php @@ -46,7 +46,9 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase { // get compiled code $compiled_tpl = $tpl->getCompiledTemplate(); // remove header code - $compiled_tpl = preg_replace('/(<\?php \/\*%%SmartyHeaderCode%%\*\/(.+?)\/\*\/%%SmartyHeaderCode%%\*\/\?>\n)/s', '', $compiled_tpl); + $compiled_tpl = preg_replace("/(<\?php \/\*%%SmartyHeaderCode:{$tpl->properties['nocache_hash']}%%\*\/(.+?)\/\*\/%%SmartyHeaderCode%%\*\/\?>\n)/s", '', $compiled_tpl); + // replace nocache_hash + $compiled_tpl = preg_replace("/{$tpl->properties['nocache_hash']}/", $compiler->template->properties['nocache_hash'], $compiled_tpl); $has_compiled_template = true; } } diff --git a/libs/sysplugins/smarty_internal_compile_private_modifier.php b/libs/sysplugins/smarty_internal_compile_private_modifier.php index a559b759..c4fbfa5e 100644 --- a/libs/sysplugins/smarty_internal_compile_private_modifier.php +++ b/libs/sysplugins/smarty_internal_compile_private_modifier.php @@ -26,32 +26,33 @@ class Smarty_Internal_Compile_Private_Modifier extends Smarty_Internal_CompileBa $this->required_attributes = array('modifier', 'params'); // check and get attributes $_attr = $this->_get_attributes($args); - // check if modifier allowed - if (!$this->compiler->template->security || $this->smarty->security_handler->isTrustedModifier($_attr['modifier'], $this->compiler)) { - // check for registered or plugin modifier - if (isset($compiler->smarty->registered_plugins['modifier'][$_attr['modifier']])) { - $function = $compiler->smarty->registered_plugins['modifier'][$_attr['modifier']][0]; - if (!is_array($function)) { - $output = "{$function}({$_attr['params']})"; - } else if (is_object($function[0])) { - $output = 'call_user_func_array($_smarty_tpl->smarty->registered_plugins[\'modifier\'][\'' . $_attr['modifier'] . '\'][0],array(' . $_attr['params'] . '))'; - } else { - $output = 'call_user_func_array(array(\'' . $function[0] . '\',\'' . $function[1] . '\'),array(' . $_attr['params'] . '))'; - } - } else if ($function = $this->compiler->getPlugin($_attr['modifier'], 'modifier')) { - if (!is_array($function)) { - $output = "{$function}({$_attr['params']})"; - } else { - $output = 'call_user_func_array(array(\'' . $function[0] . '\',\'' . $function[1] . '\'),array(' . $_attr['params'] . '))'; - } - // check if trusted PHP function - } else if (is_callable($_attr['modifier'])) { - $output = "{$_attr['modifier']}({$_attr['params']})"; + // check for registered modifier + if (isset($compiler->smarty->registered_plugins['modifier'][$_attr['modifier']])) { + $function = $compiler->smarty->registered_plugins['modifier'][$_attr['modifier']][0]; + if (!is_array($function)) { + $output = "{$function}({$_attr['params']})"; + } else if (is_object($function[0])) { + $output = 'call_user_func_array($_smarty_tpl->smarty->registered_plugins[\'modifier\'][\'' . $_attr['modifier'] . '\'][0],array(' . $_attr['params'] . '))'; } else { - $this->compiler->trigger_template_error ("unknown modifier \"" . $_attr['modifier'] . "\""); + $output = 'call_user_func_array(array(\'' . $function[0] . '\',\'' . $function[1] . '\'),array(' . $_attr['params'] . '))'; } - return $output; + // check for plugin modifier + } else if ($function = $this->compiler->getPlugin($_attr['modifier'], 'modifier')) { + if (!is_array($function)) { + $output = "{$function}({$_attr['params']})"; + } else { + $output = 'call_user_func_array(array(\'' . $function[0] . '\',\'' . $function[1] . '\'),array(' . $_attr['params'] . '))'; + } + // check if trusted PHP function + } else if (is_callable($_attr['modifier'])) { + // check if modifier allowed + if (!$this->compiler->template->security || $this->smarty->security_handler->isTrustedModifier($_attr['modifier'], $this->compiler)) { + $output = "{$_attr['modifier']}({$_attr['params']})"; + } + } else { + $this->compiler->trigger_template_error ("unknown modifier \"" . $_attr['modifier'] . "\""); } + return $output; } } diff --git a/libs/sysplugins/smarty_internal_template.php b/libs/sysplugins/smarty_internal_template.php index b1c67350..ede247b4 100644 --- a/libs/sysplugins/smarty_internal_template.php +++ b/libs/sysplugins/smarty_internal_template.php @@ -61,7 +61,7 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { // storage for plugin public $plugin_data = array(); // special properties - public $properties = array(); + public $properties = null; // storage for block data public $block_data = array(); // required plugins @@ -91,7 +91,9 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { $this->force_cache = $this->smarty->force_cache; $this->security = $this->smarty->security; $this->parent = $_parent; - $this->properties['file_dependency'] = array(); + $this->properties['file_dependency'] = array(); + $this->properties['nocache_hash'] = ''; + $this->properties['function'] = array();; // dummy local smarty variable $this->tpl_vars['smarty'] = new Smarty_Variable; // Template resource @@ -329,19 +331,21 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { return false; } // build file dependency string - $this->properties['cache_lifetime'] = $this->cache_lifetime; + $this->properties['cache_lifetime'] = $this->cache_lifetime; // get text between non-cached items - $cache_split = preg_split("!/\*%%SmartyNocache:{$this->properties['nocache_hash']}%%\*\/(.+?)/\*/%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!s",$this->rendered_content); + $cache_split = preg_split("!/\*%%SmartyNocache:{$this->properties['nocache_hash']}%%\*\/(.+?)/\*/%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!s", $this->rendered_content); // get non-cached items - preg_match_all("!/\*%%SmartyNocache:{$this->properties['nocache_hash']}%%\*\/(.+?)/\*/%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!s",$this->rendered_content,$cache_parts); - $output = ''; + preg_match_all("!/\*%%SmartyNocache:{$this->properties['nocache_hash']}%%\*\/(.+?)/\*/%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!s", $this->rendered_content, $cache_parts); + $output = ''; // loop over items, stitch back together foreach($cache_split as $curr_idx => $curr_split) { - // escape PHP tags in template content - $output .= preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '', $curr_split); - // remove nocache tags from cache output - $output .= preg_replace("!/\*/?%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!",'',$cache_parts[0][$curr_idx]); - } + // escape PHP tags in template content + $output .= preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '', $curr_split); + if (isset($cache_parts[0][$curr_idx])) { + // remove nocache tags from cache output + $output .= preg_replace("!/\*/?%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!", '', $cache_parts[0][$curr_idx]); + } + } return $this->cache_resource_object->writeCachedContent($this, $this->createPropertyHeader(true) . $output); } @@ -489,6 +493,12 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { if ($this->smarty->debugging) { Smarty_Internal_Debug::end_cache($this); } + } else { + if (!empty($this->properties['nocache_hash']) && !empty($this->parent->properties['nocache_hash'])) { + // replace nocache_hash + // var_dump($this->properties['nocache_hash'],$this->parent->properties['nocache_hash'],$this->rendered_content); + $this->rendered_content = preg_replace("/{$this->properties['nocache_hash']}/", $this->parent->properties['nocache_hash'], $this->rendered_content); + } } } @@ -711,9 +721,12 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { */ public function createPropertyHeader ($cache = false) { - $directory_security = $this->smarty->direct_access_security ? "\n" : ''; - // $properties_string = "decodeProperties(" . preg_replace('/\s*/', '',var_export($this->properties, true)) . "); ? >\n"; - $properties_string = "decodeProperties(" . var_export($this->properties, true) . "); /*/%%SmartyHeaderCode%%*/?>\n"; + $this->properties['fullpath'] = realpath($this->getTemplateFilepath()); + $properties_string = "properties['nocache_hash']}%%*/" ; + if ($this->smarty->direct_access_security) { + $properties_string .= "if(!defined('SMARTY_DIR')) exit('no direct access allowed');\n"; + } + $properties_string .= "\$_smarty_tpl->decodeProperties(" . var_export($this->properties, true) . "); /*/%%SmartyHeaderCode%%*/?>\n"; $plugins_string = ''; if (!$cache) { if (!empty($this->required_plugins['compiled'])) { @@ -725,15 +738,15 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { $plugins_string .= '?>'; } if (!empty($this->required_plugins['cache'])) { - $plugins_string .= 'properties['nocache_hash']}%%*/required_plugins['cache'] as $plugin_name => $data) { $plugin = 'smarty_' . $data['type'] . '_' . $plugin_name; $plugins_string .= "if (!is_callable(\'{$plugin}\')) include \'{$data['file']}\';\n"; } - $plugins_string .= "?>/*/%%SmartyNocache%%*/';?>\n"; + $plugins_string .= "?>/*/%%SmartyNocache:{$this->properties['nocache_hash']}%%*/';?>\n"; } } - return $directory_security . $properties_string . $plugins_string; + return $properties_string . $plugins_string; } /** @@ -741,6 +754,10 @@ class Smarty_Internal_Template extends Smarty_Internal_Data { */ public function decodeProperties ($properties) { +// if ($properties['fullpath'] != realpath($this->getTemplateFilepath())) { +// throw new Exception('CRC32 collision \'' . $properties['fullpath'] . '\''); +// } ; + $this->properties['nocache_hash'] = $properties['nocache_hash']; if (isset($properties['cache_lifetime'])) { $this->properties['cache_lifetime'] = $properties['cache_lifetime']; } diff --git a/libs/sysplugins/smarty_internal_templatecompilerbase.php b/libs/sysplugins/smarty_internal_templatecompilerbase.php index 8bb3ac05..848d4617 100644 --- a/libs/sysplugins/smarty_internal_templatecompilerbase.php +++ b/libs/sysplugins/smarty_internal_templatecompilerbase.php @@ -28,7 +28,7 @@ class Smarty_Internal_TemplateCompilerBase { */ public function __construct() { - $this->nocache_hash = md5(uniqid(rand(),true)); + $this->nocache_hash = uniqid(rand(),true); } // abstract function doCompile($_content); /** @@ -275,7 +275,7 @@ class Smarty_Internal_TemplateCompilerBase { $this->template->required_plugins['compiled'][$plugin_name] = $this->template->required_plugins['compiled'][$plugin_name]; } } - if ($type = 'modifier') { + if ($type == 'modifier') { $this->template->saved_modifer[$plugin_name] = true; } return $this->template->required_plugins_call[$plugin_name][$type]; @@ -301,7 +301,7 @@ class Smarty_Internal_TemplateCompilerBase { $this->template->required_plugins['compiled'][$plugin_name]['file'] = $file; $this->template->required_plugins['compiled'][$plugin_name]['type'] = $type; } - if ($type = 'modifier') { + if ($type == 'modifier') { $this->template->saved_modifer[$plugin_name] = true; }