2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2008 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 * Register action with the engine.
25 static void msre_engine_action_register(msre_engine *engine, const char *name,
26 unsigned int type, unsigned int argc_min, unsigned int argc_max,
27 unsigned int allow_param_plusminus, unsigned int cardinality,
28 unsigned int cardinality_group, fn_action_validate_t validate,
29 fn_action_init_t init, fn_action_execute_t execute)
31 msre_action_metadata *metadata = (msre_action_metadata *)apr_pcalloc(engine->mp,
32 sizeof(msre_action_metadata));
33 if (metadata == NULL) return;
35 metadata->name = name;
36 metadata->type = type;
37 metadata->argc_min = argc_min;
38 metadata->argc_max = argc_max;
39 metadata->allow_param_plusminus = allow_param_plusminus;
40 metadata->cardinality = cardinality;
41 metadata->cardinality_group = cardinality_group;
42 metadata->validate = validate;
43 metadata->init = init;
44 metadata->execute = execute;
46 apr_table_setn(engine->actions, name, (void *)metadata);
50 * Generates a single variable (from the supplied metadata).
52 msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr,
53 msre_rule *rule, apr_pool_t *mptmp)
55 apr_table_t *vartab = NULL;
56 const apr_table_entry_t *te = NULL;
57 const apr_array_header_t *arr = NULL;
58 msre_var *rvar = NULL;
62 if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL;
64 vartab = apr_table_make(mptmp, 16);
65 var->metadata->generate(msr, var, rule, vartab, mptmp);
67 arr = apr_table_elts(vartab);
68 if (arr->nelts == 0) return NULL;
69 te = (apr_table_entry_t *)arr->elts;
71 rvar = (msre_var *)te[0].val;
73 /* Return straight away if there were no
74 * transformation functions supplied.
76 if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) {
80 /* Copy the value so that we can transform it in place. */
81 rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len);
83 /* Transform rvar in a loop. */
84 for (i = 0; i < tfn_arr->nelts; i++) {
85 msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i];
90 rc = tfn->execute(mptmp, (unsigned char *)rvar->value,
91 rvar->value_len, &rval, &rval_len);
94 rvar->value_len = rval_len;
96 if (msr->txcfg->debuglog_level >= 9) {
97 msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name,
98 log_escape_nq_ex(mptmp, rvar->value, rvar->value_len));
108 apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr,
109 msre_rule *rule, apr_pool_t *mptmp)
111 const apr_array_header_t *tarr;
112 const apr_table_entry_t *telts;
113 apr_table_t *vartab = NULL, *tvartab = NULL;
114 msre_var *rvar = NULL;
118 if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL;
120 /* Generate variables. */
121 vartab = apr_table_make(mptmp, 16);
122 var->metadata->generate(msr, var, rule, vartab, mptmp);
124 /* Return straight away if there were no
125 * transformation functions supplied.
127 if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) {
131 tvartab = apr_table_make(mptmp, 16);
133 tarr = apr_table_elts(vartab);
134 telts = (const apr_table_entry_t*)tarr->elts;
135 for (j = 0; j < tarr->nelts; j++) {
136 rvar = (msre_var *)telts[j].val;
138 /* Copy the value so that we can transform it in place. */
139 rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len);
141 /* Transform rvar in a loop. */
142 for (i = 0; i < tfn_arr->nelts; i++) {
143 msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i];
148 rc = tfn->execute(mptmp, (unsigned char *)rvar->value,
149 rvar->value_len, &rval, &rval_len);
152 rvar->value_len = rval_len;
154 if (msr->txcfg->debuglog_level >= 9) {
155 msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name,
156 log_escape_nq_ex(mptmp, rvar->value, rvar->value_len));
160 apr_table_addn(tvartab, rvar->name, (void *)rvar);
167 * Expands macros ("%{NAME}" entities) if present
168 * in the given variable.
170 int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp) {
172 apr_array_header_t *arr = NULL;
173 char *p = NULL, *q = NULL, *t = NULL;
174 char *text_start = NULL, *next_text_start = NULL;
175 msc_string *part = NULL;
178 if (var->value == NULL) return 0;
180 /* IMP1 Duplicate the string and create the array on
181 * demand, thus not having to do it if there are
182 * no macros in the input data.
185 data = apr_pstrdup(mptmp, var->value); /* IMP1 Are we modifying data anywhere? */
186 arr = apr_array_make(mptmp, 16, sizeof(msc_string *));
187 if ((data == NULL)||(arr == NULL)) return -1;
189 text_start = next_text_start = data;
191 text_start = next_text_start;
192 p = strstr(text_start, "%");
194 char *var_name = NULL;
195 char *var_value = NULL;
197 if ((*(p + 1) == '{')&&(*(p + 2) != '\0')) {
198 char *var_start = p + 2;
201 while((*t != '\0')&&(*t != '}')) t++;
203 /* Named variable. */
205 var_name = apr_pstrmemdup(mptmp, var_start, t - var_start);
206 q = strstr(var_name, ".");
212 next_text_start = t + 1; /* *t was '}' */
214 /* Warn about a possiblly forgotten '}' */
215 if (msr->txcfg->debuglog_level >= 9) {
216 msr_log(msr, 9, "Warning: Possibly unterminated macro: \"%s\"",
217 log_escape_ex(mptmp, var_start - 2, t - var_start + 2));
220 next_text_start = t; /* *t was '\0' */
224 if (var_name != NULL) {
225 char *my_error_msg = NULL;
226 msre_var *var_generated = NULL;
227 msre_var *var_resolved = NULL;
229 /* Add the text part before the macro to the array. */
230 part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string));
231 if (part == NULL) return -1;
232 part->value_len = p - text_start;
233 part->value = apr_pstrmemdup(mptmp, text_start, part->value_len);
234 *(msc_string **)apr_array_push(arr) = part;
236 /* Resolve the macro and add that to the array. */
237 var_resolved = msre_create_var_ex(mptmp, msr->modsecurity->msre, var_name, var_value,
239 if (var_resolved != NULL) {
240 var_generated = generate_single_var(msr, var_resolved, NULL, rule, mptmp);
241 if (var_generated != NULL) {
242 part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string));
243 if (part == NULL) return -1;
244 part->value_len = var_generated->value_len;
245 part->value = (char *)var_generated->value;
246 *(msc_string **)apr_array_push(arr) = part;
247 if (msr->txcfg->debuglog_level >= 9) {
248 msr_log(msr, 9, "Resolved macro %%{%s%s%s} to \"%s\"",
250 (var_value ? "." : ""),
251 (var_value ? var_value : ""),
252 log_escape_ex(mptmp, part->value, part->value_len));
256 msr_log(msr, 4, "Failed to resolve macro %%{%s%s%s}: %s",
258 (var_value ? "." : ""),
259 (var_value ? var_value : ""),
263 /* We could not identify a valid macro so add it as text. */
264 part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string));
265 if (part == NULL) return -1;
266 part->value_len = p - text_start + 1; /* len(text)+len("%") */
267 part->value = apr_pstrmemdup(mptmp, text_start, part->value_len);
268 *(msc_string **)apr_array_push(arr) = part;
270 next_text_start = p + 1;
274 part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string));
275 part->value = apr_pstrdup(mptmp, text_start);
276 part->value_len = strlen(part->value);
277 *(msc_string **)apr_array_push(arr) = part;
281 /* If there's more than one member of the array that
282 * means there was at least one macro present. Combine
283 * text parts into a single string now.
285 if (arr->nelts > 1) {
286 /* Figure out the required size for the string. */
288 for(i = 0; i < arr->nelts; i++) {
289 part = ((msc_string **)arr->elts)[i];
290 var->value_len += part->value_len;
293 /* Allocate the string. */
294 var->value = apr_palloc(msr->mp, var->value_len + 1);
295 if (var->value == NULL) return -1;
297 /* Combine the parts. */
299 for(i = 0; i < arr->nelts; i++) {
300 part = ((msc_string **)arr->elts)[i];
301 memcpy((char *)(var->value + offset), part->value, part->value_len);
302 offset += part->value_len;
304 var->value[offset] = '\0';
311 * Record the original collection values to use to calculate deltas.
312 * This can be called multiple times and will not overwrite the first
315 apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var) {
316 apr_table_t *table = NULL;
317 msc_string *var = NULL;
318 const char *var_name = NULL;
320 if (orig_var == NULL) {
321 msr_log(msr, 1, "Internal Error: Attempt to record NULL original variable.");
325 var_name = orig_var->name;
326 table = (apr_table_t *)apr_table_get(msr->collections_original, col_name);
328 /* Does the collection exist already? */
330 table = apr_table_make(msr->mp, 24);
332 msr_log(msr, 1, "Failed to allocate space for original collection.");
335 apr_table_setn(msr->collections_original, apr_pstrdup(msr->mp, col_name), (void *)table);
338 /* Does the variable exist already? */
339 var = (msc_string *)apr_table_get(table, var_name);
341 if (msr->txcfg->debuglog_level >= 9) {
342 msr_log(msr, 9, "Original collection variable: %s.%s = \"%s\"", col_name, var_name, log_escape_ex(msr->mp, orig_var->value, orig_var->value_len));
348 var = (msc_string *)apr_palloc(msr->mp, sizeof(msc_string));
350 msr_log(msr, 1, "Failed to allocate space for original collection variable.");
354 /* Copy the original var and add to collection. */
355 var->name = orig_var->name ? apr_pstrmemdup(msr->mp, orig_var->name, orig_var->name_len) : NULL;
356 var->name_len = orig_var->name_len;
357 var->value = orig_var->value ? apr_pstrmemdup(msr->mp, orig_var->value, orig_var->value_len) : NULL;
358 var->value_len = orig_var->value_len;
359 apr_table_setn(table, apr_pstrmemdup(msr->mp, var->name, var->name_len), (void *)var);
361 if (msr->txcfg->debuglog_level >= 9) {
362 msr_log(msr, 9, "Recorded original collection variable: %s.%s = \"%s\"", col_name, var_name, log_escape_ex(msr->mp, var->value, var->value_len));
370 static apr_status_t msre_action_id_init(msre_engine *engine, msre_actionset *actionset,
373 actionset->id = action->param;
379 static apr_status_t msre_action_rev_init(msre_engine *engine, msre_actionset *actionset,
382 actionset->rev = action->param;
388 static apr_status_t msre_action_msg_init(msre_engine *engine, msre_actionset *actionset,
391 actionset->msg = action->param;
397 static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset *actionset,
400 actionset->logdata = action->param;
406 static apr_status_t msre_action_severity_init(msre_engine *engine,
407 msre_actionset *actionset, msre_action *action)
409 actionset->severity = atoi(action->param);
415 static apr_status_t msre_action_chain_init(msre_engine *engine, msre_actionset *actionset,
418 actionset->is_chained = 1;
423 static apr_status_t msre_action_log_init(msre_engine *engine, msre_actionset *actionset,
431 static apr_status_t msre_action_nolog_init(msre_engine *engine, msre_actionset *actionset,
435 actionset->auditlog = 0;
440 static apr_status_t msre_action_auditlog_init(msre_engine *engine, msre_actionset *actionset,
443 actionset->auditlog = 1;
448 static apr_status_t msre_action_noauditlog_init(msre_engine *engine, msre_actionset *actionset,
451 actionset->auditlog = 0;
456 static apr_status_t msre_action_block_init(msre_engine *engine, msre_actionset *actionset,
459 /* Right now we just set a flag and inherit the real disruptive action */
460 actionset->block = 1;
465 static apr_status_t msre_action_deny_init(msre_engine *engine, msre_actionset *actionset,
468 actionset->intercept_action = ACTION_DENY;
469 actionset->intercept_action_rec = action;
474 static char *msre_action_status_validate(msre_engine *engine, msre_action *action) {
475 /* ENH action->param must be a valid HTTP status code. */
479 static apr_status_t msre_action_status_init(msre_engine *engine, msre_actionset *actionset,
482 actionset->intercept_status = atoi(action->param);
487 static apr_status_t msre_action_drop_init(msre_engine *engine, msre_actionset *actionset,
490 actionset->intercept_action = ACTION_DROP;
491 actionset->intercept_action_rec = action;
496 static char *msre_action_pause_validate(msre_engine *engine, msre_action *action) {
497 /* ENH Validate a positive number. */
501 static apr_status_t msre_action_pause_init(msre_engine *engine, msre_actionset *actionset,
504 actionset->intercept_pause = atoi(action->param);
510 static char *msre_action_redirect_validate(msre_engine *engine, msre_action *action) {
511 /* ENH Add validation. */
515 static apr_status_t msre_action_redirect_init(msre_engine *engine, msre_actionset *actionset,
518 actionset->intercept_action = ACTION_REDIRECT;
519 actionset->intercept_uri = action->param;
520 actionset->intercept_action_rec = action;
524 static apr_status_t msre_action_redirect_execute(modsec_rec *msr, apr_pool_t *mptmp,
525 msre_rule *rule, msre_action *action)
527 msc_string *var = NULL;
529 var = apr_pcalloc(mptmp, sizeof(msc_string));
530 if (var == NULL) return -1;
531 var->value = (char *)action->param;
532 var->value_len = strlen(var->value);
533 expand_macros(msr, var, rule, mptmp);
535 rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len);
542 static char *msre_action_proxy_validate(msre_engine *engine, msre_action *action) {
543 /* ENH Add validation. */
547 static apr_status_t msre_action_proxy_init(msre_engine *engine, msre_actionset *actionset,
550 actionset->intercept_action = ACTION_PROXY;
551 actionset->intercept_uri = action->param;
552 actionset->intercept_action_rec = action;
556 static apr_status_t msre_action_proxy_execute(modsec_rec *msr, apr_pool_t *mptmp,
557 msre_rule *rule, msre_action *action)
559 msc_string *var = NULL;
561 var = apr_pcalloc(mptmp, sizeof(msc_string));
562 if (var == NULL) return -1;
563 var->value = (char *)action->param;
564 var->value_len = strlen(var->value);
565 expand_macros(msr, var, rule, mptmp);
567 rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len);
574 static apr_status_t msre_action_pass_init(msre_engine *engine, msre_actionset *actionset,
577 actionset->intercept_action = ACTION_NONE;
578 actionset->intercept_action_rec = action;
584 static char *msre_action_skip_validate(msre_engine *engine, msre_action *action) {
585 /* ENH Add validation. */
589 static apr_status_t msre_action_skip_init(msre_engine *engine, msre_actionset *actionset,
592 actionset->skip_count = atoi(action->param);
593 if (actionset->skip_count <= 0) actionset->skip_count = 1;
599 static char *msre_action_skipAfter_validate(msre_engine *engine, msre_action *action) {
600 /* ENH Add validation. */
604 static apr_status_t msre_action_skipAfter_init(msre_engine *engine, msre_actionset *actionset,
607 actionset->skip_after = action->param;
613 static apr_status_t msre_action_allow_init(msre_engine *engine, msre_actionset *actionset,
616 actionset->intercept_action = ACTION_ALLOW;
617 actionset->intercept_action_rec = action;
619 if (action->param != NULL) {
620 if (strcasecmp(action->param, "phase") == 0) {
621 actionset->intercept_action = ACTION_ALLOW_PHASE;
623 if (strcasecmp(action->param, "request") == 0) {
624 actionset->intercept_action = ACTION_ALLOW_REQUEST;
631 static char *msre_action_allow_validate(msre_engine *engine, msre_action *action) {
632 if (action->param != NULL) {
633 if (strcasecmp(action->param, "phase") == 0) {
636 if (strcasecmp(action->param, "request") == 0) {
639 return apr_psprintf(engine->mp, "Invalid parameter for allow: %s", action->param);
648 static char *msre_action_phase_validate(msre_engine *engine, msre_action *action) {
649 /* ENH Add validation. */
653 static apr_status_t msre_action_phase_init(msre_engine *engine, msre_actionset *actionset,
656 actionset->phase = atoi(action->param);
662 static char *msre_action_t_validate(msre_engine *engine, msre_action *action) {
663 msre_tfn_metadata *metadata = NULL;
664 metadata = msre_engine_tfn_resolve(engine, action->param);
665 if (metadata == NULL) return apr_psprintf(engine->mp, "Invalid transformation function: %s",
667 action->param_data = metadata;
671 static apr_status_t msre_action_t_init(msre_engine *engine, msre_actionset *actionset,
674 msre_tfn_metadata *metadata = (msre_tfn_metadata *)action->param_data;
675 action->param_data = metadata;
680 static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) {
685 if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) {
689 return apr_psprintf(engine->mp, "Missing ctl value for name: %s", name);
692 /* Validate value. */
693 if (strcasecmp(name, "ruleEngine") == 0) {
694 if (strcasecmp(value, "on") == 0) return NULL;
695 if (strcasecmp(value, "off") == 0) return NULL;
696 if (strcasecmp(value, "detectiononly") == 0) return NULL;
697 return apr_psprintf(engine->mp, "Invalid setting for ctl name ruleEngine: %s", value);
699 if (strcasecmp(name, "ruleRemoveById") == 0) {
700 /* ENH nothing yet */
703 if (strcasecmp(name, "requestBodyAccess") == 0) {
704 if (parse_boolean(value) == -1) {
705 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
706 " requestBodyAccess: %s", value);
710 if (strcasecmp(name, "requestBodyProcessor") == 0) {
711 /* ENH We will accept anything for now but it'd be nice
712 * to add a check here that the processor name is a valid one.
716 if (strcasecmp(name, "forceRequestBodyVariable") == 0) {
717 if (strcasecmp(value, "on") == 0) return NULL;
718 if (strcasecmp(value, "off") == 0) return NULL;
719 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
720 " forceRequestBodyVariable: %s", value);
722 if (strcasecmp(name, "responseBodyAccess") == 0) {
723 if (parse_boolean(value) == -1) {
724 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
725 " responseBodyAccess: %s", value);
729 if (strcasecmp(name, "auditEngine") == 0) {
730 if (strcasecmp(value, "on") == 0) return NULL;
731 if (strcasecmp(value, "off") == 0) return NULL;
732 if (strcasecmp(value, "relevantonly") == 0) return NULL;
733 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
734 " auditEngine: %s", value);
736 if (strcasecmp(name, "auditLogParts") == 0) {
737 if ((value[0] == '+')||(value[0] == '-')) {
738 if (is_valid_parts_specification(value + 1) != 1) {
739 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
740 "auditLogParts: %s", value);
744 if (is_valid_parts_specification(value) != 1) {
745 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
746 "auditLogParts: %s", value);
750 if (strcasecmp(name, "debugLogLevel") == 0) {
751 if ((atoi(value) >= 0)&&(atoi(value) <= 9)) return NULL;
752 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
753 "debugLogLevel: %s", value);
755 if (strcasecmp(name, "requestBodyLimit") == 0) {
756 long int limit = strtol(value, NULL, 10);
758 if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
759 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
760 "requestBodyLimit: %s", value);
763 if (limit > REQUEST_BODY_HARD_LIMIT) {
764 return apr_psprintf(engine->mp, "Request size limit cannot exceed "
765 "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT);
770 if (strcasecmp(name, "responseBodyLimit") == 0) {
771 long int limit = strtol(value, NULL, 10);
773 if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
774 return apr_psprintf(engine->mp, "Invalid setting for ctl name "
775 "responseBodyLimit: %s", value);
778 if (limit > RESPONSE_BODY_HARD_LIMIT) {
779 return apr_psprintf(engine->mp, "Response size limit cannot exceed "
780 "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT);
786 return apr_psprintf(engine->mp, "Invalid ctl name setting: %s", name);
790 static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset,
797 static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp,
798 msre_rule *rule, msre_action *action)
804 if (parse_name_eq_value(msr->mp, action->param, &name, &value) < 0) return -1;
805 if (value == NULL) return -1;
807 /* Validate value. */
808 if (strcasecmp(name, "ruleEngine") == 0) {
809 if (strcasecmp(value, "on") == 0) {
810 msr->txcfg->is_enabled = MODSEC_ENABLED;
811 msr->usercfg->is_enabled = MODSEC_ENABLED;
814 if (strcasecmp(value, "off") == 0) {
815 msr->txcfg->is_enabled = MODSEC_DISABLED;
816 msr->usercfg->is_enabled = MODSEC_DISABLED;
819 if (strcasecmp(value, "detectiononly") == 0) {
820 msr->txcfg->is_enabled = MODSEC_DETECTION_ONLY;
821 msr->usercfg->is_enabled = MODSEC_DETECTION_ONLY;
826 if (strcasecmp(name, "ruleRemoveById") == 0) {
827 *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value);
830 if (strcasecmp(name, "requestBodyAccess") == 0) {
831 int pv = parse_boolean(value);
833 if (pv == -1) return -1;
834 msr->txcfg->reqbody_access = pv;
835 msr->usercfg->reqbody_access = pv;
836 msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", pv);
840 if (strcasecmp(name, "forceRequestBodyVariable") == 0) {
841 if (strcasecmp(value, "on") == 0) {
842 msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_ON;
843 msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_ON;
846 if (strcasecmp(value, "off") == 0) {
847 msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF;
848 msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF;
851 msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", msr->txcfg->reqbody_buffering);
855 if (strcasecmp(name, "requestBodyProcessor") == 0) {
856 msr->msc_reqbody_processor = value;
857 msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value);
861 if (strcasecmp(name, "responseBodyAccess") == 0) {
862 int pv = parse_boolean(value);
864 if (pv == -1) return -1;
865 msr->txcfg->resbody_access = pv;
866 msr->usercfg->resbody_access = pv;
867 msr_log(msr, 4, "Ctl: Set responseBodyAccess to %d.", pv);
871 if (strcasecmp(name, "auditEngine") == 0) {
872 if (strcasecmp(value, "on") == 0) {
873 msr->txcfg->auditlog_flag = AUDITLOG_ON;
874 msr->usercfg->auditlog_flag = AUDITLOG_ON;
877 if (strcasecmp(value, "off") == 0) {
878 msr->txcfg->auditlog_flag = AUDITLOG_OFF;
879 msr->usercfg->auditlog_flag = AUDITLOG_OFF;
882 if (strcasecmp(value, "relevantonly") == 0) {
883 msr->txcfg->auditlog_flag = AUDITLOG_RELEVANT;
884 msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT;
887 msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag);
891 if (strcasecmp(name, "auditLogParts") == 0) {
892 char *new_value = value;
894 if (value[0] == '+') {
895 /* Add the listed parts. */
896 new_value = apr_pstrcat(msr->mp, msr->txcfg->auditlog_parts, value + 1, NULL);
899 if (value[0] == '-') { /* Remove the listed parts. */
900 char c, *t = value + 1;
902 /* Start with the current value. */
903 new_value = apr_pstrdup(msr->mp, msr->txcfg->auditlog_parts);
905 while((c = *t++) != '\0') {
920 /* Set the new value. */
921 msr->txcfg->auditlog_parts = new_value;
922 msr->usercfg->auditlog_parts = new_value;
923 msr_log(msr, 4, "Ctl: Set auditLogParts to %s.", msr->txcfg->auditlog_parts);
927 if (strcasecmp(name, "debugLogLevel") == 0) {
928 msr->txcfg->debuglog_level = atoi(value);
929 msr->usercfg->debuglog_level = atoi(value);
930 msr_log(msr, 4, "Ctl: Set debugLogLevel to %d.", msr->txcfg->debuglog_level);
934 if (strcasecmp(name, "requestBodyLimit") == 0) {
935 long int limit = strtol(value, NULL, 10);
937 /* ENH Accept only in correct phase warn otherwise. */
938 msr->txcfg->reqbody_limit = limit;
939 msr->usercfg->reqbody_limit = limit;
943 if (strcasecmp(name, "responseBodyLimit") == 0) {
944 long int limit = strtol(value, NULL, 10);
946 /* ENH Accept only in correct phase warn otherwise. */
947 msr->txcfg->of_limit = limit;
948 msr->usercfg->of_limit = limit;
953 /* Should never happen, but log if it does. */
954 msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name);
960 static char *msre_action_xmlns_validate(msre_engine *engine, msre_action *action) {
965 if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) {
969 return apr_psprintf(engine->mp, "Missing xmlns href for prefix: %s", name);
972 /* Don't do anything else right now, we are just storing
973 * the value for the variable, which is the real consumer
974 * for the namespace information.
981 static apr_status_t msre_action_sanitiseArg_execute(modsec_rec *msr, apr_pool_t *mptmp,
982 msre_rule *rule, msre_action *action)
984 const char *sargname = NULL;
985 const apr_array_header_t *tarr;
986 const apr_table_entry_t *telts;
989 sargname = action->param;
991 tarr = apr_table_elts(msr->arguments);
992 telts = (const apr_table_entry_t*)tarr->elts;
993 for (i = 0; i < tarr->nelts; i++) {
994 msc_arg *arg = (msc_arg *)telts[i].val;
996 if (strcasecmp(sargname, arg->name) == 0) {
997 apr_table_addn(msr->arguments_to_sanitise, arg->name, (void *)arg);
1004 #define SANITISE_ARG 1
1005 #define SANITISE_REQUEST_HEADER 2
1006 #define SANITISE_RESPONSE_HEADER 3
1008 /* sanitiseMatched */
1009 static apr_status_t msre_action_sanitiseMatched_execute(modsec_rec *msr, apr_pool_t *mptmp,
1010 msre_rule *rule, msre_action *action)
1012 const char *sargname = NULL;
1013 const apr_array_header_t *tarr;
1014 const apr_table_entry_t *telts;
1016 msc_string *mvar = msr->matched_var;
1018 if (mvar->name_len == 0) return 0;
1020 /* IMP1 We need to extract the variable name properly here,
1021 * taking into account it may have been escaped.
1023 if ((mvar->name_len > 5) && (strncmp(mvar->name, "ARGS:", 5) == 0)) {
1024 sargname = apr_pstrdup(msr->mp, mvar->name + 5);
1025 type = SANITISE_ARG;
1027 if ((mvar->name_len > 11) && (strncmp(mvar->name, "ARGS_NAMES:", 11) == 0)) {
1028 sargname = apr_pstrdup(msr->mp, mvar->name + 11);
1029 type = SANITISE_ARG;
1031 if ((mvar->name_len > 16) && (strncmp(mvar->name, "REQUEST_HEADERS:", 16) == 0)) {
1032 sargname = apr_pstrdup(msr->mp, mvar->name + 16);
1033 type = SANITISE_REQUEST_HEADER;
1035 if ((mvar->name_len > 22) && (strncmp(mvar->name, "REQUEST_HEADERS_NAMES:", 22) == 0)) {
1036 sargname = apr_pstrdup(msr->mp, mvar->name + 22);
1037 type = SANITISE_REQUEST_HEADER;
1039 if ((mvar->name_len > 17) && (strncmp(mvar->name, "RESPONSE_HEADERS:", 17) == 0)) {
1040 sargname = apr_pstrdup(msr->mp, mvar->name + 17);
1041 type = SANITISE_RESPONSE_HEADER;
1043 if ((mvar->name_len > 23) && (strncmp(mvar->name, "RESPONSE_HEADERS_NAMES:", 23) == 0)) {
1044 sargname = apr_pstrdup(msr->mp, mvar->name + 23);
1045 type = SANITISE_RESPONSE_HEADER;
1048 msr_log(msr, 3, "sanitiseMatched: Don't know how to handle variable: %s",
1055 tarr = apr_table_elts(msr->arguments);
1056 telts = (const apr_table_entry_t*)tarr->elts;
1057 for (i = 0; i < tarr->nelts; i++) {
1058 msc_arg *arg = (msc_arg *)telts[i].val;
1059 if (strcasecmp(sargname, arg->name) == 0) {
1060 apr_table_addn(msr->arguments_to_sanitise, arg->name, (void *)arg);
1065 case SANITISE_REQUEST_HEADER :
1066 apr_table_set(msr->request_headers_to_sanitise, sargname, "1");
1069 case SANITISE_RESPONSE_HEADER :
1070 apr_table_set(msr->response_headers_to_sanitise, sargname, "1");
1081 /* sanitiseRequestHeader */
1082 static apr_status_t msre_action_sanitiseRequestHeader_execute(modsec_rec *msr, apr_pool_t *mptmp,
1083 msre_rule *rule, msre_action *action)
1085 apr_table_set(msr->request_headers_to_sanitise, action->param, "1");
1089 /* sanitiseResponseHeader */
1090 static apr_status_t msre_action_sanitiseResponseHeader_execute(modsec_rec *msr, apr_pool_t *mptmp,
1091 msre_rule *rule, msre_action *action)
1093 apr_table_set(msr->response_headers_to_sanitise, action->param, "1");
1098 static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptmp,
1099 msre_rule *rule, msre_action *action)
1101 char *data = apr_pstrdup(mptmp, action->param);
1102 char *env_name = NULL, *env_value = NULL;
1104 msc_string *env = NULL;
1106 /* Extract the name and the value. */
1107 /* IMP1 We have a function for this now, parse_name_eq_value? */
1108 s = strstr(data, "=");
1118 if (msr->txcfg->debuglog_level >= 9) {
1119 msr_log(msr, 9, "Setting env variable: %s=%s", env_name, env_value);
1122 /* Expand and escape any macros in the name */
1123 env = apr_palloc(msr->mp, sizeof(msc_string));
1125 msr_log(msr, 1, "Failed to allocate space to expand name macros");
1128 env->value = env_name;
1129 env->value_len = strlen(env->value);
1130 expand_macros(msr, env, rule, mptmp);
1131 env_name = log_escape_ex(msr->mp, env->value, env->value_len);
1133 /* Execute the requested action. */
1134 if (env_name[0] == '!') {
1136 apr_table_unset(msr->r->subprocess_env, env_name + 1);
1138 if (msr->txcfg->debuglog_level >= 9) {
1139 msr_log(msr, 9, "Unset env variable \"%s\".", env_name);
1143 char * val_value = NULL;
1144 msc_string *val = apr_palloc(msr->mp, sizeof(msc_string));
1146 msr_log(msr, 1, "Failed to allocate space to expand value macros");
1150 /* Expand values in value */
1151 val->value = env_value;
1152 val->value_len = strlen(val->value);
1153 expand_macros(msr, val, rule, mptmp);
1155 /* To be safe, we escape the value as it goes in subprocess_env. */
1156 val_value = log_escape_ex(msr->mp, val->value, val->value_len);
1158 apr_table_set(msr->r->subprocess_env, env_name, val_value);
1160 if (msr->txcfg->debuglog_level >= 9) {
1161 msr_log(msr, 9, "Set env variable \"%s\" to \"%s\".",
1163 log_escape(mptmp, val_value));
1171 static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp,
1172 msre_rule *rule, msre_action *action)
1174 char *data = apr_pstrdup(mptmp, action->param);
1175 char *col_name = NULL, *var_name = NULL, *var_value = NULL;
1177 apr_table_t *target_col = NULL;
1179 msc_string *var = NULL;
1181 /* Extract the name and the value. */
1182 /* IMP1 We have a function for this now, parse_name_eq_value? */
1183 s = strstr(data, "=");
1192 while ((*var_value != '\0')&&(isspace(*var_value))) var_value++;
1195 if (msr->txcfg->debuglog_level >= 9) {
1196 msr_log(msr, 9, "Setting variable: %s=%s", var_name, var_value);
1200 /* Expand and escape any macros in the name */
1201 var = apr_palloc(msr->mp, sizeof(msc_string));
1203 msr_log(msr, 1, "Failed to allocate space to expand name macros");
1206 var->value = var_name;
1207 var->value_len = strlen(var->value);
1208 expand_macros(msr, var, rule, mptmp);
1209 var_name = log_escape_ex(msr->mp, var->value, var->value_len);
1211 /* Handle the exclamation mark. */
1212 if (var_name[0] == '!') {
1213 var_name = var_name + 1;
1217 /* ENH Not possible to use ! and = at the same time. */
1218 /* ENH Not possible to change variable "KEY". */
1220 /* Figure out the collection name. */
1221 target_col = msr->tx_vars;
1222 s = strstr(var_name, ".");
1224 msr_log(msr, 3, "Asked to set variable \"%s\", but no collection name specified. ",
1225 log_escape(msr->mp, var_name));
1228 col_name = var_name;
1232 /* Locate the collection. */
1233 if (strcasecmp(col_name, "tx") == 0) { /* Special case for TX variables. */
1234 target_col = msr->tx_vars;
1236 target_col = (apr_table_t *)apr_table_get(msr->collections, col_name);
1237 if (target_col == NULL) {
1238 msr_log(msr, 3, "Could not set variable \"%s.%s\" as the collection does not exist.",
1239 log_escape(msr->mp, col_name), log_escape(msr->mp, var_name));
1245 /* Unset variable. */
1247 /* ENH Refuse to remove certain variables, e.g. TIMEOUT, internal variables, etc... */
1249 apr_table_unset(target_col, var_name);
1251 if (msr->txcfg->debuglog_level >= 9) {
1252 msr_log(msr, 9, "Unset variable \"%s.%s\".", col_name, var_name);
1256 /* Set or change variable. */
1258 if ((var_value[0] == '+')||(var_value[0] == '-')) {
1259 /* Relative change. */
1260 msc_string *rec = NULL;
1261 msc_string *val = apr_palloc(msr->mp, sizeof(msc_string));
1265 msr_log(msr, 1, "Failed to allocate space to expand value macros");
1269 /* Retrieve variable or generate (if it does not exist). */
1270 rec = (msc_string *)apr_table_get(target_col, var_name);
1272 rec = var; /* use the already allocated space for var */
1273 rec->name = apr_pstrdup(msr->mp, var_name);
1274 rec->name_len = strlen(rec->name);
1276 rec->value = apr_psprintf(msr->mp, "%d", value);
1277 rec->value_len = strlen(rec->value);
1280 value = atoi(rec->value);
1283 /* Record the original value before we change it */
1284 collection_original_setvar(msr, col_name, rec);
1286 /* Expand values in value */
1287 val->value = var_value;
1288 val->value_len = strlen(val->value);
1289 expand_macros(msr, val, rule, mptmp);
1290 var_value = val->value;
1292 if (msr->txcfg->debuglog_level >= 9) {
1293 msr_log(msr, 9, "Relative change: %s=%d%s", var_name, value, var_value);
1297 value += atoi(var_value);
1298 if (value < 0) value = 0; /* Counters never go below zero. */
1300 /* Put the variable back. */
1301 rec->value = apr_psprintf(msr->mp, "%d", value);
1302 rec->value_len = strlen(rec->value);
1303 apr_table_setn(target_col, rec->name, (void *)rec);
1305 if (msr->txcfg->debuglog_level >= 9) {
1306 msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".",
1307 col_name, rec->name,
1308 log_escape_ex(mptmp, rec->value, rec->value_len));
1312 /* Absolute change. */
1314 var->name = apr_pstrdup(msr->mp, var_name);
1315 var->name_len = strlen(var->name);
1316 var->value = apr_pstrdup(msr->mp, var_value);
1317 var->value_len = strlen(var->value);
1318 expand_macros(msr, var, rule, mptmp);
1319 apr_table_setn(target_col, var->name, (void *)var);
1321 if (msr->txcfg->debuglog_level >= 9) {
1322 msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".",
1323 log_escape(mptmp, col_name),
1324 log_escape_ex(mptmp, var->name, var->name_len),
1325 log_escape_ex(mptmp, var->value, var->value_len));
1330 /* Make note of the change so that we know later
1331 * we need to persist the collection.
1333 apr_table_set(msr->collections_dirty, col_name, "1");
1339 static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *mptmp,
1340 msre_rule *rule, msre_action *action)
1342 char *data = apr_pstrdup(mptmp, action->param);
1343 char *col_name = NULL, *var_name = NULL, *var_value = NULL;
1345 apr_table_t *target_col = NULL;
1346 msc_string *var = NULL;
1348 /* Extract the name and the value. */
1349 /* IMP1 We have a function for this now, parse_name_eq_value? */
1350 s = strstr(data, "=");
1360 if (msr->txcfg->debuglog_level >= 9) {
1361 msr_log(msr, 9, "Expiring variable: %s=%s", var_name, var_value);
1364 /* Expand and escape any macros in the name */
1365 var = apr_palloc(msr->mp, sizeof(msc_string));
1367 msr_log(msr, 1, "Failed to allocate space to expand name macros");
1370 var->value = var_name;
1371 var->value_len = strlen(var->value);
1372 expand_macros(msr, var, rule, mptmp);
1373 var_name = log_escape_ex(msr->mp, var->value, var->value_len);
1375 /* Choose the collection to work with. */
1376 s = strstr(var_name, ".");
1378 col_name = var_name;
1382 /* IMP1 No need to handle TX here because TX variables cannot expire,
1383 * but we definitely need to have a better error message.
1386 target_col = (apr_table_t *)apr_table_get(msr->collections, col_name);
1387 if (target_col == NULL) {
1388 msr_log(msr, 3, "Could not expire variable \"%s.%s\" as the collection does not exist.",
1389 log_escape(msr->mp, col_name), log_escape(msr->mp, var_name));
1393 msr_log(msr, 3, "Asked to expire variable \"%s\", but no collection name specified. ",
1394 log_escape(msr->mp, var_name));
1398 /* To expire a variable we just place a special variable into
1399 * the collection. Expiry actually happens when the collection
1400 * is retrieved from storage the next time.
1402 var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1403 var->name = apr_psprintf(msr->mp, "__expire_%s", var_name);
1404 var->name_len = strlen(var->name);
1406 /* Expand macros in value */
1407 var->value = var_value;
1408 var->value_len = strlen(var->value);
1409 expand_macros(msr, var, rule, msr->mp);
1410 var_value = var->value;
1412 /* Calculate with the expanded value */
1413 var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time)
1414 + atoi(var_value)));
1415 var->value_len = strlen(var->value);
1417 apr_table_setn(target_col, var->name, (void *)var);
1419 msr_log(msr, 4, "Variable \"%s.%s\" set to expire in %s seconds.", col_name,
1420 var_name, var_value);
1422 apr_table_set(msr->collections_dirty, col_name, "1");
1428 static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t *mptmp,
1429 msre_rule *rule, msre_action *action)
1431 char *data = apr_pstrdup(mptmp, action->param);
1432 char *col_name = NULL, *var_name = NULL, *var_value = NULL;
1434 apr_table_t *target_col = NULL;
1435 msc_string *var = NULL, *var_last_update_time = NULL;
1436 apr_time_t last_update_time, current_time;
1437 long current_value, new_value;
1439 /* Extract the name and the value. */
1440 /* IMP1 We have a function for this now, parse_name_eq_value? */
1441 s = strstr(data, "=");
1451 if (msr->txcfg->debuglog_level >= 9) {
1452 msr_log(msr, 9, "Deprecating variable: %s=%s", var_name, var_value);
1455 /* Expand and escape any macros in the name */
1456 var = apr_palloc(msr->mp, sizeof(msc_string));
1458 msr_log(msr, 1, "Failed to allocate space to expand name macros");
1461 var->value = var_name;
1462 var->value_len = strlen(var->value);
1463 expand_macros(msr, var, rule, mptmp);
1464 var_name = log_escape_ex(msr->mp, var->value, var->value_len);
1466 /* Expand macros in value */
1467 var->value = var_value;
1468 var->value_len = strlen(var->value);
1469 expand_macros(msr, var, rule, msr->mp);
1470 var_value = var->value;
1472 /* Choose the collection to work with. */
1473 s = strstr(var_name, ".");
1475 col_name = var_name;
1479 /* IMP1 Add message TX variables cannot deprecate in value. */
1481 target_col = (apr_table_t *)apr_table_get(msr->collections, col_name);
1482 if (target_col == NULL) {
1483 msr_log(msr, 3, "Could not deprecate variable \"%s.%s\" as the collection does "
1484 "not exist.", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name));
1488 msr_log(msr, 3, "Asked to deprecate variable \"%s\", but no collection name specified. ",
1489 log_escape(msr->mp, var_name));
1493 /* Find the current value. */
1494 var = (msc_string *)apr_table_get(target_col, var_name);
1496 if (msr->txcfg->debuglog_level >= 9) {
1497 msr_log(msr, 9, "Asked to deprecate variable \"%s.%s\", but it does not exist.",
1498 log_escape(msr->mp, col_name), log_escape(msr->mp, var_name));
1502 current_value = atoi(var->value);
1504 /* Find the last update time (of the collection). */
1505 var_last_update_time = (msc_string *)apr_table_get(target_col, "LAST_UPDATE_TIME");
1506 if (var_last_update_time == NULL) {
1507 /* This is all right. If collection was created (and not restored from
1508 * storage) then it won't have LAST_UPDATE_TIME - it was never updated.
1513 current_time = apr_time_sec(apr_time_now());
1514 last_update_time = atoi(var_last_update_time->value);
1516 s = strstr(var_value, "/");
1518 msr_log(msr, 3, "Incorrect format for the deprecatevar argument: \"%s\"",
1519 log_escape(msr->mp, var_value));
1525 /* Deprecate the value using the given speed and the
1526 * time elapsed since the last update.
1528 new_value = current_value -
1529 (atol(var_value) * ((current_time - last_update_time) / atol(s)));
1530 if (new_value < 0) new_value = 0;
1532 /* Only change the value if it differs. */
1533 if (new_value != current_value) {
1534 var->value = apr_psprintf(msr->mp, "%ld", new_value);
1535 var->value_len = strlen(var->value);
1537 msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %ld to %ld (%" APR_TIME_T_FMT " seconds since "
1538 "last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name),
1539 current_value, new_value, (apr_time_t)(current_time - last_update_time));
1541 apr_table_set(msr->collections_dirty, col_name, "1");
1543 if (msr->txcfg->debuglog_level >= 9) {
1544 msr_log(msr, 9, "Not deprecating variable \"%s.%s\" because the new value (%ld) is "
1545 "the same as the old one (%ld) (%" APR_TIME_T_FMT " seconds since last update).",
1546 log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), current_value,
1547 new_value, (apr_time_t)(current_time - last_update_time));
1554 static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name,
1555 const char *col_name, const char *col_key, unsigned int col_key_len)
1557 apr_table_t *table = NULL;
1558 msc_string *var = NULL;
1560 /* IMP1 Cannot initialise the built-in collections this way. */
1562 /* Does the collection exist already? */
1563 if (apr_table_get(msr->collections, col_name) != NULL) {
1564 /* ENH Warn about this. */
1568 /* Init collection from storage. */
1569 table = collection_retrieve(msr, real_col_name, col_key, col_key_len);
1571 if (table == NULL) {
1572 /* Does not exist yet - create new. */
1573 msr_log(msr, 4, "Creating collection (name \"%s\", key \"%s\").",
1574 real_col_name, col_key);
1576 table = apr_table_make(msr->mp, 24);
1577 if (table == NULL) return -1;
1579 /* IMP1 Is the timeout hard-coded to 3600? */
1581 /* Add default timeout. */
1582 var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
1583 var->name = "__expire_KEY";
1584 var->name_len = strlen(var->name);
1585 var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) + 3600));
1586 var->value_len = strlen(var->value);
1587 apr_table_setn(table, var->name, (void *)var);
1589 /* Remember the key. */
1590 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1592 var->name_len = strlen(var->name);
1593 var->value = apr_pstrmemdup(msr->mp, col_key, col_key_len);
1594 var->value_len = col_key_len;
1595 apr_table_setn(table, var->name, (void *)var);
1598 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1599 var->name = "TIMEOUT";
1600 var->name_len = strlen(var->name);
1601 var->value = apr_psprintf(msr->mp, "%d", 3600);
1602 var->value_len = strlen(var->value);
1603 apr_table_setn(table, var->name, (void *)var);
1605 /* We may want to allow the user to unset KEY
1606 * but we still need to preserve value to identify
1607 * the collection in storage.
1610 /* IMP1 Actually I want a better way to delete collections,
1611 * perhaps a dedicated action.
1614 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1615 var->name = "__key";
1616 var->name_len = strlen(var->name);
1617 var->value = apr_pstrmemdup(msr->mp, col_key, col_key_len);
1618 var->value_len = col_key_len;
1619 apr_table_setn(table, var->name, (void *)var);
1621 /* Peristence code will need to know the name of the collection. */
1622 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1623 var->name = "__name";
1624 var->name_len = strlen(var->name);
1625 var->value = apr_pstrdup(msr->mp, real_col_name);
1626 var->value_len = strlen(var->value);
1627 apr_table_setn(table, var->name, (void *)var);
1630 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1631 var->name = "CREATE_TIME";
1632 var->name_len = strlen(var->name);
1633 var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)apr_time_sec(msr->request_time));
1634 var->value_len = strlen(var->value);
1635 apr_table_setn(table, var->name, (void *)var);
1637 /* Update counter. */
1638 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1639 var->name = "UPDATE_COUNTER";
1640 var->name_len = strlen(var->name);
1642 var->value_len = strlen(var->value);
1643 apr_table_setn(table, var->name, (void *)var);
1645 /* This is a new collection. */
1646 var = apr_pcalloc(msr->mp, sizeof(msc_string));
1647 var->name = "IS_NEW";
1648 var->name_len = strlen(var->name);
1650 var->value_len = strlen(var->value);
1651 apr_table_setn(table, var->name, (void *)var);
1654 /* Record the original counter value before we change it */
1655 var = (msc_string *)apr_table_get(table, "UPDATE_COUNTER");
1657 collection_original_setvar(msr, col_name, var);
1660 /* Add the collection to the list. */
1661 apr_table_setn(msr->collections, apr_pstrdup(msr->mp, col_name), (void *)table);
1663 if (strcmp(col_name, real_col_name) != 0) {
1664 msr_log(msr, 4, "Added collection \"%s\" to the list as \"%s\".",
1665 log_escape(msr->mp, real_col_name), log_escape(msr->mp, col_name));
1667 msr_log(msr, 4, "Added collection \"%s\" to the list.",
1668 log_escape(msr->mp, real_col_name));
1675 static apr_status_t msre_action_initcol_execute(modsec_rec *msr, apr_pool_t *mptmp,
1676 msre_rule *rule, msre_action *action)
1678 char *data = apr_pstrdup(msr->mp, action->param);
1679 char *col_name = NULL, *col_key = NULL;
1680 unsigned int col_key_len;
1682 msc_string *var = NULL;
1685 /* Extract the name and the value. */
1686 /* IMP1 We have a function for this now, parse_name_eq_value? */
1687 s = strstr(data, "=");
1688 if (s == NULL) return 0;
1693 /* Expand the key and init collection from storage. */
1694 var = apr_pcalloc(mptmp, sizeof(msc_string));
1695 var->value = col_key;
1696 var->value_len = strlen(var->value);
1697 expand_macros(msr, var, rule, mptmp);
1699 col_key = var->value;
1700 col_key_len = var->value_len;
1702 return init_collection(msr, col_name, col_name, col_key, col_key_len);
1706 static apr_status_t msre_action_setsid_execute(modsec_rec *msr, apr_pool_t *mptmp,
1707 msre_rule *rule, msre_action *action)
1709 msc_string *var = NULL;
1710 char *real_col_name = NULL, *col_key = NULL;
1711 unsigned int col_key_len;
1713 /* Construct session ID. */
1714 var = apr_pcalloc(mptmp, sizeof(msc_string));
1715 var->value = (char *)action->param;
1716 var->value_len = strlen(var->value);
1717 expand_macros(msr, var, rule, mptmp);
1718 msr->sessionid = apr_pstrdup(msr->mp, var->value);
1720 /* Construct collection name. */
1721 col_key = var->value;
1722 col_key_len = var->value_len;
1723 real_col_name = apr_psprintf(mptmp, "%s_SESSION", msr->txcfg->webappid);
1725 /* Initialise collection. */
1726 return init_collection(msr, real_col_name, "SESSION", col_key, col_key_len);
1730 static apr_status_t msre_action_setuid_execute(modsec_rec *msr, apr_pool_t *mptmp,
1731 msre_rule *rule, msre_action *action)
1733 msc_string *var = NULL;
1734 char *real_col_name = NULL, *col_key = NULL;
1735 unsigned int col_key_len;
1737 /* Construct user ID. */
1738 var = apr_pcalloc(mptmp, sizeof(msc_string));
1739 var->value = (char *)action->param;
1740 var->value_len = strlen(var->value);
1741 expand_macros(msr, var, rule, mptmp);
1742 msr->userid = apr_pstrdup(msr->mp, var->value);
1744 /* Construct collection name. */
1745 col_key = var->value;
1746 col_key_len = var->value_len;
1747 real_col_name = apr_psprintf(mptmp, "%s_USER", msr->txcfg->webappid);
1749 /* Initialise collection. */
1750 return init_collection(msr, real_col_name, "USER", col_key, col_key_len);
1754 static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) {
1755 #if defined(WITH_LUA)
1756 char *filename = (char *)action->param;
1758 /* TODO Support relative filenames. */
1760 /* Process Lua scripts internally. */
1761 if (strlen(filename) > 4) {
1762 char *p = filename + strlen(filename) - 4;
1763 if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) {
1764 /* It's a Lua script. */
1765 msc_script *script = NULL;
1767 /* Compile script. */
1768 char *msg = lua_compile(&script, filename, engine->mp);
1769 if (msg != NULL) return msg;
1771 action->param_data = script;
1779 static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp,
1780 msre_rule *rule, msre_action *action)
1782 #if defined(WITH_LUA)
1783 if (action->param_data != NULL) { /* Lua */
1784 msc_script *script = (msc_script *)action->param_data;
1785 char *my_error_msg = NULL;
1787 if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) {
1788 msr_log(msr, 1, "%s", my_error_msg);
1793 { /* Execute as shell script. */
1794 char *script_output = NULL;
1796 int rc = apache2_exec(msr, action->param, NULL, &script_output);
1798 msr_log(msr, 1, "Failed to execute: %s", action->param);
1807 static apr_status_t msre_action_prepend_execute(modsec_rec *msr, apr_pool_t *mptmp,
1808 msre_rule *rule, msre_action *action)
1810 msc_string *var = NULL;
1812 /* Expand any macros in the text */
1813 var = apr_pcalloc(mptmp, sizeof(msc_string));
1814 if (var == NULL) return -1;
1815 var->value = (char *)action->param;
1816 var->value_len = strlen(var->value);
1817 expand_macros(msr, var, rule, mptmp);
1819 /* ENH: Verify we really have to dup the data here. */
1820 msr->content_prepend = apr_pstrndup(msr->mp, var->value, var->value_len);
1821 msr->content_prepend_len = var->value_len;
1827 static apr_status_t msre_action_append_execute(modsec_rec *msr, apr_pool_t *mptmp,
1828 msre_rule *rule, msre_action *action)
1830 msc_string *var = NULL;
1832 /* Expand any macros in the text */
1833 var = apr_pcalloc(mptmp, sizeof(msc_string));
1834 if (var == NULL) return -1;
1835 var->value = (char *)action->param;
1836 var->value_len = strlen(var->value);
1837 expand_macros(msr, var, rule, mptmp);
1839 /* ENH: Verify we really have to dup the data here. */
1840 msr->content_append = apr_pstrndup(msr->mp, var->value, var->value_len);
1841 msr->content_append_len = var->value_len;
1851 void msre_engine_register_default_actions(msre_engine *engine) {
1854 msre_engine_action_register(engine,
1859 ACTION_CARDINALITY_ONE,
1862 msre_action_id_init,
1867 msre_engine_action_register(engine,
1872 ACTION_CARDINALITY_ONE,
1875 msre_action_rev_init,
1880 msre_engine_action_register(engine,
1885 ACTION_CARDINALITY_ONE,
1888 msre_action_msg_init,
1893 msre_engine_action_register(engine,
1898 ACTION_CARDINALITY_ONE,
1901 msre_action_logdata_init,
1906 msre_engine_action_register(engine,
1911 ACTION_CARDINALITY_ONE,
1914 msre_action_severity_init,
1919 msre_engine_action_register(engine,
1924 ACTION_CARDINALITY_ONE,
1927 msre_action_chain_init,
1932 msre_engine_action_register(engine,
1934 ACTION_NON_DISRUPTIVE,
1937 ACTION_CARDINALITY_ONE,
1940 msre_action_log_init,
1945 msre_engine_action_register(engine,
1947 ACTION_NON_DISRUPTIVE,
1950 ACTION_CARDINALITY_ONE,
1953 msre_action_nolog_init,
1958 msre_engine_action_register(engine,
1960 ACTION_NON_DISRUPTIVE,
1963 ACTION_CARDINALITY_ONE,
1964 ACTION_CGROUP_AUDITLOG,
1966 msre_action_auditlog_init,
1971 msre_engine_action_register(engine,
1973 ACTION_NON_DISRUPTIVE,
1976 ACTION_CARDINALITY_ONE,
1977 ACTION_CGROUP_AUDITLOG,
1979 msre_action_noauditlog_init,
1984 msre_engine_action_register(engine,
1989 ACTION_CARDINALITY_ONE,
1990 ACTION_CGROUP_DISRUPTIVE,
1992 msre_action_block_init,
1997 msre_engine_action_register(engine,
2002 ACTION_CARDINALITY_ONE,
2003 ACTION_CGROUP_DISRUPTIVE,
2005 msre_action_deny_init,
2010 msre_engine_action_register(engine,
2015 ACTION_CARDINALITY_ONE,
2017 msre_action_status_validate,
2018 msre_action_status_init,
2023 msre_engine_action_register(engine,
2028 ACTION_CARDINALITY_ONE,
2029 ACTION_CGROUP_DISRUPTIVE,
2031 msre_action_drop_init,
2036 msre_engine_action_register(engine,
2041 ACTION_CARDINALITY_ONE,
2043 msre_action_pause_validate,
2044 msre_action_pause_init,
2049 msre_engine_action_register(engine,
2054 ACTION_CARDINALITY_ONE,
2055 ACTION_CGROUP_DISRUPTIVE,
2056 msre_action_redirect_validate,
2057 msre_action_redirect_init,
2058 msre_action_redirect_execute
2062 msre_engine_action_register(engine,
2067 ACTION_CARDINALITY_ONE,
2068 ACTION_CGROUP_DISRUPTIVE,
2069 msre_action_proxy_validate,
2070 msre_action_proxy_init,
2071 msre_action_proxy_execute
2075 msre_engine_action_register(engine,
2080 ACTION_CARDINALITY_ONE,
2081 ACTION_CGROUP_DISRUPTIVE,
2083 msre_action_pass_init,
2088 msre_engine_action_register(engine,
2093 ACTION_CARDINALITY_ONE,
2094 ACTION_CGROUP_DISRUPTIVE,
2095 msre_action_skip_validate,
2096 msre_action_skip_init,
2101 msre_engine_action_register(engine,
2106 ACTION_CARDINALITY_ONE,
2107 ACTION_CGROUP_DISRUPTIVE,
2108 msre_action_skipAfter_validate,
2109 msre_action_skipAfter_init,
2114 msre_engine_action_register(engine,
2119 ACTION_CARDINALITY_ONE,
2120 ACTION_CGROUP_DISRUPTIVE,
2121 msre_action_allow_validate,
2122 msre_action_allow_init,
2127 /* ENH: This should be ACTION_NON_DISRUPTIVE or ACTION_FLOW??? */
2128 msre_engine_action_register(engine,
2133 ACTION_CARDINALITY_ONE,
2135 msre_action_phase_validate,
2136 msre_action_phase_init,
2141 msre_engine_action_register(engine,
2143 ACTION_NON_DISRUPTIVE,
2146 ACTION_CARDINALITY_MANY,
2148 msre_action_t_validate,
2154 msre_engine_action_register(engine,
2156 ACTION_NON_DISRUPTIVE,
2159 ACTION_CARDINALITY_MANY,
2161 msre_action_ctl_validate,
2162 msre_action_ctl_init,
2163 msre_action_ctl_execute
2167 msre_engine_action_register(engine,
2169 ACTION_NON_DISRUPTIVE,
2172 ACTION_CARDINALITY_MANY,
2174 msre_action_xmlns_validate,
2180 msre_engine_action_register(engine,
2182 ACTION_NON_DISRUPTIVE,
2185 ACTION_CARDINALITY_ONE,
2193 msre_engine_action_register(engine,
2195 ACTION_NON_DISRUPTIVE,
2198 ACTION_CARDINALITY_MANY,
2202 msre_action_sanitiseArg_execute
2205 /* sanitiseMatched */
2206 msre_engine_action_register(engine,
2208 ACTION_NON_DISRUPTIVE,
2211 ACTION_CARDINALITY_MANY,
2215 msre_action_sanitiseMatched_execute
2218 /* sanitiseRequestHeader */
2219 msre_engine_action_register(engine,
2220 "sanitiseRequestHeader",
2221 ACTION_NON_DISRUPTIVE,
2224 ACTION_CARDINALITY_MANY,
2228 msre_action_sanitiseRequestHeader_execute
2231 /* sanitiseResponseHeader */
2232 msre_engine_action_register(engine,
2233 "sanitiseResponseHeader",
2234 ACTION_NON_DISRUPTIVE,
2237 ACTION_CARDINALITY_MANY,
2241 msre_action_sanitiseResponseHeader_execute
2245 msre_engine_action_register(engine,
2247 ACTION_NON_DISRUPTIVE,
2250 ACTION_CARDINALITY_MANY,
2254 msre_action_setenv_execute
2258 msre_engine_action_register(engine,
2260 ACTION_NON_DISRUPTIVE,
2263 ACTION_CARDINALITY_MANY,
2267 msre_action_setvar_execute
2271 msre_engine_action_register(engine,
2273 ACTION_NON_DISRUPTIVE,
2276 ACTION_CARDINALITY_MANY,
2280 msre_action_expirevar_execute
2284 msre_engine_action_register(engine,
2286 ACTION_NON_DISRUPTIVE,
2289 ACTION_CARDINALITY_MANY,
2293 msre_action_deprecatevar_execute
2297 msre_engine_action_register(engine,
2299 ACTION_NON_DISRUPTIVE,
2302 ACTION_CARDINALITY_MANY,
2306 msre_action_initcol_execute
2310 msre_engine_action_register(engine,
2312 ACTION_NON_DISRUPTIVE,
2315 ACTION_CARDINALITY_ONE,
2319 msre_action_setsid_execute
2323 msre_engine_action_register(engine,
2325 ACTION_NON_DISRUPTIVE,
2328 ACTION_CARDINALITY_ONE,
2332 msre_action_setuid_execute
2336 msre_engine_action_register(engine,
2338 ACTION_NON_DISRUPTIVE,
2341 ACTION_CARDINALITY_MANY,
2343 msre_action_exec_validate,
2345 msre_action_exec_execute
2349 msre_engine_action_register(engine,
2351 ACTION_NON_DISRUPTIVE,
2354 ACTION_CARDINALITY_ONE,
2362 /* ENH: This should be ACTION_METADATA??? */
2363 msre_engine_action_register(engine,
2365 ACTION_NON_DISRUPTIVE,
2368 ACTION_CARDINALITY_MANY,
2376 msre_engine_action_register(engine,
2378 ACTION_NON_DISRUPTIVE,
2381 ACTION_CARDINALITY_ONE,
2385 msre_action_prepend_execute
2389 msre_engine_action_register(engine,
2391 ACTION_NON_DISRUPTIVE,
2394 ACTION_CARDINALITY_ONE,
2398 msre_action_append_execute