diff --git a/CHANGES b/CHANGES index 397d7206..047eee49 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ + * Added SecMarker directive to allow a fixed target for skipAfter. + * The invoked rule is now logged in the debug log at level 5. * New audit log part 'K' logs all matching rules. diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index b99d0f1b..34cf8718 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -657,18 +657,64 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * return "Internal Error: Failed to add placeholder to the ruleset."; } + /* No longer need to search for the ID */ apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); } return NULL; } +/** + * TODO + */ +static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, const char *p1, + const char *p2, const char *p3) +{ + char *my_error_msg = NULL; + msre_rule *rule = NULL; + extern msc_engine *modsecurity; + int p; + + /* Create a ruleset if one does not exist. */ + if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) { + dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool); + if (dcfg->ruleset == NULL) return FATAL_ERROR; + } + + /* Create the rule now. */ + rule = msre_rule_create(dcfg->ruleset, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg); + if (rule == NULL) { + return my_error_msg; + } + + /* This is a marker */ + rule->placeholder = RULE_PH_MARKER; + + /* Add placeholder to each phase */ + for (p = PHASE_FIRST; p <= PHASE_LAST; p++) { + if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) { + return "Internal Error: Failed to add marker to the ruleset."; + } + } + + /* No longer need to search for the ID */ + apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); + + return NULL; +} + /* -- Configuration directives -- */ static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) { return add_rule(cmd, (directory_config *)_dcfg, SECACTION_TARGETS, SECACTION_ARGS, p1); } +static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) { + directory_config *dcfg = (directory_config *)_dcfg; + const char *action = apr_pstrcat(dcfg->mp, SECMARKER_BASE_ACTIONS, p1, NULL); + return add_marker(cmd, (directory_config *)_dcfg, SECMARKER_TARGETS, SECMARKER_ARGS, action); +} + static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -1638,6 +1684,14 @@ const command_rec module_directives[] = { "The filename of the filter debugging log file" ), + AP_INIT_TAKE1 ( + "SecMarker", + cmd_marker, + NULL, + CMD_SCOPE_ANY, + "marker for a skipAfter target" + ), + AP_INIT_FLAG ( "SecPdfProtect", cmd_pdf_protect, diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 715172ba..95cb2e7b 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -117,6 +117,10 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define SECACTION_TARGETS "REQUEST_URI" #define SECACTION_ARGS "@unconditionalMatch" +#define SECMARKER_TARGETS "REQUEST_URI" +#define SECMARKER_ARGS "@noMatch" +#define SECMARKER_BASE_ACTIONS "t:none,pass,id:" + #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) #include "unixd.h" #define __SET_MUTEX_PERMS diff --git a/apache2/re.c b/apache2/re.c index 86a721dc..9ed1597a 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1225,6 +1225,9 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, if ((strcmp(SECACTION_TARGETS, targets) == 0) && (strcmp(SECACTION_ARGS, args) == 0)) { rule->unparsed = apr_pstrcat(ruleset->mp, "SecAction", " \"", actions, "\"", NULL); } + else if ((strcmp(SECMARKER_TARGETS, targets) == 0) && (strcmp(SECMARKER_ARGS, args) == 0) && (strncmp(SECMARKER_BASE_ACTIONS, actions, strlen(SECMARKER_BASE_ACTIONS)) == 0)) { + rule->unparsed = apr_pstrcat(ruleset->mp, "SecMarker", " \"", (actions + strlen(SECMARKER_BASE_ACTIONS)), "\"", NULL); + } else { rule->unparsed = apr_pstrcat(ruleset->mp, "SecRule", " \"", targets, "\" \"", args, "\"", (actions != NULL ? " \"" : ""), (actions != NULL ? actions : ""), (actions != NULL ? "\"" : ""), NULL); } diff --git a/apache2/re.h b/apache2/re.h index ed81a015..ab314329 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -110,7 +110,8 @@ int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset #define RULE_MATCH 1 #define RULE_PH_NONE 0 /* Not a placeholder */ -#define RULE_PH_SKIPAFTER 1 /* Placeholder for skipAfter targets */ +#define RULE_PH_SKIPAFTER 1 /* Implicit placeholder for skipAfter */ +#define RULE_PH_MARKER 2 /* Explicit placeholder for SecMarker */ struct msre_rule { apr_array_header_t *targets; diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 1de69c26..b8d86baf 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -53,6 +53,17 @@ static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule, return 1; } +/* noMatch */ + +static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule, + msre_var *var, char **error_msg) +{ + *error_msg = "No match."; + + /* Never match. */ + return 0; +} + /* rx */ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { @@ -1731,6 +1742,13 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_unconditionalmatch_execute ); + /* noMatch */ + msre_engine_op_register(engine, + "noMatch", + NULL, + msre_op_nomatch_execute + ); + /* rx */ msre_engine_op_register(engine, "rx", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index c680a220..89a4f4f1 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -678,7 +678,8 @@ SecAuditLogStorageDir logs/audit K - This part contains a - full list of every rule that matched (one per line) in the order they were matched. + full list of every rule that matched (one per line) in the order + they were matched. @@ -1167,6 +1168,35 @@ SecAuditLogStorageDir logs/audit SecGuardianLog |/path/to/httpd-guardian +
+ <literal>SecMarker</literal> + + Description: Adds a fixed rule marker in the + ruleset to be used as a target in a skipAfter + action. + + Syntax: SecMarker + id + + Example Usage: SecMarker 9999 + + ProcessingPhase: Any + + Scope: Any + + Dependencies/Notes: None + + SecRule REQUEST_URI "^/$" "chain,skipAfter:960099" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" +SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" +SecRule &REQUEST_HEADERS:Accept "@eq 0" \ + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" +SecMarker 960099 +
+
<literal>SecPdfProtect</literal> (Experimental) @@ -1290,7 +1320,7 @@ SecAuditLogStorageDir logs/audit with status code 413 Request Entity Too Large. There is a hard limit of 1 GB.
- +
<literal>SecRequestBodyNoFilesLimit</literal> @@ -1299,26 +1329,28 @@ SecAuditLogStorageDir logs/audit files being transported in the request. This directive comes handy to further reduce susceptability to DoS attacks when someone is sending request bodies of very large sizes. Web applications that require file - uploads must configure SecRequestBodyLimit to a - high value. Since large files are streamed to disk file uploads will - not increase memory consumption. However, it's still possible for - someone to take advantage of a large request body limit and send - non-upload requests with large body sizes. This directive eliminates - that loophole. + uploads must configure SecRequestBodyLimit to a high + value. Since large files are streamed to disk file uploads will not + increase memory consumption. However, it's still possible for someone to + take advantage of a large request body limit and send non-upload + requests with large body sizes. This directive eliminates that + loophole. Syntax: SecRequestBodyNoFilesLimit NUMBER_IN_BYTES + moreinfo="none">SecRequestBodyNoFilesLimit + NUMBER_IN_BYTES Example Usage: SecRequestBodyLimit 131072 Scope: Any - Dependencies/Notes: 1 MB (1048576 - bytes) is the default setting. This value is very conservative. For - most applications you should be able to reduce it down to 128 KB or - lower. Anything over the limit will be rejected with status code 413 - Request Entity Too Large. There is a hard limit of 1 GB. + Dependencies/Notes: 1 MB (1048576 bytes) is + the default setting. This value is very conservative. For most + applications you should be able to reduce it down to 128 KB or lower. + Anything over the limit will be rejected with status code 413 + Request Entity Too Large. There is a hard limit of 1 + GB.
@@ -4996,8 +5028,6 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd, "phase:2,sanitiseMatched,log,auditlog,pass,msg:'Potential credit card number'"
-
-
<literal>within</literal> @@ -5116,4 +5146,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- + \ No newline at end of file