Code

Removed smarty packages from references
[gosa.git] / gosa-core / include / smarty / sysplugins / smarty_security.php
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  */
19 /**
20  * This class does contain the security settings
21  */
22 class Smarty_Security {
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      * This is an array of trusted static classes.
53      *
54      * If empty access to all static classes is allowed.
55      * If set to 'none' none is allowed.
56      * @var array
57      */
58     public $static_classes = array();
59     /**
60      * This is an array of trusted PHP functions.
61      *
62      * If empty all functions are allowed.
63      * To disable all PHP functions set $php_functions = null.
64      * @var array
65      */
66     public $php_functions = array(
67         'isset', 'empty',
68         'count', 'sizeof',
69         'in_array', 'is_array',
70         'time',
71         'nl2br',
72     );
73     /**
74      * This is an array of trusted PHP modifers.
75      *
76      * If empty all modifiers are allowed.
77      * To disable all modifier set $modifiers = null.
78      * @var array
79      */
80     public $php_modifiers = array(
81         'escape',
82         'count'
83     );
84     /**
85      * This is an array of allowed tags.
86      *
87      * If empty no restriction by allowed_tags.
88      * @var array
89      */
90     public $allowed_tags = array();
91     /**
92      * This is an array of disabled tags.
93      *
94      * If empty no restriction by disabled_tags.
95      * @var array
96      */
97     public $disabled_tags = array();
98     /**
99      * This is an array of allowed modifier plugins.
100      *
101      * If empty no restriction by allowed_modifiers.
102      * @var array
103      */
104     public $allowed_modifiers = array();
105     /**
106      * This is an array of disabled modifier plugins.
107      *
108      * If empty no restriction by disabled_modifiers.
109      * @var array
110      */
111     public $disabled_modifiers = array();
112     /**
113      * This is an array of trusted streams.
114      *
115      * If empty all streams are allowed.
116      * To disable all streams set $streams = null.
117      * @var array
118      */
119     public $streams = array('file');
120     /**
121      * + flag if constants can be accessed from template
122      * @var boolean
123      */
124     public $allow_constants = true;
125     /**
126      * + flag if super globals can be accessed from template
127      * @var boolean
128      */
129     public $allow_super_globals = true;
131     /**
132      * Cache for $resource_dir lookups
133      * @var array
134      */
135     protected $_resource_dir = null;
136     /**
137      * Cache for $template_dir lookups
138      * @var array
139      */
140     protected $_template_dir = null;
141     /**
142      * Cache for $config_dir lookups
143      * @var array
144      */
145     protected $_config_dir = null;
146     /**
147      * Cache for $secure_dir lookups
148      * @var array
149      */
150     protected $_secure_dir = null;
151     /**
152      * Cache for $php_resource_dir lookups
153      * @var array
154      */
155     protected $_php_resource_dir = null;
156     /**
157      * Cache for $trusted_dir lookups
158      * @var array
159      */
160     protected $_trusted_dir = null;
161     
162     
163     /**
164      * @param Smarty $smarty
165      */
166     public function __construct($smarty)
167     {
168         $this->smarty = $smarty;
169     }
170     
171     /**
172      * Check if PHP function is trusted.
173      *
174      * @param string $function_name
175      * @param object $compiler compiler object
176      * @return boolean true if function is trusted
177      * @throws SmartyCompilerException if php function is not trusted
178      */
179     public function isTrustedPhpFunction($function_name, $compiler)
180     {
181         if (isset($this->php_functions) && (empty($this->php_functions) || in_array($function_name, $this->php_functions))) {
182             return true;
183         }
185         $compiler->trigger_template_error("PHP function '{$function_name}' not allowed by security setting");
186         return false; // should not, but who knows what happens to the compiler in the future?
187     }
189     /**
190      * Check if static class is trusted.
191      *
192      * @param string $class_name
193      * @param object $compiler compiler object
194      * @return boolean true if class is trusted
195      * @throws SmartyCompilerException if static class is not trusted
196      */
197     public function isTrustedStaticClass($class_name, $compiler)
198     {
199         if (isset($this->static_classes) && (empty($this->static_classes) || in_array($class_name, $this->static_classes))) {
200             return true;
201         }
203         $compiler->trigger_template_error("access to static class '{$class_name}' not allowed by security setting");
204         return false; // should not, but who knows what happens to the compiler in the future?
205     }
207     /**
208      * Check if PHP modifier is trusted.
209      *
210      * @param string $modifier_name
211      * @param object $compiler compiler object
212      * @return boolean true if modifier is trusted
213      * @throws SmartyCompilerException if modifier is not trusted
214      */
215     public function isTrustedPhpModifier($modifier_name, $compiler)
216     {
217         if (isset($this->php_modifiers) && (empty($this->php_modifiers) || in_array($modifier_name, $this->php_modifiers))) {
218             return true;
219         }
221         $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting");
222         return false; // should not, but who knows what happens to the compiler in the future?
223     }
225     /**
226      * Check if tag is trusted.
227      *
228      * @param string $tag_name
229      * @param object $compiler compiler object
230      * @return boolean true if tag is trusted
231      * @throws SmartyCompilerException if modifier is not trusted
232      */
233     public function isTrustedTag($tag_name, $compiler)
234     {
235         // check for internal always required tags
236         if (in_array($tag_name, array('assign', 'call', 'private_filter', 'private_block_plugin', 'private_function_plugin', 'private_object_block_function',
237                     'private_object_function', 'private_registered_function', 'private_registered_block', 'private_special_variable', 'private_print_expression', 'private_modifier'))) {
238             return true;
239         }
240         // check security settings
241         if (empty($this->allowed_tags)) {
242             if (empty($this->disabled_tags) || !in_array($tag_name, $this->disabled_tags)) {
243                 return true;
244             } else {
245                 $compiler->trigger_template_error("tag '{$tag_name}' disabled by security setting", $compiler->lex->taglineno);
246             }
247         } else if (in_array($tag_name, $this->allowed_tags) && !in_array($tag_name, $this->disabled_tags)) {
248             return true;
249         } else {
250             $compiler->trigger_template_error("tag '{$tag_name}' not allowed by security setting", $compiler->lex->taglineno);
251         }
252         return false; // should not, but who knows what happens to the compiler in the future?
253     }
255     /**
256      * Check if modifier plugin is trusted.
257      *
258      * @param string $modifier_name
259      * @param object $compiler compiler object
260      * @return boolean true if tag is trusted
261      * @throws SmartyCompilerException if modifier is not trusted
262      */
263     public function isTrustedModifier($modifier_name, $compiler)
264     {
265         // check for internal always allowed modifier
266         if (in_array($modifier_name, array('default'))) {
267             return true;
268         }
269         // check security settings
270         if (empty($this->allowed_modifiers)) {
271             if (empty($this->disabled_modifiers) || !in_array($modifier_name, $this->disabled_modifiers)) {
272                 return true;
273             } else {
274                 $compiler->trigger_template_error("modifier '{$modifier_name}' disabled by security setting", $compiler->lex->taglineno);
275             }
276         } else if (in_array($modifier_name, $this->allowed_modifiers) && !in_array($modifier_name, $this->disabled_modifiers)) {
277             return true;
278         } else {
279             $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting", $compiler->lex->taglineno);
280         }
281         return false; // should not, but who knows what happens to the compiler in the future?
282     }
284     /**
285      * Check if stream is trusted.
286      *
287      * @param string $stream_name
288      * @return boolean true if stream is trusted
289      * @throws SmartyException if stream is not trusted
290      */
291     public function isTrustedStream($stream_name)
292     {
293         if (isset($this->streams) && (empty($this->streams) || in_array($stream_name, $this->streams))) {
294             return true;
295         }
297         throw new SmartyException("stream '{$stream_name}' not allowed by security setting");
298     }
300     /**
301      * Check if directory of file resource is trusted.
302      *
303      * @param string $filepath
304      * @return boolean true if directory is trusted
305      * @throws SmartyException if directory is not trusted
306      */
307     public function isTrustedResourceDir($filepath)
308     {
309         $_template = false;
310         $_config = false;
311         $_secure = false;
313         $_template_dir = $this->smarty->getTemplateDir();
314         $_config_dir = $this->smarty->getConfigDir();
316         // check if index is outdated
317         if ((!$this->_template_dir || $this->_template_dir !== $_template_dir)
318                 || (!$this->_config_dir || $this->_config_dir !== $_config_dir)
319                 || (!empty($this->secure_dir) && (!$this->_secure_dir || $this->_secure_dir !== $this->secure_dir))
320         ) {
321             $this->_resource_dir = array();
322             $_template = true;
323             $_config = true;
324             $_secure = !empty($this->secure_dir);
325         }
327         // rebuild template dir index
328         if ($_template) {
329             $this->_template_dir = $_template_dir;
330             foreach ($_template_dir as $directory) {
331                 $directory = realpath($directory);
332                 $this->_resource_dir[$directory] = true;
333             }
334         }
336         // rebuild config dir index
337         if ($_config) {
338             $this->_config_dir = $_config_dir;
339             foreach ($_config_dir as $directory) {
340                 $directory = realpath($directory);
341                 $this->_resource_dir[$directory] = true;
342             }
343         }
345         // rebuild secure dir index
346         if ($_secure) {
347             $this->_secure_dir = $this->secure_dir;
348             foreach ((array) $this->secure_dir as $directory) {
349                 $directory = realpath($directory);
350                 $this->_resource_dir[$directory] = true;
351             }
352         }
354         $_filepath = realpath($filepath);
355         $directory = dirname($_filepath);
356         $_directory = array();
357         while (true) {
358             // remember the directory to add it to _resource_dir in case we're successful
359             $_directory[] = $directory;
360             // test if the directory is trusted
361             if (isset($this->_resource_dir[$directory])) {
362                 // merge sub directories of current $directory into _resource_dir to speed up subsequent lookups
363                 $this->_resource_dir = array_merge($this->_resource_dir, $_directory);
364                 return true;
365             }
366             // abort if we've reached root
367             if (($pos = strrpos($directory, DS)) === false || !isset($directory[1])) {
368                 break;
369             }
370             // bubble up one level
371             $directory = substr($directory, 0, $pos);
372         }
374         // give up
375         throw new SmartyException("directory '{$_filepath}' not allowed by security setting");
376     }
378     /**
379      * Check if directory of file resource is trusted.
380      *
381      * @param string $filepath
382      * @return boolean true if directory is trusted
383      * @throws SmartyException if PHP directory is not trusted
384      */
385     public function isTrustedPHPDir($filepath)
386     {
387         if (empty($this->trusted_dir)) {
388             throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)");
389         }
391         // check if index is outdated
392         if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) {
393             $this->_php_resource_dir = array();
395             $this->_trusted_dir = $this->trusted_dir;
396             foreach ((array) $this->trusted_dir as $directory) {
397                 $directory = realpath($directory);
398                 $this->_php_resource_dir[$directory] = true;
399             }
400         }
402         $_filepath = realpath($filepath);
403         $directory = dirname($_filepath);
404         $_directory = array();
405         while (true) {
406             // remember the directory to add it to _resource_dir in case we're successful
407             $_directory[] = $directory;
408             // test if the directory is trusted
409             if (isset($this->_php_resource_dir[$directory])) {
410                 // merge sub directories of current $directory into _resource_dir to speed up subsequent lookups
411                 $this->_php_resource_dir = array_merge($this->_php_resource_dir, $_directory);
412                 return true;
413             }
414             // abort if we've reached root
415             if (($pos = strrpos($directory, DS)) === false || !isset($directory[2])) {
416                 break;
417             }
418             // bubble up one level
419             $directory = substr($directory, 0, $pos);
420         }
422         throw new SmartyException("directory '{$_filepath}' not allowed by security setting");
423     }
427 ?>