summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjvoisin2017-11-29 15:33:53 +0100
committerblotus2017-11-29 15:33:53 +0100
commit24d414503e9831ae9a7a7871e93fdd8430911466 (patch)
treea5842ecf07b63910892a8bfe48828f5065e3393d /src
parent5aabc746bd4588a437e37a442e557c3bc7fe6f35 (diff)
Refactoring (#79)
Refactoring of should_disable().
Diffstat (limited to 'src')
-rw-r--r--src/sp_disabled_functions.c301
1 files changed, 156 insertions, 145 deletions
diff --git a/src/sp_disabled_functions.c b/src/sp_disabled_functions.c
index ca1f6a9..4e52431 100644
--- a/src/sp_disabled_functions.c
+++ b/src/sp_disabled_functions.c
@@ -5,9 +5,8 @@
5 5
6ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 6ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
7 7
8static zend_always_inline char* get_complete_function_path( 8static char* get_complete_function_path(
9 zend_execute_data const* const execute_data) { 9 zend_execute_data const* const execute_data) {
10
11 if (!(execute_data->func->common.function_name)) { 10 if (!(execute_data->func->common.function_name)) {
12 return NULL; 11 return NULL;
13 } 12 }
@@ -28,20 +27,21 @@ static zend_always_inline char* get_complete_function_path(
28 return complete_path_function; 27 return complete_path_function;
29} 28}
30 29
31static bool is_functions_list_matching(zend_execute_data *execute_data, sp_node_t *functions_list) { 30static bool is_functions_list_matching(zend_execute_data* execute_data,
31 sp_node_t* functions_list) {
32 zend_execute_data *orig_execute_data, *current; 32 zend_execute_data *orig_execute_data, *current;
33 orig_execute_data = current = execute_data; 33 orig_execute_data = current = execute_data;
34 sp_node_t *it = functions_list; 34 sp_node_t* it = functions_list;
35 35
36 while (current) { 36 while (current) {
37 if (it == NULL) { // every function in the list matched, we've got a match! 37 if (it == NULL) { // every function in the list matched, we've got a match!
38 EG(current_execute_data) = orig_execute_data; 38 EG(current_execute_data) = orig_execute_data;
39 return true; 39 return true;
40 } 40 }
41 41
42 EG(current_execute_data) = current; 42 EG(current_execute_data) = current;
43 43
44 char *complete_path_function = get_complete_function_path(current); 44 char* complete_path_function = get_complete_function_path(current);
45 if (!complete_path_function) { 45 if (!complete_path_function) {
46 goto end; 46 goto end;
47 } 47 }
@@ -61,36 +61,39 @@ end:
61 return false; 61 return false;
62} 62}
63 63
64static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disabled_function *const config_node) { 64static bool is_local_var_matching(
65 zend_execute_data *orig_execute_data = execute_data; 65 zend_execute_data* execute_data,
66 const sp_disabled_function* const config_node) {
67 zend_execute_data* orig_execute_data = execute_data;
66 68
67 /*because execute_data points to hooked function data, 69 /*because execute_data points to hooked function data,
68 which we dont care about */ 70 which we dont care about */
69 zend_execute_data *current = execute_data->prev_execute_data; 71 zend_execute_data* current = execute_data->prev_execute_data;
70 zval *value = NULL; 72 zval* value = NULL;
71 73
72 while (current) { 74 while (current) {
73 zend_string *key = NULL; 75 zend_string* key = NULL;
74 EG(current_execute_data) = current; 76 EG(current_execute_data) = current;
75 zend_array *symtable = zend_rebuild_symbol_table(); 77 zend_array* symtable = zend_rebuild_symbol_table();
76 ZEND_HASH_FOREACH_STR_KEY_VAL(symtable, key, value) { 78 ZEND_HASH_FOREACH_STR_KEY_VAL(symtable, key, value) {
77 if (0 == strcmp(config_node->var, key->val)) { // is the var name right? 79 if (0 == strcmp(config_node->var, key->val)) { // is the var name right?
78 if (Z_TYPE_P(value) == IS_INDIRECT) { 80 if (Z_TYPE_P(value) == IS_INDIRECT) {
79 value = Z_INDIRECT_P(value); 81 value = Z_INDIRECT_P(value);
80 } 82 }
81 if (Z_TYPE_P(value) != IS_ARRAY) { 83 if (Z_TYPE_P(value) != IS_ARRAY) {
82 char *var_value_str = sp_convert_to_string(value); 84 char* var_value_str = sp_convert_to_string(value);
83 if (true == sp_match_value(var_value_str, config_node->value, config_node->value_r)) { 85 if (true == sp_match_value(var_value_str, config_node->value,
84 efree(var_value_str); 86 config_node->value_r)) {
85 EG(current_execute_data) = orig_execute_data; 87 efree(var_value_str);
86 return true; 88 EG(current_execute_data) = orig_execute_data;
87 } 89 return true;
88 efree(var_value_str); 90 }
89 } 91 efree(var_value_str);
90 else { 92 } else {
91 EG(current_execute_data) = orig_execute_data; 93 EG(current_execute_data) = orig_execute_data;
92 return sp_match_array_key_recurse(value, config_node->var_array_keys, config_node->value, NULL); 94 return sp_match_array_key_recurse(value, config_node->var_array_keys,
93 } 95 config_node->value, NULL);
96 }
94 } 97 }
95 } 98 }
96 ZEND_HASH_FOREACH_END(); 99 ZEND_HASH_FOREACH_END();
@@ -101,51 +104,135 @@ static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disa
101 return false; 104 return false;
102} 105}
103 106
104static sp_node_t *get_config(const char *builtin_name) { 107static const sp_node_t* get_config_node(const char* builtin_name) {
105 if (!builtin_name) { 108 if (!builtin_name) {
106 return SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions; 109 return SNUFFLEUPAGUS_G(config)
107 } 110 .config_disabled_functions->disabled_functions;
108 if (!strcmp(builtin_name, "eval")) { 111 } else if (!strcmp(builtin_name, "eval")) {
109 return SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval; 112 return SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval;
113 } else if (!strcmp(builtin_name, "include") ||
114 !strcmp(builtin_name, "include_once") ||
115 !strcmp(builtin_name, "require") ||
116 !strcmp(builtin_name, "require_once")) {
117 return SNUFFLEUPAGUS_G(config)
118 .config_disabled_constructs->construct_include;
110 } 119 }
111 if (!strcmp(builtin_name, "include") || 120 return NULL; // This should never happen.
112 !strcmp(builtin_name, "include_once") || 121}
113 !strcmp(builtin_name, "require") || 122
114 !strcmp(builtin_name, "require_once")) { 123static bool is_param_matching(zend_execute_data* execute_data,
115 return SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include; 124 sp_disabled_function const* const config_node,
125 const char* builtin_name,
126 const char* builtin_param, const char** arg_name,
127 const char* builtin_param_name,
128 const char** arg_value_str) {
129 int nb_param = execute_data->func->common.num_args;
130 int i = 0;
131
132 if (config_node->pos != -1) {
133 if (config_node->pos <= nb_param) {
134 char* complete_function_path = get_complete_function_path(execute_data);
135 sp_log_err("config",
136 "It seems that you wrote a rule filtering on the "
137 "%d%s argument of the function '%s', but it takes only %d "
138 "arguments. "
139 "Matching on _all_ arguments instead.",
140 config_node->pos, GET_SUFFIX(config_node->pos),
141 complete_function_path, nb_param);
142 efree(complete_function_path);
143 } else {
144 i = config_node->pos;
145 nb_param = (config_node->pos) + 1;
146 }
147 }
148
149 if (builtin_name) {
150 // we are matching on a builtin param, but for PHP, it's not the same a
151 // function param
152 *arg_name = builtin_param_name;
153 *arg_value_str = builtin_param;
154 return sp_match_value(builtin_param, config_node->value,
155 config_node->value_r);
156 } else {
157 for (; i < nb_param; i++) {
158 if (ZEND_USER_CODE(execute_data->func->type)) { // yay consistency
159 *arg_name = ZSTR_VAL(execute_data->func->common.arg_info[i].name);
160 } else {
161 *arg_name = execute_data->func->internal_function.arg_info[i].name;
162 }
163
164 const bool arg_matching =
165 config_node->param && (0 == strcmp(*arg_name, config_node->param));
166 const bool pcre_matching =
167 config_node->r_param &&
168 (true == is_regexp_matching(config_node->r_param, *arg_name));
169
170 /* This is the parameter name we're looking for. */
171 if (true == arg_matching || true == pcre_matching ||
172 (config_node->pos != -1)) {
173 zval* arg_value = ZEND_CALL_VAR_NUM(execute_data, i);
174
175 if (config_node->param_type) { // Are we matching on the `type`?
176 if (config_node->param_type == Z_TYPE_P(arg_value)) {
177 return true;
178 }
179 } else if (Z_TYPE_P(arg_value) == IS_ARRAY) {
180 *arg_value_str = estrdup("Array");
181 // match on arr -> match on all key content, if a key is an array,
182 // ignore it
183 // match on arr[foo] -> match only on key foo, if the key is an
184 // array, match on all keys content
185 if (config_node->param_is_array == true) {
186 if (true == sp_match_array_key_recurse(
187 arg_value, config_node->param_array_keys,
188 config_node->value, config_node->value_r)) {
189 return true;
190 }
191 } else { // match on all keys, but don't go into subarray
192 if (true == sp_match_array_key(arg_value, config_node->value,
193 config_node->value_r)) {
194 return true;
195 }
196 }
197 } else {
198 *arg_value_str = sp_convert_to_string(arg_value);
199 if (true == sp_match_value(*arg_value_str, config_node->value,
200 config_node->value_r)) {
201 return true;
202 }
203 }
204 }
205 }
116 } 206 }
117 return NULL; 207 return false;
118} 208}
119 209
120bool should_disable(zend_execute_data* execute_data, const char *builtin_name, const char *builtin_param, 210bool should_disable(zend_execute_data* execute_data, const char* builtin_name,
121 const char *builtin_param_name) { 211 const char* builtin_param, const char* builtin_param_name) {
122 char current_file_hash[SHA256_SIZE * 2 + 1] = {0}; 212 char current_file_hash[SHA256_SIZE * 2 + 1] = {0};
123 const sp_node_t* config = get_config(builtin_name); 213 const sp_node_t* config = get_config_node(builtin_name);
124 char* complete_path_function = get_complete_function_path(execute_data); 214 char* complete_path_function = get_complete_function_path(execute_data);
125 char const* client_ip = sp_getenv("REMOTE_ADDR"); 215 char const* client_ip = sp_getenv("REMOTE_ADDR");
126 const char* current_filename; 216 const char* current_filename;
127 217
218 if (!config || !config->data) {
219 return false;
220 }
221
128 if (builtin_name && !strcmp(builtin_name, "eval")) { 222 if (builtin_name && !strcmp(builtin_name, "eval")) {
129 current_filename = get_eval_filename(zend_get_executed_filename()); 223 current_filename = get_eval_filename(zend_get_executed_filename());
130 } 224 } else {
131 else {
132 current_filename = zend_get_executed_filename(); 225 current_filename = zend_get_executed_filename();
133 } 226 }
134 227
135 if (!complete_path_function) { 228 if (!complete_path_function) {
136 if (builtin_name) { 229 if (builtin_name) {
137 complete_path_function = (char *)builtin_name; 230 complete_path_function = (char*)builtin_name;
138 } 231 } else {
139 else {
140 return false; 232 return false;
141 } 233 }
142 } 234 }
143 235
144 if (!config || !config->data) {
145 return false;
146 }
147
148
149 while (config) { 236 while (config) {
150 sp_disabled_function const* const config_node = 237 sp_disabled_function const* const config_node =
151 (sp_disabled_function*)(config->data); 238 (sp_disabled_function*)(config->data);
@@ -155,13 +242,14 @@ bool should_disable(zend_execute_data* execute_data, const char *builtin_name, c
155 /* The order matters, since when we have `config_node->functions_list`, 242 /* The order matters, since when we have `config_node->functions_list`,
156 we also do have `config_node->function` */ 243 we also do have `config_node->function` */
157 if (config_node->functions_list) { 244 if (config_node->functions_list) {
158 if (false == 245 if (false == is_functions_list_matching(execute_data,
159 is_functions_list_matching(execute_data, config_node->functions_list)) { 246 config_node->functions_list)) {
160 goto next; 247 goto next;
161 } 248 }
162 } else if (config_node->function) { /* Litteral match against the function name. */ 249 } else if (config_node
250 ->function) { /* Litteral match against the function name. */
163 if (0 != strcmp(config_node->function, complete_path_function)) { 251 if (0 != strcmp(config_node->function, complete_path_function)) {
164 goto next; 252 goto next;
165 } 253 }
166 } else if (config_node->r_function) { 254 } else if (config_node->r_function) {
167 if (false == 255 if (false ==
@@ -177,7 +265,6 @@ bool should_disable(zend_execute_data* execute_data, const char *builtin_name, c
177 } 265 }
178 266
179 if (config_node->filename) { /* Check the current file name. */ 267 if (config_node->filename) { /* Check the current file name. */
180
181 if (0 != strcmp(current_filename, config_node->filename)) { 268 if (0 != strcmp(current_filename, config_node->filename)) {
182 goto next; 269 goto next;
183 } 270 }
@@ -199,7 +286,7 @@ bool should_disable(zend_execute_data* execute_data, const char *builtin_name, c
199 286
200 if (config_node->line) { 287 if (config_node->line) {
201 if (config_node->line != zend_get_executed_lineno()) { 288 if (config_node->line != zend_get_executed_lineno()) {
202 goto next; 289 goto next;
203 } 290 }
204 } 291 }
205 292
@@ -209,87 +296,11 @@ bool should_disable(zend_execute_data* execute_data, const char *builtin_name, c
209 } 296 }
210 297
211 /* Check if we filter on parameter value*/ 298 /* Check if we filter on parameter value*/
212 if (config_node->param || config_node->r_param || (config_node->pos != -1)) { 299 if (config_node->param || config_node->r_param ||
213 int nb_param = execute_data->func->common.num_args; 300 (config_node->pos != -1)) {
214 bool arg_matched = false; 301 if (false == is_param_matching(execute_data, config_node, builtin_name,
215 int i = 0; 302 builtin_param, &arg_name,
216 303 builtin_param_name, &arg_value_str)) {
217 if (config_node->pos != -1) {
218 if (config_node->pos <= nb_param) {
219 sp_log_err("config", "It seems that you wrote a rule filtering on the "
220 "%d%s argument of the function '%s', but it takes only %d arguments. "
221 "Matching on _all_ arguments instead.",
222 config_node->pos,
223 GET_SUFFIX(config_node->pos),
224 complete_path_function, nb_param);
225 } else {
226 i = config_node->pos;
227 nb_param = (config_node->pos) + 1;
228 }
229 }
230
231 if (builtin_name) {
232 // we are matching on a builtin param, but for PHP, it's not the same a function param
233 arg_matched = sp_match_value(builtin_param, config_node->value, config_node->value_r);
234 arg_name = builtin_param_name;
235 arg_value_str = builtin_param;
236 }
237 else {
238 for (; i < nb_param; i++) {
239 arg_matched = false;
240 if (ZEND_USER_CODE(execute_data->func->type)) { // yay consistency
241 arg_name = ZSTR_VAL(execute_data->func->common.arg_info[i].name);
242 } else {
243 arg_name = execute_data->func->internal_function.arg_info[i].name;
244 }
245
246 const bool arg_matching =
247 config_node->param && (0 == strcmp(arg_name, config_node->param));
248 const bool pcre_matching =
249 config_node->r_param &&
250 (true == is_regexp_matching(config_node->r_param, arg_name));
251
252 /* This is the parameter name we're looking for. */
253 if (true == arg_matching || true == pcre_matching || (config_node->pos != -1)) {
254 zval* arg_value = ZEND_CALL_VAR_NUM(execute_data, i);
255
256 if (config_node->param_type) { // Are we matching on the `type`?
257 if (config_node->param_type == Z_TYPE_P(arg_value)) {
258 arg_matched = true;
259 break;
260 }
261 } else if (Z_TYPE_P(arg_value) == IS_ARRAY) {
262 arg_value_str = estrdup("Array");
263 // match on arr -> match on all key content, if a key is an array,
264 // ignore it
265 // match on arr[foo] -> match only on key foo, if the key is an
266 // array, match on all keys content
267 if (config_node->param_is_array == true) {
268 if (true == sp_match_array_key_recurse(
269 arg_value, config_node->param_array_keys,
270 config_node->value, config_node->value_r)) {
271 arg_matched = true;
272 break;
273 }
274 } else { // match on all keys, but don't go into subarray
275 if (true == sp_match_array_key(arg_value, config_node->value,
276 config_node->value_r)) {
277 arg_matched = true;
278 break;
279 }
280 }
281 } else {
282 arg_value_str = sp_convert_to_string(arg_value);
283 if (true == sp_match_value(arg_value_str, config_node->value,
284 config_node->value_r)) {
285 arg_matched = true;
286 break;
287 }
288 }
289 }
290 }
291 }
292 if (false == arg_matched) {
293 goto next; 304 goto next;
294 } 305 }
295 } 306 }
@@ -302,21 +313,21 @@ bool should_disable(zend_execute_data* execute_data, const char *builtin_name, c
302 313
303 if (config_node->functions_list) { 314 if (config_node->functions_list) {
304 sp_log_disable(config_node->function, arg_name, arg_value_str, 315 sp_log_disable(config_node->function, arg_name, arg_value_str,
305 config_node); 316 config_node);
306 } else { 317 } else {
307 sp_log_disable(complete_path_function, arg_name, arg_value_str, 318 sp_log_disable(complete_path_function, arg_name, arg_value_str,
308 config_node); 319 config_node);
309 } 320 }
310 if (true == config_node->simulation) { 321 if (true == config_node->simulation) {
311 goto next; 322 goto next;
312 } else { // We've got a match, the function won't be executed 323 } else { // We've got a match, the function won't be executed
313 if (builtin_name == NULL) { 324 if (builtin_name == NULL) {
314 efree(complete_path_function); 325 efree(complete_path_function);
315 } 326 }
316 return true; 327 return true;
317 } 328 }
318next: 329 next:
319config = config->next; 330 config = config->next;
320 } 331 }
321allow: 332allow:
322 if (builtin_name == NULL) { 333 if (builtin_name == NULL) {
@@ -381,10 +392,10 @@ static bool should_drop_on_ret(zval* return_value,
381 ret_value_str = sp_convert_to_string(return_value); 392 ret_value_str = sp_convert_to_string(return_value);
382 393
383 match_type = (config_node->ret_type) && 394 match_type = (config_node->ret_type) &&
384 (config_node->ret_type == Z_TYPE_P(return_value)); 395 (config_node->ret_type == Z_TYPE_P(return_value));
385 match_value = (config_node->ret || config_node->r_ret) && 396 match_value = (config_node->ret || config_node->r_ret) &&
386 (true == sp_match_value(ret_value_str, config_node->ret, 397 (true == sp_match_value(ret_value_str, config_node->ret,
387 config_node->r_ret)); 398 config_node->r_ret));
388 399
389 if (true == match_type || true == match_value) { 400 if (true == match_type || true == match_value) {
390 if (true == config_node->allow) { 401 if (true == config_node->allow) {