diff options
| -rw-r--r-- | doc/source/features.rst | 5 | ||||
| -rw-r--r-- | src/sp_config.h | 2 | ||||
| -rw-r--r-- | src/sp_config_keywords.c | 1 | ||||
| -rw-r--r-- | src/sp_utils.c | 43 |
4 files changed, 30 insertions, 21 deletions
diff --git a/doc/source/features.rst b/doc/source/features.rst index 407c0c0..e560925 100644 --- a/doc/source/features.rst +++ b/doc/source/features.rst | |||
| @@ -345,6 +345,11 @@ line number. By using the *right* set of restrictive rules (or by using the | |||
| 345 | *overly* restrictives ones in ``simulation`` mode), you might be able | 345 | *overly* restrictives ones in ``simulation`` mode), you might be able |
| 346 | to gather interesting vulnerabilities used against your website. | 346 | to gather interesting vulnerabilities used against your website. |
| 347 | 347 | ||
| 348 | Dumps are stored in the folder that you pass to the ``dump()`` filter, | ||
| 349 | in files named ``sp_dump.SHA`` with ``SHA`` being the *sha256* of the | ||
| 350 | rule that matched. This approach allows to mitigate denial of services attacks | ||
| 351 | that could fill up your filesystem. | ||
| 352 | |||
| 348 | 353 | ||
| 349 | Misc low-hanging fruits in the default configuration file | 354 | Misc low-hanging fruits in the default configuration file |
| 350 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 355 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
diff --git a/src/sp_config.h b/src/sp_config.h index b8fd679..8ef62a2 100644 --- a/src/sp_config.h +++ b/src/sp_config.h | |||
| @@ -66,6 +66,8 @@ typedef struct { | |||
| 66 | } sp_config_unserialize; | 66 | } sp_config_unserialize; |
| 67 | 67 | ||
| 68 | typedef struct { | 68 | typedef struct { |
| 69 | char *textual_representation; | ||
| 70 | |||
| 69 | char *filename; | 71 | char *filename; |
| 70 | pcre *r_filename; | 72 | pcre *r_filename; |
| 71 | 73 | ||
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c index 9bb8021..dd9a880 100644 --- a/src/sp_config_keywords.c +++ b/src/sp_config_keywords.c | |||
| @@ -287,6 +287,7 @@ int parse_disabled_functions(char *line) { | |||
| 287 | } | 287 | } |
| 288 | } | 288 | } |
| 289 | df->allow = allow; | 289 | df->allow = allow; |
| 290 | df->textual_representation = estrdup(line); | ||
| 290 | 291 | ||
| 291 | if (df->function) { | 292 | if (df->function) { |
| 292 | df->functions_list = parse_functions_list(df->function); | 293 | df->functions_list = parse_functions_list(df->function); |
diff --git a/src/sp_utils.c b/src/sp_utils.c index e2747fb..74fbff7 100644 --- a/src/sp_utils.c +++ b/src/sp_utils.c | |||
| @@ -80,7 +80,8 @@ int compute_hash(const char* const filename, char* file_hash) { | |||
| 80 | return SUCCESS; | 80 | return SUCCESS; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | static int construct_filename(char* filename, const char* folder) { | 83 | static int construct_filename(char* filename, const char* folder, |
| 84 | const char* textual) { | ||
| 84 | time_t t = time(NULL); | 85 | time_t t = time(NULL); |
| 85 | struct tm* tm = localtime(&t); // FIXME use `localtime_r` instead | 86 | struct tm* tm = localtime(&t); // FIXME use `localtime_r` instead |
| 86 | struct timeval tval; | 87 | struct timeval tval; |
| @@ -91,25 +92,22 @@ static int construct_filename(char* filename, const char* folder) { | |||
| 91 | return -1; | 92 | return -1; |
| 92 | } | 93 | } |
| 93 | 94 | ||
| 94 | memcpy(filename, folder, strlen(folder)); | 95 | /* We're using the sha256 sum of the rule's textual representation |
| 95 | strcat(filename, "sp_dump_"); | 96 | * as filename, in order to only have one dump per rule, to migitate |
| 96 | strftime(filename + strlen(filename), 27, "%F_%T:", tm); | 97 | * DoS attacks. */ |
| 97 | gettimeofday(&tval, NULL); | 98 | PHP_SHA256_CTX context; |
| 98 | sprintf(filename + strlen(filename), "%04ld", tval.tv_usec); | 99 | unsigned char digest[SHA256_SIZE] = {0}; |
| 99 | strcat(filename, "_"); | 100 | char strhash[65] = {0}; |
| 100 | 101 | PHP_SHA256Init(&context); | |
| 101 | char* remote_addr = getenv("REMOTE_ADDR"); | 102 | PHP_SHA256Update(&context, (const unsigned char *) textual, strlen(textual)); |
| 102 | if (remote_addr) { // ipv6: 8*4 bytes + 7 colons = 39 chars max | 103 | PHP_SHA256Final(digest, &context); |
| 103 | strncat(filename, remote_addr, 40); | 104 | make_digest_ex(strhash, digest, SHA256_SIZE); |
| 104 | } else { | 105 | snprintf(filename, MAX_FOLDER_LEN-1, "%s/sp_dump.%s", folder, strhash); |
| 105 | strcat(filename, "0.0.0.0"); | ||
| 106 | } | ||
| 107 | strcat(filename, ".dump"); | ||
| 108 | 106 | ||
| 109 | return 0; | 107 | return 0; |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | int sp_log_request(const char* folder) { | 110 | int sp_log_request(const char* folder, const char* text_repr) { |
| 113 | FILE* file; | 111 | FILE* file; |
| 114 | const char* current_filename = zend_get_executed_filename(TSRMLS_C); | 112 | const char* current_filename = zend_get_executed_filename(TSRMLS_C); |
| 115 | const int current_line = zend_get_executed_lineno(TSRMLS_C); | 113 | const int current_line = zend_get_executed_lineno(TSRMLS_C); |
| @@ -124,15 +122,18 @@ int sp_log_request(const char* folder) { | |||
| 124 | // Apparently, PHP has trouble always giving SERVER, | 122 | // Apparently, PHP has trouble always giving SERVER, |
| 125 | // and REQUEST is never used in its source code. | 123 | // and REQUEST is never used in its source code. |
| 126 | 124 | ||
| 127 | if (0 != construct_filename(filename, folder)) { | 125 | if (0 != construct_filename(filename, folder, text_repr)) { |
| 128 | return -1; | 126 | return -1; |
| 129 | } | 127 | } |
| 130 | if (NULL == (file = fopen(filename, "w+"))) { | 128 | if (NULL == (file = fopen(filename, "w+"))) { |
| 131 | sp_log_err("request_logging", "Unable to open %s", filename); | 129 | sp_log_err("request_logging", "Unable to open %s: %s", filename, |
| 130 | strerror(errno)); | ||
| 132 | return -1; | 131 | return -1; |
| 133 | } | 132 | } |
| 134 | 133 | ||
| 135 | fprintf(file, "%s:%d\n", current_filename, current_line); | 134 | fprintf(file, "RULE: %s\n", text_repr); |
| 135 | |||
| 136 | fprintf(file, "FILE: %s:%d\n", current_filename, current_line); | ||
| 136 | for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) { | 137 | for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) { |
| 137 | zval* variable_value; | 138 | zval* variable_value; |
| 138 | zend_string* variable_key; | 139 | zend_string* variable_key; |
| @@ -249,7 +250,7 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name, | |||
| 249 | } | 250 | } |
| 250 | } | 251 | } |
| 251 | if (dump) { | 252 | if (dump) { |
| 252 | sp_log_request(config_node->dump); | 253 | sp_log_request(config_node->dump, config_node->textual_representation); |
| 253 | } | 254 | } |
| 254 | } | 255 | } |
| 255 | 256 | ||
| @@ -273,7 +274,7 @@ void sp_log_disable_ret(const char* restrict path, | |||
| 273 | zend_get_executed_lineno(TSRMLS_C), ret_value?ret_value:"?", path); | 274 | zend_get_executed_lineno(TSRMLS_C), ret_value?ret_value:"?", path); |
| 274 | } | 275 | } |
| 275 | if (dump) { | 276 | if (dump) { |
| 276 | sp_log_request(dump); | 277 | sp_log_request(dump, config_node->textual_representation); |
| 277 | } | 278 | } |
| 278 | } | 279 | } |
| 279 | 280 | ||
