summaryrefslogtreecommitdiff
path: root/src/sp_wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp_wrapper.c')
-rw-r--r--src/sp_wrapper.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/sp_wrapper.c b/src/sp_wrapper.c
index 9eb5cbc..54a3a7a 100644
--- a/src/sp_wrapper.c
+++ b/src/sp_wrapper.c
@@ -1,5 +1,7 @@
1#include "php_snuffleupagus.h" 1#include "php_snuffleupagus.h"
2 2
3#define LOG_FEATURE "wrappers_whitelist"
4
3static bool wrapper_is_whitelisted(const zend_string *const zs) { 5static bool wrapper_is_whitelisted(const zend_string *const zs) {
4 const sp_list_node *list = SPCFG(wrapper).whitelist; 6 const sp_list_node *list = SPCFG(wrapper).whitelist;
5 7
@@ -16,6 +18,131 @@ static bool wrapper_is_whitelisted(const zend_string *const zs) {
16 return false; 18 return false;
17} 19}
18 20
21static bool sp_php_stream_is_filtered(void) {
22 const sp_list_node *list = SPCFG(wrapper).php_stream_allowlist;
23
24 return list != NULL;
25}
26
27static bool sp_php_stream_is_whitelisted(const char *const kind) {
28 const sp_list_node *list = SPCFG(wrapper).php_stream_allowlist;
29
30 while (list) {
31 if (!strcasecmp(kind, ZSTR_VAL((const zend_string *)list->data))) {
32 return true;
33 }
34 list = list->next;
35 }
36 return false;
37}
38
39/*
40 * Adopted from
41 * https://github.com/php/php-src/blob/8896bd3200892000d8aaa01595d6c64b926a26f7/ext/standard/php_fopen_wrapper.c#L176
42 */
43static php_stream * sp_php_stream_url_wrap_php(php_stream_wrapper *wrapper,
44 const char *path, const char *mode,
45 int options, zend_string **opened_path,
46 php_stream_context *context STREAMS_DC) {
47 if (!strncasecmp(path, "php://", 6)) {
48 path += 6;
49 }
50
51 if (!strncasecmp(path, "temp", 4)) {
52 if (!sp_php_stream_is_whitelisted("temp")) {
53 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"temp\" dropped");
54 return NULL;
55 }
56 } else if (!strcasecmp(path, "memory")) {
57 if (!sp_php_stream_is_whitelisted("memory")) {
58 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"memory\" dropped");
59 return NULL;
60 }
61 } else if (!strcasecmp(path, "output")) {
62 if (!sp_php_stream_is_whitelisted("output")) {
63 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"output\" dropped");
64 return NULL;
65 }
66 } else if (!strcasecmp(path, "input")) {
67 if (!sp_php_stream_is_whitelisted("input")) {
68 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"input\" dropped");
69 return NULL;
70 }
71 } else if (!strcasecmp(path, "stdin")) {
72 if (!sp_php_stream_is_whitelisted("stdin")) {
73 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"stdin\" dropped");
74 return NULL;
75 }
76 } else if (!strcasecmp(path, "stdout")) {
77 if (!sp_php_stream_is_whitelisted("stdout")) {
78 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"stdout\" dropped");
79 return NULL;
80 }
81 } else if (!strcasecmp(path, "stderr")) {
82 if (!sp_php_stream_is_whitelisted("stderr")) {
83 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"stderr\" dropped");
84 return NULL;
85 }
86 } else if (!strncasecmp(path, "fd/", 3)) {
87 if (!sp_php_stream_is_whitelisted("fd")) {
88 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"fd\" dropped");
89 return NULL;
90 }
91 } else if (!strncasecmp(path, "filter/", 7)) {
92 if (!sp_php_stream_is_whitelisted("filter")) {
93 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"filter\" dropped");
94 return NULL;
95 }
96 } else {
97 sp_log_warn(LOG_FEATURE, "Call to unknown php stream type dropped");
98 return NULL;
99 }
100
101 extern PHPAPI const php_stream_wrapper php_stream_php_wrapper;
102
103 return php_stream_php_wrapper.wops->stream_opener(wrapper, path, mode, options, opened_path, context STREAMS_DC);
104}
105
106/*
107 * Adopted from
108 * https://github.com/php/php-src/blob/8896bd3200892000d8aaa01595d6c64b926a26f7/ext/standard/php_fopen_wrapper.c#L428-L446
109 */
110static const php_stream_wrapper_ops sp_php_stdio_wops = {
111 sp_php_stream_url_wrap_php,
112 NULL, /* close */
113 NULL, /* fstat */
114 NULL, /* stat */
115 NULL, /* opendir */
116 "PHP",
117 NULL, /* unlink */
118 NULL, /* rename */
119 NULL, /* mkdir */
120 NULL, /* rmdir */
121 NULL
122};
123static const php_stream_wrapper sp_php_stream_php_wrapper = {
124 &sp_php_stdio_wops,
125 NULL,
126 0, /* is_url */
127};
128
129static void sp_reregister_php_wrapper(void) {
130 if (!sp_php_stream_is_filtered()) {
131 return;
132 }
133
134 if (php_unregister_url_stream_wrapper("php") != SUCCESS) {
135 sp_log_warn(LOG_FEATURE, "Failed to unregister stream wrapper \"php\"");
136 return;
137 }
138
139 if (php_register_url_stream_wrapper("php", &sp_php_stream_php_wrapper) != SUCCESS) {
140 sp_log_warn(LOG_FEATURE, "Failed to register custom stream wrapper \"php\"");
141 }
142
143 sp_log_debug(LOG_FEATURE, "Stream \"php\" successfully re-registered");
144}
145
19void sp_disable_wrapper() { 146void sp_disable_wrapper() {
20 HashTable *orig = php_stream_get_url_stream_wrappers_hash(); 147 HashTable *orig = php_stream_get_url_stream_wrappers_hash();
21 HashTable *orig_complete = pemalloc(sizeof(HashTable), 1); 148 HashTable *orig_complete = pemalloc(sizeof(HashTable), 1);
@@ -50,6 +177,12 @@ PHP_FUNCTION(sp_stream_wrapper_register) {
50 zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "S*", &protocol_name, &params, &param_count); 177 zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "S*", &protocol_name, &params, &param_count);
51 // ignore proper arguments here and just let the original handler deal with it 178 // ignore proper arguments here and just let the original handler deal with it
52 if (!protocol_name || wrapper_is_whitelisted(protocol_name)) { 179 if (!protocol_name || wrapper_is_whitelisted(protocol_name)) {
180
181 // reject manual loading of "php" wrapper
182 if (!strcasecmp(ZSTR_VAL(protocol_name), "php") && sp_php_stream_is_filtered()) {
183 return;
184 }
185
53 orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("stream_wrapper_register")); 186 orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("stream_wrapper_register"));
54 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); 187 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
55 } 188 }
@@ -61,5 +194,7 @@ int hook_stream_wrappers() {
61 HOOK_FUNCTION("stream_wrapper_register", sp_internal_functions_hook, 194 HOOK_FUNCTION("stream_wrapper_register", sp_internal_functions_hook,
62 PHP_FN(sp_stream_wrapper_register)); 195 PHP_FN(sp_stream_wrapper_register));
63 196
197 sp_reregister_php_wrapper();
198
64 return SUCCESS; 199 return SUCCESS;
65} 200}