diff options
Diffstat (limited to 'src/sp_execute.c')
| -rw-r--r-- | src/sp_execute.c | 105 |
1 files changed, 69 insertions, 36 deletions
diff --git a/src/sp_execute.c b/src/sp_execute.c index 3a474a3..b4e5c6c 100644 --- a/src/sp_execute.c +++ b/src/sp_execute.c | |||
| @@ -11,11 +11,10 @@ static zend_result (*orig_zend_stream_open)(zend_file_handle *handle) = NULL; | |||
| 11 | #endif | 11 | #endif |
| 12 | 12 | ||
| 13 | // FIXME handle symlink | 13 | // FIXME handle symlink |
| 14 | ZEND_COLD static inline void terminate_if_writable(const char *filename) { | 14 | ZEND_COLD static inline void terminate_if_writable(char const* const filename) { |
| 15 | const sp_config_readonly_exec *config_ro_exec = &(SPCFG(readonly_exec)); | 15 | sp_config_readonly_exec const* const config_ro_exec = &(SPCFG(readonly_exec)); |
| 16 | char *errmsg = "unknown access problem"; | 16 | char const *errmsg = "unknown access problem"; |
| 17 | 17 | ||
| 18 | // check write access | ||
| 19 | if (0 == access(filename, W_OK)) { | 18 | if (0 == access(filename, W_OK)) { |
| 20 | errmsg = "Attempted execution of a writable file"; | 19 | errmsg = "Attempted execution of a writable file"; |
| 21 | goto violation; | 20 | goto violation; |
| @@ -29,21 +28,20 @@ ZEND_COLD static inline void terminate_if_writable(const char *filename) { | |||
| 29 | return; | 28 | return; |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | // check effective uid | ||
| 33 | struct stat buf; | 31 | struct stat buf; |
| 34 | if (0 != stat(filename, &buf)) { | 32 | if (0 != stat(filename, &buf)) { |
| 35 | goto err; | 33 | goto err; |
| 36 | } | 34 | } |
| 37 | if (buf.st_uid == geteuid()) { | 35 | if (buf.st_uid == geteuid()) { |
| 38 | errmsg = "Attempted execution of file owned by process"; | 36 | errmsg = "Attempted execution of a file owned by the PHP process"; |
| 39 | goto violation; | 37 | goto violation; |
| 40 | } | 38 | } |
| 41 | 39 | ||
| 42 | // check write access on directory | 40 | char *const dirname = estrndup(filename, strlen(filename)); |
| 43 | char *dirname = estrndup(filename, strlen(filename)); | ||
| 44 | php_dirname(dirname, strlen(dirname)); | 41 | php_dirname(dirname, strlen(dirname)); |
| 45 | if (0 == access(dirname, W_OK)) { | 42 | if (0 == access(dirname, W_OK)) { |
| 46 | errmsg = "Attempted execution of file in writable directory"; | 43 | errmsg = "Attempted execution of a file in a writable directory"; |
| 44 | |||
| 47 | efree(dirname); | 45 | efree(dirname); |
| 48 | goto violation; | 46 | goto violation; |
| 49 | } | 47 | } |
| @@ -52,18 +50,16 @@ ZEND_COLD static inline void terminate_if_writable(const char *filename) { | |||
| 52 | goto err; | 50 | goto err; |
| 53 | } | 51 | } |
| 54 | 52 | ||
| 55 | // check effecite uid of directory | ||
| 56 | if (0 != stat(dirname, &buf)) { | 53 | if (0 != stat(dirname, &buf)) { |
| 57 | efree(dirname); | 54 | efree(dirname); |
| 58 | goto err; | 55 | goto err; |
| 59 | } | 56 | } |
| 60 | efree(dirname); | 57 | efree(dirname); |
| 61 | if (buf.st_uid == geteuid()) { | 58 | if (buf.st_uid == geteuid()) { |
| 62 | errmsg = "Attempted execution of file in directory owned by process"; | 59 | errmsg = "Attempted execution of a file in directory owned by the PHP process"; |
| 63 | goto violation; | 60 | goto violation; |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | // we would actually need to check all parent directories as well, but that task is left for other tools | ||
| 67 | return; | 63 | return; |
| 68 | 64 | ||
| 69 | violation: | 65 | violation: |
| @@ -93,8 +89,8 @@ inline static void is_builtin_matching( | |||
| 93 | should_disable_ht(EG(current_execute_data), function_name, param_value, param_name, SPCFG(disabled_functions_reg).disabled_functions, ht); | 89 | should_disable_ht(EG(current_execute_data), function_name, param_value, param_name, SPCFG(disabled_functions_reg).disabled_functions, ht); |
| 94 | } | 90 | } |
| 95 | 91 | ||
| 96 | static void ZEND_HOT is_in_eval_and_whitelisted(const zend_execute_data *execute_data) { | 92 | static void ZEND_HOT is_in_eval_and_whitelisted(zend_execute_data const* const execute_data) { |
| 97 | const sp_config_eval *config_eval = &(SPCFG(eval)); | 93 | sp_config_eval const* const config_eval = &(SPCFG(eval)); |
| 98 | 94 | ||
| 99 | if (EXPECTED(0 == SPG(in_eval))) { | 95 | if (EXPECTED(0 == SPG(in_eval))) { |
| 100 | return; | 96 | return; |
| @@ -113,18 +109,18 @@ static void ZEND_HOT is_in_eval_and_whitelisted(const zend_execute_data *execute | |||
| 113 | return; | 109 | return; |
| 114 | } | 110 | } |
| 115 | 111 | ||
| 116 | if (UNEXPECTED(false == check_is_in_eval_whitelist(function_name))) { | 112 | if (UNEXPECTED(false == check_is_in_eval_whitelist(function_name))) { |
| 117 | if (config_eval->dump) { | 113 | if (config_eval->dump) { |
| 118 | sp_log_request(config_eval->dump, config_eval->textual_representation); | 114 | sp_log_request(config_eval->dump, config_eval->textual_representation); |
| 119 | } | ||
| 120 | if (config_eval->simulation) { | ||
| 121 | sp_log_simulation("Eval_whitelist", "The function '%s' isn't in the eval whitelist, logging its call.", function_name); | ||
| 122 | goto out; | ||
| 123 | } else { | ||
| 124 | sp_log_drop("Eval_whitelist", "The function '%s' isn't in the eval whitelist, dropping its call.", function_name); | ||
| 125 | } | ||
| 126 | } | 115 | } |
| 127 | // } | 116 | if (config_eval->simulation) { |
| 117 | sp_log_simulation("Eval_whitelist", "The function '%s' isn't in the eval whitelist, logging its call.", function_name); | ||
| 118 | goto out; | ||
| 119 | } else { | ||
| 120 | sp_log_drop("Eval_whitelist", "The function '%s' isn't in the eval whitelist, dropping its call.", function_name); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 128 | out: | 124 | out: |
| 129 | efree(function_name); | 125 | efree(function_name); |
| 130 | } | 126 | } |
| @@ -185,11 +181,13 @@ static inline void sp_execute_handler(INTERNAL_FUNCTION_PARAMETERS, bool interna | |||
| 185 | 181 | ||
| 186 | if (!internal) { | 182 | if (!internal) { |
| 187 | if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) { | 183 | if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) { |
| 188 | const sp_list_node *config = zend_hash_str_find_ptr(SPCFG(disabled_functions), ZEND_STRL("eval")); | 184 | sp_list_node const* const config = zend_hash_str_find_ptr(SPCFG(disabled_functions), ZEND_STRL("eval")); |
| 189 | 185 | ||
| 190 | zend_string *filename = get_eval_filename(zend_get_executed_filename()); | 186 | #if PHP_VERSION_ID >= 80000 |
| 191 | is_builtin_matching(filename, "eval", NULL, config, SPCFG(disabled_functions)); | 187 | is_builtin_matching(SPG(eval_source_string), "eval", "code", config, SPCFG(disabled_functions)); |
| 192 | zend_string_release(filename); | 188 | #else |
| 189 | is_builtin_matching(Z_STR_P(SPG(eval_source_string)), "eval", "code", config, SPCFG(disabled_functions)); | ||
| 190 | #endif | ||
| 193 | 191 | ||
| 194 | SPG(in_eval)++; | 192 | SPG(in_eval)++; |
| 195 | sp_orig_execute(execute_data); | 193 | sp_orig_execute(execute_data); |
| @@ -255,10 +253,8 @@ static inline void sp_execute_handler(INTERNAL_FUNCTION_PARAMETERS, bool interna | |||
| 255 | if (EX(return_value) == &ret_val) { | 253 | if (EX(return_value) == &ret_val) { |
| 256 | return_value = EX(return_value) = NULL; | 254 | return_value = EX(return_value) = NULL; |
| 257 | } | 255 | } |
| 258 | |||
| 259 | } | 256 | } |
| 260 | 257 | ||
| 261 | |||
| 262 | static void sp_execute_ex(zend_execute_data *execute_data) { | 258 | static void sp_execute_ex(zend_execute_data *execute_data) { |
| 263 | sp_execute_handler(execute_data, execute_data ? EX(return_value) : NULL, false); | 259 | sp_execute_handler(execute_data, execute_data ? EX(return_value) : NULL, false); |
| 264 | } | 260 | } |
| @@ -275,7 +271,7 @@ static inline void sp_stream_open_checks(zend_string *zend_filename, zend_file_h | |||
| 275 | return; | 271 | return; |
| 276 | } | 272 | } |
| 277 | 273 | ||
| 278 | const HashTable *disabled_functions_hooked = SPCFG(disabled_functions_hooked); | 274 | HashTable const* const disabled_functions_hooked = SPCFG(disabled_functions_hooked); |
| 279 | 275 | ||
| 280 | switch (data->opline->opcode) { | 276 | switch (data->opline->opcode) { |
| 281 | case ZEND_INCLUDE_OR_EVAL: | 277 | case ZEND_INCLUDE_OR_EVAL: |
| @@ -316,10 +312,6 @@ static inline void sp_stream_open_checks(zend_string *zend_filename, zend_file_h | |||
| 316 | EMPTY_SWITCH_DEFAULT_CASE(); // LCOV_EXCL_LINE | 312 | EMPTY_SWITCH_DEFAULT_CASE(); // LCOV_EXCL_LINE |
| 317 | } | 313 | } |
| 318 | } | 314 | } |
| 319 | // efree(zend_filename); | ||
| 320 | |||
| 321 | // end: | ||
| 322 | // return orig_zend_stream_open(filename, handle); | ||
| 323 | } | 315 | } |
| 324 | 316 | ||
| 325 | #if PHP_VERSION_ID < 80100 | 317 | #if PHP_VERSION_ID < 80100 |
| @@ -342,6 +334,36 @@ static zend_result sp_stream_open(zend_file_handle *handle) { | |||
| 342 | 334 | ||
| 343 | #endif | 335 | #endif |
| 344 | 336 | ||
| 337 | ZEND_API zend_op_array* (*orig_zend_compile_file)(zend_file_handle* file_handle, | ||
| 338 | int type) = NULL; | ||
| 339 | #if PHP_VERSION_ID >= 80000 | ||
| 340 | ZEND_API zend_op_array* (*orig_zend_compile_string)( | ||
| 341 | zend_string* source_string, const char* filename) = NULL; | ||
| 342 | #else | ||
| 343 | ZEND_API zend_op_array* (*orig_zend_compile_string)(zval* source_string, | ||
| 344 | char* filename) = NULL; | ||
| 345 | #endif | ||
| 346 | |||
| 347 | #if PHP_VERSION_ID >= 80000 | ||
| 348 | ZEND_API zend_op_array* sp_compile_string(zend_string* source_string, | ||
| 349 | const char* filename) { | ||
| 350 | #else | ||
| 351 | ZEND_API zend_op_array* sp_compile_string(zval* source_string, char* filename) { | ||
| 352 | #endif | ||
| 353 | // TODO(jvoisin) handle recursive calls to `eval` | ||
| 354 | SPG(eval_source_string) = source_string; | ||
| 355 | zend_op_array* opline = orig_zend_compile_string(source_string, filename); | ||
| 356 | sp_sloppy_modify_opcode(opline); | ||
| 357 | return opline; | ||
| 358 | } | ||
| 359 | |||
| 360 | ZEND_API zend_op_array* sp_compile_file(zend_file_handle* file_handle, | ||
| 361 | int type) { | ||
| 362 | zend_op_array* opline = orig_zend_compile_file(file_handle, type); | ||
| 363 | sp_sloppy_modify_opcode(opline); | ||
| 364 | return opline; | ||
| 365 | } | ||
| 366 | |||
| 345 | int hook_execute(void) { | 367 | int hook_execute(void) { |
| 346 | TSRMLS_FETCH(); | 368 | TSRMLS_FETCH(); |
| 347 | 369 | ||
| @@ -365,5 +387,16 @@ int hook_execute(void) { | |||
| 365 | } | 387 | } |
| 366 | } | 388 | } |
| 367 | 389 | ||
| 390 | if (NULL == orig_zend_compile_file && zend_compile_file != sp_compile_file) { | ||
| 391 | orig_zend_compile_file = zend_compile_file; | ||
| 392 | zend_compile_file = sp_compile_file; | ||
| 393 | } | ||
| 394 | |||
| 395 | if (NULL == orig_zend_compile_string && | ||
| 396 | zend_compile_string != sp_compile_string) { | ||
| 397 | orig_zend_compile_string = zend_compile_string; | ||
| 398 | zend_compile_string = sp_compile_string; | ||
| 399 | } | ||
| 400 | |||
| 368 | return SUCCESS; | 401 | return SUCCESS; |
| 369 | } | 402 | } |
