packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/smarty/sysplugins/smarty_security.php
changeset 0 3906ca745819
equal deleted inserted replaced
-1:000000000000 0:3906ca745819
       
     1 <?php
       
     2 /**
       
     3  * Smarty plugin
       
     4  *
       
     5  * @package Smarty
       
     6  * @subpackage Security
       
     7  * @author Uwe Tews
       
     8  */
       
     9  
       
    10 /*
       
    11  * FIXME: Smarty_Security API
       
    12  *      - getter and setter instead of public properties would allow cultivating an internal cache properly
       
    13  *      - current implementation of isTrustedResourceDir() assumes that Smarty::$template_dir and Smarty::$config_dir are immutable
       
    14  *        the cache is killed every time either of the variables change. That means that two distinct Smarty objects with differing
       
    15  *        $template_dir or $config_dir should NOT share the same Smarty_Security instance, 
       
    16  *        as this would lead to (severe) performance penalty! how should this be handled? 
       
    17  */
       
    18 
       
    19 /**
       
    20  * This class does contain the security settings
       
    21  */
       
    22 class Smarty_Security {
       
    23 
       
    24     /**
       
    25      * This determines how Smarty handles "<?php ... ?>" tags in templates.
       
    26      * possible values:
       
    27      * <ul>
       
    28      *   <li>Smarty::PHP_PASSTHRU -> echo PHP tags as they are</li>
       
    29      *   <li>Smarty::PHP_QUOTE    -> escape tags as entities</li>
       
    30      *   <li>Smarty::PHP_REMOVE   -> remove php tags</li>
       
    31      *   <li>Smarty::PHP_ALLOW    -> execute php tags</li>
       
    32      * </ul>
       
    33      *
       
    34      * @var integer
       
    35      */
       
    36     public $php_handling = Smarty::PHP_PASSTHRU;
       
    37     /**
       
    38      * This is the list of template directories that are considered secure.
       
    39      * $template_dir is in this list implicitly.
       
    40      *
       
    41      * @var array
       
    42      */
       
    43     public $secure_dir = array();
       
    44     /**
       
    45      * This is an array of directories where trusted php scripts reside.
       
    46      * {@link $security} is disabled during their inclusion/execution.
       
    47      *
       
    48      * @var array
       
    49      */
       
    50     public $trusted_dir = array();
       
    51     /**
       
    52      * List of regular expressions (PCRE) that include trusted URIs
       
    53      *
       
    54      * @var array
       
    55      */
       
    56     public $trusted_uri = array();
       
    57     /**
       
    58      * This is an array of trusted static classes.
       
    59      *
       
    60      * If empty access to all static classes is allowed.
       
    61      * If set to 'none' none is allowed.
       
    62      * @var array
       
    63      */
       
    64     public $static_classes = array();
       
    65     /**
       
    66      * This is an array of trusted PHP functions.
       
    67      *
       
    68      * If empty all functions are allowed.
       
    69      * To disable all PHP functions set $php_functions = null.
       
    70      * @var array
       
    71      */
       
    72     public $php_functions = array(
       
    73         'isset', 'empty',
       
    74         'count', 'sizeof',
       
    75         'in_array', 'is_array',
       
    76         'time',
       
    77         'nl2br',
       
    78     );
       
    79     /**
       
    80      * This is an array of trusted PHP modifers.
       
    81      *
       
    82      * If empty all modifiers are allowed.
       
    83      * To disable all modifier set $modifiers = null.
       
    84      * @var array
       
    85      */
       
    86     public $php_modifiers = array(
       
    87         'escape',
       
    88         'count'
       
    89     );
       
    90     /**
       
    91      * This is an array of allowed tags.
       
    92      *
       
    93      * If empty no restriction by allowed_tags.
       
    94      * @var array
       
    95      */
       
    96     public $allowed_tags = array();
       
    97     /**
       
    98      * This is an array of disabled tags.
       
    99      *
       
   100      * If empty no restriction by disabled_tags.
       
   101      * @var array
       
   102      */
       
   103     public $disabled_tags = array();
       
   104     /**
       
   105      * This is an array of allowed modifier plugins.
       
   106      *
       
   107      * If empty no restriction by allowed_modifiers.
       
   108      * @var array
       
   109      */
       
   110     public $allowed_modifiers = array();
       
   111     /**
       
   112      * This is an array of disabled modifier plugins.
       
   113      *
       
   114      * If empty no restriction by disabled_modifiers.
       
   115      * @var array
       
   116      */
       
   117     public $disabled_modifiers = array();
       
   118     /**
       
   119      * This is an array of trusted streams.
       
   120      *
       
   121      * If empty all streams are allowed.
       
   122      * To disable all streams set $streams = null.
       
   123      * @var array
       
   124      */
       
   125     public $streams = array('file');
       
   126     /**
       
   127      * + flag if constants can be accessed from template
       
   128      * @var boolean
       
   129      */
       
   130     public $allow_constants = true;
       
   131     /**
       
   132      * + flag if super globals can be accessed from template
       
   133      * @var boolean
       
   134      */
       
   135     public $allow_super_globals = true;
       
   136 
       
   137     /**
       
   138      * Cache for $resource_dir lookups
       
   139      * @var array
       
   140      */
       
   141     protected $_resource_dir = null;
       
   142     /**
       
   143      * Cache for $template_dir lookups
       
   144      * @var array
       
   145      */
       
   146     protected $_template_dir = null;
       
   147     /**
       
   148      * Cache for $config_dir lookups
       
   149      * @var array
       
   150      */
       
   151     protected $_config_dir = null;
       
   152     /**
       
   153      * Cache for $secure_dir lookups
       
   154      * @var array
       
   155      */
       
   156     protected $_secure_dir = null;
       
   157     /**
       
   158      * Cache for $php_resource_dir lookups
       
   159      * @var array
       
   160      */
       
   161     protected $_php_resource_dir = null;
       
   162     /**
       
   163      * Cache for $trusted_dir lookups
       
   164      * @var array
       
   165      */
       
   166     protected $_trusted_dir = null;
       
   167     
       
   168     
       
   169     /**
       
   170      * @param Smarty $smarty
       
   171      */
       
   172     public function __construct($smarty)
       
   173     {
       
   174         $this->smarty = $smarty;
       
   175     }
       
   176     
       
   177     /**
       
   178      * Check if PHP function is trusted.
       
   179      *
       
   180      * @param string $function_name
       
   181      * @param object $compiler compiler object
       
   182      * @return boolean true if function is trusted
       
   183      * @throws SmartyCompilerException if php function is not trusted
       
   184      */
       
   185     public function isTrustedPhpFunction($function_name, $compiler)
       
   186     {
       
   187         if (isset($this->php_functions) && (empty($this->php_functions) || in_array($function_name, $this->php_functions))) {
       
   188             return true;
       
   189         }
       
   190 
       
   191         $compiler->trigger_template_error("PHP function '{$function_name}' not allowed by security setting");
       
   192         return false; // should not, but who knows what happens to the compiler in the future?
       
   193     }
       
   194 
       
   195     /**
       
   196      * Check if static class is trusted.
       
   197      *
       
   198      * @param string $class_name
       
   199      * @param object $compiler compiler object
       
   200      * @return boolean true if class is trusted
       
   201      * @throws SmartyCompilerException if static class is not trusted
       
   202      */
       
   203     public function isTrustedStaticClass($class_name, $compiler)
       
   204     {
       
   205         if (isset($this->static_classes) && (empty($this->static_classes) || in_array($class_name, $this->static_classes))) {
       
   206             return true;
       
   207         }
       
   208 
       
   209         $compiler->trigger_template_error("access to static class '{$class_name}' not allowed by security setting");
       
   210         return false; // should not, but who knows what happens to the compiler in the future?
       
   211     }
       
   212 
       
   213     /**
       
   214      * Check if PHP modifier is trusted.
       
   215      *
       
   216      * @param string $modifier_name
       
   217      * @param object $compiler compiler object
       
   218      * @return boolean true if modifier is trusted
       
   219      * @throws SmartyCompilerException if modifier is not trusted
       
   220      */
       
   221     public function isTrustedPhpModifier($modifier_name, $compiler)
       
   222     {
       
   223         if (isset($this->php_modifiers) && (empty($this->php_modifiers) || in_array($modifier_name, $this->php_modifiers))) {
       
   224             return true;
       
   225         }
       
   226 
       
   227         $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting");
       
   228         return false; // should not, but who knows what happens to the compiler in the future?
       
   229     }
       
   230 
       
   231     /**
       
   232      * Check if tag is trusted.
       
   233      *
       
   234      * @param string $tag_name
       
   235      * @param object $compiler compiler object
       
   236      * @return boolean true if tag is trusted
       
   237      * @throws SmartyCompilerException if modifier is not trusted
       
   238      */
       
   239     public function isTrustedTag($tag_name, $compiler)
       
   240     {
       
   241         // check for internal always required tags
       
   242         if (in_array($tag_name, array('assign', 'call', 'private_filter', 'private_block_plugin', 'private_function_plugin', 'private_object_block_function',
       
   243                     'private_object_function', 'private_registered_function', 'private_registered_block', 'private_special_variable', 'private_print_expression', 'private_modifier'))) {
       
   244             return true;
       
   245         }
       
   246         // check security settings
       
   247         if (empty($this->allowed_tags)) {
       
   248             if (empty($this->disabled_tags) || !in_array($tag_name, $this->disabled_tags)) {
       
   249                 return true;
       
   250             } else {
       
   251                 $compiler->trigger_template_error("tag '{$tag_name}' disabled by security setting", $compiler->lex->taglineno);
       
   252             }
       
   253         } else if (in_array($tag_name, $this->allowed_tags) && !in_array($tag_name, $this->disabled_tags)) {
       
   254             return true;
       
   255         } else {
       
   256             $compiler->trigger_template_error("tag '{$tag_name}' not allowed by security setting", $compiler->lex->taglineno);
       
   257         }
       
   258         return false; // should not, but who knows what happens to the compiler in the future?
       
   259     }
       
   260 
       
   261     /**
       
   262      * Check if modifier plugin is trusted.
       
   263      *
       
   264      * @param string $modifier_name
       
   265      * @param object $compiler compiler object
       
   266      * @return boolean true if tag is trusted
       
   267      * @throws SmartyCompilerException if modifier is not trusted
       
   268      */
       
   269     public function isTrustedModifier($modifier_name, $compiler)
       
   270     {
       
   271         // check for internal always allowed modifier
       
   272         if (in_array($modifier_name, array('default'))) {
       
   273             return true;
       
   274         }
       
   275         // check security settings
       
   276         if (empty($this->allowed_modifiers)) {
       
   277             if (empty($this->disabled_modifiers) || !in_array($modifier_name, $this->disabled_modifiers)) {
       
   278                 return true;
       
   279             } else {
       
   280                 $compiler->trigger_template_error("modifier '{$modifier_name}' disabled by security setting", $compiler->lex->taglineno);
       
   281             }
       
   282         } else if (in_array($modifier_name, $this->allowed_modifiers) && !in_array($modifier_name, $this->disabled_modifiers)) {
       
   283             return true;
       
   284         } else {
       
   285             $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting", $compiler->lex->taglineno);
       
   286         }
       
   287         return false; // should not, but who knows what happens to the compiler in the future?
       
   288     }
       
   289 
       
   290     /**
       
   291      * Check if stream is trusted.
       
   292      *
       
   293      * @param string $stream_name
       
   294      * @return boolean true if stream is trusted
       
   295      * @throws SmartyException if stream is not trusted
       
   296      */
       
   297     public function isTrustedStream($stream_name)
       
   298     {
       
   299         if (isset($this->streams) && (empty($this->streams) || in_array($stream_name, $this->streams))) {
       
   300             return true;
       
   301         }
       
   302 
       
   303         throw new SmartyException("stream '{$stream_name}' not allowed by security setting");
       
   304     }
       
   305 
       
   306     /**
       
   307      * Check if directory of file resource is trusted.
       
   308      *
       
   309      * @param string $filepath
       
   310      * @return boolean true if directory is trusted
       
   311      * @throws SmartyException if directory is not trusted
       
   312      */
       
   313     public function isTrustedResourceDir($filepath)
       
   314     {
       
   315         $_template = false;
       
   316         $_config = false;
       
   317         $_secure = false;
       
   318 
       
   319         $_template_dir = $this->smarty->getTemplateDir();
       
   320         $_config_dir = $this->smarty->getConfigDir();
       
   321 
       
   322         // check if index is outdated
       
   323         if ((!$this->_template_dir || $this->_template_dir !== $_template_dir)
       
   324                 || (!$this->_config_dir || $this->_config_dir !== $_config_dir)
       
   325                 || (!empty($this->secure_dir) && (!$this->_secure_dir || $this->_secure_dir !== $this->secure_dir))
       
   326         ) {
       
   327             $this->_resource_dir = array();
       
   328             $_template = true;
       
   329             $_config = true;
       
   330             $_secure = !empty($this->secure_dir);
       
   331         }
       
   332 
       
   333         // rebuild template dir index
       
   334         if ($_template) {
       
   335             $this->_template_dir = $_template_dir;
       
   336             foreach ($_template_dir as $directory) {
       
   337                 $directory = realpath($directory);
       
   338                 $this->_resource_dir[$directory] = true;
       
   339             }
       
   340         }
       
   341 
       
   342         // rebuild config dir index
       
   343         if ($_config) {
       
   344             $this->_config_dir = $_config_dir;
       
   345             foreach ($_config_dir as $directory) {
       
   346                 $directory = realpath($directory);
       
   347                 $this->_resource_dir[$directory] = true;
       
   348             }
       
   349         }
       
   350 
       
   351         // rebuild secure dir index
       
   352         if ($_secure) {
       
   353             $this->_secure_dir = $this->secure_dir;
       
   354             foreach ((array) $this->secure_dir as $directory) {
       
   355                 $directory = realpath($directory);
       
   356                 $this->_resource_dir[$directory] = true;
       
   357             }
       
   358         }
       
   359 
       
   360         $_filepath = realpath($filepath);
       
   361         $directory = dirname($_filepath);
       
   362         $_directory = array();
       
   363         while (true) {
       
   364             // remember the directory to add it to _resource_dir in case we're successful
       
   365             $_directory[$directory] = true;
       
   366             // test if the directory is trusted
       
   367             if (isset($this->_resource_dir[$directory])) {
       
   368                 // merge sub directories of current $directory into _resource_dir to speed up subsequent lookups
       
   369                 $this->_resource_dir = array_merge($this->_resource_dir, $_directory);
       
   370                 return true;
       
   371             }
       
   372             // abort if we've reached root
       
   373             if (($pos = strrpos($directory, DS)) === false || !isset($directory[1])) {
       
   374                 break;
       
   375             }
       
   376             // bubble up one level
       
   377             $directory = substr($directory, 0, $pos);
       
   378         }
       
   379 
       
   380         // give up
       
   381         throw new SmartyException("directory '{$_filepath}' not allowed by security setting");
       
   382     }
       
   383     
       
   384     /**
       
   385      * Check if URI (e.g. {fetch} or {html_image}) is trusted
       
   386      *
       
   387      * To simplify things, isTrustedUri() resolves all input to "{$PROTOCOL}://{$HOSTNAME}".
       
   388      * So "http://username:password@hello.world.example.org:8080/some-path?some=query-string"
       
   389      * is reduced to "http://hello.world.example.org" prior to applying the patters from {@link $trusted_uri}.
       
   390      * @param string $uri 
       
   391      * @return boolean true if URI is trusted
       
   392      * @throws SmartyException if URI is not trusted
       
   393      * @uses $trusted_uri for list of patterns to match against $uri
       
   394      */
       
   395     public function isTrustedUri($uri)
       
   396     {
       
   397         $_uri = parse_url($uri);
       
   398         if (!empty($_uri['scheme']) && !empty($_uri['host'])) {
       
   399             $_uri = $_uri['scheme'] . '://' . $_uri['host'];
       
   400             foreach ($this->trusted_uri as $pattern) {
       
   401                 if (preg_match($pattern, $_uri)) {
       
   402                     return true;
       
   403                 }
       
   404             }
       
   405         }
       
   406         
       
   407         throw new SmartyException("URI '{$uri}' not allowed by security setting");
       
   408     }
       
   409     
       
   410     /**
       
   411      * Check if directory of file resource is trusted.
       
   412      *
       
   413      * @param string $filepath
       
   414      * @return boolean true if directory is trusted
       
   415      * @throws SmartyException if PHP directory is not trusted
       
   416      */
       
   417     public function isTrustedPHPDir($filepath)
       
   418     {
       
   419         if (empty($this->trusted_dir)) {
       
   420             throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)");
       
   421         }
       
   422 
       
   423         // check if index is outdated
       
   424         if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) {
       
   425             $this->_php_resource_dir = array();
       
   426 
       
   427             $this->_trusted_dir = $this->trusted_dir;
       
   428             foreach ((array) $this->trusted_dir as $directory) {
       
   429                 $directory = realpath($directory);
       
   430                 $this->_php_resource_dir[$directory] = true;
       
   431             }
       
   432         }
       
   433 
       
   434         $_filepath = realpath($filepath);
       
   435         $directory = dirname($_filepath);
       
   436         $_directory = array();
       
   437         while (true) {
       
   438             // remember the directory to add it to _resource_dir in case we're successful
       
   439             $_directory[] = $directory;
       
   440             // test if the directory is trusted
       
   441             if (isset($this->_php_resource_dir[$directory])) {
       
   442                 // merge sub directories of current $directory into _resource_dir to speed up subsequent lookups
       
   443                 $this->_php_resource_dir = array_merge($this->_php_resource_dir, $_directory);
       
   444                 return true;
       
   445             }
       
   446             // abort if we've reached root
       
   447             if (($pos = strrpos($directory, DS)) === false || !isset($directory[2])) {
       
   448                 break;
       
   449             }
       
   450             // bubble up one level
       
   451             $directory = substr($directory, 0, $pos);
       
   452         }
       
   453 
       
   454         throw new SmartyException("directory '{$_filepath}' not allowed by security setting");
       
   455     }
       
   456 
       
   457 }
       
   458 
       
   459 ?>