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 "http_core.h"
22 #include "http_request.h"
24 #include "modsecurity.h"
26 #include "http_main.h"
27 #include "pdf_protect.h"
29 #include "msc_logging.h"
33 /* ModSecurity structure */
35 msc_engine DSOLOCAL *modsecurity = NULL;
37 /* Global module variables; these are used for the Apache-specific functionality */
39 char DSOLOCAL *chroot_dir = NULL;
41 unsigned int DSOLOCAL chroot_completed = 0;
43 char DSOLOCAL *new_server_signature = NULL;
45 char DSOLOCAL *real_server_signature = NULL;
47 char DSOLOCAL *guardianlog_name = NULL;
49 apr_file_t DSOLOCAL *guardianlog_fd = NULL;
51 char DSOLOCAL *guardianlog_condition = NULL;
54 /* -- Miscellaneous functions -- */
57 * Intercepts transaction, using the method specified
58 * in the structure itself. MUST return an HTTP status code,
59 * which will be used to terminate the transaction.
61 int perform_interception(modsec_rec *msr) {
62 msre_actionset *actionset = NULL;
63 const char *message = NULL;
64 const char *phase_text = "";
65 int status = DECLINED;
68 /* Sanity checks first. */
70 if (msr->was_intercepted == 0) {
71 msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero");
76 msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %d.", msr->phase);
77 msr->was_intercepted = 0;
81 /* OK, we're good to go. */
83 actionset = msr->intercept_actionset;
84 phase_text = apr_psprintf(msr->mp, " (phase %d)", msr->phase);
86 /* By default we log at level 1 but we switch to 4
87 * if a nolog action was used or this is not the initial request
88 * to hide the message.
90 log_level = (actionset->log != 1) ? 4 : 1;
92 /* Pause the request first (if configured and the initial request). */
93 if (actionset->intercept_pause) {
94 msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for "
95 "%d msec.", actionset->intercept_pause);
96 /* apr_sleep accepts microseconds */
97 apr_sleep((apr_interval_time_t)(actionset->intercept_pause * 1000));
100 /* Determine how to respond and prepare the log message. */
101 switch(actionset->intercept_action) {
103 if (actionset->intercept_status != 0) {
104 status = actionset->intercept_status;
105 message = apr_psprintf(msr->mp, "Access denied with code %d%s.",
109 status = HTTP_INTERNAL_SERVER_ERROR;
110 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
111 "(Internal Error: Invalid status code requested %d).",
112 phase_text, actionset->intercept_status);
117 if (msr->phase < 3) {
118 if (ap_find_linked_module("mod_proxy.c") == NULL) {
120 status = HTTP_INTERNAL_SERVER_ERROR;
121 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
122 "(Configuration Error: Proxy action to %s requested but mod_proxy not found).",
124 log_escape_nq(msr->mp, actionset->intercept_uri));
126 msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri);
127 msr->r->proxyreq = PROXYREQ_REVERSE;
128 msr->r->handler = "proxy-server";
130 message = apr_psprintf(msr->mp, "Access denied using proxy to%s %s.",
132 log_escape_nq(msr->mp, actionset->intercept_uri));
136 status = HTTP_INTERNAL_SERVER_ERROR;
137 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
138 "(Configuration Error: Proxy action requested but it does not work in output phases).",
144 /* ENH This does not seem to work on Windows. Is there a
145 * better way to drop a connection anyway?
149 extern module core_module;
150 apr_socket_t *csd = ap_get_module_config(msr->r->connection->conn_config,
154 if (apr_socket_close(csd) == APR_SUCCESS) {
155 status = HTTP_FORBIDDEN;
156 message = apr_psprintf(msr->mp, "Access denied with connection close%s.",
160 status = HTTP_INTERNAL_SERVER_ERROR;
161 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
162 "(Error: Connection drop requested but failed to close the "
168 status = HTTP_INTERNAL_SERVER_ERROR;
169 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
170 "(Error: Connection drop requested but socket not found.",
176 status = HTTP_INTERNAL_SERVER_ERROR;
177 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
178 "(Error: Connection drop not implemented on this platform).",
183 case ACTION_REDIRECT :
184 apr_table_setn(msr->r->headers_out, "Location", actionset->intercept_uri);
185 if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302)
186 ||(actionset->intercept_status == 303)||(actionset->intercept_status == 307))
188 status = actionset->intercept_status;
190 status = HTTP_MOVED_TEMPORARILY;
192 message = apr_psprintf(msr->mp, "Access denied with redirection to %s using "
194 log_escape_nq(msr->mp, actionset->intercept_uri), status,
200 message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text);
201 msr->was_intercepted = 0;
202 msr->allow_scope = ACTION_ALLOW;
205 case ACTION_ALLOW_PHASE :
207 message = apr_psprintf(msr->mp, "Access to phase allowed%s.", phase_text);
208 msr->was_intercepted = 0;
209 msr->allow_scope = ACTION_ALLOW_PHASE;
212 case ACTION_ALLOW_REQUEST :
214 message = apr_psprintf(msr->mp, "Access to request allowed%s.", phase_text);
215 msr->was_intercepted = 0;
216 msr->allow_scope = ACTION_ALLOW_REQUEST;
221 status = HTTP_INTERNAL_SERVER_ERROR;
222 message = apr_psprintf(msr->mp, "Access denied with code 500%s "
223 "(Internal Error: invalid interception action %d).",
224 phase_text, actionset->intercept_action);
228 /* Log the message now. */
229 msc_alert(msr, log_level, actionset, message, msr->intercept_message);
235 * Retrieves a previously stored transaction context by
236 * looking at the main request, and the previous requests.
238 static modsec_rec *retrieve_tx_context(request_rec *r) {
239 modsec_rec *msr = NULL;
240 request_rec *rx = NULL;
242 /* Look in the current request first. */
243 msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR);
249 /* If this is a subrequest then look in the main request. */
250 if (r->main != NULL) {
251 msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR);
258 /* If the request was redirected then look in the previous requests. */
261 msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR);
273 * Stores transaction context where it can be found in subsequent
274 * phases, redirections, or subrequests.
276 static void store_tx_context(modsec_rec *msr, request_rec *r) {
277 apr_table_setn(r->notes, NOTE_MSR, (void *)msr);
281 * Creates a new transaction context.
283 static modsec_rec *create_tx_context(request_rec *r) {
284 apr_allocator_t *allocator = NULL;
285 modsec_rec *msr = NULL;
287 msr = (modsec_rec *)apr_pcalloc(r->pool, sizeof(modsec_rec));
288 if (msr == NULL) return NULL;
290 apr_allocator_create(&allocator);
291 apr_allocator_max_free_set(allocator, 1024);
292 apr_pool_create_ex(&msr->mp, r->pool, NULL, allocator);
293 if (msr->mp == NULL) return NULL;
294 apr_allocator_owner_set(allocator, msr->mp);
296 msr->modsecurity = modsecurity;
299 msr->request_time = r->request_time;
300 msr->dcfg1 = (directory_config *)ap_get_module_config(r->per_dir_config,
304 * Create a special user configuration. This is where
305 * explicit instructions will be stored. This will be used
306 * to override the default settings (and to override the
307 * configuration in the second phase, dcfg2, with the user
308 * setting executed in the first phase.
310 msr->usercfg = create_directory_config(msr->mp, NULL);
311 if (msr->usercfg == NULL) return NULL;
313 /* Create a transaction context and populate
314 * it using the directory config we just
317 msr->txcfg = create_directory_config(msr->mp, NULL);
318 if (msr->txcfg == NULL) return NULL;
320 if (msr->dcfg1 != NULL) {
321 msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg1);
322 if (msr->txcfg == NULL) return NULL;
324 init_directory_config(msr->txcfg);
326 msr->txid = get_env_var(r, "UNIQUE_ID");
327 if (msr->txid == NULL) {
328 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
329 "ModSecurity: ModSecurity requires mod_unique_id to be installed.");
333 if (msr->txcfg->debuglog_level >= 4) {
334 msr_log(msr, 4, "Initialising transaction (txid %s).", msr->txid);
337 /* Populate tx fields */
338 msr->error_messages = apr_array_make(msr->mp, 5, sizeof(error_message *));
339 msr->alerts = apr_array_make(msr->mp, 5, sizeof(char *));
341 msr->server_software = real_server_signature;
342 msr->local_addr = r->connection->local_ip;
343 msr->local_port = r->connection->local_addr->port;
345 msr->remote_addr = r->connection->remote_ip;
346 msr->remote_port = r->connection->remote_addr->port;
348 msr->request_line = r->the_request;
349 msr->request_uri = r->uri;
350 msr->request_method = r->method;
351 msr->query_string = r->args;
352 msr->request_protocol = r->protocol;
353 msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
354 msr->hostname = ap_get_server_name(r);
356 msr->msc_rule_mptmp = NULL;
358 /* Invoke the engine to continue with initialisation */
359 if (modsecurity_tx_init(msr) < 0) {
360 msr_log(msr, 1, "Failed to initialise transaction (txid %s).", msr->txid);
364 store_tx_context(msr, r);
366 if (msr->txcfg->debuglog_level >= 4) {
367 msr_log(msr, 4, "Transaction context created (dcfg %pp).", msr->dcfg1);
377 * Change the signature of the server if change was requested in
378 * configuration. We do this by locating the signature in server
379 * memory and writing over it.
381 static apr_status_t change_server_signature(server_rec *s) {
382 char *server_version = NULL;
384 if (new_server_signature == NULL) return 0;
386 server_version = (char *)apache_get_server_version();
388 if (server_version == NULL) {
389 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
390 "SecServerSignature: Apache returned null as signature.");
394 if (strlen(server_version) >= strlen(new_server_signature)) {
395 strcpy(server_version, new_server_signature);
398 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
399 "SecServerSignature: original signature too short. Please set "
400 "ServerTokens to Full.");
404 /* Check that it really changed. This assumes that what we got was
405 * not a copy and this could change in future versions of Apache.
407 server_version = (char *)apache_get_server_version();
408 if ((server_version == NULL) || (strcmp(server_version, new_server_signature) != 0)) {
409 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, "SecServerSignature: Failed to change server signature to \"%s\".", new_server_signature);
413 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, s, "SecServerSignature: Changed server signature to \"%s\".", server_version);
420 * Executed at the end of server lifetime to cleanup the allocated resources.
422 static apr_status_t module_cleanup(void *data) {
423 modsecurity_shutdown(modsecurity);
428 * Pre-configuration initialisation hook.
430 static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
431 /* Initialise ModSecurity engine */
432 modsecurity = modsecurity_create(mp, MODSEC_ONLINE);
433 if (modsecurity == NULL) {
434 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
435 "ModSecurity: Failed to initialise engine.");
436 return HTTP_INTERNAL_SERVER_ERROR;
443 * Main (post-configuration) module initialisation.
445 static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp, server_rec *s) {
446 void *init_flag = NULL;
449 /* ENH Figure out a way to validate config before we start
450 * - skipafter: need to make sure we found all of our targets
453 /* Figure out if we are here for the first time */
454 apr_pool_userdata_get(&init_flag, "modsecurity-init-flag", s->process->pool);
455 if (init_flag == NULL) {
457 apr_pool_userdata_set((const void *)1, "modsecurity-init-flag",
458 apr_pool_cleanup_null, s->process->pool);
460 modsecurity_init(modsecurity, mp);
463 /* Store the original server signature */
464 real_server_signature = apr_pstrdup(mp, apache_get_server_version());
466 /* Make some space in the server signature for later */
467 if (new_server_signature != NULL) {
468 ap_add_version_component(mp, new_server_signature);
469 change_server_signature(s);
472 #if (!(defined(WIN32) || defined(NETWARE)))
474 /* Internal chroot functionality */
476 if (chroot_dir != NULL) {
478 /* ENH Is it safe to simply return with an error, instead
482 if (first_time == 0) {
483 ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
484 "ModSecurity: chroot checkpoint #2 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid());
486 if (chdir(chroot_dir) < 0) {
487 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
488 "ModSecurity: chroot failed, unable to chdir to %s, errno=%d (%s)",
489 chroot_dir, errno, strerror(errno));
493 if (chroot(chroot_dir) < 0) {
494 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
495 "ModSecurity: chroot failed, path=%s, errno=%d(%s)",
496 chroot_dir, errno, strerror(errno));
500 if (chdir("/") < 0) {
501 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
502 "ModSecurity: chdoot failed, unable to chdir to /, errno=%d (%s)",
503 errno, strerror(errno));
507 chroot_completed = 1;
509 ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
510 "ModSecurity: chroot successful, path=%s", chroot_dir);
512 ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
513 "ModSecurity: chroot checkpoint #1 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid());
518 /* Schedule main cleanup for later, when the main pool is destroyed. */
519 apr_pool_cleanup_register(mp, (void *)s, module_cleanup, apr_pool_cleanup_null);
521 /* Log our presence to the error log. */
523 ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
524 "%s configured.", MODSEC_MODULE_NAME_FULL);
526 /* If we've changed the server signature make note of the original. */
527 if (new_server_signature != NULL) {
528 ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
529 "Original server signature: %s", real_server_signature);
533 srand((unsigned int)(time(NULL) * getpid()));
539 * Initialisation performed for every new child process.
541 static void hook_child_init(apr_pool_t *mp, server_rec *s) {
542 modsecurity_child_init(modsecurity);
546 * Initial request processing, executed immediatelly after
547 * Apache receives the request headers.
549 static int hook_request_early(request_rec *r) {
550 modsec_rec *msr = NULL;
553 /* This function needs to run only once per transaction
554 * (i.e. subrequests and redirects are excluded).
556 if ((r->main != NULL)||(r->prev != NULL)) {
560 /* Initialise transaction context and
561 * create the initial configuration.
563 msr = create_tx_context(r);
564 if (msr == NULL) return DECLINED;
566 /* Are we allowed to continue? */
567 if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
568 if (msr->txcfg->debuglog_level >= 4) {
569 msr_log(msr, 4, "Processing disabled, skipping (hook request_early).");
574 /* Process phase REQUEST_HEADERS */
576 if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) {
577 rc = perform_interception(msr);
580 if ( (msr->txcfg->is_enabled != MODSEC_DISABLED)
581 && (msr->txcfg->reqbody_access == 1)
584 /* Check request body limit (non-chunked requests only). */
585 if (msr->request_content_length > msr->txcfg->reqbody_limit) {
586 msr_log(msr, 1, "Request body (Content-Length) is larger than the "
587 "configured limit (%ld).", msr->txcfg->reqbody_limit);
588 return HTTP_REQUEST_ENTITY_TOO_LARGE;
596 * Invoked as the first hook in the handler chain, this function
597 * executes the second phase of ModSecurity request processing.
599 static int hook_request_late(request_rec *r) {
600 char *my_error_msg = NULL;
601 modsec_rec *msr = NULL;
604 /* This function needs to run only once per transaction
605 * (i.e. subrequests and redirects are excluded).
607 if ((r->main != NULL)||(r->prev != NULL)) {
611 /* Find the transaction context and make sure
612 * we are supposed to proceed.
614 msr = retrieve_tx_context(r);
616 /* If we can't find the context that probably means it's
617 * a subrequest that was not initiated from the outside.
622 /* Has this phase been completed already? */
623 if (msr->phase_request_body_complete) {
624 msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once.");
627 msr->phase_request_body_complete = 1;
629 msr->remote_user = r->user;
631 /* Get the second configuration context. */
632 msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config,
635 /* Create a transaction context. */
636 msr->txcfg = create_directory_config(msr->mp, NULL);
637 if (msr->txcfg == NULL) return DECLINED;
638 if (msr->dcfg2 != NULL) {
639 msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg2);
640 if (msr->txcfg == NULL) return DECLINED;
643 /* Update with the explicit user settings. */
644 msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->usercfg);
646 init_directory_config(msr->txcfg);
648 /* Perform the PDF tests now. */
649 rc = pdfp_check(msr);
651 /* The PDF protection module has decided it needs to
652 * redirect the current transaction. So we let it do that.
657 if (msr->txcfg->is_enabled == 0) {
658 if (msr->txcfg->debuglog_level >= 4) {
659 msr_log(msr, 4, "Processing disabled, skipping (hook request_late).");
664 if (msr->txcfg->debuglog_level >= 4) {
665 msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2);
668 /* Figure out whether or not to extract multipart files. */
669 if ((msr->txcfg->upload_keep_files != KEEP_FILES_OFF) /* user might want to keep them */
670 || (msr->txcfg->upload_validates_files)) /* user might want to validate them */
672 msr->upload_extract_files = 1;
673 msr->upload_remove_files = 1;
676 rc = read_request_body(msr, &my_error_msg);
680 if (my_error_msg != NULL) {
681 msr_log(msr, 1, "%s", my_error_msg);
683 return HTTP_INTERNAL_SERVER_ERROR;
685 case -4 : /* Timeout. */
686 if (my_error_msg != NULL) {
687 msr_log(msr, 4, "%s", my_error_msg);
689 r->connection->keepalive = AP_CONN_CLOSE;
690 return HTTP_REQUEST_TIME_OUT;
692 case -5 : /* Request body limit reached. */
693 if (my_error_msg != NULL) {
694 msr_log(msr, 1, "%s", my_error_msg);
696 r->connection->keepalive = AP_CONN_CLOSE;
697 return HTTP_REQUEST_ENTITY_TOO_LARGE;
704 msr->msc_reqbody_error = 1;
705 msr->msc_reqbody_error_msg = my_error_msg;
708 /* Update the request headers. They might have changed after
709 * the body was read (trailers).
711 /* NOTE We still need to keep a copy of the original headers
712 * to log in the audit log.
714 msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
716 /* Process phase REQUEST_BODY */
717 record_time_checkpoint(msr, 1);
720 if (modsecurity_process_phase(msr, PHASE_REQUEST_BODY) > 0) {
721 rc = perform_interception(msr);
724 record_time_checkpoint(msr, 2);
730 * Invoked every time Apache has something to write to the error log.
732 static void hook_error_log(const char *file, int line, int level, apr_status_t status,
733 const server_rec *s, const request_rec *r, apr_pool_t *mp, const char *fmt)
735 modsec_rec *msr = NULL;
736 error_message *em = NULL;
738 if (r == NULL) return;
739 msr = retrieve_tx_context((request_rec *)r);
741 /* Create a context for requests we never had the chance to process */
743 && ((level & APLOG_LEVELMASK) < APLOG_DEBUG)
744 && apr_table_get(r->subprocess_env, "UNIQUE_ID"))
746 msr = create_tx_context((request_rec *)r);
747 if (msr->txcfg->debuglog_level >= 9) {
749 msr_log(msr, 9, "Failed to create context after request failure.");
752 msr_log(msr, 9, "Context created after request failure.");
757 if (msr == NULL) return;
759 /* Store the error message for later */
760 em = (error_message *)apr_pcalloc(msr->mp, sizeof(error_message));
761 if (em == NULL) return;
763 if (file != NULL) em->file = apr_pstrdup(msr->mp, file);
767 if (fmt != NULL) em->message = apr_pstrdup(msr->mp, fmt);
769 /* Remove \n from the end of the message */
770 if (em->message != NULL) {
771 char *p = (char *)em->message;
773 if ((*(p + 1) == '\0')&&(*p == '\n')) {
781 *(const error_message **)apr_array_push(msr->error_messages) = em;
785 * Guardian logger is used to interface to the external
786 * script for web server protection - httpd_guardian.
788 static void sec_guardian_logger(request_rec *r, request_rec *origr, modsec_rec *msr) {
789 char *str1, *str2, *text;
790 char *modsec_message = "-";
791 int modsec_rating = 0; /* not used yet */
792 apr_size_t nbytes, nbytes_written;
793 apr_time_t duration = (apr_time_now() - origr->request_time);
794 int limit, was_limited;
796 /* bail out if we do not have where to write */
797 if ((guardianlog_name == NULL)||(guardianlog_fd == NULL)) return;
799 /* process the condition, if we have one */
800 if (guardianlog_condition != NULL) {
801 if (*guardianlog_condition == '!') {
802 if (apr_table_get(r->subprocess_env, guardianlog_condition + 1) != NULL) {
807 if (apr_table_get(r->subprocess_env, guardianlog_condition) == NULL) {
814 * Log format is as follows:
816 * %V %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %{UNIQUE_ID}e
817 * "SESSION_ID" %T %D "MODSEC_MESSAGE" MODSEC_RATING
819 * The fields SESSION_ID, MODSEC_MESSAGE, and MODSEC_RATING are not used at the moment.
822 str2 = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT " %" APR_TIME_T_FMT " \"%s\" %d",
823 duration, apr_time_sec(duration), log_escape(msr->mp, modsec_message), modsec_rating);
824 if (str2 == NULL) return;
826 /* We do not want the index line to be longer than 3980 bytes. */
830 /* If we are logging to a pipe we need to observe and
831 * obey the pipe atomic write limit - PIPE_BUF. For
832 * more details see the discussion in sec_guardian_logger,
835 if (msr->txcfg->auditlog_name[0] == '|') {
836 if (PIPE_BUF < limit) {
841 limit = limit - strlen(str2) - 5;
843 msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF);
847 str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited);
848 if (str1 == NULL) return;
850 if (was_limited == 0) {
851 text = apr_psprintf(msr->mp, "%s %s \n", str1, str2);
853 text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2);
855 if (text == NULL) return;
857 nbytes = strlen(text);
858 apr_file_write_full(guardianlog_fd, text, nbytes, &nbytes_written);
862 * Invoked at the end of each transaction.
864 static int hook_log_transaction(request_rec *r) {
865 const apr_array_header_t *arr = NULL;
866 request_rec *origr = NULL;
867 modsec_rec *msr = NULL;
869 msr = retrieve_tx_context(r);
874 if (msr->txcfg->debuglog_level >= 4) {
875 msr_log(msr, 4, "Initialising logging.");
878 /* Find the first (origr) and the last (r) request */
887 /* At this point r is the last request in the
888 * chain. However, we now need to detect a case when
889 * a bad ErrorDocument was used and back out of it. That's
890 * how Apache does it internally. Except where Apache knows
891 * exactly what is happening we will have to rely on the missing
892 * headers in the final request to detect this condition.
894 arr = apr_table_elts(r->headers_out);
895 while ((arr->nelts == 0)&&(r->prev != NULL)) {
897 arr = apr_table_elts(r->headers_out);
901 msr->response_status = r->status;
902 msr->status_line = ((r->status_line != NULL)
903 ? r->status_line : ap_get_status_line(r->status));
904 msr->response_protocol = get_response_protocol(origr);
905 msr->response_headers = apr_table_copy(msr->mp, r->headers_out);
906 if (!r->assbackwards) msr->response_headers_sent = 1;
907 msr->bytes_sent = r->bytes_sent;
908 msr->local_user = r->user;
909 msr->remote_user = r->connection->remote_logname;
913 sec_guardian_logger(r, origr, msr);
915 /* Invoke the engine to do the rest of the work now. */
916 modsecurity_process_phase(msr, PHASE_LOGGING);
922 * Invoked right before request processing begins. This is
923 * when we need to decide if we want to hook into the output
926 static void hook_insert_filter(request_rec *r) {
927 modsec_rec *msr = NULL;
929 /* Find the transaction context first. */
930 msr = retrieve_tx_context(r);
931 if (msr == NULL) return;
933 /* Add the input filter, but only if we need it to run. */
934 if (msr->if_status == IF_STATUS_WANTS_TO_RUN) {
935 if (msr->txcfg->debuglog_level >= 4) {
936 msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %pp).",
937 (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r);
940 ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection);
943 /* The output filters only need to be added only once per transaction
944 * (i.e. subrequests and redirects are excluded).
946 if ((r->main != NULL)||(r->prev != NULL)) {
950 /* We always add the PDF XSS protection filter. */
951 if (msr->txcfg->debuglog_level >= 4) {
952 msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %pp).", r);
955 ap_add_output_filter("PDFP_OUT", msr, r, r->connection);
957 /* Only proceed to add the second filter if the engine is enabled. */
958 if (msr->txcfg->is_enabled == 0) {
959 if (msr->txcfg->debuglog_level >= 4) {
960 msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping.");
966 /* We always add the output filter because that's where we need to
967 * initiate our 3rd and 4th processing phases from. The filter is
968 * smart enough not to buffer the data if it is not supposed to.
970 if (msr->of_status != OF_STATUS_COMPLETE) {
971 if (msr->txcfg->debuglog_level >= 4) {
972 msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %pp).", r);
975 ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection);
979 // TODO: Holding off on this for now (needs more testing)
982 * Invoked whenever Apache starts processing an error. A chance
983 * to insert ourselves into the output filter chain.
985 static void hook_insert_error_filter(request_rec *r) {
986 modsec_rec *msr = NULL;
988 /* Find the transaction context and make sure we are
989 * supposed to proceed.
991 msr = retrieve_tx_context(r);
992 if (msr == NULL) return;
994 /* Do not run if not enabled. */
995 if (msr->txcfg->is_enabled == 0) {
996 if (msr->txcfg->debuglog_level >= 4) {
997 msr_log(msr, 4, "Hook insert_error_filter: Processing disabled, skipping.");
1002 /* Do not run if the output filter already completed. This will
1003 * happen if we intercept in phase 4.
1005 if (msr->of_status != OF_STATUS_COMPLETE) {
1006 if (msr->txcfg->debuglog_level >= 4) {
1007 msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %pp).", r);
1010 /* Make a note that the output we will be receiving is a
1011 * result of error processing.
1013 msr->of_is_error = 1;
1015 ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection);
1017 if (msr->txcfg->debuglog_level >= 4) {
1018 msr_log(msr, 4, "Hook insert_error_filter: Output buffering already complete.");
1024 #if (!defined(NO_MODSEC_API))
1026 * This function is exported for other Apache modules to
1027 * register new transformation functions.
1029 static void modsec_register_tfn(const char *name, void *fn) {
1030 if (modsecurity != NULL) {
1031 msre_engine_tfn_register(modsecurity->msre, name, (fn_tfn_execute_t)fn);
1036 * This function is exported for other Apache modules to
1037 * register new operators.
1039 static void modsec_register_operator(const char *name, void *fn_init, void *fn_exec) {
1040 if (modsecurity != NULL) {
1041 msre_engine_op_register(modsecurity->msre, name, (fn_op_param_init_t)fn_init, (fn_op_execute_t)fn_exec);
1046 * This function is exported for other Apache modules to
1047 * register new variables.
1049 static void modsec_register_variable(const char *name, unsigned int type,
1050 unsigned int argc_min, unsigned int argc_max,
1051 void *fn_validate, void *fn_generate,
1052 unsigned int is_cacheable, unsigned int availability) {
1053 if (modsecurity != NULL) {
1054 msre_engine_variable_register(modsecurity->msre, name, type, argc_min, argc_max, (fn_var_validate_t)fn_validate, (fn_var_generate_t)fn_generate, is_cacheable, availability);
1057 fprintf(stderr,"modsecurity is NULL\n");
1063 * Registers module hooks with Apache.
1065 static void register_hooks(apr_pool_t *mp) {
1066 static const char *postconfig_beforeme_list[] = {
1071 static const char *postconfig_afterme_list[] = {
1076 static const char *postread_beforeme_list[] = {
1079 "mod_extract_forwarded2.c",
1080 "mod_custom_header.c",
1081 "mod_breach_realip.c",
1082 "mod_breach_trans.c",
1086 static const char *postread_afterme_list[] = {
1087 "mod_log_forensic.c",
1091 /* Add the MODSEC_a.b define */
1092 *(char **)apr_array_push(ap_server_config_defines) = apr_psprintf(mp, "MODSEC_%s.%s", MODSEC_VERSION_MAJOR, MODSEC_VERSION_MINOR);
1094 #if (!defined(NO_MODSEC_API))
1095 /* Export optional functions. */
1096 APR_REGISTER_OPTIONAL_FN(modsec_register_tfn);
1097 APR_REGISTER_OPTIONAL_FN(modsec_register_operator);
1098 APR_REGISTER_OPTIONAL_FN(modsec_register_variable);
1102 ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST);
1103 ap_hook_post_config(hook_post_config, postconfig_beforeme_list,
1104 postconfig_afterme_list, APR_HOOK_REALLY_LAST);
1105 ap_hook_child_init(hook_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1107 /* Our own hook to handle RPC transactions (not used at the moment).
1108 * // ap_hook_handler(hook_handler, NULL, NULL, APR_HOOK_MIDDLE);
1111 /* Transaction processing hooks */
1112 ap_hook_post_read_request(hook_request_early,
1113 postread_beforeme_list, postread_afterme_list, APR_HOOK_REALLY_FIRST);
1115 ap_hook_fixups(hook_request_late, NULL, NULL, APR_HOOK_REALLY_FIRST);
1118 ap_hook_error_log(hook_error_log, NULL, NULL, APR_HOOK_MIDDLE);
1119 ap_hook_log_transaction(hook_log_transaction, NULL, NULL, APR_HOOK_MIDDLE);
1122 ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST);
1124 ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST);
1127 ap_register_input_filter("MODSECURITY_IN", input_filter,
1128 NULL, AP_FTYPE_CONTENT_SET);
1130 /* Ensure that the output filter runs before other modules so that
1131 * we get a request that has a better chance of not being modified:
1139 ap_register_output_filter("MODSECURITY_OUT", output_filter,
1140 NULL, AP_FTYPE_CONTENT_SET - 3);
1142 ap_register_output_filter("PDFP_OUT", pdfp_output_filter,
1143 NULL, AP_FTYPE_CONTENT_SET);
1146 /* Defined in apache2_config.c */
1147 extern const command_rec module_directives[];
1149 /* Module entry points */
1150 module AP_MODULE_DECLARE_DATA security2_module = {
1151 STANDARD20_MODULE_STUFF,
1152 create_directory_config,
1153 merge_directory_configs,
1154 NULL, /* create_server_config */
1155 NULL, /* merge_server_configs */