2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/)
5 * This product is released under the terms of the General Public Licence,
6 * version 2 (GPLv2). Please refer to the file LICENSE (included with this
7 * distribution) which contains the complete text of the licence.
9 * There are special exceptions to the terms and conditions of the GPL
10 * as it is applied to this software. View the full text of the exception in
11 * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software
14 * If any of the files related to licensing are missing or if you have any
15 * other questions related to licensing please contact Breach Security, Inc.
16 * directly using the email address support@breach.com.
21 #include "apr_global_mutex.h"
23 #include "modsecurity.h"
24 #include "msc_parsers.h"
29 * Format an alert message.
31 const char * msc_alert_message(modsec_rec *msr, msre_actionset *actionset, const char *action_message,
32 const char *rule_message)
34 const char *message = NULL;
36 if (rule_message == NULL) rule_message = "Unknown error.";
38 if (action_message == NULL) {
39 message = apr_psprintf(msr->mp, "%s%s",
40 rule_message, msre_format_metadata(msr, actionset));
43 message = apr_psprintf(msr->mp, "%s %s%s", action_message,
44 rule_message, msre_format_metadata(msr, actionset));
51 * Log an alert message to the log, adding the rule metadata at the end.
53 void msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message,
54 const char *rule_message)
56 const char *message = msc_alert_message(msr, actionset, action_message, rule_message);
58 msr_log(msr, level, "%s", message);
63 * Return phase name associated with the given phase number.
65 static const char *phase_name(int phase) {
68 return "REQUEST_HEADERS";
71 return "REQUEST_BODY";
74 return "RESPONSE_HEADERS";
77 return "RESPONSE_BODY";
88 * Creates and initialises a ModSecurity engine instance.
90 msc_engine *modsecurity_create(apr_pool_t *mp, int processing_mode) {
91 msc_engine *msce = NULL;
93 msce = apr_pcalloc(mp, sizeof(msc_engine));
94 if (msce == NULL) return NULL;
97 msce->processing_mode = processing_mode;
99 msce->msre = msre_engine_create(msce->mp);
100 if (msce->msre == NULL) return NULL;
101 msre_engine_register_default_variables(msce->msre);
102 msre_engine_register_default_operators(msce->msre);
103 msre_engine_register_default_tfns(msce->msre);
104 msre_engine_register_default_actions(msce->msre);
110 * Initialise the modsecurity engine. This function must be invoked
111 * after configuration processing is complete as Apache needs to know the
112 * username it is running as.
114 int modsecurity_init(msc_engine *msce, apr_pool_t *mp) {
117 /* Serial audit log mutext */
118 rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp);
119 if (rc != APR_SUCCESS) {
120 //ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mod_security: Could not create modsec_auditlog_lock");
121 //return HTTP_INTERNAL_SERVER_ERROR;
125 #ifdef __SET_MUTEX_PERMS
126 rc = unixd_set_global_mutex_perms(msce->auditlog_lock);
127 if (rc != APR_SUCCESS) {
128 // ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, "mod_security: Could not set permissions on modsec_auditlog_lock; check User and Group directives");
129 // return HTTP_INTERNAL_SERVER_ERROR;
138 * Performs per-child (new process) initialisation.
140 void modsecurity_child_init(msc_engine *msce) {
141 /* Need to call this once per process before any other XML calls. */
144 if (msce->auditlog_lock != NULL) {
145 apr_status_t rc = apr_global_mutex_child_init(&msce->auditlog_lock, NULL, msce->mp);
146 if (rc != APR_SUCCESS) {
147 // ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init auditlog mutex");
153 * Releases resources held by engine instance.
155 void modsecurity_shutdown(msc_engine *msce) {
156 if (msce == NULL) return;
162 static apr_status_t modsecurity_tx_cleanup(void *data) {
163 modsec_rec *msr = (modsec_rec *)data;
164 const apr_array_header_t *arr;
165 apr_table_entry_t *te;
166 int collect_garbage = 0;
168 char *my_error_msg = NULL;
170 if (msr == NULL) return APR_SUCCESS;
172 if (rand() < RAND_MAX/100) {
176 /* Collections, store & remove stale. */
177 arr = apr_table_elts(msr->collections);
178 te = (apr_table_entry_t *)arr->elts;
179 for (i = 0; i < arr->nelts; i++) {
180 apr_table_t *col = (apr_table_t *)te[i].val;
182 /* Only store those collections that changed. */
183 if (apr_table_get(msr->collections_dirty, te[i].key)) {
184 collection_store(msr, col);
187 if (collect_garbage) {
188 collections_remove_stale(msr, te[i].key);
192 /* Multipart processor cleanup. */
193 if (msr->mpd != NULL) multipart_cleanup(msr);
195 /* XML processor cleanup. */
196 if (msr->xml != NULL) xml_cleanup(msr);
198 // TODO: Why do we ignore return code here?
199 modsecurity_request_body_clear(msr, &my_error_msg);
200 if (my_error_msg != NULL) {
201 msr_log(msr, 1, "%s", my_error_msg);
210 apr_status_t modsecurity_tx_init(modsec_rec *msr) {
211 const char *s = NULL;
212 const apr_array_header_t *arr;
213 apr_table_entry_t *te;
216 /* Register TX cleanup */
217 apr_pool_cleanup_register(msr->mp, msr, modsecurity_tx_cleanup, apr_pool_cleanup_null);
220 msr->request_content_length = -1;
221 s = apr_table_get(msr->request_headers, "Content-Length");
223 msr->request_content_length = strtol(s, NULL, 10);
226 /* Figure out whether this request has a body */
227 msr->reqbody_chunked = 0;
228 msr->reqbody_should_exist = 0;
229 if (msr->request_content_length == -1) {
230 /* There's no C-L, but is chunked encoding used? */
231 char *transfer_encoding = (char *)apr_table_get(msr->request_headers, "Transfer-Encoding");
232 if ((transfer_encoding != NULL)&&(strstr(transfer_encoding, "chunked") != NULL)) {
233 msr->reqbody_should_exist = 1;
234 msr->reqbody_chunked = 1;
238 msr->reqbody_should_exist = 1;
242 msr->request_content_type = NULL;
243 s = apr_table_get(msr->request_headers, "Content-Type");
244 if (s != NULL) msr->request_content_type = s;
246 /* Decide what to do with the request body. */
247 if ((msr->request_content_type != NULL)
248 && (strncasecmp(msr->request_content_type, "application/x-www-form-urlencoded", 33) == 0))
250 /* Always place POST requests with
251 * "application/x-www-form-urlencoded" payloads in memory.
253 msr->msc_reqbody_storage = MSC_REQBODY_MEMORY;
254 msr->msc_reqbody_spilltodisk = 0;
255 msr->msc_reqbody_processor = "URLENCODED";
257 /* If the C-L is known and there's more data than
258 * our limit go to disk straight away.
260 if ((msr->request_content_length != -1)
261 && (msr->request_content_length > msr->txcfg->reqbody_inmemory_limit))
263 msr->msc_reqbody_storage = MSC_REQBODY_DISK;
266 /* In all other cases, try using the memory first
267 * but switch over to disk for larger bodies.
269 msr->msc_reqbody_storage = MSC_REQBODY_MEMORY;
270 msr->msc_reqbody_spilltodisk = 1;
272 if (msr->request_content_type != NULL) {
273 if (strncasecmp(msr->request_content_type, "multipart/form-data", 19) == 0) {
274 msr->msc_reqbody_processor = "MULTIPART";
279 /* Check if we are forcing buffering, then use memory only. */
280 if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
281 msr->msc_reqbody_storage = MSC_REQBODY_MEMORY;
282 msr->msc_reqbody_spilltodisk = 0;
285 /* Initialise arguments */
286 msr->arguments = apr_table_make(msr->mp, 32);
287 if (msr->arguments == NULL) return -1;
288 if (msr->query_string != NULL) {
289 int invalid_count = 0;
291 if (parse_arguments(msr, msr->query_string, strlen(msr->query_string),
292 msr->txcfg->argument_separator, "QUERY_STRING", msr->arguments,
295 msr_log(msr, 1, "Initialisation: Error occurred while parsing QUERY_STRING arguments.");
300 msr->arguments_to_sanitise = apr_table_make(msr->mp, 16);
301 if (msr->arguments_to_sanitise == NULL) return -1;
302 msr->request_headers_to_sanitise = apr_table_make(msr->mp, 16);
303 if (msr->request_headers_to_sanitise == NULL) return -1;
304 msr->response_headers_to_sanitise = apr_table_make(msr->mp, 16);
305 if (msr->response_headers_to_sanitise == NULL) return -1;
307 /* Initialise cookies */
308 msr->request_cookies = apr_table_make(msr->mp, 16);
309 if (msr->request_cookies == NULL) return -1;
311 /* Locate the cookie headers and parse them */
312 arr = apr_table_elts(msr->request_headers);
313 te = (apr_table_entry_t *)arr->elts;
314 for (i = 0; i < arr->nelts; i++) {
315 if (strcasecmp(te[i].key, "Cookie") == 0) {
316 if (msr->txcfg->cookie_format == COOKIES_V0) {
317 parse_cookies_v0(msr, te[i].val, msr->request_cookies);
319 parse_cookies_v1(msr, te[i].val, msr->request_cookies);
325 msr->tx_vars = apr_table_make(msr->mp, 32);
326 if (msr->tx_vars == NULL) return -1;
328 msr->geo_vars = apr_table_make(msr->mp, 8);
329 if (msr->geo_vars == NULL) return -1;
331 msr->collections_original = apr_table_make(msr->mp, 8);
332 if (msr->collections_original == NULL) return -1;
333 msr->collections = apr_table_make(msr->mp, 8);
334 if (msr->collections == NULL) return -1;
335 msr->collections_dirty = apr_table_make(msr->mp, 8);
336 if (msr->collections_dirty == NULL) return -1;
340 msr->tcache_items = 0;
342 msr->matched_rules = apr_array_make(msr->mp, 16, sizeof(void *));
343 if (msr->matched_rules == NULL) return -1;
345 msr->matched_var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
346 if (msr->matched_var == NULL) return -1;
348 msr->highest_severity = 255; /* high, invalid value */
350 msr->removed_rules = apr_array_make(msr->mp, 16, sizeof(char *));
351 if (msr->removed_rules == NULL) return -1;
359 static int is_response_status_relevant(modsec_rec *msr, int status) {
360 char *my_error_msg = NULL;
364 /* ENH: Setting is_relevant here will cause an audit even if noauditlog
365 * was set for the last rule that matched. Is this what we want?
368 if ((msr->txcfg->auditlog_relevant_regex == NULL)
369 ||(msr->txcfg->auditlog_relevant_regex == NOT_SET_P))
374 apr_snprintf(buf, sizeof(buf), "%d", status);
376 rc = msc_regexec(msr->txcfg->auditlog_relevant_regex, buf, strlen(buf), &my_error_msg);
377 if (rc >= 0) return 1;
378 if (rc == PCRE_ERROR_NOMATCH) return 0;
380 msr_log(msr, 1, "Regex processing failed (rc %d): %s", rc, my_error_msg);
387 static apr_status_t modsecurity_process_phase_request_headers(modsec_rec *msr) {
388 msr_log(msr, 4, "Starting phase REQUEST_HEADERS.");
390 if (msr->txcfg->ruleset != NULL) {
391 return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
400 static apr_status_t modsecurity_process_phase_request_body(modsec_rec *msr) {
401 if ((msr->allow_scope == ACTION_ALLOW_REQUEST)||(msr->allow_scope == ACTION_ALLOW)) {
402 msr_log(msr, 4, "Skipping phase REQUEST_BODY (allow used).");
405 msr_log(msr, 4, "Starting phase REQUEST_BODY.");
408 if (msr->txcfg->ruleset != NULL) {
409 return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
418 static apr_status_t modsecurity_process_phase_response_headers(modsec_rec *msr) {
419 if (msr->allow_scope == ACTION_ALLOW) {
420 msr_log(msr, 4, "Skipping phase RESPONSE_HEADERS (allow used).");
423 msr_log(msr, 4, "Starting phase RESPONSE_HEADERS.");
426 if (msr->txcfg->ruleset != NULL) {
427 return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
436 static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) {
437 if (msr->allow_scope == ACTION_ALLOW) {
438 msr_log(msr, 4, "Skipping phase RESPONSE_BODY (allow used).");
441 msr_log(msr, 4, "Starting phase RESPONSE_BODY.");
444 if (msr->txcfg->ruleset != NULL) {
445 return msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
454 static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) {
455 msr_log(msr, 4, "Starting phase LOGGING.");
457 if (msr->txcfg->ruleset != NULL) {
458 msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
461 /* Is this request relevant for logging purposes? */
462 if (msr->is_relevant == 0) {
463 /* Check the status */
464 msr->is_relevant += is_response_status_relevant(msr, msr->r->status);
466 /* If we processed two requests and statuses are different then
467 * check the other status too.
469 if (msr->r_early->status != msr->r->status) {
470 msr->is_relevant += is_response_status_relevant(msr, msr->r_early->status);
474 /* Figure out if we want to keep the files (if there are any, of course). */
475 if ((msr->txcfg->upload_keep_files == KEEP_FILES_ON)
476 || ((msr->txcfg->upload_keep_files == KEEP_FILES_RELEVANT_ONLY)&&(msr->is_relevant)))
478 msr->upload_remove_files = 0;
480 msr->upload_remove_files = 1;
483 /* Are we configured for audit logging? */
484 switch(msr->txcfg->auditlog_flag) {
486 msr_log(msr, 4, "Audit log: Not configured to run for this request.");
490 case AUDITLOG_RELEVANT :
491 if (msr->is_relevant == 0) {
492 msr_log(msr, 4, "Audit log: Ignoring a non-relevant request.");
498 /* All right, do nothing */
502 msr_log(msr, 1, "Internal error: Could not determine if auditing is needed, so forcing auditing.");
506 /* Invoke the Audit logger */
507 msr_log(msr, 4, "Audit log: Logging this transaction.");
509 sec_audit_logger(msr);
515 * Processes one transaction phase. The phase number does not
516 * need to be explicitly provided since it's already available
517 * in the modsec_rec structure.
519 apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) {
520 /* Check if we should run. */
521 if ((msr->was_intercepted)&&(phase != PHASE_LOGGING)) {
522 msr_log(msr, 4, "Skipping phase %d as request was already intercepted.", phase);
526 /* Do not process the same phase twice. */
527 if (msr->phase >= phase) {
528 msr_log(msr, 4, "Skipping phase %d because it was previously run (at %d now).",
535 /* Clear out the transformation cache at the start of each phase */
536 if (msr->txcfg->cache_trans == MODSEC_CACHE_ENABLED) {
538 apr_hash_index_t *hi;
544 apr_pool_t *mp = msr->msc_rule_mptmp;
545 const apr_array_header_t *ctarr;
546 const apr_table_entry_t *ctelts;
551 apr_pool_t *mp = msr->mp;
554 for (hi = apr_hash_first(mp, msr->tcache); hi; hi = apr_hash_next(hi)) {
555 apr_hash_this(hi, &key, &klen, &dummy);
556 tab = (apr_table_t *)dummy;
558 if (tab == NULL) continue;
561 /* Dump the cache out as we clear */
562 ctarr = apr_table_elts(tab);
563 ctelts = (const apr_table_entry_t*)ctarr->elts;
564 for (ri = 0; ri < ctarr->nelts; ri++) {
566 rec = (msre_cache_rec *)ctelts[ri].val;
568 if (msr->txcfg->debuglog_level >= 9) {
569 msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=\"%s\" (%pp - %pp)", cn, rec->hits, key, rec->num, rec->path, log_escape_nq_ex(mp, rec->val, rec->val_len), rec->val, rec->val + rec->val_len);
573 if (msr->txcfg->debuglog_level >= 9) {
574 msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=<no change>", cn, rec->hits, key, rec->num, rec->path);
580 apr_table_clear(tab);
581 apr_hash_set(msr->tcache, key, klen, NULL);
584 msr_log(msr, 9, "Cleared transformation cache for phase %d", msr->phase);
587 msr->tcache_items = 0;
588 msr->tcache = apr_hash_make(msr->mp);
589 if (msr->tcache == NULL) return -1;
594 return modsecurity_process_phase_request_headers(msr);
596 return modsecurity_process_phase_request_body(msr);
598 return modsecurity_process_phase_response_headers(msr);
600 return modsecurity_process_phase_response_body(msr);
602 return modsecurity_process_phase_logging(msr);
604 msr_log(msr, 1, "Invalid processing phase: %d", msr->phase);