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.
23 #include "apr_strmatch.h"
29 void msre_engine_op_register(msre_engine *engine, const char *name,
30 fn_op_param_init_t fn1, fn_op_execute_t fn2)
32 msre_op_metadata *metadata = (msre_op_metadata *)apr_pcalloc(engine->mp,
33 sizeof(msre_op_metadata));
34 if (metadata == NULL) return;
36 metadata->name = name;
37 metadata->param_init = fn1;
38 metadata->execute = fn2;
39 apr_table_setn(engine->operators, name, (void *)metadata);
45 msre_op_metadata *msre_engine_op_resolve(msre_engine *engine, const char *name) {
46 return (msre_op_metadata *)apr_table_get(engine->operators, name);
53 /* unconditionalMatch */
55 static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule,
56 msre_var *var, char **error_msg)
58 *error_msg = "Unconditional match in SecAction.";
66 static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule,
67 msre_var *var, char **error_msg)
69 *error_msg = "No match.";
77 static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) {
78 const char *errptr = NULL;
81 const char *pattern = rule->op_param;
83 if (error_msg == NULL) return -1;
87 regex = msc_pregcomp(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset);
89 *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
94 rule->op_param_data = regex;
99 static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
100 msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
102 unsigned int target_length;
103 char *my_error_msg = NULL;
108 if (error_msg == NULL) return -1;
112 *error_msg = "Internal Error: regex data is null.";
116 /* If the given target is null run against an empty
117 * string. This is a behaviour consistent with previous
120 if (var->value == NULL) {
125 target_length = var->value_len;
128 /* Are we supposed to capture subexpressions? */
129 capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
131 /* Show when the regex captures but "capture" is not set */
132 if (msr->txcfg->debuglog_level >= 6) {
134 rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount);
135 if ((capture == 0) && (capcount > 0)) {
136 msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled.");
140 /* We always use capture so that ovector can be used as working space
141 * and no memory has to be allocated for any backreferences.
143 rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg);
145 *error_msg = apr_psprintf(msr->mp, "Regex execution failed: %s", my_error_msg);
149 /* Handle captured subexpressions. */
150 if (capture && rc > 0) {
153 /* Use the available captures. */
154 for(i = 0; i < rc; i++) {
155 msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
156 if (s == NULL) return -1;
157 s->name = apr_psprintf(msr->mp, "%d", i);
158 s->value = apr_pstrmemdup(msr->mp,
159 target + ovector[2*i], ovector[2*i + 1] - ovector[2*i]);
160 s->value_len = (ovector[2*i + 1] - ovector[2*i]);
161 if ((s->name == NULL)||(s->value == NULL)) return -1;
162 apr_table_setn(msr->tx_vars, s->name, (void *)s);
163 if (msr->txcfg->debuglog_level >= 9) {
164 msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
165 log_escape_nq_ex(msr->mp, s->value, s->value_len));
169 /* Unset the remaining ones (from previous invocations). */
170 for(i = rc; i <= 9; i++) {
172 apr_snprintf(buf, sizeof(buf), "%d", i);
173 apr_table_unset(msr->tx_vars, buf);
177 if (rc != PCRE_ERROR_NOMATCH) { /* Match. */
178 /* We no longer escape the pattern here as it is done when logging */
179 char *pattern = apr_pstrdup(msr->mp, regex->pattern);
181 /* This message will be logged. */
182 if (strlen(pattern) > 252) {
183 *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.",
186 *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.",
199 static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) {
204 if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) {
205 *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'.");
206 return 0; /* ERROR */
209 p = acmp_create(0, rule->ruleset->mp);
210 if (p == NULL) return 0;
212 phrase = apr_pstrdup(rule->ruleset->mp, rule->op_param);
214 /* Loop through phrases */
215 /* ENH: Need to allow quoted phrases w/space */
217 while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++;
218 if (*phrase == '\0') break;
220 while((apr_isspace(*next) == 0) && (*next != 0)) next++;
221 acmp_add_pattern(p, phrase, NULL, NULL, next - phrase);
225 rule->op_param_data = p;
231 static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) {
233 char buf[HUGE_STRING_LEN + 1];
237 const char *rulefile_path;
242 if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) {
243 *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pmFromFile'.");
244 return 0; /* ERROR */
247 p = acmp_create(0, rule->ruleset->mp);
248 if (p == NULL) return 0;
250 fn = apr_pstrdup(rule->ruleset->mp, rule->op_param);
252 /* Get the path of the rule filename to use as a base */
253 rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename)));
256 fprintf(stderr, "Rulefile path: \"%s\"\n", rulefile_path);
259 /* Loop through filenames */
260 /* ENH: Need to allow quoted filenames w/space */
262 const char *rootpath = NULL;
263 const char *filepath = NULL;
266 /* Trim whitespace */
267 while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++;
268 if (*fn == '\0') break;
270 while((apr_isspace(*next) == 0) && (*next != '\0')) next++;
271 while((apr_isspace(*next) != 0) && (*next != '\0')) *(next++) = '\0';
273 /* Add path of the rule filename for a relative phrase filename */
275 if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) {
276 /* We are not an absolute path. It could mean an error, but
277 * let that pass through to the open call for a better error */
278 apr_filepath_merge(&fn, rulefile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp);
281 /* Open file and read */
282 rc = apr_file_open(&fd, fn, APR_READ | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp);
283 if (rc != APR_SUCCESS) {
284 *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open phrase file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024));
289 fprintf(stderr, "Loading phrase file: \"%s\"\n", fn);
292 /* Read one pattern per line skipping empty/commented */
295 rc = apr_file_gets(buf, HUGE_STRING_LEN, fd);
296 if (rc == APR_EOF) break;
297 if (rc != APR_SUCCESS) {
298 *error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024));
304 while(*ptr != '\0') ptr++;
305 if ((ptr > buf) && (*(ptr - 1) == '\n')) *(ptr - 1) = '\0';
307 /* Ignore empty lines and comments */
309 while((*ptr != '\0') && apr_isspace(*ptr)) ptr++;
310 if ((*ptr == '\0') || (*ptr == '#')) continue;
313 fprintf(stderr, "Adding phrase file pattern: \"%s\"\n", buf);
316 acmp_add_pattern(p, buf, NULL, NULL, strlen(buf));
320 if (fd != NULL) apr_file_close(fd);
322 rule->op_param_data = p;
326 static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
327 const char *match = NULL;
332 /* Nothing to read */
333 if ((var->value == NULL) || (var->value_len == 0)) return 0;
335 /* Are we supposed to capture subexpressions? */
336 capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
338 pt.parser = (ACMP *)rule->op_param_data;
341 rc = acmp_process_quick(&pt, &match, var->value, var->value_len);
343 char *match_escaped = log_escape(msr->mp, match ? match : "<Unknown Match>");
345 /* This message will be logged. */
346 if (strlen(match_escaped) > 252) {
347 *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.",
348 match_escaped, var->name);
350 *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.",
351 match_escaped, var->name);
354 /* Handle capture as tx.0=match */
357 msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
359 if (s == NULL) return -1;
362 s->value = apr_pstrdup(msr->mp, match);
363 if (s->value == NULL) return -1;
364 s->value_len = strlen(s->value);
365 apr_table_setn(msr->tx_vars, s->name, (void *)s);
367 if (msr->txcfg->debuglog_level >= 9) {
368 msr_log(msr, 9, "Added phrase match to TX.0: %s",
369 log_escape_nq_ex(msr->mp, s->value, s->value_len));
372 /* Unset the remaining ones (from previous invocations). */
373 for(i = rc; i <= 9; i++) {
375 apr_snprintf(buf, sizeof(buf), "%d", i);
376 apr_table_unset(msr->tx_vars, buf);
387 static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
388 msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
389 const char *match = NULL;
391 unsigned int match_length;
392 unsigned int target_length = 0;
393 unsigned int i, i_max;
395 str->value = (char *)rule->op_param;
396 str->value_len = strlen(str->value);
398 if (error_msg == NULL) return -1;
401 if (str->value == NULL) {
402 *error_msg = "Internal Error: match string is null.";
406 expand_macros(msr, str, rule, msr->mp);
408 match = (const char *)str->value;
409 match_length = str->value_len;
411 /* If the given target is null we give up without a match */
412 if (var->value == NULL) {
418 target_length = var->value_len;
420 /* The empty string always matches */
421 if (target_length == 0) {
423 *error_msg = apr_psprintf(msr->mp, "String match within \"\" at %s.",
428 /* This is impossible to match */
429 if (target_length > match_length) {
434 /* scan for first character, then compare from there until we
435 * have a match or there is no room left in the target
437 i_max = match_length - target_length;
438 for (i = 0; i <= i_max; i++) {
439 if (match[i] == target[0]) {
440 if (memcmp((target + 1), (match + i + 1), (target_length - 1)) == 0) {
442 *error_msg = apr_psprintf(msr->mp, "String match within \"%s\" at %s.",
443 log_escape_ex(msr->mp, match, match_length),
456 static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
457 msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
458 const char *match = NULL;
460 unsigned int match_length;
461 unsigned int target_length = 0;
462 unsigned int i, i_max;
464 str->value = (char *)rule->op_param;
465 str->value_len = strlen(str->value);
467 if (error_msg == NULL) return -1;
470 if (str->value == NULL) {
471 *error_msg = "Internal Error: match string is null.";
475 expand_macros(msr, str, rule, msr->mp);
477 match = (const char *)str->value;
478 match_length = str->value_len;
480 /* If the given target is null run against an empty
481 * string. This is a behaviour consistent with previous
484 if (var->value == NULL) {
489 target_length = var->value_len;
492 /* The empty string always matches */
493 if (match_length == 0) {
495 *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
499 /* This is impossible to match */
500 if (match_length > target_length) {
505 /* scan for first character, then compare from there until we
506 * have a match or there is no room left in the target
508 i_max = target_length - match_length;
509 for (i = 0; i <= i_max; i++) {
510 /* First character matched - avoid func call */
511 if (target[i] == match[0]) {
512 /* See if remaining matches */
513 if ( (match_length == 1)
514 || (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0))
517 *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.",
518 log_escape_ex(msr->mp, match, match_length),
531 static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
532 msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
533 const char *match = NULL;
535 unsigned int match_length;
536 unsigned int target_length = 0;
537 unsigned int i, i_max;
540 str->value = (char *)rule->op_param;
541 str->value_len = strlen(str->value);
543 if (error_msg == NULL) return -1;
546 if (str->value == NULL) {
547 *error_msg = "Internal Error: match string is null.";
551 expand_macros(msr, str, rule, msr->mp);
553 match = (const char *)str->value;
554 match_length = str->value_len;
556 /* If the given target is null run against an empty
557 * string. This is a behaviour consistent with previous
560 if (var->value == NULL) {
565 target_length = var->value_len;
568 /* The empty string always matches */
569 if (match_length == 0) {
571 *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
575 /* This is impossible to match */
576 if (match_length > target_length) {
581 /* scan for first character, then compare from there until we
582 * have a match or there is no room left in the target
584 i_max = target_length - match_length;
585 for (i = 0; i <= i_max; i++) {
587 /* Previous char must have been a start or non-word */
588 if ((i > 0) && (apr_isalnum(target[i-1])||(target[i-1] == '_')))
591 /* First character matched - avoid func call */
592 if (target[i] == match[0]) {
593 /* See if remaining matches */
594 if ( (match_length == 1)
595 || (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0))
597 /* check boundaries */
599 /* exact/end word match */
602 else if (!(apr_isalnum(target[i + match_length])||(target[i + match_length] == '_'))) {
603 /* start/mid word match */
612 *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.",
613 log_escape_ex(msr->mp, match, match_length),
625 static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
626 msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
627 const char *match = NULL;
629 unsigned int match_length;
630 unsigned int target_length;
632 str->value = (char *)rule->op_param;
633 str->value_len = strlen(str->value);
635 if (error_msg == NULL) return -1;
638 if (str->value == NULL) {
639 *error_msg = "Internal Error: match string is null.";
643 expand_macros(msr, str, rule, msr->mp);
645 match = (const char *)str->value;
646 match_length = str->value_len;
648 /* If the given target is null run against an empty
649 * string. This is a behaviour consistent with previous
652 if (var->value == NULL) {
657 target_length = var->value_len;
660 /* These are impossible to match */
661 if (match_length != target_length) {
666 if (memcmp(match, target, target_length) == 0) {
668 *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.",
669 log_escape_ex(msr->mp, match, match_length),
680 static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
681 msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
682 const char *match = NULL;
684 unsigned int match_length;
685 unsigned int target_length;
687 str->value = (char *)rule->op_param;
688 str->value_len = strlen(str->value);
690 if (error_msg == NULL) return -1;
693 if (str->value == NULL) {
694 *error_msg = "Internal Error: match string is null.";
698 expand_macros(msr, str, rule, msr->mp);
700 match = (const char *)str->value;
701 match_length = str->value_len;
703 /* If the given target is null run against an empty
704 * string. This is a behaviour consistent with previous
707 if (var->value == NULL) {
712 target_length = var->value_len;
715 /* The empty string always matches */
716 if (match_length == 0) {
718 *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
722 /* This is impossible to match */
723 if (match_length > target_length) {
728 if (memcmp(match, target, match_length) == 0) {
730 *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.",
731 log_escape_ex(msr->mp, match, match_length),
742 static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
743 msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
744 const char *match = NULL;
746 unsigned int match_length;
747 unsigned int target_length;
749 str->value = (char *)rule->op_param;
750 str->value_len = strlen(str->value);
752 if (error_msg == NULL) return -1;
755 if (str->value == NULL) {
756 *error_msg = "Internal Error: match string is null.";
760 expand_macros(msr, str, rule, msr->mp);
762 match = (const char *)str->value;
763 match_length = str->value_len;
765 /* If the given target is null run against an empty
766 * string. This is a behaviour consistent with previous
769 if (var->value == NULL) {
774 target_length = var->value_len;
777 /* The empty string always matches */
778 if (match_length == 0) {
780 *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
784 /* This is impossible to match */
785 if (match_length > target_length) {
790 if (memcmp(match, (target + (target_length - match_length)), match_length) == 0) {
792 *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.",
793 log_escape_ex(msr->mp, match, match_length),
804 static int msre_op_m_param_init(msre_rule *rule, char **error_msg) {
805 const apr_strmatch_pattern *compiled_pattern;
806 const char *pattern = rule->op_param;
808 if (error_msg == NULL) return -1;
811 /* Compile pattern */
812 compiled_pattern = apr_strmatch_precompile(rule->ruleset->mp, pattern, 1);
813 if (compiled_pattern == NULL) {
814 *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern: %s", pattern);
818 rule->op_param_data = (void *)compiled_pattern;
823 static int msre_op_m_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
824 apr_strmatch_pattern *compiled_pattern = (apr_strmatch_pattern *)rule->op_param_data;
826 unsigned int target_length;
829 if (error_msg == NULL) return -1;
832 if (compiled_pattern == NULL) {
833 *error_msg = "Internal Error: strnmatch data is null.";
837 /* If the given target is null run against an empty
838 * string. This is a behaviour consistent with previous
841 if (var->value == NULL) {
846 target_length = var->value_len;
849 rc = apr_strmatch(compiled_pattern, target, target_length);
855 *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.",
856 log_escape(msr->mp, rule->op_param), var->name);
864 static int msre_op_validateDTD_init(msre_rule *rule, char **error_msg) {
865 /* ENH Verify here the file actually exists. */
869 static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
875 if ((msr->xml == NULL)||(msr->xml->doc == NULL)) {
876 *error_msg = apr_psprintf(msr->mp,
877 "XML document tree could not be found for DTD validation.");
881 if (msr->xml->well_formed != 1) {
882 *error_msg = apr_psprintf(msr->mp,
883 "XML: DTD validation failed because content is not well formed.");
887 /* Make sure there were no other generic processing errors */
888 if (msr->msc_reqbody_error) {
889 *error_msg = apr_psprintf(msr->mp,
890 "XML: DTD validation could not proceed due to previous"
891 " processing errors.");
895 dtd = xmlParseDTD(NULL, (const xmlChar *)rule->op_param); /* EHN support relative filenames */
897 *error_msg = apr_psprintf(msr->mp, "XML: Failed to load DTD: %s", rule->op_param);
901 cvp = xmlNewValidCtxt();
903 *error_msg = "XML: Failed to create a validation context.";
908 /* Send validator errors/warnings to msr_log */
909 /* NOTE: No xmlDtdSetValidErrors()? */
910 cvp->error = (xmlSchemaValidityErrorFunc)msr_log_error;
911 cvp->warning = (xmlSchemaValidityErrorFunc)msr_log_warn;
914 if (!xmlValidateDtd(cvp, msr->xml->doc, dtd)) {
915 *error_msg = "XML: DTD validation failed.";
916 xmlFreeValidCtxt(cvp);
918 return 1; /* No match. */
921 msr_log(msr, 4, "XML: Successfully validated payload against DTD: %s", rule->op_param);
923 xmlFreeValidCtxt(cvp);
932 static int msre_op_validateSchema_init(msre_rule *rule, char **error_msg) {
933 /* ENH Verify here the file actually exists. */
937 static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
940 xmlSchemaParserCtxtPtr parserCtx;
941 xmlSchemaValidCtxtPtr validCtx;
945 if ((msr->xml == NULL)||(msr->xml->doc == NULL)) {
946 *error_msg = apr_psprintf(msr->mp,
947 "XML document tree could not be found for schema validation.");
951 if (msr->xml->well_formed != 1) {
952 *error_msg = apr_psprintf(msr->mp,
953 "XML: Schema validation failed because content is not well formed.");
957 /* Make sure there were no other generic processing errors */
958 if (msr->msc_reqbody_error) {
959 *error_msg = apr_psprintf(msr->mp,
960 "XML: Schema validation could not proceed due to previous"
961 " processing errors.");
965 parserCtx = xmlSchemaNewParserCtxt(rule->op_param); /* ENH support relative filenames */
966 if (parserCtx == NULL) {
967 *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema from file: %s",
972 /* Send parser errors/warnings to msr_log */
973 xmlSchemaSetParserErrors(parserCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr);
975 schema = xmlSchemaParse(parserCtx);
976 if (schema == NULL) {
977 *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema: %s", rule->op_param);
978 xmlSchemaFreeParserCtxt(parserCtx);
982 validCtx = xmlSchemaNewValidCtxt(schema);
983 if (validCtx == NULL) {
984 *error_msg = "XML: Failed to create validation context.";
985 xmlSchemaFree(schema);
986 xmlSchemaFreeParserCtxt(parserCtx);
990 /* Send validator errors/warnings to msr_log */
991 xmlSchemaSetValidErrors(validCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr);
993 rc = xmlSchemaValidateDoc(validCtx, msr->xml->doc);
995 *error_msg = "XML: Schema validation failed.";
996 xmlSchemaFree(schema);
997 xmlSchemaFreeParserCtxt(parserCtx);
998 return 1; /* No match. */
1001 msr_log(msr, 4, "XML: Successfully validated payload against Schema: %s", rule->op_param);
1003 xmlSchemaFree(schema);
1004 xmlSchemaFreeValidCtxt(validCtx);
1012 * Luhn Mod-10 Method (ISO 2894/ANSI 4.13)
1014 static int luhn_verify(const char *ccnumber, int len) {
1015 int sum[2] = { 0, 0 };
1020 /* Weighted lookup table which is just a precalculated (i = index):
1021 * i*2 + (( (i*2) > 9 ) ? -9 : 0)
1023 static int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; /* weight lookup table */
1025 /* Add up only digits (weighted digits via lookup table)
1026 * for both odd and even CC numbers to avoid 2 passes.
1028 for (i = 0; i < len; i++) {
1029 if (apr_isdigit(ccnumber[i])) {
1030 sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0'));
1031 sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0'));
1032 odd = 1 - odd; /* alternate weights */
1037 /* No digits extracted */
1038 if (digits == 0) return 0;
1040 /* Do a mod 10 on the sum */
1043 /* If the result is a zero the card is valid. */
1044 return sum[odd] ? 0 : 1;
1047 static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) {
1048 const char *errptr = NULL;
1052 if (error_msg == NULL) return -1;
1055 /* Compile rule->op_param */
1056 regex = msc_pregcomp(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset);
1057 if (regex == NULL) {
1058 *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
1063 rule->op_param_data = regex;
1068 static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
1069 msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
1071 unsigned int target_length;
1072 char *my_error_msg = NULL;
1078 if (error_msg == NULL) return -1;
1081 if (regex == NULL) {
1082 *error_msg = "Internal Error: regex data is null.";
1086 memset(ovector, 0, sizeof(ovector));
1088 /* If the given target is null run against an empty
1089 * string. This is a behaviour consistent with previous
1092 if (var->value == NULL) {
1096 target = var->value;
1097 target_length = var->value_len;
1100 for (offset = 0; ((unsigned int)offset < target_length) && (is_cc == 0); offset++) {
1101 if (msr->txcfg->debuglog_level >= 9) {
1103 msr_log(msr, 9, "Continuing CC# search at target offset %d.", offset);
1107 rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg);
1109 /* If there was no match, then we are done. */
1110 if (rc == PCRE_ERROR_NOMATCH) {
1115 *error_msg = apr_psprintf(msr->mp, "CC# regex execution failed: %s", my_error_msg);
1119 /* Verify a match. */
1121 const char *match = target + ovector[0];
1122 int length = ovector[1] - ovector[0];
1125 offset = ovector[2*i];
1127 /* Check the Luhn using the match string */
1128 is_cc = luhn_verify(match, length);
1130 /* Not a CC number, then try another match where we left off. */
1132 if (msr->txcfg->debuglog_level >= 9) {
1133 msr_log(msr, 9, "CC# Luhn check failed at target offset %d: \"%.*s\"", offset, length, match);
1139 /* We have a potential CC number and need to set any captures
1143 if (apr_table_get(rule->actionset->actions, "capture")) {
1144 for(; i < rc; i++) {
1145 msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1146 if (s == NULL) return -1;
1147 s->name = apr_psprintf(msr->mp, "%d", i);
1148 s->value = apr_pstrmemdup(msr->mp, match, length);
1149 s->value_len = length;
1150 if ((s->name == NULL)||(s->value == NULL)) return -1;
1152 apr_table_setn(msr->tx_vars, s->name, (void *)s);
1154 if (msr->txcfg->debuglog_level >= 9) {
1155 msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
1156 log_escape_nq_ex(msr->mp, s->value, s->value_len));
1161 /* Unset the remaining TX vars (from previous invocations). */
1162 for(; i <= 9; i++) {
1164 apr_snprintf(buf, sizeof(buf), "%i", i);
1165 apr_table_unset(msr->tx_vars, buf);
1175 /* This message will be logged. */
1176 *error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s. [offset \"%d\"]",
1177 regex->pattern, var->name, offset);
1188 * Perform geograpical lookups on an IP/Host.
1190 static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1194 geo_db *geo = msr->txcfg->geo;
1195 const char *geo_host = var->value;
1196 msc_string *s = NULL;
1202 msr_log(msr, 1, "Geo lookup for \"%s\" attempted without a database. Set SecGeoLookupDB.", log_escape(msr->mp, geo_host));
1207 rc = geo_lookup(msr, &rec, geo_host, error_msg);
1210 *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed at %s.", log_escape_nq(msr->mp, geo_host), var->name);
1212 apr_table_clear(msr->geo_vars);
1216 *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded at %s.",
1217 log_escape_nq(msr->mp, geo_host), var->name);
1220 if (msr->txcfg->debuglog_level >= 9) {
1221 msr_log(msr, 9, "GEO: %s={country_code=%s, country_code3=%s, country_name=%s, country_continent=%s, region=%s, city=%s, postal_code=%s, latitude=%f, longitude=%f, dma_code=%d, area_code=%d}",
1226 rec.country_continent,
1236 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1237 s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE");
1238 s->name_len = strlen(s->name);
1239 s->value = apr_pstrdup(msr->mp, rec.country_code ? rec.country_code : "");
1240 s->value_len = strlen(s->value);
1241 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1243 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1244 s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE3");
1245 s->name_len = strlen(s->name);
1246 s->value = apr_pstrdup(msr->mp, rec.country_code3 ? rec.country_code3 : "");
1247 s->value_len = strlen(s->value);
1248 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1250 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1251 s->name = apr_pstrdup(msr->mp, "COUNTRY_NAME");
1252 s->name_len = strlen(s->name);
1253 s->value = apr_pstrdup(msr->mp, rec.country_name ? rec.country_name : "");
1254 s->value_len = strlen(s->value);
1255 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1257 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1258 s->name = apr_pstrdup(msr->mp, "COUNTRY_CONTINENT");
1259 s->name_len = strlen(s->name);
1260 s->value = apr_pstrdup(msr->mp, rec.country_continent ? rec.country_continent : "");
1261 s->value_len = strlen(s->value);
1262 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1264 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1265 s->name = apr_pstrdup(msr->mp, "REGION");
1266 s->name_len = strlen(s->name);
1267 s->value = apr_pstrdup(msr->mp, rec.region ? rec.region : "");
1268 s->value_len = strlen(s->value);
1269 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1271 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1272 s->name = apr_pstrdup(msr->mp, "CITY");
1273 s->name_len = strlen(s->name);
1274 s->value = apr_pstrdup(msr->mp, rec.city ? rec.city : "");
1275 s->value_len = strlen(s->value);
1276 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1278 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1279 s->name = apr_pstrdup(msr->mp, "POSTAL_CODE");
1280 s->name_len = strlen(s->name);
1281 s->value = apr_pstrdup(msr->mp, rec.postal_code ? rec.postal_code : "");
1282 s->value_len = strlen(s->value);
1283 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1285 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1286 s->name = apr_pstrdup(msr->mp, "LATITUDE");
1287 s->name_len = strlen(s->name);
1288 s->value = apr_psprintf(msr->mp, "%f", rec.latitude);
1289 s->value_len = strlen(s->value);
1290 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1292 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1293 s->name = apr_pstrdup(msr->mp, "LONGITUDE");
1294 s->name_len = strlen(s->name);
1295 s->value = apr_psprintf(msr->mp, "%f", rec.longitude);
1296 s->value_len = strlen(s->value);
1297 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1299 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1300 s->name = apr_pstrdup(msr->mp, "DMA_CODE");
1301 s->name_len = strlen(s->name);
1302 s->value = apr_psprintf(msr->mp, "%d", rec.dma_code);
1303 s->value_len = strlen(s->value);
1304 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1306 s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1307 s->name = apr_pstrdup(msr->mp, "AREA_CODE");
1308 s->name_len = strlen(s->name);
1309 s->value = apr_psprintf(msr->mp, "%d", rec.area_code);
1310 s->value_len = strlen(s->value);
1311 apr_table_setn(msr->geo_vars, s->name, (void *)s);
1318 static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
1319 unsigned int h0, h1, h2, h3;
1320 char *name_to_check = NULL;
1321 char *target = NULL;
1322 apr_sockaddr_t *sa = NULL;
1325 if (error_msg == NULL) return -1;
1328 /* ENH Add IPv6 support. */
1330 target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1331 if (target == NULL) return -1;
1333 /* Construct the host name we want to resolve. */
1334 if (sscanf(target, "%d.%d.%d.%d", &h0, &h1, &h2, &h3) == 4) {
1336 name_to_check = apr_psprintf(msr->mp, "%d.%d.%d.%d.%s", h3, h2, h1, h0, rule->op_param);
1338 /* Assume the input is a domain name. */
1339 name_to_check = apr_psprintf(msr->mp, "%s.%s", target, rule->op_param);
1342 if (name_to_check == NULL) return -1;
1344 rc = apr_sockaddr_info_get(&sa, name_to_check,
1345 APR_UNSPEC/*msr->r->connection->remote_addr->family*/, 0, 0, msr->mp);
1346 if (rc == APR_SUCCESS) {
1347 *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s.",
1348 log_escape_nq(msr->mp, name_to_check), var->name);
1349 return 1; /* Match. */
1352 msr_log(msr, 5, "RBL lookup of %s failed at %s.", log_escape_nq(msr->mp, name_to_check), var->name);
1360 static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) {
1361 char *filename = (char *)rule->op_param;
1363 if (error_msg == NULL) return -1;
1366 if ((filename == NULL)||(is_empty_string(filename))) {
1367 *error_msg = apr_psprintf(rule->ruleset->mp, "Operator @inspectFile requires parameter.");
1371 filename = resolve_relative_path(rule->ruleset->mp, rule->filename, filename);
1373 #if defined(WITH_LUA)
1374 /* ENH Write & use string_ends(s, e). */
1375 if (strlen(rule->op_param) > 4) {
1376 char *p = filename + strlen(filename) - 4;
1377 if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a'))
1379 msc_script *script = NULL;
1381 /* Compile script. */
1382 *error_msg = lua_compile(&script, filename, rule->ruleset->mp);
1383 if (*error_msg != NULL) return -1;
1385 rule->op_param_data = script;
1390 if (rule->op_param_data == NULL) {
1391 /* ENH Verify the script exists and that we have
1392 * the rights to execute it.
1399 static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1402 if (error_msg == NULL) return -1;
1405 if (rule->op_param_data == NULL) {
1406 /* Execute externally, as native binary/shell script. */
1407 char *script_output = NULL;
1408 char const *argv[5];
1409 const char *approver_script = rule->op_param;
1410 const char *target_file = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1412 msr_log(msr, 4, "Executing %s to inspect %s.", approver_script, target_file);
1414 argv[0] = approver_script;
1415 argv[1] = target_file;
1418 if (apache2_exec(msr, approver_script, (const char **)argv, &script_output) <= 0) {
1419 *error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (invocation failed).",
1420 log_escape(msr->mp, approver_script));
1424 if (script_output == NULL) {
1425 *error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (no output).",
1426 log_escape(msr->mp, approver_script));
1430 if (script_output[0] != '1') {
1431 *error_msg = apr_psprintf(msr->mp, "File \"%s\" rejected by the approver script \"%s\": %s",
1432 log_escape(msr->mp, target_file), log_escape(msr->mp, approver_script),
1433 log_escape_nq(msr->mp, script_output));
1434 return 1; /* Match. */
1437 #if defined(WITH_LUA)
1439 /* Execute internally, as Lua script. */
1440 char *target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1441 msc_script *script = (msc_script *)rule->op_param_data;
1444 rc = lua_execute(script, target, msr, rule, error_msg);
1458 /* validateByteRange */
1460 static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) {
1461 char *p = NULL, *saveptr = NULL;
1462 char *table = NULL, *data = NULL;
1464 if (error_msg == NULL) return -1;
1467 if (rule->op_param == NULL) {
1468 *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for validateByteRange.");
1473 data = apr_pstrdup(rule->ruleset->mp, rule->op_param);
1474 rule->op_param_data = apr_pcalloc(rule->ruleset->mp, 32);
1475 if ((data == NULL)||(rule->op_param_data == NULL)) return -1;
1476 table = rule->op_param_data;
1478 /* Extract parameters and update table. */
1479 p = apr_strtok(data, ",", &saveptr);
1481 char *s = strstr(p, "-");
1485 if ((x < 0)||(x > 255)) {
1486 *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range value: %d", x);
1489 table[x>>3] = (table[x>>3] | (1 << (x & 0x7)));
1492 int start = atoi(p);
1493 int end = atoi(s + 1);
1495 if ((start < 0)||(start > 255)) {
1496 *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range start value: %d",
1500 if ((end < 0)||(end > 255)) {
1501 *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range end value: %d", end);
1505 *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range: %d-%d", start, end);
1509 while(start <= end) {
1510 table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7)));
1515 p = apr_strtok(NULL, ",", &saveptr);
1521 static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1524 char *table = rule->op_param_data;
1525 unsigned int i, count;
1527 if (error_msg == NULL) return -1;
1530 if (table == NULL) {
1531 *error_msg = apr_psprintf(msr->mp, "Internal Error: validateByteRange table not "
1536 /* Check every byte of the target to detect characters that are not allowed. */
1539 for(i = 0; i < var->value_len; i++) {
1540 int x = ((unsigned char *)var->value)[i];
1541 if (!(table[x >> 3] & (1 << (x & 0x7)))) {
1542 if (msr->txcfg->debuglog_level >= 9) {
1543 msr_log(msr, 9, "Value %d in %s outside range: %s", x, var->name, rule->op_param);
1549 if (count == 0) return 0; /* Valid - no match. */
1551 *error_msg = apr_psprintf(msr->mp, "Found %d byte(s) in %s outside range: %s.",
1552 count, var->name, rule->op_param);
1554 return 1; /* Invalid - match.*/
1557 /* validateUrlEncoding */
1559 static int validate_url_encoding(const char *input, long int input_length) {
1562 if ((input == NULL)||(input_length < 0)) return -1;
1565 while (i < input_length) {
1566 if (input[i] == '%') {
1567 if (i + 2 >= input_length) {
1568 /* Not enough bytes. */
1572 /* Here we only decode a %xx combination if it is valid,
1573 * leaving it as is otherwise.
1575 char c1 = input[i + 1];
1576 char c2 = input[i + 2];
1578 if ( (((c1 >= '0')&&(c1 <= '9')) || ((c1 >= 'a')&&(c1 <= 'f')) || ((c1 >= 'A')&&(c1 <= 'F')))
1579 && (((c2 >= '0')&&(c2 <= '9')) || ((c2 >= 'a')&&(c2 <= 'f')) || ((c2 >= 'A')&&(c2 <= 'F'))) )
1583 /* Non-hexadecimal characters used in encoding. */
1595 static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1598 int rc = validate_url_encoding(var->value, var->value_len);
1601 /* Encoding is valid */
1602 *error_msg = apr_psprintf(msr->mp, "Valid URL Encoding at %s.", var->name);
1605 *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Non-hexadecimal "
1606 "digits used at %s.", var->name);
1607 return 1; /* Invalid match. */
1610 *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Not enough characters "
1611 "at the end of input at %s.", var->name);
1612 return 1; /* Invalid match. */
1616 *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Internal Error (rc = %d) at %s", rc, var->name);
1626 /* validateUtf8Encoding */
1628 #define UNICODE_ERROR_CHARACTERS_MISSING -1
1629 #define UNICODE_ERROR_INVALID_ENCODING -2
1630 #define UNICODE_ERROR_OVERLONG_CHARACTER -3
1631 #define UNICODE_ERROR_RESTRICTED_CHARACTER -4
1632 #define UNICODE_ERROR_DECODING_ERROR -5
1634 /* NOTE: This is over-commented for ease of verification */
1635 static int detect_utf8_character(const unsigned char *p_read, unsigned int length) {
1636 int unicode_len = 0;
1640 if (p_read == NULL) return UNICODE_ERROR_DECODING_ERROR;
1643 /* If first byte begins with binary 0 it is single byte encoding */
1644 if ((c & 0x80) == 0) {
1645 /* single byte unicode (7 bit ASCII equivilent) has no validation */
1648 /* If first byte begins with binary 110 it is two byte encoding*/
1649 else if ((c & 0xE0) == 0xC0) {
1650 /* check we have at least two bytes */
1651 if (length < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
1652 /* check second byte starts with binary 10 */
1653 else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
1656 /* compute character number */
1657 d = ((c & 0x1F) << 6) | (*(p_read + 1) & 0x3F);
1660 /* If first byte begins with binary 1110 it is three byte encoding */
1661 else if ((c & 0xF0) == 0xE0) {
1662 /* check we have at least three bytes */
1663 if (length < 3) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
1664 /* check second byte starts with binary 10 */
1665 else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
1666 /* check third byte starts with binary 10 */
1667 else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
1670 /* compute character number */
1671 d = ((c & 0x0F) << 12) | ((*(p_read + 1) & 0x3F) << 6) | (*(p_read + 2) & 0x3F);
1674 /* If first byte begins with binary 11110 it is four byte encoding */
1675 else if ((c & 0xF8) == 0xF0) {
1676 /* restrict characters to UTF-8 range (U+0000 - U+10FFFF)*/
1678 return UNICODE_ERROR_RESTRICTED_CHARACTER;
1680 /* check we have at least four bytes */
1681 if (length < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
1682 /* check second byte starts with binary 10 */
1683 else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
1684 /* check third byte starts with binary 10 */
1685 else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
1686 /* check forth byte starts with binary 10 */
1687 else if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
1690 /* compute character number */
1691 d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F);
1694 /* any other first byte is invalid (RFC 3629) */
1696 return UNICODE_ERROR_INVALID_ENCODING;
1699 /* invalid UTF-8 character number range (RFC 3629) */
1700 if ((d >= 0xD800) && (d <= 0xDFFF)) {
1701 return UNICODE_ERROR_RESTRICTED_CHARACTER;
1704 /* check for overlong */
1705 if ((unicode_len == 4) && (d < 0x010000)) {
1706 /* four byte could be represented with less bytes */
1707 return UNICODE_ERROR_OVERLONG_CHARACTER;
1709 else if ((unicode_len == 3) && (d < 0x0800)) {
1710 /* three byte could be represented with less bytes */
1711 return UNICODE_ERROR_OVERLONG_CHARACTER;
1713 else if ((unicode_len == 2) && (d < 0x80)) {
1714 /* two byte could be represented with less bytes */
1715 return UNICODE_ERROR_OVERLONG_CHARACTER;
1721 static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1724 unsigned int i, bytes_left;
1726 bytes_left = var->value_len;
1728 for(i = 0; i < var->value_len;) {
1729 int rc = detect_utf8_character((unsigned char *)&var->value[i], bytes_left);
1732 case UNICODE_ERROR_CHARACTERS_MISSING :
1733 *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
1734 "not enough bytes in character "
1735 "at %s. [offset \"%d\"]", var->name, i);
1738 case UNICODE_ERROR_INVALID_ENCODING :
1739 *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
1740 "invalid byte value in character "
1741 "at %s. [offset \"%d\"]", var->name, i);
1744 case UNICODE_ERROR_OVERLONG_CHARACTER :
1745 *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
1746 "overlong character detected "
1747 "at %s. [offset \"%d\"]", var->name, i);
1750 case UNICODE_ERROR_RESTRICTED_CHARACTER :
1751 *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
1752 "use of restricted character "
1753 "at %s. [offset \"%d\"]", var->name, i);
1756 case UNICODE_ERROR_DECODING_ERROR :
1757 *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding "
1758 "at %s. [offset \"%d\"]", var->name, i);
1764 *error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation "
1765 "at %s.", var->name);
1778 static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1782 char *target = NULL;
1784 if ((var->value == NULL)||(rule->op_param == NULL)) {
1785 /* NULL values do not match anything. */
1789 target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1790 if (target == NULL) return -1;
1791 left = atoi(target);
1792 right = atoi(rule->op_param);
1794 if (left != right) {
1799 *error_msg = apr_psprintf(msr->mp, "Operator EQ matched %d at %s.", right, var->name);
1807 static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1811 char *target = NULL;
1813 if ((var->value == NULL)||(rule->op_param == NULL)) {
1814 /* NULL values do not match anything. */
1818 target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1819 if (target == NULL) return -1;
1820 left = atoi(target);
1821 right = atoi(rule->op_param);
1823 if (left <= right) {
1828 *error_msg = apr_psprintf(msr->mp, "Operator GT matched %d at %s.", right, var->name);
1836 static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1840 char *target = NULL;
1842 if ((var->value == NULL)||(rule->op_param == NULL)) {
1843 /* NULL values do not match anything. */
1847 target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1848 if (target == NULL) return -1;
1849 left = atoi(target);
1850 right = atoi(rule->op_param);
1852 if (left >= right) {
1857 *error_msg = apr_psprintf(msr->mp, "Operator LT matched %d at %s.", right, var->name);
1865 static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1869 char *target = NULL;
1871 if ((var->value == NULL)||(rule->op_param == NULL)) {
1872 /* NULL values do not match anything. */
1876 target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1877 if (target == NULL) return -1;
1878 left = atoi(target);
1879 right = atoi(rule->op_param);
1886 *error_msg = apr_psprintf(msr->mp, "Operator GE matched %d at %s.", right, var->name);
1894 static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
1898 char *target = NULL;
1900 if ((var->value == NULL)||(rule->op_param == NULL)) {
1901 /* NULL values do not match anything. */
1905 target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
1906 if (target == NULL) return -1;
1907 left = atoi(target);
1908 right = atoi(rule->op_param);
1915 *error_msg = apr_psprintf(msr->mp, "Operator LE matched %d at %s.", right, var->name);
1921 /* ------------------------------------------------------------------------------- */
1926 void msre_engine_register_default_operators(msre_engine *engine) {
1927 /* unconditionalMatch */
1928 msre_engine_op_register(engine,
1929 "unconditionalMatch",
1931 msre_op_unconditionalmatch_execute
1935 msre_engine_op_register(engine,
1938 msre_op_nomatch_execute
1942 msre_engine_op_register(engine,
1944 msre_op_rx_param_init,
1949 msre_engine_op_register(engine,
1951 msre_op_pm_param_init,
1956 msre_engine_op_register(engine,
1958 msre_op_pmFromFile_param_init,
1963 msre_engine_op_register(engine,
1965 NULL, /* ENH init function to flag var substitution */
1966 msre_op_within_execute
1970 msre_engine_op_register(engine,
1972 NULL, /* ENH init function to flag var substitution */
1973 msre_op_contains_execute
1977 msre_engine_op_register(engine,
1979 NULL, /* ENH init function to flag var substitution */
1980 msre_op_containsWord_execute
1984 msre_engine_op_register(engine,
1986 NULL, /* ENH init function to flag var substitution */
1987 msre_op_streq_execute
1991 msre_engine_op_register(engine,
1993 NULL, /* ENH init function to flag var substitution */
1994 msre_op_beginsWith_execute
1998 msre_engine_op_register(engine,
2000 NULL, /* ENH init function to flag var substitution */
2001 msre_op_endsWith_execute
2005 msre_engine_op_register(engine,
2007 msre_op_m_param_init,
2012 msre_engine_op_register(engine,
2014 msre_op_validateDTD_init,
2015 msre_op_validateDTD_execute
2018 /* validateSchema */
2019 msre_engine_op_register(engine,
2021 msre_op_validateSchema_init,
2022 msre_op_validateSchema_execute
2026 msre_engine_op_register(engine,
2028 msre_op_verifyCC_init,
2029 msre_op_verifyCC_execute
2033 msre_engine_op_register(engine,
2036 msre_op_geoLookup_execute
2040 msre_engine_op_register(engine,
2042 NULL, /* ENH init function to validate DNS server */
2047 msre_engine_op_register(engine,
2049 msre_op_inspectFile_init,
2050 msre_op_inspectFile_execute
2053 /* validateByteRange */
2054 msre_engine_op_register(engine,
2055 "validateByteRange",
2056 msre_op_validateByteRange_init,
2057 msre_op_validateByteRange_execute
2060 /* validateUrlEncoding */
2061 msre_engine_op_register(engine,
2062 "validateUrlEncoding",
2064 msre_op_validateUrlEncoding_execute
2067 /* validateUtf8Encoding */
2068 msre_engine_op_register(engine,
2069 "validateUtf8Encoding",
2071 msre_op_validateUtf8Encoding_execute
2075 msre_engine_op_register(engine,
2082 msre_engine_op_register(engine,
2089 msre_engine_op_register(engine,
2096 msre_engine_op_register(engine,
2103 msre_engine_op_register(engine,