summaryrefslogtreecommitdiff
path: root/src/sp_utils.c
diff options
context:
space:
mode:
authorjvoisin2022-03-20 18:20:45 +0100
committerjvoisin2022-03-20 18:20:45 +0100
commit81dd7f2ef07af306fe83d7755cbac4529aa9fc8d (patch)
tree32cc44c6231b30db5ac7b15699297863460784aa /src/sp_utils.c
parent83b01942dfc80474cc05e09aeef4b44307a7120b (diff)
parentc38df1077a6c1dfbca1baca049214d053e2e7684 (diff)
Merge remote-tracking branch 'sektioneins/master'
Diffstat (limited to 'src/sp_utils.c')
-rw-r--r--src/sp_utils.c158
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
3bool 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
10static const char* default_ipaddr = "0.0.0.0"; 3static const char* default_ipaddr = "0.0.0.0";
11const char* get_ipaddr() { 4const 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
73int compute_hash(const char* const restrict filename, 66int 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
126int sp_log_request(const zend_string* restrict folder, 119int 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
207static 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
216const zend_string* sp_zval_to_zend_string(const zval* zv) { 220const 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
252bool sp_match_value(const zend_string* value, const zend_string* to_match, 256bool 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
267void sp_log_disable(const char* restrict path, const char* restrict arg_name, 265void 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
339bool sp_match_array_key(const zval* zv, const zend_string* to_match, 337bool 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
364bool sp_match_array_value(const zval* arr, const zend_string* to_match, 361bool 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
381bool hook_function(const char* original_name, HashTable* hook_table, 377bool /* 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
399bool 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
434int hook_regexp(const sp_pcre* regexp, HashTable* hook_table, 427int 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
449bool check_is_in_eval_whitelist(const zend_string* const function_name) { 441void 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
454bool 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 }