1: | <?php
|
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: |
|
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: |
|
18: |
|
19: | |
20: | |
21: |
|
22: | class Smarty_Security
|
23: | {
|
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
35: |
|
36: | public $php_handling = Smarty::PHP_PASSTHRU;
|
37: |
|
38: | |
39: | |
40: | |
41: | |
42: | |
43: |
|
44: | public $secure_dir = array();
|
45: |
|
46: | |
47: | |
48: | |
49: | |
50: | |
51: |
|
52: | public $trusted_dir = array();
|
53: |
|
54: | |
55: | |
56: | |
57: | |
58: |
|
59: | public $trusted_uri = array();
|
60: |
|
61: | |
62: | |
63: | |
64: | |
65: |
|
66: | public $trusted_constants = array();
|
67: |
|
68: | |
69: | |
70: | |
71: | |
72: | |
73: | |
74: |
|
75: | public $static_classes = array();
|
76: |
|
77: | |
78: | |
79: | |
80: | |
81: | |
82: | |
83: | |
84: | |
85: | |
86: | |
87: | |
88: |
|
89: | public $trusted_static_methods = array();
|
90: |
|
91: | |
92: | |
93: | |
94: | |
95: | |
96: | |
97: | |
98: | |
99: | |
100: | |
101: | |
102: |
|
103: | public $trusted_static_properties = array();
|
104: |
|
105: | |
106: | |
107: | |
108: | |
109: | |
110: | |
111: |
|
112: | public $php_functions = array('isset', 'empty', 'count', 'sizeof', 'in_array', 'is_array', 'time',);
|
113: |
|
114: | |
115: | |
116: | |
117: | |
118: | |
119: | |
120: |
|
121: | public $php_modifiers = array('escape', 'count', 'nl2br',);
|
122: |
|
123: | |
124: | |
125: | |
126: | |
127: | |
128: |
|
129: | public $allowed_tags = array();
|
130: |
|
131: | |
132: | |
133: | |
134: | |
135: | |
136: |
|
137: | public $disabled_tags = array();
|
138: |
|
139: | |
140: | |
141: | |
142: | |
143: | |
144: |
|
145: | public $allowed_modifiers = array();
|
146: |
|
147: | |
148: | |
149: | |
150: | |
151: | |
152: |
|
153: | public $disabled_modifiers = array();
|
154: |
|
155: | |
156: | |
157: | |
158: | |
159: |
|
160: | public $disabled_special_smarty_vars = array();
|
161: |
|
162: | |
163: | |
164: | |
165: | |
166: | |
167: | |
168: |
|
169: | public $streams = array('file');
|
170: |
|
171: | |
172: | |
173: | |
174: | |
175: |
|
176: | public $allow_constants = true;
|
177: |
|
178: | |
179: | |
180: | |
181: | |
182: |
|
183: | public $allow_super_globals = true;
|
184: |
|
185: | |
186: | |
187: | |
188: | |
189: |
|
190: | public $max_template_nesting = 0;
|
191: |
|
192: | |
193: | |
194: | |
195: | |
196: |
|
197: | private $_current_template_nesting = 0;
|
198: |
|
199: | |
200: | |
201: | |
202: | |
203: |
|
204: | protected $_resource_dir = array();
|
205: |
|
206: | |
207: | |
208: | |
209: | |
210: |
|
211: | protected $_template_dir = array();
|
212: |
|
213: | |
214: | |
215: | |
216: | |
217: |
|
218: | protected $_config_dir = array();
|
219: |
|
220: | |
221: | |
222: | |
223: | |
224: |
|
225: | protected $_secure_dir = array();
|
226: |
|
227: | |
228: | |
229: | |
230: | |
231: |
|
232: | protected $_php_resource_dir = null;
|
233: |
|
234: | |
235: | |
236: | |
237: | |
238: |
|
239: | protected $_trusted_dir = null;
|
240: |
|
241: | |
242: | |
243: | |
244: | |
245: |
|
246: | protected $_include_path_status = false;
|
247: |
|
248: | |
249: | |
250: | |
251: | |
252: |
|
253: | protected $_include_dir = array();
|
254: |
|
255: | |
256: | |
257: |
|
258: | public function __construct($smarty)
|
259: | {
|
260: | $this->smarty = $smarty;
|
261: | }
|
262: |
|
263: | |
264: | |
265: | |
266: | |
267: | |
268: | |
269: | |
270: |
|
271: | public function isTrustedPhpFunction($function_name, $compiler)
|
272: | {
|
273: | if (isset($this->php_functions)
|
274: | && (empty($this->php_functions) || in_array($function_name, $this->php_functions))
|
275: | ) {
|
276: | return true;
|
277: | }
|
278: | $compiler->trigger_template_error("PHP function '{$function_name}' not allowed by security setting");
|
279: | return false;
|
280: | }
|
281: |
|
282: | |
283: | |
284: | |
285: | |
286: | |
287: | |
288: | |
289: |
|
290: | public function isTrustedStaticClass($class_name, $compiler)
|
291: | {
|
292: | if (isset($this->static_classes)
|
293: | && (empty($this->static_classes) || in_array($class_name, $this->static_classes))
|
294: | ) {
|
295: | return true;
|
296: | }
|
297: | $compiler->trigger_template_error("access to static class '{$class_name}' not allowed by security setting");
|
298: | return false;
|
299: | }
|
300: |
|
301: | |
302: | |
303: | |
304: | |
305: | |
306: | |
307: | |
308: | |
309: |
|
310: | public function isTrustedStaticClassAccess($class_name, $params, $compiler)
|
311: | {
|
312: | if (!isset($params[ 2 ])) {
|
313: |
|
314: | return $this->isTrustedStaticClass($class_name, $compiler);
|
315: | }
|
316: | if ($params[ 2 ] === 'method') {
|
317: | $allowed = $this->trusted_static_methods;
|
318: | $name = substr($params[ 0 ], 0, strpos($params[ 0 ], '('));
|
319: | } else {
|
320: | $allowed = $this->trusted_static_properties;
|
321: |
|
322: | $name = substr($params[ 0 ], 1);
|
323: | }
|
324: | if (isset($allowed)) {
|
325: | if (empty($allowed)) {
|
326: |
|
327: | return $this->isTrustedStaticClass($class_name, $compiler);
|
328: | }
|
329: | if (isset($allowed[ $class_name ])
|
330: | && (empty($allowed[ $class_name ]) || in_array($name, $allowed[ $class_name ]))
|
331: | ) {
|
332: | return true;
|
333: | }
|
334: | }
|
335: | $compiler->trigger_template_error("access to static class '{$class_name}' {$params[2]} '{$name}' not allowed by security setting");
|
336: | return false;
|
337: | }
|
338: |
|
339: | |
340: | |
341: | |
342: | |
343: | |
344: | |
345: | |
346: |
|
347: | public function isTrustedPhpModifier($modifier_name, $compiler)
|
348: | {
|
349: | if (isset($this->php_modifiers)
|
350: | && (empty($this->php_modifiers) || in_array($modifier_name, $this->php_modifiers))
|
351: | ) {
|
352: | return true;
|
353: | }
|
354: | $compiler->trigger_template_error("modifier '{$modifier_name}' not allowed by security setting");
|
355: | return false;
|
356: | }
|
357: |
|
358: | |
359: | |
360: | |
361: | |
362: | |
363: | |
364: | |
365: |
|
366: | public function isTrustedTag($tag_name, $compiler)
|
367: | {
|
368: |
|
369: | if (in_array(
|
370: | $tag_name,
|
371: | array(
|
372: | 'assign', 'call', 'private_filter', 'private_block_plugin', 'private_function_plugin',
|
373: | 'private_object_block_function', 'private_object_function', 'private_registered_function',
|
374: | 'private_registered_block', 'private_special_variable', 'private_print_expression',
|
375: | 'private_modifier'
|
376: | )
|
377: | )
|
378: | ) {
|
379: | return true;
|
380: | }
|
381: |
|
382: | if (empty($this->allowed_tags)) {
|
383: | if (empty($this->disabled_tags) || !in_array($tag_name, $this->disabled_tags)) {
|
384: | return true;
|
385: | } else {
|
386: | $compiler->trigger_template_error("tag '{$tag_name}' disabled by security setting", null, true);
|
387: | }
|
388: | } elseif (in_array($tag_name, $this->allowed_tags) && !in_array($tag_name, $this->disabled_tags)) {
|
389: | return true;
|
390: | } else {
|
391: | $compiler->trigger_template_error("tag '{$tag_name}' not allowed by security setting", null, true);
|
392: | }
|
393: | return false;
|
394: | }
|
395: |
|
396: | |
397: | |
398: | |
399: | |
400: | |
401: | |
402: | |
403: |
|
404: | public function isTrustedSpecialSmartyVar($var_name, $compiler)
|
405: | {
|
406: | if (!in_array($var_name, $this->disabled_special_smarty_vars)) {
|
407: | return true;
|
408: | } else {
|
409: | $compiler->trigger_template_error(
|
410: | "special variable '\$smarty.{$var_name}' not allowed by security setting",
|
411: | null,
|
412: | true
|
413: | );
|
414: | }
|
415: | return false;
|
416: | }
|
417: |
|
418: | |
419: | |
420: | |
421: | |
422: | |
423: | |
424: | |
425: |
|
426: | public function isTrustedModifier($modifier_name, $compiler)
|
427: | {
|
428: |
|
429: | if (in_array($modifier_name, array('default'))) {
|
430: | return true;
|
431: | }
|
432: |
|
433: | if (empty($this->allowed_modifiers)) {
|
434: | if (empty($this->disabled_modifiers) || !in_array($modifier_name, $this->disabled_modifiers)) {
|
435: | return true;
|
436: | } else {
|
437: | $compiler->trigger_template_error(
|
438: | "modifier '{$modifier_name}' disabled by security setting",
|
439: | null,
|
440: | true
|
441: | );
|
442: | }
|
443: | } elseif (in_array($modifier_name, $this->allowed_modifiers)
|
444: | && !in_array($modifier_name, $this->disabled_modifiers)
|
445: | ) {
|
446: | return true;
|
447: | } else {
|
448: | $compiler->trigger_template_error(
|
449: | "modifier '{$modifier_name}' not allowed by security setting",
|
450: | null,
|
451: | true
|
452: | );
|
453: | }
|
454: | return false;
|
455: | }
|
456: |
|
457: | |
458: | |
459: | |
460: | |
461: | |
462: | |
463: | |
464: |
|
465: | public function isTrustedConstant($const, $compiler)
|
466: | {
|
467: | if (in_array($const, array('true', 'false', 'null'))) {
|
468: | return true;
|
469: | }
|
470: | if (!empty($this->trusted_constants)) {
|
471: | if (!in_array(strtolower($const), $this->trusted_constants)) {
|
472: | $compiler->trigger_template_error("Security: access to constant '{$const}' not permitted");
|
473: | return false;
|
474: | }
|
475: | return true;
|
476: | }
|
477: | if ($this->allow_constants) {
|
478: | return true;
|
479: | }
|
480: | $compiler->trigger_template_error("Security: access to constants not permitted");
|
481: | return false;
|
482: | }
|
483: |
|
484: | |
485: | |
486: | |
487: | |
488: | |
489: | |
490: | |
491: |
|
492: | public function isTrustedStream($stream_name)
|
493: | {
|
494: | if (isset($this->streams) && (empty($this->streams) || in_array($stream_name, $this->streams))) {
|
495: | return true;
|
496: | }
|
497: | throw new SmartyException("stream '{$stream_name}' not allowed by security setting");
|
498: | }
|
499: |
|
500: | |
501: | |
502: | |
503: | |
504: | |
505: | |
506: | |
507: | |
508: |
|
509: | public function isTrustedResourceDir($filepath, $isConfig = null)
|
510: | {
|
511: | if ($this->_include_path_status !== $this->smarty->use_include_path) {
|
512: | $_dir =
|
513: | $this->smarty->use_include_path ? $this->smarty->ext->_getIncludePath->getIncludePathDirs($this->smarty) : array();
|
514: | if ($this->_include_dir !== $_dir) {
|
515: | $this->_updateResourceDir($this->_include_dir, $_dir);
|
516: | $this->_include_dir = $_dir;
|
517: | }
|
518: | $this->_include_path_status = $this->smarty->use_include_path;
|
519: | }
|
520: | $_dir = $this->smarty->getTemplateDir();
|
521: | if ($this->_template_dir !== $_dir) {
|
522: | $this->_updateResourceDir($this->_template_dir, $_dir);
|
523: | $this->_template_dir = $_dir;
|
524: | }
|
525: | $_dir = $this->smarty->getConfigDir();
|
526: | if ($this->_config_dir !== $_dir) {
|
527: | $this->_updateResourceDir($this->_config_dir, $_dir);
|
528: | $this->_config_dir = $_dir;
|
529: | }
|
530: | if ($this->_secure_dir !== $this->secure_dir) {
|
531: | $this->secure_dir = (array)$this->secure_dir;
|
532: | foreach ($this->secure_dir as $k => $d) {
|
533: | $this->secure_dir[ $k ] = $this->smarty->_realpath($d . DIRECTORY_SEPARATOR, true);
|
534: | }
|
535: | $this->_updateResourceDir($this->_secure_dir, $this->secure_dir);
|
536: | $this->_secure_dir = $this->secure_dir;
|
537: | }
|
538: | $addPath = $this->_checkDir($filepath, $this->_resource_dir);
|
539: | if ($addPath !== false) {
|
540: | $this->_resource_dir = array_merge($this->_resource_dir, $addPath);
|
541: | }
|
542: | return true;
|
543: | }
|
544: |
|
545: | |
546: | |
547: | |
548: | |
549: | |
550: | |
551: | |
552: | |
553: | |
554: | |
555: | |
556: |
|
557: | public function isTrustedUri($uri)
|
558: | {
|
559: | $_uri = parse_url($uri);
|
560: | if (!empty($_uri[ 'scheme' ]) && !empty($_uri[ 'host' ])) {
|
561: | $_uri = $_uri[ 'scheme' ] . '://' . $_uri[ 'host' ];
|
562: | foreach ($this->trusted_uri as $pattern) {
|
563: | if (preg_match($pattern, $_uri)) {
|
564: | return true;
|
565: | }
|
566: | }
|
567: | }
|
568: | throw new SmartyException("URI '{$uri}' not allowed by security setting");
|
569: | }
|
570: |
|
571: | |
572: | |
573: | |
574: | |
575: | |
576: | |
577: | |
578: |
|
579: | public function isTrustedPHPDir($filepath)
|
580: | {
|
581: | if (empty($this->trusted_dir)) {
|
582: | throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)");
|
583: | }
|
584: |
|
585: | if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) {
|
586: | $this->_php_resource_dir = array();
|
587: | $this->_trusted_dir = $this->trusted_dir;
|
588: | foreach ((array)$this->trusted_dir as $directory) {
|
589: | $directory = $this->smarty->_realpath($directory . '/', true);
|
590: | $this->_php_resource_dir[ $directory ] = true;
|
591: | }
|
592: | }
|
593: | $addPath = $this->_checkDir($filepath, $this->_php_resource_dir);
|
594: | if ($addPath !== false) {
|
595: | $this->_php_resource_dir = array_merge($this->_php_resource_dir, $addPath);
|
596: | }
|
597: | return true;
|
598: | }
|
599: |
|
600: | |
601: | |
602: | |
603: | |
604: | |
605: |
|
606: | private function _updateResourceDir($oldDir, $newDir)
|
607: | {
|
608: | foreach ($oldDir as $directory) {
|
609: |
|
610: | $length = strlen($directory);
|
611: | foreach ($this->_resource_dir as $dir) {
|
612: | if (substr($dir, 0, $length) === $directory) {
|
613: | unset($this->_resource_dir[ $dir ]);
|
614: | }
|
615: | }
|
616: | }
|
617: | foreach ($newDir as $directory) {
|
618: |
|
619: | $this->_resource_dir[ $directory ] = true;
|
620: | }
|
621: | }
|
622: |
|
623: | |
624: | |
625: | |
626: | |
627: | |
628: | |
629: | |
630: | |
631: |
|
632: | private function _checkDir($filepath, $dirs)
|
633: | {
|
634: | $directory = dirname($this->smarty->_realpath($filepath, true)) . DIRECTORY_SEPARATOR;
|
635: | $_directory = array();
|
636: | if (!preg_match('#[\\\\/][.][.][\\\\/]#', $directory)) {
|
637: | while (true) {
|
638: |
|
639: | if (isset($dirs[ $directory ])) {
|
640: | return $_directory;
|
641: | }
|
642: |
|
643: | if (!preg_match('#[\\\\/][^\\\\/]+[\\\\/]$#', $directory)) {
|
644: |
|
645: | break;
|
646: | }
|
647: |
|
648: | $_directory[ $directory ] = true;
|
649: |
|
650: | $directory = preg_replace('#[\\\\/][^\\\\/]+[\\\\/]$#', DIRECTORY_SEPARATOR, $directory);
|
651: | }
|
652: | }
|
653: |
|
654: | throw new SmartyException(sprintf('Smarty Security: not trusted file path \'%s\' ', $filepath));
|
655: | }
|
656: |
|
657: | |
658: | |
659: | |
660: | |
661: | |
662: | |
663: | |
664: | |
665: |
|
666: | public static function enableSecurity(Smarty $smarty, $security_class)
|
667: | {
|
668: | if ($security_class instanceof Smarty_Security) {
|
669: | $smarty->security_policy = $security_class;
|
670: | return $smarty;
|
671: | } elseif (is_object($security_class)) {
|
672: | throw new SmartyException("Class '" . get_class($security_class) . "' must extend Smarty_Security.");
|
673: | }
|
674: | if ($security_class === null) {
|
675: | $security_class = $smarty->security_class;
|
676: | }
|
677: | if (!class_exists($security_class)) {
|
678: | throw new SmartyException("Security class '$security_class' is not defined");
|
679: | } elseif ($security_class !== 'Smarty_Security' && !is_subclass_of($security_class, 'Smarty_Security')) {
|
680: | throw new SmartyException("Class '$security_class' must extend Smarty_Security.");
|
681: | } else {
|
682: | $smarty->security_policy = new $security_class($smarty);
|
683: | }
|
684: | return $smarty;
|
685: | }
|
686: |
|
687: | |
688: | |
689: | |
690: | |
691: | |
692: | |
693: |
|
694: | public function startTemplate($template)
|
695: | {
|
696: | if ($this->max_template_nesting > 0 && $this->_current_template_nesting++ >= $this->max_template_nesting) {
|
697: | throw new SmartyException("maximum template nesting level of '{$this->max_template_nesting}' exceeded when calling '{$template->template_resource}'");
|
698: | }
|
699: | }
|
700: |
|
701: | |
702: | |
703: |
|
704: | public function endTemplate()
|
705: | {
|
706: | if ($this->max_template_nesting > 0) {
|
707: | $this->_current_template_nesting--;
|
708: | }
|
709: | }
|
710: |
|
711: | |
712: | |
713: | |
714: | |
715: |
|
716: | public function registerCallBacks(Smarty_Internal_Template $template)
|
717: | {
|
718: | $template->startRenderCallbacks[] = array($this, 'startTemplate');
|
719: | $template->endRenderCallbacks[] = array($this, 'endTemplate');
|
720: | }
|
721: | }
|
722: | |