- update for security fixes

- make modifier plugins always trusted
This commit is contained in:
Uwe.Tews
2009-12-28 15:27:13 +00:00
parent f908aa0a27
commit de4957a151
6 changed files with 70 additions and 46 deletions

View File

@@ -1,3 +1,7 @@
12/28/2009
- update for security fixes
- make modifier plugins always trusted
12/27/2009 12/27/2009
--- this is a major update with a couple of internal changes --- --- this is a major update with a couple of internal changes ---
- new config file lexer/parser (thanks to Thue Jnaus Kristensen) - new config file lexer/parser (thanks to Thue Jnaus Kristensen)

View File

@@ -68,7 +68,6 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_CompileBase {
$_tpl->forceNocache = true; $_tpl->forceNocache = true;
$_tpl->suppressHeader = true; $_tpl->suppressHeader = true;
$_tpl->suppressFileDependency = true; $_tpl->suppressFileDependency = true;
if (strpos($this->smarty->block_data[$_name]['source'], '%%%%SMARTY_PARENT%%%%') !== false) { if (strpos($this->smarty->block_data[$_name]['source'], '%%%%SMARTY_PARENT%%%%') !== false) {
$_output = str_replace('%%%%SMARTY_PARENT%%%%', $compiler->template->extracted_compiled_code, $_tpl->getCompiledTemplate()); $_output = str_replace('%%%%SMARTY_PARENT%%%%', $compiler->template->extracted_compiled_code, $_tpl->getCompiledTemplate());
} elseif ($this->smarty->block_data[$_name]['mode'] == 'prepend') { } 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])) { } elseif (!empty($this->smarty->block_data[$_name])) {
$_output = $_tpl->getCompiledTemplate(); $_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); unset($_tpl);
} else { } else {
$_output = $compiler->template->extracted_compiled_code; $_output = $compiler->template->extracted_compiled_code;

View File

@@ -46,7 +46,9 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase {
// get compiled code // get compiled code
$compiled_tpl = $tpl->getCompiledTemplate(); $compiled_tpl = $tpl->getCompiledTemplate();
// remove header code // 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; $has_compiled_template = true;
} }
} }

View File

@@ -26,32 +26,33 @@ class Smarty_Internal_Compile_Private_Modifier extends Smarty_Internal_CompileBa
$this->required_attributes = array('modifier', 'params'); $this->required_attributes = array('modifier', 'params');
// check and get attributes // check and get attributes
$_attr = $this->_get_attributes($args); $_attr = $this->_get_attributes($args);
// check if modifier allowed // check for registered modifier
if (!$this->compiler->template->security || $this->smarty->security_handler->isTrustedModifier($_attr['modifier'], $this->compiler)) { if (isset($compiler->smarty->registered_plugins['modifier'][$_attr['modifier']])) {
// check for registered or plugin modifier $function = $compiler->smarty->registered_plugins['modifier'][$_attr['modifier']][0];
if (isset($compiler->smarty->registered_plugins['modifier'][$_attr['modifier']])) { if (!is_array($function)) {
$function = $compiler->smarty->registered_plugins['modifier'][$_attr['modifier']][0]; $output = "{$function}({$_attr['params']})";
if (!is_array($function)) { } else if (is_object($function[0])) {
$output = "{$function}({$_attr['params']})"; $output = 'call_user_func_array($_smarty_tpl->smarty->registered_plugins[\'modifier\'][\'' . $_attr['modifier'] . '\'][0],array(' . $_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']})";
} else { } 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;
} }
} }

View File

@@ -61,7 +61,7 @@ class Smarty_Internal_Template extends Smarty_Internal_Data {
// storage for plugin // storage for plugin
public $plugin_data = array(); public $plugin_data = array();
// special properties // special properties
public $properties = array(); public $properties = null;
// storage for block data // storage for block data
public $block_data = array(); public $block_data = array();
// required plugins // required plugins
@@ -92,6 +92,8 @@ class Smarty_Internal_Template extends Smarty_Internal_Data {
$this->security = $this->smarty->security; $this->security = $this->smarty->security;
$this->parent = $_parent; $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 // dummy local smarty variable
$this->tpl_vars['smarty'] = new Smarty_Variable; $this->tpl_vars['smarty'] = new Smarty_Variable;
// Template resource // Template resource
@@ -331,16 +333,18 @@ class Smarty_Internal_Template extends Smarty_Internal_Data {
// build file dependency string // build file dependency string
$this->properties['cache_lifetime'] = $this->cache_lifetime; $this->properties['cache_lifetime'] = $this->cache_lifetime;
// get text between non-cached items // 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 // get non-cached items
preg_match_all("!/\*%%SmartyNocache:{$this->properties['nocache_hash']}%%\*\/(.+?)/\*/%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!s",$this->rendered_content,$cache_parts); preg_match_all("!/\*%%SmartyNocache:{$this->properties['nocache_hash']}%%\*\/(.+?)/\*/%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!s", $this->rendered_content, $cache_parts);
$output = ''; $output = '';
// loop over items, stitch back together // loop over items, stitch back together
foreach($cache_split as $curr_idx => $curr_split) { foreach($cache_split as $curr_idx => $curr_split) {
// escape PHP tags in template content // escape PHP tags in template content
$output .= preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '<?php echo \'$1\'; ?>', $curr_split); $output .= preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '<?php echo \'$1\'; ?>', $curr_split);
// remove nocache tags from cache output if (isset($cache_parts[0][$curr_idx])) {
$output .= preg_replace("!/\*/?%%SmartyNocache:{$this->properties['nocache_hash']}%%\*/!",'',$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); 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) { if ($this->smarty->debugging) {
Smarty_Internal_Debug::end_cache($this); 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) public function createPropertyHeader ($cache = false)
{ {
$directory_security = $this->smarty->direct_access_security ? "<?php /*%%SmartyHeaderCode%%*/ if(!defined('SMARTY_DIR')) exit('no direct access allowed'); /*/%%SmartyHeaderCode%%*/?>\n" : ''; $this->properties['fullpath'] = realpath($this->getTemplateFilepath());
// $properties_string = "<?php \$_smarty_tpl->decodeProperties(" . preg_replace('/\s*/', '',var_export($this->properties, true)) . "); ? >\n"; $properties_string = "<?php /*%%SmartyHeaderCode:{$this->properties['nocache_hash']}%%*/" ;
$properties_string = "<?php /*%%SmartyHeaderCode%%*/ \$_smarty_tpl->decodeProperties(" . var_export($this->properties, true) . "); /*/%%SmartyHeaderCode%%*/?>\n"; 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 = ''; $plugins_string = '';
if (!$cache) { if (!$cache) {
if (!empty($this->required_plugins['compiled'])) { if (!empty($this->required_plugins['compiled'])) {
@@ -725,15 +738,15 @@ class Smarty_Internal_Template extends Smarty_Internal_Data {
$plugins_string .= '?>'; $plugins_string .= '?>';
} }
if (!empty($this->required_plugins['cache'])) { if (!empty($this->required_plugins['cache'])) {
$plugins_string .= '<?php echo \'/*%%SmartyNocache%%*/<?php '; $plugins_string .= "<?php echo '/*%%SmartyNocache:{$this->properties['nocache_hash']}%%*/<?php ";
foreach($this->required_plugins['cache'] as $plugin_name => $data) { foreach($this->required_plugins['cache'] as $plugin_name => $data) {
$plugin = 'smarty_' . $data['type'] . '_' . $plugin_name; $plugin = 'smarty_' . $data['type'] . '_' . $plugin_name;
$plugins_string .= "if (!is_callable(\'{$plugin}\')) include \'{$data['file']}\';\n"; $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) 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'])) { if (isset($properties['cache_lifetime'])) {
$this->properties['cache_lifetime'] = $properties['cache_lifetime']; $this->properties['cache_lifetime'] = $properties['cache_lifetime'];
} }

View File

@@ -28,7 +28,7 @@ class Smarty_Internal_TemplateCompilerBase {
*/ */
public function __construct() public function __construct()
{ {
$this->nocache_hash = md5(uniqid(rand(),true)); $this->nocache_hash = uniqid(rand(),true);
} }
// abstract function doCompile($_content); // 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]; $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; $this->template->saved_modifer[$plugin_name] = true;
} }
return $this->template->required_plugins_call[$plugin_name][$type]; 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]['file'] = $file;
$this->template->required_plugins['compiled'][$plugin_name]['type'] = $type; $this->template->required_plugins['compiled'][$plugin_name]['type'] = $type;
} }
if ($type = 'modifier') { if ($type == 'modifier') {
$this->template->saved_modifer[$plugin_name] = true; $this->template->saved_modifer[$plugin_name] = true;
} }