diff options
| author | jvoisin | 2022-03-20 18:20:45 +0100 |
|---|---|---|
| committer | jvoisin | 2022-03-20 18:20:45 +0100 |
| commit | 81dd7f2ef07af306fe83d7755cbac4529aa9fc8d (patch) | |
| tree | 32cc44c6231b30db5ac7b15699297863460784aa /src/sp_utils.c | |
| parent | 83b01942dfc80474cc05e09aeef4b44307a7120b (diff) | |
| parent | c38df1077a6c1dfbca1baca049214d053e2e7684 (diff) | |
Merge remote-tracking branch 'sektioneins/master'
Diffstat (limited to 'src/sp_utils.c')
| -rw-r--r-- | src/sp_utils.c | 158 |
1 files changed, 81 insertions, 77 deletions
diff --git a/src/sp_utils.c b/src/sp_utils.c index b78f7b1..6161859 100644 --- a/src/sp_utils.c +++ b/src/sp_utils.c | |||
| @@ -1,12 +1,5 @@ | |||
| 1 | #include "php_snuffleupagus.h" | 1 | #include "php_snuffleupagus.h" |
| 2 | 2 | ||
| 3 | bool sp_zend_string_equals(const zend_string* s1, const zend_string* s2) { | ||
| 4 | // We can't use `zend_string_equals` here because it doesn't work on | ||
| 5 | // `const` zend_string. | ||
| 6 | return ZSTR_LEN(s1) == ZSTR_LEN(s2) && | ||
| 7 | !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); | ||
| 8 | } | ||
| 9 | |||
| 10 | static const char* default_ipaddr = "0.0.0.0"; | 3 | static const char* default_ipaddr = "0.0.0.0"; |
| 11 | const char* get_ipaddr() { | 4 | const char* get_ipaddr() { |
| 12 | const char* client_ip = getenv("REMOTE_ADDR"); | 5 | const char* client_ip = getenv("REMOTE_ADDR"); |
| @@ -46,7 +39,7 @@ void sp_log_msgf(char const* restrict feature, int level, int type, | |||
| 46 | break; | 39 | break; |
| 47 | } | 40 | } |
| 48 | 41 | ||
| 49 | switch (SNUFFLEUPAGUS_G(config).log_media) { | 42 | switch (SPCFG(log_media)) { |
| 50 | case SP_SYSLOG: { | 43 | case SP_SYSLOG: { |
| 51 | const char* error_filename = zend_get_executed_filename(); | 44 | const char* error_filename = zend_get_executed_filename(); |
| 52 | int syslog_level = (level == E_ERROR) ? LOG_ERR : LOG_INFO; | 45 | int syslog_level = (level == E_ERROR) ? LOG_ERR : LOG_INFO; |
| @@ -72,8 +65,8 @@ void sp_log_msgf(char const* restrict feature, int level, int type, | |||
| 72 | 65 | ||
| 73 | int compute_hash(const char* const restrict filename, | 66 | int compute_hash(const char* const restrict filename, |
| 74 | char* restrict file_hash) { | 67 | char* restrict file_hash) { |
| 75 | unsigned char buf[1024]; | 68 | unsigned char buf[1024] = {0}; |
| 76 | unsigned char digest[SHA256_SIZE]; | 69 | unsigned char digest[SHA256_SIZE] = {0}; |
| 77 | PHP_SHA256_CTX context; | 70 | PHP_SHA256_CTX context; |
| 78 | size_t n; | 71 | size_t n; |
| 79 | 72 | ||
| @@ -123,9 +116,7 @@ static int construct_filename(char* filename, | |||
| 123 | return 0; | 116 | return 0; |
| 124 | } | 117 | } |
| 125 | 118 | ||
| 126 | int sp_log_request(const zend_string* restrict folder, | 119 | int sp_log_request(const zend_string* restrict folder, const zend_string* restrict text_repr) { |
| 127 | const zend_string* restrict text_repr, | ||
| 128 | char const* const from) { | ||
| 129 | FILE* file; | 120 | FILE* file; |
| 130 | const char* current_filename = zend_get_executed_filename(TSRMLS_C); | 121 | const char* current_filename = zend_get_executed_filename(TSRMLS_C); |
| 131 | const int current_line = zend_get_executed_lineno(TSRMLS_C); | 122 | const int current_line = zend_get_executed_lineno(TSRMLS_C); |
| @@ -146,7 +137,7 @@ int sp_log_request(const zend_string* restrict folder, | |||
| 146 | return -1; | 137 | return -1; |
| 147 | } | 138 | } |
| 148 | 139 | ||
| 149 | fprintf(file, "RULE: sp%s%s\n", from, ZSTR_VAL(text_repr)); | 140 | fprintf(file, "RULE: %s\n", ZSTR_VAL(text_repr)); |
| 150 | 141 | ||
| 151 | fprintf(file, "FILE: %s:%d\n", current_filename, current_line); | 142 | fprintf(file, "FILE: %s:%d\n", current_filename, current_line); |
| 152 | 143 | ||
| @@ -157,8 +148,8 @@ int sp_log_request(const zend_string* restrict folder, | |||
| 157 | char* const complete_path_function = get_complete_function_path(current); | 148 | char* const complete_path_function = get_complete_function_path(current); |
| 158 | if (complete_path_function) { | 149 | if (complete_path_function) { |
| 159 | const int current_line = zend_get_executed_lineno(TSRMLS_C); | 150 | const int current_line = zend_get_executed_lineno(TSRMLS_C); |
| 160 | fprintf(file, "STACKTRACE: %s:%d\n", complete_path_function, | 151 | fprintf(file, "STACKTRACE: %s:%d\n", complete_path_function, current_line); |
| 161 | current_line); | 152 | efree(complete_path_function); |
| 162 | } | 153 | } |
| 163 | current = current->prev_execute_data; | 154 | current = current->prev_execute_data; |
| 164 | } | 155 | } |
| @@ -213,6 +204,19 @@ static char* zend_string_to_char(const zend_string* zs) { | |||
| 213 | return copy; | 204 | return copy; |
| 214 | } | 205 | } |
| 215 | 206 | ||
| 207 | static void sp_sanitize_charstring(char* c, size_t maxlen) | ||
| 208 | { | ||
| 209 | for (size_t i = 0; *c; c++, i++) { | ||
| 210 | if (maxlen && i > maxlen - 1) { | ||
| 211 | *c = 0; | ||
| 212 | return; | ||
| 213 | } | ||
| 214 | if (*c < 32 || *c > 126) { | ||
| 215 | *c = '*'; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 216 | const zend_string* sp_zval_to_zend_string(const zval* zv) { | 220 | const zend_string* sp_zval_to_zend_string(const zval* zv) { |
| 217 | switch (Z_TYPE_P(zv)) { | 221 | switch (Z_TYPE_P(zv)) { |
| 218 | case IS_LONG: { | 222 | case IS_LONG: { |
| @@ -233,35 +237,29 @@ const zend_string* sp_zval_to_zend_string(const zval* zv) { | |||
| 233 | return Z_STR_P(zv); | 237 | return Z_STR_P(zv); |
| 234 | } | 238 | } |
| 235 | case IS_FALSE: | 239 | case IS_FALSE: |
| 236 | return zend_string_init("FALSE", sizeof("FALSE") - 1, 0); | 240 | return zend_string_init(ZEND_STRL("FALSE"), 0); |
| 237 | case IS_TRUE: | 241 | case IS_TRUE: |
| 238 | return zend_string_init("TRUE", sizeof("TRUE") - 1, 0); | 242 | return zend_string_init(ZEND_STRL("TRUE"), 0); |
| 239 | case IS_NULL: | 243 | case IS_NULL: |
| 240 | return zend_string_init("NULL", sizeof("NULL") - 1, 0); | 244 | return zend_string_init(ZEND_STRL("NULL"), 0); |
| 241 | case IS_OBJECT: | 245 | case IS_OBJECT: |
| 242 | return zend_string_init("OBJECT", sizeof("OBJECT") - 1, 0); | 246 | return zend_string_init(ZEND_STRL("OBJECT"), 0); |
| 243 | case IS_ARRAY: | 247 | case IS_ARRAY: |
| 244 | return zend_string_init("ARRAY", sizeof("ARRAY") - 1, 0); | 248 | return zend_string_init(ZEND_STRL("ARRAY"), 0); |
| 245 | case IS_RESOURCE: | 249 | case IS_RESOURCE: |
| 246 | return zend_string_init("RESOURCE", sizeof("RESOURCE") - 1, 0); | 250 | return zend_string_init(ZEND_STRL("RESOURCE"), 0); |
| 247 | default: // LCOV_EXCL_LINE | 251 | default: // LCOV_EXCL_LINE |
| 248 | return zend_string_init("", 0, 0); // LCOV_EXCL_LINE | 252 | return zend_string_init("", 0, 0); // LCOV_EXCL_LINE |
| 249 | } | 253 | } |
| 250 | } | 254 | } |
| 251 | 255 | ||
| 252 | bool sp_match_value(const zend_string* value, const zend_string* to_match, | 256 | bool sp_match_value(const zend_string* value, const zend_string* to_match, const sp_regexp* rx) { |
| 253 | const sp_pcre* rx) { | ||
| 254 | if (to_match) { | 257 | if (to_match) { |
| 255 | return (sp_zend_string_equals(to_match, value)); | 258 | return (sp_zend_string_equals(to_match, value)); |
| 256 | } else if (rx) { | 259 | } else if (rx) { |
| 257 | char* tmp = zend_string_to_char(value); | 260 | return sp_is_regexp_matching_zstr(rx, value); |
| 258 | bool ret = sp_is_regexp_matching(rx, tmp); | ||
| 259 | efree(tmp); | ||
| 260 | return ret; | ||
| 261 | } else { | ||
| 262 | return true; | ||
| 263 | } | 261 | } |
| 264 | return false; | 262 | return true; |
| 265 | } | 263 | } |
| 266 | 264 | ||
| 267 | void sp_log_disable(const char* restrict path, const char* restrict arg_name, | 265 | void sp_log_disable(const char* restrict path, const char* restrict arg_name, |
| @@ -272,13 +270,13 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name, | |||
| 272 | const int sim = config_node->simulation; | 270 | const int sim = config_node->simulation; |
| 273 | 271 | ||
| 274 | if (dump) { | 272 | if (dump) { |
| 275 | sp_log_request(config_node->dump, config_node->textual_representation, | 273 | sp_log_request(config_node->dump, config_node->textual_representation); |
| 276 | SP_TOKEN_DISABLE_FUNC); | ||
| 277 | } | 274 | } |
| 278 | if (arg_name) { | 275 | if (arg_name) { |
| 279 | char* char_repr = NULL; | 276 | char* char_repr = NULL; |
| 280 | if (arg_value) { | 277 | if (arg_value) { |
| 281 | char_repr = zend_string_to_char(arg_value); | 278 | char_repr = zend_string_to_char(arg_value); |
| 279 | sp_sanitize_charstring(char_repr, 255); | ||
| 282 | } | 280 | } |
| 283 | if (alias) { | 281 | if (alias) { |
| 284 | sp_log_auto( | 282 | sp_log_auto( |
| @@ -315,11 +313,11 @@ void sp_log_disable_ret(const char* restrict path, | |||
| 315 | char* char_repr = NULL; | 313 | char* char_repr = NULL; |
| 316 | 314 | ||
| 317 | if (dump) { | 315 | if (dump) { |
| 318 | sp_log_request(dump, config_node->textual_representation, | 316 | sp_log_request(dump, config_node->textual_representation); |
| 319 | SP_TOKEN_DISABLE_FUNC); | ||
| 320 | } | 317 | } |
| 321 | if (ret_value) { | 318 | if (ret_value) { |
| 322 | char_repr = zend_string_to_char(ret_value); | 319 | char_repr = zend_string_to_char(ret_value); |
| 320 | sp_sanitize_charstring(char_repr, 255); | ||
| 323 | } | 321 | } |
| 324 | if (alias) { | 322 | if (alias) { |
| 325 | sp_log_auto( | 323 | sp_log_auto( |
| @@ -336,8 +334,7 @@ void sp_log_disable_ret(const char* restrict path, | |||
| 336 | efree(char_repr); | 334 | efree(char_repr); |
| 337 | } | 335 | } |
| 338 | 336 | ||
| 339 | bool sp_match_array_key(const zval* zv, const zend_string* to_match, | 337 | bool sp_match_array_key(const zval* zv, const zend_string* to_match, const sp_regexp* rx) { |
| 340 | const sp_pcre* rx) { | ||
| 341 | zend_string* key; | 338 | zend_string* key; |
| 342 | zend_ulong idx; | 339 | zend_ulong idx; |
| 343 | 340 | ||
| @@ -361,8 +358,7 @@ bool sp_match_array_key(const zval* zv, const zend_string* to_match, | |||
| 361 | return false; | 358 | return false; |
| 362 | } | 359 | } |
| 363 | 360 | ||
| 364 | bool sp_match_array_value(const zval* arr, const zend_string* to_match, | 361 | bool sp_match_array_value(const zval* arr, const zend_string* to_match, const sp_regexp* rx) { |
| 365 | const sp_pcre* rx) { | ||
| 366 | zval* value; | 362 | zval* value; |
| 367 | 363 | ||
| 368 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) { | 364 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) { |
| @@ -378,61 +374,57 @@ bool sp_match_array_value(const zval* arr, const zend_string* to_match, | |||
| 378 | return false; | 374 | return false; |
| 379 | } | 375 | } |
| 380 | 376 | ||
| 381 | bool hook_function(const char* original_name, HashTable* hook_table, | 377 | bool /* success */ _hook_function(const char* original_name, HashTable* hook_table, zif_handler new_function) { |
| 382 | zif_handler new_function) { | 378 | zend_function* func; |
| 383 | zend_internal_function* func; | 379 | if ((func = zend_hash_str_find_ptr(CG(function_table), VAR_AND_LEN(original_name)))) { |
| 384 | bool ret = false; | 380 | if (func->type != ZEND_INTERNAL_FUNCTION) { |
| 385 | 381 | return false; | |
| 386 | /* The `mb` module likes to hook functions, like strlen->mb_strlen, | ||
| 387 | * so we have to hook both of them. */ | ||
| 388 | |||
| 389 | if ((func = zend_hash_str_find_ptr(CG(function_table), | ||
| 390 | VAR_AND_LEN(original_name)))) { | ||
| 391 | if (func->handler == new_function) { | ||
| 392 | return SUCCESS; // the function is already hooked | ||
| 393 | } else { | ||
| 394 | if (zend_hash_str_add_new_ptr((hook_table), VAR_AND_LEN(original_name), | ||
| 395 | func->handler) == NULL) { | ||
| 396 | // LCOV_EXCL_START | ||
| 397 | sp_log_err("function_pointer_saving", | ||
| 398 | "Could not save function pointer for %s", original_name); | ||
| 399 | return FAILURE; | ||
| 400 | // LCOV_EXCL_STOP | ||
| 401 | } | ||
| 402 | func->handler = new_function; | ||
| 403 | ret = true; | ||
| 404 | } | 382 | } |
| 383 | if (func->internal_function.handler == new_function) { | ||
| 384 | return true; | ||
| 385 | } | ||
| 386 | if (zend_hash_str_add_new_ptr((hook_table), VAR_AND_LEN(original_name), | ||
| 387 | func->internal_function.handler) == NULL) { | ||
| 388 | // LCOV_EXCL_START | ||
| 389 | sp_log_err("function_pointer_saving", "Could not save function pointer for %s", original_name); | ||
| 390 | return false; | ||
| 391 | // LCOV_EXCL_STOP | ||
| 392 | } | ||
| 393 | func->internal_function.handler = new_function; | ||
| 394 | return true; | ||
| 405 | } | 395 | } |
| 396 | return false; | ||
| 397 | } | ||
| 398 | |||
| 399 | bool hook_function(const char* original_name, HashTable* hook_table, zif_handler new_function) { | ||
| 400 | bool ret = _hook_function(original_name, hook_table, new_function); | ||
| 406 | 401 | ||
| 407 | #if PHP_VERSION_ID < 80000 | 402 | #if PHP_VERSION_ID < 80000 |
| 408 | CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; | 403 | CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; |
| 409 | #endif | 404 | #endif |
| 410 | 405 | ||
| 411 | if (0 == strncmp(original_name, "mb_", 3) && !CG(multibyte)) { | 406 | /* The `mb` module likes to hook functions, like strlen->mb_strlen, |
| 412 | if (zend_hash_str_find(CG(function_table), | 407 | * so we have to hook both of them. */ |
| 413 | VAR_AND_LEN(original_name + 3))) { | 408 | |
| 414 | return hook_function(original_name + 3, hook_table, new_function); | 409 | if (!CG(multibyte) && 0 == strncmp(original_name, "mb_", 3)) { |
| 415 | } | 410 | _hook_function(original_name + 3, hook_table, new_function); |
| 416 | } else if (CG(multibyte)) { | 411 | } else if (CG(multibyte)) { |
| 417 | // LCOV_EXCL_START | 412 | // LCOV_EXCL_START |
| 418 | char* mb_name = ecalloc(strlen(original_name) + 3 + 1, 1); | 413 | char* mb_name = ecalloc(strlen(original_name) + 3 + 1, 1); |
| 419 | if (NULL == mb_name) { | 414 | if (NULL == mb_name) { |
| 420 | return FAILURE; | 415 | return FAILURE; |
| 421 | } | 416 | } |
| 422 | memcpy(mb_name, "mb_", sizeof("mb_") - 1); | 417 | memcpy(mb_name, ZEND_STRL("mb_")); |
| 423 | memcpy(mb_name + 3, VAR_AND_LEN(original_name)); | 418 | memcpy(mb_name + 3, VAR_AND_LEN(original_name)); |
| 424 | if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) { | 419 | _hook_function(mb_name, hook_table, new_function); |
| 425 | return hook_function(mb_name, hook_table, new_function); | 420 | efree(mb_name); |
| 426 | } | ||
| 427 | free(mb_name); | ||
| 428 | // LCOV_EXCL_STOP | 421 | // LCOV_EXCL_STOP |
| 429 | } | 422 | } |
| 430 | 423 | ||
| 431 | return ret; | 424 | return ret; |
| 432 | } | 425 | } |
| 433 | 426 | ||
| 434 | int hook_regexp(const sp_pcre* regexp, HashTable* hook_table, | 427 | int hook_regexp(const sp_pcre* regexp, HashTable* hook_table, zif_handler new_function) { |
| 435 | zif_handler new_function) { | ||
| 436 | zend_string* key; | 428 | zend_string* key; |
| 437 | 429 | ||
| 438 | ZEND_HASH_FOREACH_STR_KEY(CG(function_table), key) | 430 | ZEND_HASH_FOREACH_STR_KEY(CG(function_table), key) |
| @@ -446,9 +438,21 @@ int hook_regexp(const sp_pcre* regexp, HashTable* hook_table, | |||
| 446 | return SUCCESS; | 438 | return SUCCESS; |
| 447 | } | 439 | } |
| 448 | 440 | ||
| 449 | bool check_is_in_eval_whitelist(const zend_string* const function_name) { | 441 | void unhook_functions(HashTable *ht) { |
| 450 | const sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->whitelist; | 442 | zend_string *fname; |
| 443 | zif_handler orig_handler; | ||
| 444 | zend_ulong idx; | ||
| 445 | |||
| 446 | ZEND_HASH_REVERSE_FOREACH_KEY_PTR(ht, idx, fname, orig_handler) | ||
| 447 | zend_function *func = zend_hash_find_ptr(CG(function_table), fname); | ||
| 448 | if (func && func->type == ZEND_INTERNAL_FUNCTION && orig_handler) { | ||
| 449 | func->internal_function.handler = orig_handler; | ||
| 450 | } | ||
| 451 | ZEND_HASH_FOREACH_END_DEL(); | ||
| 452 | } | ||
| 451 | 453 | ||
| 454 | bool check_is_in_eval_whitelist(const char* function_name) { | ||
| 455 | const sp_list_node* it = SPCFG(eval).whitelist; | ||
| 452 | if (!it) { | 456 | if (!it) { |
| 453 | return false; | 457 | return false; |
| 454 | } | 458 | } |
| @@ -456,7 +460,7 @@ bool check_is_in_eval_whitelist(const zend_string* const function_name) { | |||
| 456 | /* yes, we could use a HashTable instead, but since the list is pretty | 460 | /* yes, we could use a HashTable instead, but since the list is pretty |
| 457 | * small, it doesn't make a difference in practise. */ | 461 | * small, it doesn't make a difference in practise. */ |
| 458 | while (it && it->data) { | 462 | while (it && it->data) { |
| 459 | if (sp_zend_string_equals(function_name, (const zend_string*)(it->data))) { | 463 | if (sp_zend_string_equals_str((const zend_string*)(it->data), VAR_AND_LEN(function_name))) { |
| 460 | /* We've got a match, the function is whiteslited. */ | 464 | /* We've got a match, the function is whiteslited. */ |
| 461 | return true; | 465 | return true; |
| 462 | } | 466 | } |
