summaryrefslogtreecommitdiff
path: root/src/sp_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp_utils.c')
-rw-r--r--src/sp_utils.c147
1 files changed, 90 insertions, 57 deletions
diff --git a/src/sp_utils.c b/src/sp_utils.c
index 8b7ce49..a94ab2a 100644
--- a/src/sp_utils.c
+++ b/src/sp_utils.c
@@ -19,6 +19,14 @@ static inline void _sp_log_err(const char* fmt, ...) {
19 php_log_err(msg); 19 php_log_err(msg);
20} 20}
21 21
22static bool sp_zend_string_equals(const zend_string* s1,
23 const zend_string* s2) {
24 // We can't use `zend_string_equals` here because it doesn't work on
25 // `const` zend_string.
26 return ZSTR_LEN(s1) == ZSTR_LEN(s2) &&
27 !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1));
28}
29
22void sp_log_msg(char const* feature, char const* level, const char* fmt, ...) { 30void sp_log_msg(char const* feature, char const* level, const char* fmt, ...) {
23 char* msg; 31 char* msg;
24 va_list args; 32 va_list args;
@@ -56,14 +64,15 @@ int compute_hash(const char* const filename, char* file_hash) {
56 return SUCCESS; 64 return SUCCESS;
57} 65}
58 66
59static int construct_filename(char* filename, const char* folder, 67static int construct_filename(char* filename, const zend_string* folder,
60 const char* textual) { 68 const zend_string* textual) {
61 PHP_SHA256_CTX context; 69 PHP_SHA256_CTX context;
62 unsigned char digest[SHA256_SIZE] = {0}; 70 unsigned char digest[SHA256_SIZE] = {0};
63 char strhash[65] = {0}; 71 char strhash[65] = {0};
64 72
65 if (-1 == mkdir(folder, 0700) && errno != EEXIST) { 73 if (-1 == mkdir(ZSTR_VAL(folder), 0700) && errno != EEXIST) {
66 sp_log_err("request_logging", "Unable to create the folder '%s'.", folder); 74 sp_log_err("request_logging", "Unable to create the folder '%s'.",
75 ZSTR_VAL(folder));
67 return -1; 76 return -1;
68 } 77 }
69 78
@@ -71,15 +80,17 @@ static int construct_filename(char* filename, const char* folder,
71 * as filename, in order to only have one dump per rule, to migitate 80 * as filename, in order to only have one dump per rule, to migitate
72 * DoS attacks. */ 81 * DoS attacks. */
73 PHP_SHA256Init(&context); 82 PHP_SHA256Init(&context);
74 PHP_SHA256Update(&context, (const unsigned char*)textual, strlen(textual)); 83 PHP_SHA256Update(&context, (const unsigned char*)ZSTR_VAL(textual),
84 ZSTR_LEN(textual));
75 PHP_SHA256Final(digest, &context); 85 PHP_SHA256Final(digest, &context);
76 make_digest_ex(strhash, digest, SHA256_SIZE); 86 make_digest_ex(strhash, digest, SHA256_SIZE);
77 snprintf(filename, PATH_MAX - 1, "%s/sp_dump.%s", folder, strhash); 87 snprintf(filename, PATH_MAX - 1, "%s/sp_dump.%s", ZSTR_VAL(folder), strhash);
78 88
79 return 0; 89 return 0;
80} 90}
81 91
82int sp_log_request(const char* folder, const char* text_repr, char* from) { 92int sp_log_request(const zend_string* folder, const zend_string* text_repr,
93 char* from) {
83 FILE* file; 94 FILE* file;
84 const char* current_filename = zend_get_executed_filename(TSRMLS_C); 95 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
85 const int current_line = zend_get_executed_lineno(TSRMLS_C); 96 const int current_line = zend_get_executed_lineno(TSRMLS_C);
@@ -100,7 +111,7 @@ int sp_log_request(const char* folder, const char* text_repr, char* from) {
100 return -1; 111 return -1;
101 } 112 }
102 113
103 fprintf(file, "RULE: sp%s%s\n", from, text_repr); 114 fprintf(file, "RULE: sp%s%s\n", from, ZSTR_VAL(text_repr));
104 115
105 fprintf(file, "FILE: %s:%d\n", current_filename, current_line); 116 fprintf(file, "FILE: %s:%d\n", current_filename, current_line);
106 for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) { 117 for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) {
@@ -130,57 +141,65 @@ int sp_log_request(const char* folder, const char* text_repr, char* from) {
130 return 0; 141 return 0;
131} 142}
132 143
133static char* zv_str_to_char(zval* zv) { 144static char* zend_string_to_char(const zend_string* zs) {
134 zval copy; 145 // Remove \0 from the middle of a string
146 char* copy = emalloc(ZSTR_LEN(zs) + 1);
135 147
136 ZVAL_ZVAL(&copy, zv, 1, 0); 148 copy[ZSTR_LEN(zs)] = 0;
137 for (size_t i = 0; i < Z_STRLEN(copy); i++) { 149 for (size_t i = 0; i < ZSTR_LEN(zs); i++) {
138 if (Z_STRVAL(copy)[i] == '\0') { 150 if (ZSTR_VAL(zs)[i] == '\0') {
139 Z_STRVAL(copy)[i] = '0'; 151 copy[i] = '0';
152 } else {
153 copy[i] = ZSTR_VAL(zs)[i];
140 } 154 }
141 } 155 }
142 return estrdup(Z_STRVAL(copy)); 156 return copy;
143} 157}
144 158
145char* sp_convert_to_string(zval* zv) { 159const zend_string* sp_zval_to_zend_string(zval* zv) {
146 switch (Z_TYPE_P(zv)) { 160 switch (Z_TYPE_P(zv)) {
147 case IS_FALSE:
148 return estrdup("FALSE");
149 case IS_TRUE:
150 return estrdup("TRUE");
151 case IS_NULL:
152 return estrdup("NULL");
153 case IS_LONG: { 161 case IS_LONG: {
154 char* msg; 162 char* msg;
155 spprintf(&msg, 0, ZEND_LONG_FMT, Z_LVAL_P(zv)); 163 spprintf(&msg, 0, ZEND_LONG_FMT, Z_LVAL_P(zv));
156 return msg; 164 zend_string* zs = zend_string_init(msg, strlen(msg), 0);
165 efree(msg);
166 return zs;
157 } 167 }
158 case IS_DOUBLE: { 168 case IS_DOUBLE: {
159 char* msg; 169 char* msg;
160 spprintf(&msg, 0, "%f", Z_DVAL_P(zv)); 170 spprintf(&msg, 0, "%f", Z_DVAL_P(zv));
161 return msg; 171 zend_string* zs = zend_string_init(msg, strlen(msg), 0);
172 efree(msg);
173 return zs;
162 } 174 }
163 case IS_STRING: { 175 case IS_STRING: {
164 return zv_str_to_char(zv); 176 return Z_STR_P(zv);
165 } 177 }
178 case IS_FALSE:
179 return zend_string_init("FALSE", 5, 0);
180 case IS_TRUE:
181 return zend_string_init("TRUE", 4, 0);
182 case IS_NULL:
183 return zend_string_init("NULL", 4, 0);
166 case IS_OBJECT: 184 case IS_OBJECT:
167 return estrdup("OBJECT"); 185 return zend_string_init("OBJECT", 6, 0);
168 case IS_ARRAY: 186 case IS_ARRAY:
169 return estrdup("ARRAY"); 187 return zend_string_init("ARRAY", 5, 0);
170 case IS_RESOURCE: 188 case IS_RESOURCE:
171 return estrdup("RESOURCE"); 189 return zend_string_init("RESOURCE", 8, 0);
172 } 190 }
173 return estrdup(""); 191 return zend_string_init("", 0, 0);
174} 192}
175 193
176bool sp_match_value(const char* value, const char* to_match, 194bool sp_match_value(const zend_string* value, const zend_string* to_match,
177 const sp_pcre* rx) { 195 const sp_pcre* rx) {
178 if (to_match) { 196 if (to_match) {
179 if (0 == strcmp(to_match, value)) { 197 return (sp_zend_string_equals(to_match, value));
180 return true;
181 }
182 } else if (rx) { 198 } else if (rx) {
183 return sp_is_regexp_matching(rx, value); 199 char* tmp = zend_string_to_char(value);
200 bool ret = sp_is_regexp_matching(rx, tmp);
201 efree(tmp);
202 return ret;
184 } else { 203 } else {
185 return true; 204 return true;
186 } 205 }
@@ -188,35 +207,41 @@ bool sp_match_value(const char* value, const char* to_match,
188} 207}
189 208
190void sp_log_disable(const char* restrict path, const char* restrict arg_name, 209void sp_log_disable(const char* restrict path, const char* restrict arg_name,
191 const char* restrict arg_value, 210 const zend_string* restrict arg_value,
192 const sp_disabled_function* config_node, unsigned int line, 211 const sp_disabled_function* config_node, unsigned int line,
193 const char* restrict filename) { 212 const char* restrict filename) {
194 const char* dump = config_node->dump; 213 const zend_string* dump = config_node->dump;
195 const char* alias = config_node->alias; 214 const zend_string* alias = config_node->alias;
196 const int sim = config_node->simulation; 215 const int sim = config_node->simulation;
197 216
198 filename = filename ? filename : zend_get_executed_filename(TSRMLS_C); 217 filename = filename ? filename : zend_get_executed_filename(TSRMLS_C);
199 line = line ? line : zend_get_executed_lineno(TSRMLS_C); 218 line = line ? line : zend_get_executed_lineno(TSRMLS_C);
200 219
201 if (arg_name) { 220 if (arg_name) {
221 char* char_repr = NULL;
222 if (arg_value) {
223 char_repr = zend_string_to_char(arg_value);
224 }
202 if (alias) { 225 if (alias) {
203 sp_log_msg( 226 sp_log_msg(
204 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 227 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
205 "Aborted execution on call of the function '%s' in %s:%d, " 228 "Aborted execution on call of the function '%s' in %s:%d, "
206 "because its argument '%s' content (%s) matched the rule '%s'.", 229 "because its argument '%s' content (%s) matched the rule '%s'.",
207 path, filename, line, arg_name, arg_value ? arg_value : "?", alias); 230 path, filename, line, arg_name, char_repr ? char_repr : "?",
231 ZSTR_VAL(alias));
208 } else { 232 } else {
209 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 233 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
210 "Aborted execution on call of the function '%s' in %s:%d, " 234 "Aborted execution on call of the function '%s' in %s:%d, "
211 "because its argument '%s' content (%s) matched a rule.", 235 "because its argument '%s' content (%s) matched a rule.",
212 path, filename, line, arg_name, arg_value ? arg_value : "?"); 236 path, filename, line, arg_name, char_repr ? char_repr : "?");
213 } 237 }
238 efree(char_repr);
214 } else { 239 } else {
215 if (alias) { 240 if (alias) {
216 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 241 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
217 "Aborted execution on call of the function '%s' in %s:%d, " 242 "Aborted execution on call of the function '%s' in %s:%d, "
218 "because of the the rule '%s'.", 243 "because of the the rule '%s'.",
219 path, filename, line, alias); 244 path, filename, line, ZSTR_VAL(alias));
220 } else { 245 } else {
221 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 246 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
222 "Aborted execution on call of the function '%s' in %s:%d.", 247 "Aborted execution on call of the function '%s' in %s:%d.",
@@ -230,45 +255,53 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name,
230} 255}
231 256
232void sp_log_disable_ret(const char* restrict path, 257void sp_log_disable_ret(const char* restrict path,
233 const char* restrict ret_value, 258 const zend_string* restrict ret_value,
234 const sp_disabled_function* config_node) { 259 const sp_disabled_function* config_node) {
235 const char* dump = config_node->dump; 260 const zend_string* dump = config_node->dump;
236 const char* alias = config_node->alias; 261 const zend_string* alias = config_node->alias;
237 const int sim = config_node->simulation; 262 const int sim = config_node->simulation;
263 char* char_repr = NULL;
264
265 if (ret_value) {
266 char_repr = zend_string_to_char(ret_value);
267 }
238 if (alias) { 268 if (alias) {
239 sp_log_msg( 269 sp_log_msg(
240 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 270 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
241 "Aborted execution on return of the function '%s' in %s:%d, " 271 "Aborted execution on return of the function '%s' in %s:%d, "
242 "because the function returned '%s', which matched the rule '%s'.", 272 "because the function returned '%s', which matched the rule '%s'.",
243 path, zend_get_executed_filename(TSRMLS_C), 273 path, zend_get_executed_filename(TSRMLS_C),
244 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", alias); 274 zend_get_executed_lineno(TSRMLS_C), char_repr ? char_repr : "?",
275 ZSTR_VAL(alias));
245 } else { 276 } else {
246 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 277 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
247 "Aborted execution on return of the function '%s' in %s:%d, " 278 "Aborted execution on return of the function '%s' in %s:%d, "
248 "because the function returned '%s', which matched a rule.", 279 "because the function returned '%s', which matched a rule.",
249 path, zend_get_executed_filename(TSRMLS_C), 280 path, zend_get_executed_filename(TSRMLS_C),
250 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?"); 281 zend_get_executed_lineno(TSRMLS_C), char_repr ? char_repr : "?");
251 } 282 }
283 efree(char_repr);
252 if (dump) { 284 if (dump) {
253 sp_log_request(dump, config_node->textual_representation, 285 sp_log_request(dump, config_node->textual_representation,
254 SP_TOKEN_DISABLE_FUNC); 286 SP_TOKEN_DISABLE_FUNC);
255 } 287 }
256} 288}
257 289
258bool sp_match_array_key(const zval* zv, const char* to_match, 290bool sp_match_array_key(const zval* zv, const zend_string* to_match,
259 const sp_pcre* rx) { 291 const sp_pcre* rx) {
260 zend_string* key; 292 zend_string* key;
261 zend_ulong idx; 293 zend_ulong idx;
262 294
263 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(zv), idx, key) { 295 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(zv), idx, key) {
264 if (key) { 296 if (key) {
265 if (sp_match_value(ZSTR_VAL(key), to_match, rx)) { 297 if (sp_match_value(key, to_match, rx)) {
266 return true; 298 return true;
267 } 299 }
268 } else { 300 } else {
269 char* idx_str = NULL; 301 char* idx_str = NULL;
270 spprintf(&idx_str, 0, "%lu", idx); 302 spprintf(&idx_str, 0, "%lu", idx);
271 if (sp_match_value(idx_str, to_match, rx)) { 303 zend_string* tmp = zend_string_init(idx_str, strlen(idx_str), 0);
304 if (sp_match_value(tmp, to_match, rx)) {
272 efree(idx_str); 305 efree(idx_str);
273 return true; 306 return true;
274 } 307 }
@@ -279,18 +312,16 @@ bool sp_match_array_key(const zval* zv, const char* to_match,
279 return false; 312 return false;
280} 313}
281 314
282bool sp_match_array_value(const zval* arr, const char* to_match, 315bool sp_match_array_value(const zval* arr, const zend_string* to_match,
283 const sp_pcre* rx) { 316 const sp_pcre* rx) {
284 zval* value; 317 zval* value;
285 318
286 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) { 319 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) {
287 if (Z_TYPE_P(value) != IS_ARRAY) { 320 if (Z_TYPE_P(value) != IS_ARRAY) {
288 char* value_str = sp_convert_to_string(value); 321 const zend_string* value_str = sp_zval_to_zend_string(value);
289 if (sp_match_value(value_str, to_match, rx)) { 322 if (sp_match_value(value_str, to_match, rx)) {
290 efree(value_str);
291 return true; 323 return true;
292 } else { 324 } else {
293 efree(value_str);
294 } 325 }
295 } else if (sp_match_array_value(value, to_match, rx)) { 326 } else if (sp_match_array_value(value, to_match, rx)) {
296 return true; 327 return true;
@@ -303,6 +334,7 @@ bool sp_match_array_value(const zval* arr, const char* to_match,
303int hook_function(const char* original_name, HashTable* hook_table, 334int hook_function(const char* original_name, HashTable* hook_table,
304 void (*new_function)(INTERNAL_FUNCTION_PARAMETERS)) { 335 void (*new_function)(INTERNAL_FUNCTION_PARAMETERS)) {
305 zend_internal_function* func; 336 zend_internal_function* func;
337 bool ret = FAILURE;
306 338
307 /* The `mb` module likes to hook functions, like strlen->mb_strlen, 339 /* The `mb` module likes to hook functions, like strlen->mb_strlen,
308 * so we have to hook both of them. */ 340 * so we have to hook both of them. */
@@ -317,6 +349,7 @@ int hook_function(const char* original_name, HashTable* hook_table,
317 return FAILURE; 349 return FAILURE;
318 } 350 }
319 func->handler = new_function; 351 func->handler = new_function;
352 ret = SUCCESS;
320 } else { 353 } else {
321 return SUCCESS; 354 return SUCCESS;
322 } 355 }
@@ -326,7 +359,7 @@ int hook_function(const char* original_name, HashTable* hook_table,
326 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; 359 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
327 if (zend_hash_str_find(CG(function_table), 360 if (zend_hash_str_find(CG(function_table),
328 VAR_AND_LEN(original_name + 3))) { 361 VAR_AND_LEN(original_name + 3))) {
329 hook_function(original_name + 3, hook_table, new_function); 362 return hook_function(original_name + 3, hook_table, new_function);
330 } 363 }
331 } else { // TODO this can be moved somewhere else to gain some marginal perfs 364 } else { // TODO this can be moved somewhere else to gain some marginal perfs
332 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; 365 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
@@ -334,11 +367,11 @@ int hook_function(const char* original_name, HashTable* hook_table,
334 memcpy(mb_name, "mb_", 3); 367 memcpy(mb_name, "mb_", 3);
335 memcpy(mb_name + 3, VAR_AND_LEN(original_name)); 368 memcpy(mb_name + 3, VAR_AND_LEN(original_name));
336 if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) { 369 if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) {
337 hook_function(mb_name, hook_table, new_function); 370 return hook_function(mb_name, hook_table, new_function);
338 } 371 }
339 } 372 }
340 373
341 return SUCCESS; 374 return ret;
342} 375}
343 376
344int hook_regexp(const sp_pcre* regexp, HashTable* hook_table, 377int hook_regexp(const sp_pcre* regexp, HashTable* hook_table,
@@ -356,7 +389,7 @@ int hook_regexp(const sp_pcre* regexp, HashTable* hook_table,
356 return SUCCESS; 389 return SUCCESS;
357} 390}
358 391
359bool check_is_in_eval_whitelist(const char* const function_name) { 392bool check_is_in_eval_whitelist(const zend_string* const function_name) {
360 const sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->whitelist; 393 const sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->whitelist;
361 394
362 if (!it) { 395 if (!it) {
@@ -366,7 +399,7 @@ bool check_is_in_eval_whitelist(const char* const function_name) {
366 /* yes, we could use a HashTable instead, but since the list is pretty 399 /* yes, we could use a HashTable instead, but since the list is pretty
367 * small, it doesn't maka a difference in practise. */ 400 * small, it doesn't maka a difference in practise. */
368 while (it && it->data) { 401 while (it && it->data) {
369 if (0 == strcmp(function_name, (char*)(it->data))) { 402 if (sp_zend_string_equals(function_name, (const zend_string*)(it->data))) {
370 /* We've got a match, the function is whiteslited. */ 403 /* We've got a match, the function is whiteslited. */
371 return true; 404 return true;
372 } 405 }