summaryrefslogtreecommitdiff
path: root/src/sp_utils.c
diff options
context:
space:
mode:
authorBen Fuhrmannek2021-02-16 11:16:59 +0100
committerBen Fuhrmannek2021-02-16 11:16:59 +0100
commit5484bcb5eb2714e7438927e2566c86a74d7c51af (patch)
treeb78326d2999397be4c08e06b23209981f82a4ea9 /src/sp_utils.c
parent7ac1e3866ef4f146c6c93a5ca13b9aebb14e936a (diff)
parentcecfdd808da67be908dbe7144cc8c74dfb3f855e (diff)
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src/sp_utils.c')
-rw-r--r--src/sp_utils.c158
1 files changed, 111 insertions, 47 deletions
diff --git a/src/sp_utils.c b/src/sp_utils.c
index 0f87f17..a7a3d27 100644
--- a/src/sp_utils.c
+++ b/src/sp_utils.c
@@ -7,7 +7,41 @@ bool sp_zend_string_equals(const zend_string* s1, const zend_string* s2) {
7 !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); 7 !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1));
8} 8}
9 9
10void sp_log_msg(char const* feature, int type, const char* fmt, ...) { 10static const char* default_ipaddr = "0.0.0.0";
11const char* get_ipaddr() {
12 const char* client_ip = getenv("REMOTE_ADDR");
13 if (client_ip) {
14 return client_ip;
15 }
16
17 const char* fwd_ip = getenv("HTTP_X_FORWARDED_FOR");
18 if (fwd_ip) {
19 return fwd_ip;
20 }
21
22 /* Some hosters (like heroku, see
23 * https://github.com/jvoisin/snuffleupagus/issues/336) are clearing the
24 * environment variables, so we don't have access to them, hence why we're
25 * resorting to $_SERVER['REMOTE_ADDR'].
26 */
27 if (!Z_ISUNDEF(PG(http_globals)[TRACK_VARS_SERVER])) {
28 const zval* const globals_client_ip =
29 zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]),
30 "REMOTE_ADDR", sizeof("REMOTE_ADDR") - 1);
31 if (globals_client_ip) {
32 if (Z_TYPE_P(globals_client_ip) == IS_STRING) {
33 if (Z_STRLEN_P(globals_client_ip) != 0) {
34 return estrdup(Z_STRVAL_P(globals_client_ip));
35 }
36 }
37 }
38 }
39
40 return default_ipaddr;
41}
42
43void sp_log_msgf(char const* restrict feature, int level, int type,
44 const char* restrict fmt, ...) {
11 char* msg; 45 char* msg;
12 va_list args; 46 va_list args;
13 47
@@ -15,31 +49,45 @@ void sp_log_msg(char const* feature, int type, const char* fmt, ...) {
15 vspprintf(&msg, 0, fmt, args); 49 vspprintf(&msg, 0, fmt, args);
16 va_end(args); 50 va_end(args);
17 51
18 const char *client_ip = getenv("REMOTE_ADDR"); 52 const char* client_ip = get_ipaddr();
19 if (!client_ip) { 53 const char* logtype = NULL;
20 client_ip = "0.0.0.0"; 54 switch (type) {
55 case SP_TYPE_SIMULATION:
56 logtype = "simulation";
57 break;
58 case SP_TYPE_DROP:
59 logtype = "drop";
60 break;
61 case SP_TYPE_LOG:
62 default:
63 logtype = "log";
64 break;
21 } 65 }
66
22 switch (SNUFFLEUPAGUS_G(config).log_media) { 67 switch (SNUFFLEUPAGUS_G(config).log_media) {
23 case SP_SYSLOG: 68 case SP_SYSLOG: {
24 openlog(PHP_SNUFFLEUPAGUS_EXTNAME, LOG_PID, LOG_AUTH);
25 const char* error_filename = zend_get_executed_filename(); 69 const char* error_filename = zend_get_executed_filename();
26 int syslog_level = SP_LOG_DROP ? LOG_ERR : LOG_INFO; 70 int syslog_level = (level == E_ERROR) ? LOG_ERR : LOG_INFO;
27 int error_lineno = zend_get_executed_lineno(TSRMLS_C); 71 int error_lineno = zend_get_executed_lineno(TSRMLS_C);
28 syslog(syslog_level, "[snuffleupagus][%s][%s] %s in %s on line %d", client_ip, feature, msg, 72 openlog(PHP_SNUFFLEUPAGUS_EXTNAME, LOG_PID, LOG_AUTH);
29 error_filename, error_lineno); 73 syslog(syslog_level, "[snuffleupagus][%s][%s][%s] %s in %s on line %d",
74 client_ip, feature, logtype, msg, error_filename, error_lineno);
30 closelog(); 75 closelog();
31 if (type == SP_LOG_DROP) { 76 if (type == SP_TYPE_DROP) {
32 zend_bailout(); 77 zend_bailout();
33 } 78 }
34 break; 79 break;
80 }
35 case SP_ZEND: 81 case SP_ZEND:
36 default: 82 default:
37 zend_error(type, "[snuffleupagus][%s][%s] %s", client_ip, feature, msg); 83 zend_error(level, "[snuffleupagus][%s][%s][%s] %s", client_ip, feature,
84 logtype, msg);
38 break; 85 break;
39 } 86 }
40} 87}
41 88
42int compute_hash(const char* const filename, char* file_hash) { 89int compute_hash(const char* const restrict filename,
90 char* restrict file_hash) {
43 unsigned char buf[1024]; 91 unsigned char buf[1024];
44 unsigned char digest[SHA256_SIZE]; 92 unsigned char digest[SHA256_SIZE];
45 PHP_SHA256_CTX context; 93 PHP_SHA256_CTX context;
@@ -65,8 +113,9 @@ int compute_hash(const char* const filename, char* file_hash) {
65 return SUCCESS; 113 return SUCCESS;
66} 114}
67 115
68static int construct_filename(char* filename, const zend_string* folder, 116static int construct_filename(char* filename,
69 const zend_string* textual) { 117 const zend_string* restrict folder,
118 const zend_string* restrict textual) {
70 PHP_SHA256_CTX context; 119 PHP_SHA256_CTX context;
71 unsigned char digest[SHA256_SIZE] = {0}; 120 unsigned char digest[SHA256_SIZE] = {0};
72 char strhash[65] = {0}; 121 char strhash[65] = {0};
@@ -78,7 +127,7 @@ static int construct_filename(char* filename, const zend_string* folder,
78 } 127 }
79 128
80 /* We're using the sha256 sum of the rule's textual representation 129 /* We're using the sha256 sum of the rule's textual representation
81 * as filename, in order to only have one dump per rule, to migitate 130 * as filename, in order to only have one dump per rule, to mitigate
82 * DoS attacks. */ 131 * DoS attacks. */
83 PHP_SHA256Init(&context); 132 PHP_SHA256Init(&context);
84 PHP_SHA256Update(&context, (const unsigned char*)ZSTR_VAL(textual), 133 PHP_SHA256Update(&context, (const unsigned char*)ZSTR_VAL(textual),
@@ -90,14 +139,15 @@ static int construct_filename(char* filename, const zend_string* folder,
90 return 0; 139 return 0;
91} 140}
92 141
93int sp_log_request(const zend_string* folder, const zend_string* text_repr, 142int sp_log_request(const zend_string* restrict folder,
94 char* from) { 143 const zend_string* restrict text_repr,
144 char const* const from) {
95 FILE* file; 145 FILE* file;
96 const char* current_filename = zend_get_executed_filename(TSRMLS_C); 146 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
97 const int current_line = zend_get_executed_lineno(TSRMLS_C); 147 const int current_line = zend_get_executed_lineno(TSRMLS_C);
98 char filename[PATH_MAX] = {0}; 148 char filename[PATH_MAX] = {0};
99 const struct { 149 const struct {
100 const char* str; 150 char const* const str;
101 const int key; 151 const int key;
102 } zones[] = {{"GET", TRACK_VARS_GET}, {"POST", TRACK_VARS_POST}, 152 } zones[] = {{"GET", TRACK_VARS_GET}, {"POST", TRACK_VARS_POST},
103 {"COOKIE", TRACK_VARS_COOKIE}, {"SERVER", TRACK_VARS_SERVER}, 153 {"COOKIE", TRACK_VARS_COOKIE}, {"SERVER", TRACK_VARS_SERVER},
@@ -115,7 +165,22 @@ int sp_log_request(const zend_string* folder, const zend_string* text_repr,
115 fprintf(file, "RULE: sp%s%s\n", from, ZSTR_VAL(text_repr)); 165 fprintf(file, "RULE: sp%s%s\n", from, ZSTR_VAL(text_repr));
116 166
117 fprintf(file, "FILE: %s:%d\n", current_filename, current_line); 167 fprintf(file, "FILE: %s:%d\n", current_filename, current_line);
118 for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) { 168
169 zend_execute_data* orig_execute_data = EG(current_execute_data);
170 zend_execute_data* current = EG(current_execute_data);
171 while (current) {
172 EG(current_execute_data) = current;
173 char* const complete_path_function = get_complete_function_path(current);
174 if (complete_path_function) {
175 const int current_line = zend_get_executed_lineno(TSRMLS_C);
176 fprintf(file, "STACKTRACE: %s:%d\n", complete_path_function,
177 current_line);
178 }
179 current = current->prev_execute_data;
180 }
181 EG(current_execute_data) = orig_execute_data;
182
183 for (size_t i = 0; zones[i].str; i++) {
119 zval* variable_value; 184 zval* variable_value;
120 zend_string* variable_key; 185 zend_string* variable_key;
121 186
@@ -232,26 +297,27 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name,
232 char_repr = zend_string_to_char(arg_value); 297 char_repr = zend_string_to_char(arg_value);
233 } 298 }
234 if (alias) { 299 if (alias) {
235 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 300 sp_log_auto(
236 "Aborted execution on call of the function '%s', " 301 "disabled_function", sim,
237 "because its argument '%s' content (%s) matched the rule '%s'", 302 "Aborted execution on call of the function '%s', "
238 path, arg_name, char_repr ? char_repr : "?", ZSTR_VAL(alias)); 303 "because its argument '%s' content (%s) matched the rule '%s'",
304 path, arg_name, char_repr ? char_repr : "?", ZSTR_VAL(alias));
239 } else { 305 } else {
240 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 306 sp_log_auto("disabled_function", sim,
241 "Aborted execution on call of the function '%s', " 307 "Aborted execution on call of the function '%s', "
242 "because its argument '%s' content (%s) matched a rule", 308 "because its argument '%s' content (%s) matched a rule",
243 path, arg_name, char_repr ? char_repr : "?"); 309 path, arg_name, char_repr ? char_repr : "?");
244 } 310 }
245 efree(char_repr); 311 efree(char_repr);
246 } else { 312 } else {
247 if (alias) { 313 if (alias) {
248 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 314 sp_log_auto("disabled_function", sim,
249 "Aborted execution on call of the function '%s', " 315 "Aborted execution on call of the function '%s', "
250 "because of the the rule '%s'", 316 "because of the the rule '%s'",
251 path, ZSTR_VAL(alias)); 317 path, ZSTR_VAL(alias));
252 } else { 318 } else {
253 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 319 sp_log_auto("disabled_function", sim,
254 "Aborted execution on call of the function '%s'", path); 320 "Aborted execution on call of the function '%s'", path);
255 } 321 }
256 } 322 }
257} 323}
@@ -272,16 +338,16 @@ void sp_log_disable_ret(const char* restrict path,
272 char_repr = zend_string_to_char(ret_value); 338 char_repr = zend_string_to_char(ret_value);
273 } 339 }
274 if (alias) { 340 if (alias) {
275 sp_log_msg( 341 sp_log_auto(
276 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 342 "disabled_function", sim,
277 "Aborted execution on return of the function '%s', " 343 "Aborted execution on return of the function '%s', "
278 "because the function returned '%s', which matched the rule '%s'", 344 "because the function returned '%s', which matched the rule '%s'",
279 path, char_repr ? char_repr : "?", ZSTR_VAL(alias)); 345 path, char_repr ? char_repr : "?", ZSTR_VAL(alias));
280 } else { 346 } else {
281 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 347 sp_log_auto("disabled_function", sim,
282 "Aborted execution on return of the function '%s', " 348 "Aborted execution on return of the function '%s', "
283 "because the function returned '%s', which matched a rule", 349 "because the function returned '%s', which matched a rule",
284 path, char_repr ? char_repr : "?"); 350 path, char_repr ? char_repr : "?");
285 } 351 }
286 efree(char_repr); 352 efree(char_repr);
287} 353}
@@ -317,10 +383,8 @@ bool sp_match_array_value(const zval* arr, const zend_string* to_match,
317 383
318 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) { 384 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) {
319 if (Z_TYPE_P(value) != IS_ARRAY) { 385 if (Z_TYPE_P(value) != IS_ARRAY) {
320 const zend_string* value_str = sp_zval_to_zend_string(value); 386 if (sp_match_value(sp_zval_to_zend_string(value), to_match, rx)) {
321 if (sp_match_value(value_str, to_match, rx)) {
322 return true; 387 return true;
323 } else {
324 } 388 }
325 } else if (sp_match_array_value(value, to_match, rx)) { 389 } else if (sp_match_array_value(value, to_match, rx)) {
326 return true; 390 return true;
@@ -330,10 +394,10 @@ bool sp_match_array_value(const zval* arr, const zend_string* to_match,
330 return false; 394 return false;
331} 395}
332 396
333int hook_function(const char* original_name, HashTable* hook_table, 397bool hook_function(const char* original_name, HashTable* hook_table,
334 zif_handler new_function) { 398 zif_handler new_function) {
335 zend_internal_function* func; 399 zend_internal_function* func;
336 bool ret = FAILURE; 400 bool ret = false;
337 401
338 /* The `mb` module likes to hook functions, like strlen->mb_strlen, 402 /* The `mb` module likes to hook functions, like strlen->mb_strlen,
339 * so we have to hook both of them. */ 403 * so we have to hook both of them. */
@@ -352,7 +416,7 @@ int hook_function(const char* original_name, HashTable* hook_table,
352 // LCOV_EXCL_STOP 416 // LCOV_EXCL_STOP
353 } 417 }
354 func->handler = new_function; 418 func->handler = new_function;
355 ret = SUCCESS; 419 ret = true;
356 } 420 }
357 } 421 }
358 422
@@ -406,7 +470,7 @@ bool check_is_in_eval_whitelist(const zend_string* const function_name) {
406 } 470 }
407 471
408 /* yes, we could use a HashTable instead, but since the list is pretty 472 /* yes, we could use a HashTable instead, but since the list is pretty
409 * small, it doesn't maka a difference in practise. */ 473 * small, it doesn't make a difference in practise. */
410 while (it && it->data) { 474 while (it && it->data) {
411 if (sp_zend_string_equals(function_name, (const zend_string*)(it->data))) { 475 if (sp_zend_string_equals(function_name, (const zend_string*)(it->data))) {
412 /* We've got a match, the function is whiteslited. */ 476 /* We've got a match, the function is whiteslited. */