diff options
| author | jvoisin | 2018-01-10 14:56:33 +0100 |
|---|---|---|
| committer | GitHub | 2018-01-10 14:56:33 +0100 |
| commit | ad6b3e723fe26bf1a3a573aed776960916d35499 (patch) | |
| tree | eec9e15028f4529d776489d273bf9699333aa987 | |
| parent | b6e5bc4557cca3abbcfd179e7143ea54b9844e49 (diff) | |
Eval whitelist
Implement whitelist in eval
| -rw-r--r-- | doc/source/config.rst | 9 | ||||
| -rw-r--r-- | src/php_snuffleupagus.h | 4 | ||||
| -rw-r--r-- | src/snuffleupagus.c | 4 | ||||
| -rw-r--r-- | src/sp_config.c | 3 | ||||
| -rw-r--r-- | src/sp_config.h | 6 | ||||
| -rw-r--r-- | src/sp_config_keywords.c | 16 | ||||
| -rw-r--r-- | src/sp_config_keywords.h | 3 | ||||
| -rw-r--r-- | src/sp_disabled_functions.c | 12 | ||||
| -rw-r--r-- | src/sp_disabled_functions.h | 1 | ||||
| -rw-r--r-- | src/sp_execute.c | 88 | ||||
| -rw-r--r-- | src/tests/config/eval_backlist.ini | 2 | ||||
| -rw-r--r-- | src/tests/config/eval_backlist_list.ini | 2 | ||||
| -rw-r--r-- | src/tests/config/eval_backlist_simulation.ini | 2 | ||||
| -rw-r--r-- | src/tests/config/eval_whitelist.ini | 1 | ||||
| -rw-r--r-- | src/tests/eval_backlist_whitelist.phpt | 27 | ||||
| -rw-r--r-- | src/tests/eval_whitelist_builtin.phpt | 19 | ||||
| -rw-r--r-- | src/tests/eval_whitelist_include_then_user.phpt | 29 | ||||
| -rw-r--r-- | src/tests/eval_whitelist_user_then_builtin.phpt | 23 |
18 files changed, 208 insertions, 43 deletions
diff --git a/doc/source/config.rst b/doc/source/config.rst index d16474f..602b78d 100644 --- a/doc/source/config.rst +++ b/doc/source/config.rst | |||
| @@ -251,13 +251,14 @@ Eval white and blacklist | |||
| 251 | * `default: disabled` | 251 | * `default: disabled` |
| 252 | * :ref:`more <eval-feature>` | 252 | * :ref:`more <eval-feature>` |
| 253 | 253 | ||
| 254 | ``eval_filter`` allows to specify white and blacklist of functions allowed and | 254 | ``eval_whitelist`` and ``eval_blacklist`` allow to respectively specify |
| 255 | forbidden from being called inside ``eval``. The functions names are comma-separated. | 255 | functions allowed and forbidden from being called inside ``eval``. The |
| 256 | functions names are comma-separated. | ||
| 256 | 257 | ||
| 257 | :: | 258 | :: |
| 258 | 259 | ||
| 259 | sp.eval_filter.blacklist("system,exec,shell_exec"); | 260 | sp.eval_blacklist.list("system,exec,shell_exec"); |
| 260 | sp.eval_filter.whitelist("strlen,strcmp").simulation(); | 261 | sp.eval_whitelist.list("strlen,strcmp").simulation(); |
| 261 | 262 | ||
| 262 | 263 | ||
| 263 | Virtual-patching | 264 | Virtual-patching |
diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h index ca39bb8..93ef472 100644 --- a/src/php_snuffleupagus.h +++ b/src/php_snuffleupagus.h | |||
| @@ -63,7 +63,7 @@ sp_config config; | |||
| 63 | bool is_config_valid; | 63 | bool is_config_valid; |
| 64 | HashTable *disabled_functions_hook; | 64 | HashTable *disabled_functions_hook; |
| 65 | HashTable *sp_internal_functions_hook; | 65 | HashTable *sp_internal_functions_hook; |
| 66 | HashTable *sp_eval_filter_functions_hook; | 66 | HashTable *sp_eval_blacklist_functions_hook; |
| 67 | ZEND_END_MODULE_GLOBALS(snuffleupagus) | 67 | ZEND_END_MODULE_GLOBALS(snuffleupagus) |
| 68 | 68 | ||
| 69 | #define SNUFFLEUPAGUS_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(snuffleupagus, v) | 69 | #define SNUFFLEUPAGUS_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(snuffleupagus, v) |
| @@ -85,7 +85,7 @@ ZEND_TSRMLS_CACHE_EXTERN() | |||
| 85 | #endif | 85 | #endif |
| 86 | 86 | ||
| 87 | PHP_FUNCTION(check_disabled_function); | 87 | PHP_FUNCTION(check_disabled_function); |
| 88 | PHP_FUNCTION(eval_filter_callback); | 88 | PHP_FUNCTION(eval_blacklist_callback); |
| 89 | 89 | ||
| 90 | static inline void sp_terminate() { zend_bailout(); } | 90 | static inline void sp_terminate() { zend_bailout(); } |
| 91 | 91 | ||
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c index 4f11e1e..cb79b23 100644 --- a/src/snuffleupagus.c +++ b/src/snuffleupagus.c | |||
| @@ -62,7 +62,7 @@ PHP_GINIT_FUNCTION(snuffleupagus) { | |||
| 62 | 62 | ||
| 63 | SP_INIT_HT(snuffleupagus_globals->disabled_functions_hook); | 63 | SP_INIT_HT(snuffleupagus_globals->disabled_functions_hook); |
| 64 | SP_INIT_HT(snuffleupagus_globals->sp_internal_functions_hook); | 64 | SP_INIT_HT(snuffleupagus_globals->sp_internal_functions_hook); |
| 65 | SP_INIT_HT(snuffleupagus_globals->sp_eval_filter_functions_hook); | 65 | SP_INIT_HT(snuffleupagus_globals->sp_eval_blacklist_functions_hook); |
| 66 | 66 | ||
| 67 | SP_INIT(snuffleupagus_globals->config.config_unserialize); | 67 | SP_INIT(snuffleupagus_globals->config.config_unserialize); |
| 68 | SP_INIT(snuffleupagus_globals->config.config_random); | 68 | SP_INIT(snuffleupagus_globals->config.config_random); |
| @@ -106,7 +106,7 @@ PHP_MSHUTDOWN_FUNCTION(snuffleupagus) { | |||
| 106 | pefree(SNUFFLEUPAGUS_G(F), 1); | 106 | pefree(SNUFFLEUPAGUS_G(F), 1); |
| 107 | 107 | ||
| 108 | FREE_HT(disabled_functions_hook); | 108 | FREE_HT(disabled_functions_hook); |
| 109 | FREE_HT(sp_eval_filter_functions_hook); | 109 | FREE_HT(sp_eval_blacklist_functions_hook); |
| 110 | 110 | ||
| 111 | #undef FREE_HT | 111 | #undef FREE_HT |
| 112 | 112 | ||
diff --git a/src/sp_config.c b/src/sp_config.c index dc5aae9..4037e26 100644 --- a/src/sp_config.c +++ b/src/sp_config.c | |||
| @@ -19,7 +19,8 @@ sp_config_tokens const sp_func[] = { | |||
| 19 | {.func = parse_global, .token = SP_TOKEN_GLOBAL}, | 19 | {.func = parse_global, .token = SP_TOKEN_GLOBAL}, |
| 20 | {.func = parse_auto_cookie_secure, .token = SP_TOKEN_AUTO_COOKIE_SECURE}, | 20 | {.func = parse_auto_cookie_secure, .token = SP_TOKEN_AUTO_COOKIE_SECURE}, |
| 21 | {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE}, | 21 | {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE}, |
| 22 | {.func = parse_eval_filter, .token = SP_TOKEN_EVAL}, | 22 | {.func = parse_eval_blacklist, .token = SP_TOKEN_EVAL_BLACKLIST}, |
| 23 | {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST}, | ||
| 23 | {NULL, NULL}}; | 24 | {NULL, NULL}}; |
| 24 | 25 | ||
| 25 | /* Top level keyword parsing */ | 26 | /* Top level keyword parsing */ |
diff --git a/src/sp_config.h b/src/sp_config.h index a4a4f10..25963f1 100644 --- a/src/sp_config.h +++ b/src/sp_config.h | |||
| @@ -177,7 +177,8 @@ typedef struct { | |||
| 177 | #define SP_TOKEN_UNSERIALIZE_HMAC ".unserialize_hmac" | 177 | #define SP_TOKEN_UNSERIALIZE_HMAC ".unserialize_hmac" |
| 178 | #define SP_TOKEN_UPLOAD_VALIDATION ".upload_validation" | 178 | #define SP_TOKEN_UPLOAD_VALIDATION ".upload_validation" |
| 179 | #define SP_TOKEN_DISABLE_XXE ".disable_xxe" | 179 | #define SP_TOKEN_DISABLE_XXE ".disable_xxe" |
| 180 | #define SP_TOKEN_EVAL ".eval_filter" | 180 | #define SP_TOKEN_EVAL_BLACKLIST ".eval_blacklist" |
| 181 | #define SP_TOKEN_EVAL_WHITELIST ".eval_whitelist" | ||
| 181 | 182 | ||
| 182 | // common tokens | 183 | // common tokens |
| 183 | #define SP_TOKEN_ENABLE ".enable(" | 184 | #define SP_TOKEN_ENABLE ".enable(" |
| @@ -231,8 +232,7 @@ typedef struct { | |||
| 231 | #define SP_TOKEN_UPLOAD_SCRIPT ".script(" | 232 | #define SP_TOKEN_UPLOAD_SCRIPT ".script(" |
| 232 | 233 | ||
| 233 | // eval blacklist | 234 | // eval blacklist |
| 234 | #define SP_TOKEN_EVAL_BLACKLIST ".blacklist(" | 235 | #define SP_TOKEN_EVAL_LIST ".list(" |
| 235 | #define SP_TOKEN_EVAL_WHITELIST ".whitelist(" | ||
| 236 | 236 | ||
| 237 | int sp_parse_config(const char *); | 237 | int sp_parse_config(const char *); |
| 238 | int parse_array(sp_disabled_function *); | 238 | int parse_array(sp_disabled_function *); |
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c index 85e04ab..c5cc950 100644 --- a/src/sp_config_keywords.c +++ b/src/sp_config_keywords.c | |||
| @@ -102,11 +102,11 @@ int parse_global(char *line) { | |||
| 102 | return parse_keywords(sp_config_funcs_global, line); | 102 | return parse_keywords(sp_config_funcs_global, line); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | int parse_eval_filter(char *line) { | 105 | static int parse_eval_filter_conf(char *line, sp_list_node *list) { |
| 106 | char *token; | 106 | char *token; |
| 107 | char *rest; | 107 | char *rest; |
| 108 | sp_config_functions sp_config_funcs[] = { | 108 | sp_config_functions sp_config_funcs[] = { |
| 109 | {parse_str, SP_TOKEN_EVAL_BLACKLIST, &rest}, | 109 | {parse_str, SP_TOKEN_EVAL_LIST, &rest}, |
| 110 | {parse_empty, SP_TOKEN_SIMULATION, | 110 | {parse_empty, SP_TOKEN_SIMULATION, |
| 111 | &(SNUFFLEUPAGUS_G(config).config_eval->simulation)}, | 111 | &(SNUFFLEUPAGUS_G(config).config_eval->simulation)}, |
| 112 | {0}}; | 112 | {0}}; |
| @@ -116,11 +116,21 @@ int parse_eval_filter(char *line) { | |||
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | while ((token = strtok_r(rest, ",", &rest))) { | 118 | while ((token = strtok_r(rest, ",", &rest))) { |
| 119 | sp_list_insert(SNUFFLEUPAGUS_G(config).config_eval->blacklist, token); | 119 | sp_list_insert(list, token); |
| 120 | } | 120 | } |
| 121 | return SUCCESS; | 121 | return SUCCESS; |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | int parse_eval_blacklist(char *line) { | ||
| 125 | return parse_eval_filter_conf(line, | ||
| 126 | SNUFFLEUPAGUS_G(config).config_eval->blacklist); | ||
| 127 | } | ||
| 128 | |||
| 129 | int parse_eval_whitelist(char *line) { | ||
| 130 | return parse_eval_filter_conf(line, | ||
| 131 | SNUFFLEUPAGUS_G(config).config_eval->whitelist); | ||
| 132 | } | ||
| 133 | |||
| 124 | int parse_cookie(char *line) { | 134 | int parse_cookie(char *line) { |
| 125 | int ret = 0; | 135 | int ret = 0; |
| 126 | char *samesite = NULL; | 136 | char *samesite = NULL; |
diff --git a/src/sp_config_keywords.h b/src/sp_config_keywords.h index d7ea36a..4205dac 100644 --- a/src/sp_config_keywords.h +++ b/src/sp_config_keywords.h | |||
| @@ -12,6 +12,7 @@ int parse_unserialize(char *line); | |||
| 12 | int parse_readonly_exec(char *line); | 12 | int parse_readonly_exec(char *line); |
| 13 | int parse_disabled_functions(char *line); | 13 | int parse_disabled_functions(char *line); |
| 14 | int parse_upload_validation(char *line); | 14 | int parse_upload_validation(char *line); |
| 15 | int parse_eval_filter(char *line); | 15 | int parse_eval_blacklist(char *line); |
| 16 | int parse_eval_whitelist(char *line); | ||
| 16 | 17 | ||
| 17 | #endif // __SP_CONFIG_KEYWORDS_H | 18 | #endif // __SP_CONFIG_KEYWORDS_H |
diff --git a/src/sp_disabled_functions.c b/src/sp_disabled_functions.c index 8e96085..fa9d625 100644 --- a/src/sp_disabled_functions.c +++ b/src/sp_disabled_functions.c | |||
| @@ -5,7 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) | 6 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) |
| 7 | 7 | ||
| 8 | static char* get_complete_function_path( | 8 | |
| 9 | char* get_complete_function_path( | ||
| 9 | zend_execute_data const* const execute_data) { | 10 | zend_execute_data const* const execute_data) { |
| 10 | if (zend_is_executing() && !EG(current_execute_data)->func) { | 11 | if (zend_is_executing() && !EG(current_execute_data)->func) { |
| 11 | return NULL; | 12 | return NULL; |
| @@ -107,6 +108,7 @@ static const sp_list_node* get_config_node(const char* builtin_name) { | |||
| 107 | return SNUFFLEUPAGUS_G(config) | 108 | return SNUFFLEUPAGUS_G(config) |
| 108 | .config_disabled_constructs->construct_include; | 109 | .config_disabled_constructs->construct_include; |
| 109 | } | 110 | } |
| 111 | ZEND_ASSUME(0); | ||
| 110 | return NULL; // This should never happen. | 112 | return NULL; // This should never happen. |
| 111 | } | 113 | } |
| 112 | 114 | ||
| @@ -463,7 +465,7 @@ static int hook_functions(const sp_list_node* config) { | |||
| 463 | return SUCCESS; | 465 | return SUCCESS; |
| 464 | } | 466 | } |
| 465 | 467 | ||
| 466 | ZEND_FUNCTION(eval_filter_callback) { | 468 | ZEND_FUNCTION(eval_blacklist_callback) { |
| 467 | void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); | 469 | void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); |
| 468 | const char* current_function_name = get_active_function_name(TSRMLS_C); | 470 | const char* current_function_name = get_active_function_name(TSRMLS_C); |
| 469 | 471 | ||
| @@ -483,7 +485,7 @@ ZEND_FUNCTION(eval_filter_callback) { | |||
| 483 | } | 485 | } |
| 484 | 486 | ||
| 485 | orig_handler = zend_hash_str_find_ptr( | 487 | orig_handler = zend_hash_str_find_ptr( |
| 486 | SNUFFLEUPAGUS_G(sp_eval_filter_functions_hook), current_function_name, | 488 | SNUFFLEUPAGUS_G(sp_eval_blacklist_functions_hook), current_function_name, |
| 487 | strlen(current_function_name)); | 489 | strlen(current_function_name)); |
| 488 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); | 490 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); |
| 489 | } | 491 | } |
| @@ -503,8 +505,8 @@ int hook_disabled_functions(void) { | |||
| 503 | 505 | ||
| 504 | while (it) { | 506 | while (it) { |
| 505 | hook_function((char*)it->data, | 507 | hook_function((char*)it->data, |
| 506 | SNUFFLEUPAGUS_G(sp_eval_filter_functions_hook), | 508 | SNUFFLEUPAGUS_G(sp_eval_blacklist_functions_hook), |
| 507 | PHP_FN(eval_filter_callback), false); | 509 | PHP_FN(eval_blacklist_callback), false); |
| 508 | it = it->next; | 510 | it = it->next; |
| 509 | } | 511 | } |
| 510 | } | 512 | } |
diff --git a/src/sp_disabled_functions.h b/src/sp_disabled_functions.h index f80c9c2..9629308 100644 --- a/src/sp_disabled_functions.h +++ b/src/sp_disabled_functions.h | |||
| @@ -5,5 +5,6 @@ int hook_disabled_functions(); | |||
| 5 | bool should_disable(zend_execute_data *, const char *, const char *, | 5 | bool should_disable(zend_execute_data *, const char *, const char *, |
| 6 | const char *); | 6 | const char *); |
| 7 | bool should_drop_on_ret(zval *, const zend_execute_data *const); | 7 | bool should_drop_on_ret(zval *, const zend_execute_data *const); |
| 8 | char* get_complete_function_path(zend_execute_data const* const execute_data); | ||
| 8 | 9 | ||
| 9 | #endif /* __SP_DISABLE_FUNCTIONS_H */ | 10 | #endif /* __SP_DISABLE_FUNCTIONS_H */ |
diff --git a/src/sp_execute.c b/src/sp_execute.c index 3ce6643..c1d68d7 100644 --- a/src/sp_execute.c +++ b/src/sp_execute.c | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) | 6 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) |
| 7 | 7 | ||
| 8 | static void (*orig_execute_ex)(zend_execute_data *execute_data); | 8 | static void (*orig_execute_ex)(zend_execute_data *execute_data); |
| 9 | static void (*orig_zend_execute_internal)(zend_execute_data *execute_data, | ||
| 10 | zval *return_value); | ||
| 9 | static int (*orig_zend_stream_open)(const char *filename, | 11 | static int (*orig_zend_stream_open)(const char *filename, |
| 10 | zend_file_handle *handle); | 12 | zend_file_handle *handle); |
| 11 | 13 | ||
| @@ -29,9 +31,9 @@ ZEND_COLD static inline void terminate_if_writable(const char *filename) { | |||
| 29 | } | 31 | } |
| 30 | 32 | ||
| 31 | static void is_builtin_matching(const char *restrict const filename, | 33 | static void is_builtin_matching(const char *restrict const filename, |
| 32 | char *restrict function_name, | 34 | const char *restrict const function_name, |
| 33 | char *restrict param_name, | 35 | const char *restrict const param_name, |
| 34 | sp_list_node *config) { | 36 | const sp_list_node *config) { |
| 35 | if (!config || !config->data) { | 37 | if (!config || !config->data) { |
| 36 | return; | 38 | return; |
| 37 | } | 39 | } |
| @@ -42,6 +44,43 @@ static void is_builtin_matching(const char *restrict const filename, | |||
| 42 | } | 44 | } |
| 43 | } | 45 | } |
| 44 | 46 | ||
| 47 | static void is_in_eval_and_whitelisted(zend_execute_data *execute_data) { | ||
| 48 | if (EXPECTED(0 == SNUFFLEUPAGUS_G(in_eval))) { | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (EXPECTED(NULL == SNUFFLEUPAGUS_G(config).config_eval->whitelist->data)) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | |||
| 56 | if (zend_is_executing() && !EG(current_execute_data)->func) { | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (!(execute_data->func->common.function_name)) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | char const* const current_function = ZSTR_VAL(EX(func)->common.function_name); | ||
| 65 | |||
| 66 | if (EXPECTED(current_function)) { | ||
| 67 | const sp_list_node *it = SNUFFLEUPAGUS_G(config).config_eval->whitelist; | ||
| 68 | while (it) { | ||
| 69 | if (0 == strcmp(current_function, (char *)(it->data))) { | ||
| 70 | /* We've got a match, the function is whiteslited. */ | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | it = it->next; | ||
| 74 | } | ||
| 75 | |||
| 76 | sp_log_msg( | ||
| 77 | "Eval_whitelist", SP_LOG_DROP, | ||
| 78 | "The function '%s' isn't in the eval whitelist, dropping its call.", | ||
| 79 | current_function); | ||
| 80 | sp_terminate(); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 45 | /* This function gets the filename in which `eval()` is called from, | 84 | /* This function gets the filename in which `eval()` is called from, |
| 46 | * since it looks like "foo.php(1) : eval()'d code", so we're starting | 85 | * since it looks like "foo.php(1) : eval()'d code", so we're starting |
| 47 | * from the end of the string until the second closing parenthesis. */ | 86 | * from the end of the string until the second closing parenthesis. */ |
| @@ -67,28 +106,42 @@ static void sp_execute_ex(zend_execute_data *execute_data) { | |||
| 67 | sp_terminate(); | 106 | sp_terminate(); |
| 68 | } | 107 | } |
| 69 | 108 | ||
| 70 | if (execute_data->func->op_array.type == ZEND_EVAL_CODE) { | 109 | if (EX(func)->op_array.type == ZEND_EVAL_CODE) { |
| 71 | SNUFFLEUPAGUS_G(in_eval)++; | 110 | SNUFFLEUPAGUS_G(in_eval)++; |
| 72 | sp_list_node *config = | 111 | const sp_list_node *config = |
| 73 | SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval; | 112 | SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval; |
| 74 | char *filename = get_eval_filename((char *)zend_get_executed_filename()); | 113 | char *filename = get_eval_filename((char *)zend_get_executed_filename()); |
| 75 | is_builtin_matching(filename, "eval", NULL, config); | 114 | is_builtin_matching(filename, "eval", NULL, config); |
| 76 | efree(filename); | 115 | efree(filename); |
| 77 | } | 116 | } |
| 78 | 117 | ||
| 79 | if (NULL != execute_data->func->op_array.filename) { | 118 | is_in_eval_and_whitelisted(execute_data); |
| 119 | |||
| 120 | if (NULL != EX(func)->op_array.filename) { | ||
| 80 | if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { | 121 | if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { |
| 81 | terminate_if_writable(ZSTR_VAL(execute_data->func->op_array.filename)); | 122 | terminate_if_writable(ZSTR_VAL(EX(func)->op_array.filename)); |
| 82 | } | 123 | } |
| 83 | } | 124 | } |
| 84 | 125 | ||
| 85 | orig_execute_ex(execute_data); | 126 | orig_execute_ex(execute_data); |
| 86 | 127 | ||
| 87 | if (true == should_drop_on_ret(execute_data->return_value, execute_data)) { | 128 | if (true == should_drop_on_ret(EX(return_value), execute_data)) { |
| 88 | sp_terminate(); | 129 | sp_terminate(); |
| 89 | } | 130 | } |
| 90 | 131 | ||
| 91 | SNUFFLEUPAGUS_G(in_eval)--; | 132 | if (ZEND_EVAL_CODE == EX(func)->op_array.type) { |
| 133 | SNUFFLEUPAGUS_G(in_eval)--; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | static void sp_zend_execute_internal(INTERNAL_FUNCTION_PARAMETERS) { | ||
| 138 | is_in_eval_and_whitelisted(execute_data); | ||
| 139 | |||
| 140 | EX(func)->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); | ||
| 141 | |||
| 142 | if (UNEXPECTED(NULL != orig_zend_execute_internal)) { | ||
| 143 | orig_zend_execute_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); | ||
| 144 | } | ||
| 92 | } | 145 | } |
| 93 | 146 | ||
| 94 | static int sp_stream_open(const char *filename, zend_file_handle *handle) { | 147 | static int sp_stream_open(const char *filename, zend_file_handle *handle) { |
| @@ -121,11 +174,7 @@ static int sp_stream_open(const char *filename, zend_file_handle *handle) { | |||
| 121 | is_builtin_matching(filename, "include_once", "inclusion path", | 174 | is_builtin_matching(filename, "include_once", "inclusion path", |
| 122 | config); | 175 | config); |
| 123 | break; | 176 | break; |
| 124 | case ZEND_EVAL: | 177 | EMPTY_SWITCH_DEFAULT_CASE(); |
| 125 | is_builtin_matching(filename, "eval", NULL, config); | ||
| 126 | break; | ||
| 127 | default: | ||
| 128 | break; | ||
| 129 | } | 178 | } |
| 130 | } | 179 | } |
| 131 | 180 | ||
| @@ -136,16 +185,17 @@ end: | |||
| 136 | int hook_execute(void) { | 185 | int hook_execute(void) { |
| 137 | TSRMLS_FETCH(); | 186 | TSRMLS_FETCH(); |
| 138 | 187 | ||
| 139 | /* zend_execute_ex is used for "classic" function calls */ | 188 | /* zend_execute_ex is used for "user" function calls */ |
| 140 | orig_execute_ex = zend_execute_ex; | 189 | orig_execute_ex = zend_execute_ex; |
| 141 | zend_execute_ex = sp_execute_ex; | 190 | zend_execute_ex = sp_execute_ex; |
| 142 | 191 | ||
| 143 | /* zend_stream_open_function is used FIXME */ | 192 | /* zend_execute_internal is used for "builtin" functions calls */ |
| 193 | orig_zend_execute_internal = zend_execute_internal; | ||
| 194 | zend_execute_internal = sp_zend_execute_internal; | ||
| 195 | |||
| 196 | /* zend_stream_open_function is used for include-related stuff */ | ||
| 144 | orig_zend_stream_open = zend_stream_open_function; | 197 | orig_zend_stream_open = zend_stream_open_function; |
| 145 | zend_stream_open_function = sp_stream_open; | 198 | zend_stream_open_function = sp_stream_open; |
| 146 | 199 | ||
| 147 | /* zend_execute_internal is used for "indirect" functions call, | ||
| 148 | * like array_map or call_user_func. */ | ||
| 149 | |||
| 150 | return SUCCESS; | 200 | return SUCCESS; |
| 151 | } | 201 | } |
diff --git a/src/tests/config/eval_backlist.ini b/src/tests/config/eval_backlist.ini index 1e34b5b..b181598 100644 --- a/src/tests/config/eval_backlist.ini +++ b/src/tests/config/eval_backlist.ini | |||
| @@ -1 +1 @@ | |||
| sp.eval_filter.blacklist("strlen"); | sp.eval_blacklist.list("strlen"); | ||
diff --git a/src/tests/config/eval_backlist_list.ini b/src/tests/config/eval_backlist_list.ini index da5650d..b395d03 100644 --- a/src/tests/config/eval_backlist_list.ini +++ b/src/tests/config/eval_backlist_list.ini | |||
| @@ -1 +1 @@ | |||
| sp.eval_filter.blacklist("strcmp,strlen"); | sp.eval_blacklist.list("strcmp,strlen"); | ||
diff --git a/src/tests/config/eval_backlist_simulation.ini b/src/tests/config/eval_backlist_simulation.ini index fafebd5..2d8dc73 100644 --- a/src/tests/config/eval_backlist_simulation.ini +++ b/src/tests/config/eval_backlist_simulation.ini | |||
| @@ -1 +1 @@ | |||
| sp.eval_filter.blacklist("strlen").simulation(); | sp.eval_blacklist.list("strlen").simulation(); | ||
diff --git a/src/tests/config/eval_whitelist.ini b/src/tests/config/eval_whitelist.ini new file mode 100644 index 0000000..7a8f6ef --- /dev/null +++ b/src/tests/config/eval_whitelist.ini | |||
| @@ -0,0 +1 @@ | |||
| sp.eval_whitelist.list("my_fun,cos"); | |||
diff --git a/src/tests/eval_backlist_whitelist.phpt b/src/tests/eval_backlist_whitelist.phpt new file mode 100644 index 0000000..1611288 --- /dev/null +++ b/src/tests/eval_backlist_whitelist.phpt | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | --TEST-- | ||
| 2 | Eval whitelist | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) die "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/eval_whitelist.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | function my_fun($p) { | ||
| 10 | return "my_fun: $p"; | ||
| 11 | } | ||
| 12 | |||
| 13 | function my_other_fun($p) { | ||
| 14 | return "my_other_fun: $p"; | ||
| 15 | } | ||
| 16 | |||
| 17 | $a = my_fun("1337 1337 1337"); | ||
| 18 | echo "Outside of eval: $a\n"; | ||
| 19 | eval('$a = my_fun("1234");'); | ||
| 20 | echo "After allowed eval: $a\n"; | ||
| 21 | eval('$a = my_other_fun("1234");'); | ||
| 22 | echo "After eval: $a\n"; | ||
| 23 | ?> | ||
| 24 | --EXPECTF-- | ||
| 25 | Outside of eval: my_fun: 1337 1337 1337 | ||
| 26 | After allowed eval: my_fun: 1234 | ||
| 27 | [snuffleupagus][0.0.0.0][Eval_whitelist][drop] The function 'my_other_fun' isn't in the eval whitelist, dropping its call. | ||
diff --git a/src/tests/eval_whitelist_builtin.phpt b/src/tests/eval_whitelist_builtin.phpt new file mode 100644 index 0000000..bd7c2ac --- /dev/null +++ b/src/tests/eval_whitelist_builtin.phpt | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | --TEST-- | ||
| 2 | Eval whitelist - builtin function | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) die "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/eval_whitelist.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | $a = cos(1); | ||
| 10 | echo "Outside of eval: $a\n"; | ||
| 11 | eval('$a = cos(5);'); | ||
| 12 | echo "After allowed eval: $a\n"; | ||
| 13 | eval('$a = sin(4);'); | ||
| 14 | echo "After eval: $a\n"; | ||
| 15 | ?> | ||
| 16 | --EXPECTF-- | ||
| 17 | Outside of eval: 0.54030230586814 | ||
| 18 | After allowed eval: 0.28366218546323 | ||
| 19 | [snuffleupagus][0.0.0.0][Eval_whitelist][drop] The function 'sin' isn't in the eval whitelist, dropping its call. | ||
diff --git a/src/tests/eval_whitelist_include_then_user.phpt b/src/tests/eval_whitelist_include_then_user.phpt new file mode 100644 index 0000000..6d4e36a --- /dev/null +++ b/src/tests/eval_whitelist_include_then_user.phpt | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | --TEST-- | ||
| 2 | Eval whitelist - builtin function | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) die "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/eval_whitelist.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | $b = 1337; | ||
| 10 | $dir = __DIR__; | ||
| 11 | |||
| 12 | file_put_contents($dir . '/test.bla', '<?php $b = sin(1) ?>'); | ||
| 13 | |||
| 14 | $a = cos(1); | ||
| 15 | echo "Outside of eval: $a\n"; | ||
| 16 | eval('$a = cos(5);'); | ||
| 17 | echo "After allowed eval: $a\n"; | ||
| 18 | eval("include_once('$dir' . '/test.bla');"); | ||
| 19 | echo "After eval: $b\n"; | ||
| 20 | ?> | ||
| 21 | --CLEAN-- | ||
| 22 | <?php | ||
| 23 | $dir = __DIR__; | ||
| 24 | unlink($dir . '/test.bla'); | ||
| 25 | ?> | ||
| 26 | --EXPECTF-- | ||
| 27 | Outside of eval: 0.54030230586814 | ||
| 28 | After allowed eval: 0.28366218546323 | ||
| 29 | [snuffleupagus][0.0.0.0][Eval_whitelist][drop] The function 'sin' isn't in the eval whitelist, dropping its call. | ||
diff --git a/src/tests/eval_whitelist_user_then_builtin.phpt b/src/tests/eval_whitelist_user_then_builtin.phpt new file mode 100644 index 0000000..8db36fc --- /dev/null +++ b/src/tests/eval_whitelist_user_then_builtin.phpt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | --TEST-- | ||
| 2 | Eval whitelist - builtin function | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) die "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/eval_whitelist.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | |||
| 10 | function my_fun() { | ||
| 11 | return sin(10); | ||
| 12 | } | ||
| 13 | |||
| 14 | $a = my_fun(1); | ||
| 15 | echo "Outside of eval: $a\n"; | ||
| 16 | eval('$a = my_fun(5);'); | ||
| 17 | echo "After allowed eval: $a\n"; | ||
| 18 | eval('$a = my_fun(4);'); | ||
| 19 | echo "After eval: $a\n"; | ||
| 20 | ?> | ||
| 21 | --EXPECTF-- | ||
| 22 | Outside of eval: -0.54402111088937 | ||
| 23 | [snuffleupagus][0.0.0.0][Eval_whitelist][drop] The function 'sin' isn't in the eval whitelist, dropping its call. | ||
