- added trusted stream checking to security

- internal changes at file dependency check for caching
This commit is contained in:
Uwe.Tews
2009-04-26 16:56:17 +00:00
parent 4f4f2d3dd3
commit c724b720be
9 changed files with 112 additions and 43 deletions

View File

@@ -1,3 +1,7 @@
04/26/2009
- added trusted stream checking to security
- internal changes at file dependency check for caching
04/24/2009 04/24/2009
- changed name of {template} tag to {function} - changed name of {template} tag to {function}
- added new {template} tag - added new {template} tag

View File

@@ -47,6 +47,13 @@ define('SMARTY_PARENT_SCOPE', 1);
define('SMARTY_ROOT_SCOPE', 2); define('SMARTY_ROOT_SCOPE', 2);
define('SMARTY_GLOBAL_SCOPE', 3); 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 * load required base class for creation of the smarty object
*/ */
@@ -111,7 +118,7 @@ class Smarty extends Smarty_Internal_TemplateBase {
// config var settings // config var settings
public $config_overwrite = true; //Controls whether variables with the same name overwrite each other. 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_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 // config vars
public $config_vars = array(); public $config_vars = array();
// assigned tpl vars // assigned tpl vars
@@ -172,9 +179,9 @@ class Smarty extends Smarty_Internal_TemplateBase {
* Class constructor, initializes basic smarty properties * Class constructor, initializes basic smarty properties
*/ */
public function __construct() public function __construct()
{ {
// set instance object // set instance object
self::instance($this); self::instance($this);
if (is_callable('mb_internal_encoding')) { if (is_callable('mb_internal_encoding')) {
$this->has_mb = true; $this->has_mb = true;
@@ -204,7 +211,11 @@ class Smarty extends Smarty_Internal_TemplateBase {
$this->loadPlugin('Smarty_Internal_Run_Filter'); $this->loadPlugin('Smarty_Internal_Run_Filter');
$this->filter_handler = new Smarty_Internal_Run_Filter; $this->filter_handler = new Smarty_Internal_Run_Filter;
if (!$this->debugging && $this->debugging_ctrl == 'URL') { 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)) {
if (false !== strpos($_query_string, $this->smarty_debug_id . '=on')) { if (false !== strpos($_query_string, $this->smarty_debug_id . '=on')) {
// enable debugging for this browser session // enable debugging for this browser session

View File

@@ -65,6 +65,15 @@ class Smarty_Security_Policy {
* @var array * @var array
*/ */
public $modifiers = array('escape','count'); 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 + flag if constants can be accessed from template
*/ */

View File

@@ -44,7 +44,8 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase {
$_parent_scope = SMARTY_GLOBAL_SCOPE; $_parent_scope = SMARTY_GLOBAL_SCOPE;
} }
} }
// default for included templates
$_caching = SMARTY_CACHING_OFF;
/* /*
* if the {include} tag provides individual parameter for caching * if the {include} tag provides individual parameter for caching
* it will not be included into the common cache file and treated like * 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 (isset($_attr['nocache'])) {
if ($_attr['nocache'] == 'true') { if ($_attr['nocache'] == 'true') {
$_caching = 'false';
$this->compiler->tag_nocache = true; $this->compiler->tag_nocache = true;
} }
} }
if (isset($_attr['caching'])) { if (isset($_attr['caching'])) {
if ($_attr['caching'] == 'true') { 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 // create template object
$_output = "<?php \$_template = new Smarty_Template ($include_file, \$_smarty_tpl, \$_smarty_tpl->cache_id, \$_smarty_tpl->compile_id);"; $_output = "<?php \$_template = new Smarty_Template ($include_file, \$_smarty_tpl, \$_smarty_tpl->cache_id, \$_smarty_tpl->compile_id);";
// delete {include} standard attributes // delete {include} standard attributes
@@ -90,11 +84,7 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase {
if (isset($_caching_lifetime)) { if (isset($_caching_lifetime)) {
$_output .= "\$_template->caching_lifetime = $_caching_lifetime;"; $_output .= "\$_template->caching_lifetime = $_caching_lifetime;";
} }
if (isset($_caching)) { $_output .= "\$_template->caching = $_caching;";
$_output .= "\$_template->caching = $_caching;";
} elseif (isset($_caching_lifetime)) {
$_output .= "\$_template->caching = true;";
}
// was there an assign attribute // was there an assign attribute
if (isset($_assign)) { if (isset($_assign)) {
$_output .= "\$_smarty_tpl->assign($_assign,\$_smarty_tpl->smarty->fetch(\$_template)); ?>"; $_output .= "\$_smarty_tpl->assign($_assign,\$_smarty_tpl->smarty->fetch(\$_template)); ?>";

View File

@@ -1,20 +1,20 @@
<?php <?php
/** /**
* Smarty Internal Plugin Compile Internalfunctioncall * Smarty Internal Plugin Compile Internal_Function_Call
* *
* Compiles the {internalfunctioncall} tag * Compiles the {internal_function_call} tag
* *
* @package Smarty * @package Smarty
* @subpackage Compiler * @subpackage Compiler
* @author Uwe Tews * @author Uwe Tews
*/ */
/** /**
* Smarty Internal Plugin Compile Internalfunctioncall Class * Smarty Internal Plugin Compile Internal_Function_Call Class
*/ */
class Smarty_Internal_Compile_Internalfunctioncall extends Smarty_Internal_CompileBase { class Smarty_Internal_Compile_Internal_Function_Call extends Smarty_Internal_CompileBase {
/** /**
* Compiles code for the {internalfunctioncall} tag * Compiles code for the {internal_function_call} tag
* *
* @param array $args array with attributes from parser * @param array $args array with attributes from parser
* @param object $compiler compiler object * @param object $compiler compiler object
@@ -31,8 +31,8 @@ class Smarty_Internal_Compile_Internalfunctioncall extends Smarty_Internal_Compi
if (isset($_attr['assign'])) { if (isset($_attr['assign'])) {
// output will be stored in a smarty variable instead of beind displayed // output will be stored in a smarty variable instead of beind displayed
$_assign = $_attr['assign']; $_assign = $_attr['assign'];
} }
$_name = trim( $_attr['name'],"'"); $_name = trim($_attr['name'], "'");
// create template object // create template object
$_output = "<?php \$_template = new Smarty_Template ('string:', \$_smarty_tpl);\n"; $_output = "<?php \$_template = new Smarty_Template ('string:', \$_smarty_tpl);\n";
// assign default paramter // assign default paramter
@@ -51,14 +51,14 @@ class Smarty_Internal_Compile_Internalfunctioncall extends Smarty_Internal_Compi
foreach ($_attr as $_key => $_value) { foreach ($_attr as $_key => $_value) {
$_output .= "\$_template->assign('$_key',$_value);\n"; $_output .= "\$_template->assign('$_key',$_value);\n";
} }
} }
if (isset($compiler->template->properties['function'][$_name]['compiled'])) { 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"; $_output .= "\$_template->compiled_template = '$_compiled';\n \$_template->mustCompile = false;\n";
} else { } else {
// for recursion // for recursion
$_output .= "\$_template->compiled_template = \$_smarty_tpl->compiled_template;\n \$_template->mustCompile = false;\n"; $_output .= "\$_template->compiled_template = \$_smarty_tpl->compiled_template;\n \$_template->mustCompile = false;\n";
} }
// was there an assign attribute // was there an assign attribute
if (isset($_assign)) { if (isset($_assign)) {
$_output .= "\$_smarty_tpl->assign($_assign,\$_smarty_tpl->smarty->fetch(\$_template)); ?>"; $_output .= "\$_smarty_tpl->assign($_assign,\$_smarty_tpl->smarty->fetch(\$_template)); ?>";

View File

@@ -18,8 +18,7 @@ class Smarty_Internal_Debug extends Smarty_Internal_TemplateBase {
*/ */
public function display_debug() public function display_debug()
{ {
$this->smarty = Smarty::instance(); $this->smarty = Smarty::instance();
// get template names // get template names
$i = 0; $i = 0;
$_template_data = array(); $_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]['render_time'] = $_template_obj->render_time;
$_template_data[$i]['cache_time'] = $_template_obj->cache_time; $_template_data[$i]['cache_time'] = $_template_obj->cache_time;
$i++; $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++;
}
}
} }
} }
} }

View File

@@ -10,7 +10,6 @@
* This class contains all methods for security checking * This class contains all methods for security checking
*/ */
class Smarty_Internal_Security_Handler extends Smarty_Internal_Base { class Smarty_Internal_Security_Handler extends Smarty_Internal_Base {
/** /**
* Check if PHP function is trusted. * Check if PHP function is trusted.
* *
@@ -44,6 +43,22 @@ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base {
return false; 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. * Check if directory of file resource is trusted.
@@ -70,7 +85,7 @@ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base {
if ($_cd == $_rp) { if ($_cd == $_rp) {
return true; return true;
} elseif (strncmp($_rp, $_cd, strlen($_cd)) == 0 && } 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; return true;
} }
} }
@@ -90,7 +105,7 @@ class Smarty_Internal_Security_Handler extends Smarty_Internal_Base {
function isTrustedPHPDir($filepath) function isTrustedPHPDir($filepath)
{ {
$_rp = realpath($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) { foreach ((array)$this->smarty->security_policy->trusted_dir as $curr_dir) {
if (($_cd = realpath($curr_dir)) !== false) { if (($_cd = realpath($curr_dir)) !== false) {
if ($_cd == $_rp) { if ($_cd == $_rp) {

View File

@@ -89,7 +89,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase {
$this->caching_type = $this->smarty->default_caching_type; $this->caching_type = $this->smarty->default_caching_type;
$this->security = $this->smarty->security; $this->security = $this->smarty->security;
$this->cache_resource_class = 'Smarty_Internal_CacheResource_' . ucfirst($this->caching_type); $this->cache_resource_class = 'Smarty_Internal_CacheResource_' . ucfirst($this->caching_type);
$this->parent = $_parent; $this->parent = $_parent;
$this->properties['file_dependency'] = array();
// Template resource // Template resource
$this->template_resource = $template_resource; $this->template_resource = $template_resource;
// parse resource name // parse resource name
@@ -286,7 +287,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase {
if ($this->compiler_object->compileTemplate($this)) { if ($this->compiler_object->compileTemplate($this)) {
// compiling succeded // compiling succeded
if (!$this->isEvaluated()) { if (!$this->isEvaluated()) {
// build file dependency string // build template property string
$this->properties_string = '<?php /*' . serialize($this->properties) . "*/ ?>\n"; $this->properties_string = '<?php /*' . serialize($this->properties) . "*/ ?>\n";
// write compiled template // write compiled template
$this->smarty->write_file_object->writeFile($this->getCompiledFilepath(), $this->properties_string . $this->dir_acc_sec_string . $this->getCompiledTemplate()); $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 ()}"); throw new Exception("Error compiling template {$this->getTemplateFilepath ()}");
return false; 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 () public function writeCachedContent ()
{ {
// build file dependency string // build file dependency string
$this->properties['caching_lifetime'] = $this->caching_lifetime;
$this->properties_string = '<?php /*' . serialize($this->properties) . "*/ ?>\n"; $this->properties_string = '<?php /*' . serialize($this->properties) . "*/ ?>\n";
$this->rendered_content = $this->properties_string . $this->dir_acc_sec_string . $this->rendered_content; $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); 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) { if ($this->isCached === null) {
$this->isCached = false; $this->isCached = false;
if ($this->caching && !$this->isEvaluated()) { if ($this->caching && !$this->isEvaluated() && !$this->force_compile) {
if (!$this->mustCompile() && $this->getTemplateTimestamp() <= $this->getCachedTimestamp() && ((time() <= $this->getCachedTimestamp() + $this->caching_lifetime) || $this->caching_lifetime < 0)) { 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); $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; $this->isCached = true;
} }
} }
@@ -415,10 +437,17 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase {
// include PHP template // include PHP template
include($this->getTemplateFilepath ()); include($this->getTemplateFilepath ());
} }
$this->render_time = $this->_get_time() - $_start_time; $this->render_time += $this->_get_time() - $_start_time;
$this->rendered_content = ob_get_clean(); $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 // write to cache when nessecary
if (!$this->isEvaluated() && $this->caching) { if (!$this->isEvaluated() && $this->caching) {
$this->properties['file_dependency'] = array_unique($this->properties['file_dependency']);
// write rendered template // write rendered template
$this->writeCachedContent($this); $this->writeCachedContent($this);
} }
@@ -452,7 +481,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase {
ob_start(); ob_start();
eval("?>" . $this->rendered_content); eval("?>" . $this->rendered_content);
$this->updateParentVariables(); $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']))? 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(); $this->smarty->filter_handler->execute('output', ob_get_clean()) : ob_get_clean();
} else { } else {
@@ -502,6 +531,9 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase {
$_known_stream = stream_get_wrappers(); $_known_stream = stream_get_wrappers();
if (in_array($this->resource_type, $_known_stream)) { if (in_array($this->resource_type, $_known_stream)) {
// is known stream // is known stream
if ($this->smarty->security) {
$this->smarty->security_handler->isTrustedStream($this->resource_type);
}
if (!isset($this->resource_objects['stream'])) { if (!isset($this->resource_objects['stream'])) {
$this->smarty->loadPlugin('Smarty_Internal_Resource_Stream'); $this->smarty->loadPlugin('Smarty_Internal_Resource_Stream');
$this->resource_objects['stream'] = new Smarty_Internal_Resource_Stream; $this->resource_objects['stream'] = new Smarty_Internal_Resource_Stream;

View File

@@ -115,7 +115,7 @@ class Smarty_Internal_TemplateCompilerBase extends Smarty_Internal_Base {
if (isset($this->template->properties['function'][$tag])) { if (isset($this->template->properties['function'][$tag])) {
// template defined by {template} tag // template defined by {template} tag
$args['name'] = $tag; $args['name'] = $tag;
$tag = 'internalfunctioncall'; $tag = 'internal_function_call';
} }
if (!($_output = $this->$tag($args, $this)) === false) { if (!($_output = $this->$tag($args, $this)) === false) {
if ($_output !== true) { if ($_output !== true) {