Imported Upstream version 2.5.11
[libapache-mod-security.git] / apache2 / re_actions.c
1 /*
2  * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3  * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/)
4  *
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.
8  *
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
12  * distribution.
13  *
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.
17  *
18  */
19 #include "re.h"
20 #include <ctype.h>
21
22 /**
23  * Register action with the engine.
24  */
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)
30 {
31     msre_action_metadata *metadata = (msre_action_metadata *)apr_pcalloc(engine->mp,
32         sizeof(msre_action_metadata));
33     if (metadata == NULL) return;
34
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;
45
46     apr_table_setn(engine->actions, name, (void *)metadata);
47 }
48
49 /**
50  * Generates a single variable (from the supplied metadata).
51  */
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)
54 {
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;
59     int i;
60
61     /* Sanity check. */
62     if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL;
63
64     vartab = apr_table_make(mptmp, 16);
65     var->metadata->generate(msr, var, rule, vartab, mptmp);
66
67     arr = apr_table_elts(vartab);
68     if (arr->nelts == 0) return NULL;
69     te = (apr_table_entry_t *)arr->elts;
70
71     rvar = (msre_var *)te[0].val;
72
73     /* Return straight away if there were no
74      * transformation functions supplied.
75      */
76     if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) {
77         return rvar;
78     }
79
80     /* Copy the value so that we can transform it in place. */
81     rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len);
82
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];
86         char *rval;
87         int rc;
88         long int rval_len;
89
90         rc = tfn->execute(mptmp, (unsigned char *)rvar->value,
91                     rvar->value_len, &rval, &rval_len);
92
93         rvar->value = rval;
94         rvar->value_len = rval_len;
95
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));
99         }
100     }
101
102     return rvar;
103 }
104
105 /**
106  *
107  */
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)
110 {
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;
115     int i, j;
116
117     /* Sanity check. */
118     if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL;
119
120     /* Generate variables. */
121     vartab = apr_table_make(mptmp, 16);
122     var->metadata->generate(msr, var, rule, vartab, mptmp);
123
124     /* Return straight away if there were no
125      * transformation functions supplied.
126      */
127     if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) {
128         return vartab;
129     }
130
131     tvartab = apr_table_make(mptmp, 16);
132
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;
137
138         /* Copy the value so that we can transform it in place. */
139         rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len);
140
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];
144             char *rval;
145             int rc;
146             long int rval_len;
147
148             rc = tfn->execute(mptmp, (unsigned char *)rvar->value,
149                 rvar->value_len, &rval, &rval_len);
150
151             rvar->value = rval;
152             rvar->value_len = rval_len;
153
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));
157             }
158         }
159
160         apr_table_addn(tvartab, rvar->name, (void *)rvar);
161     }
162
163     return tvartab;
164 }
165
166 /**
167  * Expands macros ("%{NAME}" entities) if present
168  * in the given variable.
169  */
170 int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp) {
171     char *data = NULL;
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;
176     int i, offset = 0;
177
178     if (var->value == NULL) return 0;
179
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.
183      */
184
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;
188
189     text_start = next_text_start = data;
190     do {
191         text_start = next_text_start;
192         p = strstr(text_start, "%");
193         if (p != NULL) {
194             char *var_name = NULL;
195             char *var_value = NULL;
196
197             if ((*(p + 1) == '{')&&(*(p + 2) != '\0')) {
198                 char *var_start = p + 2;
199
200                 t = var_start;
201                 while((*t != '\0')&&(*t != '}')) t++;
202                 if (*t == '}') {
203                     /* Named variable. */
204
205                     var_name = apr_pstrmemdup(mptmp, var_start, t - var_start);
206                     q = strstr(var_name, ".");
207                     if (q != NULL) {
208                         var_value = q + 1;
209                         *q = '\0';
210                     }
211
212                     next_text_start = t + 1; /* *t was '}' */
213                 } else {
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));
218                     }
219
220                     next_text_start = t; /* *t was '\0' */
221                 }
222             }
223
224             if (var_name != NULL) {
225                 char *my_error_msg = NULL;
226                 msre_var *var_generated = NULL;
227                 msre_var *var_resolved = NULL;
228
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;
235
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,
238                     msr, &my_error_msg);
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\"",
249                                 var_name,
250                                 (var_value ? "." : ""),
251                                 (var_value ? var_value : ""),
252                                 log_escape_ex(mptmp, part->value, part->value_len));
253                         }
254                     }
255                 } else {
256                     msr_log(msr, 4, "Failed to resolve macro %%{%s%s%s}: %s",
257                         var_name,
258                         (var_value ? "." : ""),
259                         (var_value ? var_value : ""),
260                         my_error_msg);
261                 }
262             } else {
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;
269
270                 next_text_start = p + 1;
271             }
272         } else {
273             /* Text part. */
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;
278         }
279     } while (p != NULL);
280
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.
284      */
285     if (arr->nelts > 1) {
286         /* Figure out the required size for the string. */
287         var->value_len = 0;
288         for(i = 0; i < arr->nelts; i++) {
289             part = ((msc_string **)arr->elts)[i];
290             var->value_len += part->value_len;
291         }
292
293         /* Allocate the string. */
294         var->value = apr_palloc(msr->mp, var->value_len + 1);
295         if (var->value == NULL) return -1;
296
297         /* Combine the parts. */
298         offset = 0;
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;
303         }
304         var->value[offset] = '\0';
305     }
306
307     return 1;
308 }
309
310 /**
311  * Record the original collection values to use to calculate deltas.
312  * This can be called multiple times and will not overwrite the first
313  * value that is set.
314  */
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;
319
320     if (orig_var == NULL) {
321         msr_log(msr, 1, "Internal Error: Attempt to record NULL original variable.");
322         return -1;
323     }
324
325     var_name = orig_var->name;
326     table = (apr_table_t *)apr_table_get(msr->collections_original, col_name);
327
328     /* Does the collection exist already? */
329     if (table == NULL) {
330         table = apr_table_make(msr->mp, 24);
331         if (table == NULL) {
332             msr_log(msr, 1, "Failed to allocate space for original collection.");
333             return -1;
334         }
335         apr_table_setn(msr->collections_original, apr_pstrdup(msr->mp, col_name), (void *)table);
336     }
337     else {
338         /* Does the variable exist already? */
339         var = (msc_string *)apr_table_get(table, var_name);
340         if (var != NULL) {
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));
343             }
344             return 1;
345         }
346     }
347
348     var = (msc_string *)apr_palloc(msr->mp, sizeof(msc_string));
349     if (var == NULL) {
350         msr_log(msr, 1, "Failed to allocate space for original collection variable.");
351         return -1;
352     }
353
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);
360
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));
363     }
364
365     return 0;
366 }
367
368 /* id */
369
370 static apr_status_t msre_action_id_init(msre_engine *engine, msre_actionset *actionset,
371     msre_action *action)
372 {
373     actionset->id = action->param;
374     return 1;
375 }
376
377 /* rev */
378
379 static apr_status_t msre_action_rev_init(msre_engine *engine, msre_actionset *actionset,
380     msre_action *action)
381 {
382     actionset->rev = action->param;
383     return 1;
384 }
385
386 /* msg */
387
388 static apr_status_t msre_action_msg_init(msre_engine *engine, msre_actionset *actionset,
389     msre_action *action)
390 {
391     actionset->msg = action->param;
392     return 1;
393 }
394
395 /* logdata */
396
397 static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset *actionset,
398     msre_action *action)
399 {
400     actionset->logdata = action->param;
401     return 1;
402 }
403
404 /* severity */
405
406 static apr_status_t msre_action_severity_init(msre_engine *engine,
407     msre_actionset *actionset, msre_action *action)
408 {
409     actionset->severity = atoi(action->param);
410     return 1;
411 }
412
413 /* chain */
414
415 static apr_status_t msre_action_chain_init(msre_engine *engine, msre_actionset *actionset,
416     msre_action *action)
417 {
418     actionset->is_chained = 1;
419     return 1;
420 }
421
422 /* log */
423 static apr_status_t msre_action_log_init(msre_engine *engine, msre_actionset *actionset,
424     msre_action *action)
425 {
426     actionset->log = 1;
427     return 1;
428 }
429
430 /* nolog */
431 static apr_status_t msre_action_nolog_init(msre_engine *engine, msre_actionset *actionset,
432     msre_action *action)
433 {
434     actionset->log = 0;
435     actionset->auditlog = 0;
436     return 1;
437 }
438
439 /* auditlog */
440 static apr_status_t msre_action_auditlog_init(msre_engine *engine, msre_actionset *actionset,
441     msre_action *action)
442 {
443     actionset->auditlog = 1;
444     return 1;
445 }
446
447 /* noauditlog */
448 static apr_status_t msre_action_noauditlog_init(msre_engine *engine, msre_actionset *actionset,
449     msre_action *action)
450 {
451     actionset->auditlog = 0;
452     return 1;
453 }
454
455 /* block */
456 static apr_status_t msre_action_block_init(msre_engine *engine, msre_actionset *actionset,
457     msre_action *action)
458 {
459     /* Right now we just set a flag and inherit the real disruptive action */
460     actionset->block = 1;
461     return 1;
462 }
463
464 /* deny */
465 static apr_status_t msre_action_deny_init(msre_engine *engine, msre_actionset *actionset,
466     msre_action *action)
467 {
468     actionset->intercept_action = ACTION_DENY;
469     actionset->intercept_action_rec = action;
470     return 1;
471 }
472
473 /* status */
474 static char *msre_action_status_validate(msre_engine *engine, msre_action *action) {
475     /* ENH action->param must be a valid HTTP status code. */
476     return NULL;
477 }
478
479 static apr_status_t msre_action_status_init(msre_engine *engine, msre_actionset *actionset,
480     msre_action *action)
481 {
482     actionset->intercept_status = atoi(action->param);
483     return 1;
484 }
485
486 /* drop */
487 static apr_status_t msre_action_drop_init(msre_engine *engine, msre_actionset *actionset,
488     msre_action *action)
489 {
490     actionset->intercept_action = ACTION_DROP;
491     actionset->intercept_action_rec = action;
492     return 1;
493 }
494
495 /* pause */
496 static char *msre_action_pause_validate(msre_engine *engine, msre_action *action) {
497     /* ENH Validate a positive number. */
498     return NULL;
499 }
500
501 static apr_status_t msre_action_pause_init(msre_engine *engine, msre_actionset *actionset,
502     msre_action *action)
503 {
504     actionset->intercept_pause = atoi(action->param);
505     return 1;
506 }
507
508 /* redirect */
509
510 static char *msre_action_redirect_validate(msre_engine *engine, msre_action *action) {
511     /* ENH Add validation. */
512     return NULL;
513 }
514
515 static apr_status_t msre_action_redirect_init(msre_engine *engine, msre_actionset *actionset,
516     msre_action *action)
517 {
518     actionset->intercept_action = ACTION_REDIRECT;
519     actionset->intercept_uri = action->param;
520     actionset->intercept_action_rec = action;
521     return 1;
522 }
523
524 static apr_status_t msre_action_redirect_execute(modsec_rec *msr, apr_pool_t *mptmp,
525     msre_rule *rule, msre_action *action)
526 {
527     msc_string *var = NULL;
528
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);
534
535     rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len);
536
537     return 1;
538 }
539
540 /* proxy */
541
542 static char *msre_action_proxy_validate(msre_engine *engine, msre_action *action) {
543     /* ENH Add validation. */
544     return NULL;
545 }
546
547 static apr_status_t msre_action_proxy_init(msre_engine *engine, msre_actionset *actionset,
548     msre_action *action)
549 {
550     actionset->intercept_action = ACTION_PROXY;
551     actionset->intercept_uri = action->param;
552     actionset->intercept_action_rec = action;
553     return 1;
554 }
555
556 static apr_status_t msre_action_proxy_execute(modsec_rec *msr, apr_pool_t *mptmp,
557     msre_rule *rule, msre_action *action)
558 {
559     msc_string *var = NULL;
560
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);
566
567     rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len);
568
569     return 1;
570 }
571
572 /* pass */
573
574 static apr_status_t msre_action_pass_init(msre_engine *engine, msre_actionset *actionset,
575     msre_action *action)
576 {
577     actionset->intercept_action = ACTION_NONE;
578     actionset->intercept_action_rec = action;
579     return 1;
580 }
581
582 /* skip */
583
584 static char *msre_action_skip_validate(msre_engine *engine, msre_action *action) {
585     /* ENH Add validation. */
586     return NULL;
587 }
588
589 static apr_status_t msre_action_skip_init(msre_engine *engine, msre_actionset *actionset,
590     msre_action *action)
591 {
592     actionset->skip_count = atoi(action->param);
593     if (actionset->skip_count <= 0) actionset->skip_count = 1;
594     return 1;
595 }
596
597 /* skipAfter */
598
599 static char *msre_action_skipAfter_validate(msre_engine *engine, msre_action *action) {
600     /* ENH Add validation. */
601     return NULL;
602 }
603
604 static apr_status_t msre_action_skipAfter_init(msre_engine *engine, msre_actionset *actionset,
605     msre_action *action)
606 {
607     actionset->skip_after = action->param;
608     return 1;
609 }
610
611 /* allow */
612
613 static apr_status_t msre_action_allow_init(msre_engine *engine, msre_actionset *actionset,
614     msre_action *action)
615 {
616     actionset->intercept_action = ACTION_ALLOW;
617     actionset->intercept_action_rec = action;
618
619     if (action->param != NULL) {
620         if (strcasecmp(action->param, "phase") == 0) {
621             actionset->intercept_action = ACTION_ALLOW_PHASE;
622         } else
623         if (strcasecmp(action->param, "request") == 0) {
624             actionset->intercept_action = ACTION_ALLOW_REQUEST;
625         }
626     }
627
628     return 1;
629 }
630
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) {
634             return NULL;
635         } else
636         if (strcasecmp(action->param, "request") == 0) {
637             return NULL;
638         } else {
639             return apr_psprintf(engine->mp, "Invalid parameter for allow: %s", action->param);
640         }
641     }
642
643     return NULL;
644 }
645
646 /* phase */
647
648 static char *msre_action_phase_validate(msre_engine *engine, msre_action *action) {
649     /* ENH Add validation. */
650     return NULL;
651 }
652
653 static apr_status_t msre_action_phase_init(msre_engine *engine, msre_actionset *actionset,
654     msre_action *action)
655 {
656     actionset->phase = atoi(action->param);
657     return 1;
658 }
659
660 /* t */
661
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",
666         action->param);
667     action->param_data = metadata;
668     return NULL;
669 }
670
671 static apr_status_t msre_action_t_init(msre_engine *engine, msre_actionset *actionset,
672     msre_action *action)
673 {
674     msre_tfn_metadata *metadata = (msre_tfn_metadata *)action->param_data;
675     action->param_data = metadata;
676     return 1;
677 }
678
679 /* ctl */
680 static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) {
681     char *name = NULL;
682     char *value = NULL;
683
684     /* Parse first. */
685     if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) {
686         return FATAL_ERROR;
687     }
688     if (value == NULL) {
689         return apr_psprintf(engine->mp, "Missing ctl value for name: %s", name);
690     }
691
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);
698     } else
699     if (strcasecmp(name, "ruleRemoveById") == 0) {
700         /* ENH nothing yet */
701         return NULL;
702     } else
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);
707         }
708         return NULL;
709     } else
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.
713          */
714         return NULL;
715     } else
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);
721     } else
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);
726         }
727         return NULL;
728     } else
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);
735     } else
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);
741             }
742         }
743         else
744         if (is_valid_parts_specification(value) != 1) {
745             return apr_psprintf(engine->mp, "Invalid setting for ctl name "
746                 "auditLogParts: %s", value);
747         }
748         return NULL;
749     } else
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);
754     } else
755     if (strcasecmp(name, "requestBodyLimit") == 0) {
756         long int limit = strtol(value, NULL, 10);
757
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);
761         }
762
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);
766         }
767
768         return NULL;
769     } else
770     if (strcasecmp(name, "responseBodyLimit") == 0) {
771         long int limit = strtol(value, NULL, 10);
772
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);
776         }
777
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);
781         }
782
783         return NULL;
784     }
785     else {
786         return apr_psprintf(engine->mp, "Invalid ctl name setting: %s", name);
787     }
788 }
789
790 static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset,
791     msre_action *action)
792 {
793     /* Do nothing. */
794     return 1;
795 }
796
797 static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp,
798     msre_rule *rule, msre_action *action)
799 {
800     char *name = NULL;
801     char *value = NULL;
802
803     /* Parse first. */
804     if (parse_name_eq_value(msr->mp, action->param, &name, &value) < 0) return -1;
805     if (value == NULL) return -1;
806
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;
812         }
813         else
814         if (strcasecmp(value, "off") == 0) {
815             msr->txcfg->is_enabled = MODSEC_DISABLED;
816             msr->usercfg->is_enabled = MODSEC_DISABLED;
817         }
818         else
819         if (strcasecmp(value, "detectiononly") == 0) {
820             msr->txcfg->is_enabled = MODSEC_DETECTION_ONLY;
821             msr->usercfg->is_enabled = MODSEC_DETECTION_ONLY;
822         }
823
824         return 1;
825     } else
826     if (strcasecmp(name, "ruleRemoveById") == 0) {
827         *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value);
828         return 1;
829     } else
830     if (strcasecmp(name, "requestBodyAccess") == 0) {
831         int pv = parse_boolean(value);
832
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);
837
838         return 1;
839     } else
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;
844         }
845         else
846         if (strcasecmp(value, "off") == 0) {
847             msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF;
848             msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF;
849         }
850
851         msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", msr->txcfg->reqbody_buffering);
852
853         return 1;
854     } else
855     if (strcasecmp(name, "requestBodyProcessor") == 0) {
856         msr->msc_reqbody_processor = value;
857         msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value);
858
859         return 1;
860     } else
861     if (strcasecmp(name, "responseBodyAccess") == 0) {
862         int pv = parse_boolean(value);
863
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);
868
869         return 1;
870     } else
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;
875         }
876         else
877         if (strcasecmp(value, "off") == 0) {
878             msr->txcfg->auditlog_flag = AUDITLOG_OFF;
879             msr->usercfg->auditlog_flag = AUDITLOG_OFF;
880         }
881         else
882         if (strcasecmp(value, "relevantonly") == 0) {
883             msr->txcfg->auditlog_flag = AUDITLOG_RELEVANT;
884             msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT;
885         }
886
887         msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag);
888
889         return 1;
890     } else
891     if (strcasecmp(name, "auditLogParts") == 0) {
892         char *new_value = value;
893
894         if (value[0] == '+') {
895             /* Add the listed parts. */
896             new_value = apr_pstrcat(msr->mp, msr->txcfg->auditlog_parts, value + 1, NULL);
897         }
898         else
899         if (value[0] == '-') { /* Remove the listed parts. */
900             char c, *t = value + 1;
901
902             /* Start with the current value. */
903             new_value = apr_pstrdup(msr->mp, msr->txcfg->auditlog_parts);
904
905             while((c = *t++) != '\0') {
906                 char *s = new_value;
907                 char *d = new_value;
908
909                 while(*s != '\0') {
910                     if (*s != c) {
911                         *(d++) = *(s++);
912                     } else {
913                         s++;
914                     }
915                 }
916                 *d = '\0';
917             }
918         }
919
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);
924
925         return 1;
926     } else
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);
931
932         return 1;
933     } else
934     if (strcasecmp(name, "requestBodyLimit") == 0) {
935         long int limit = strtol(value, NULL, 10);
936
937         /* ENH Accept only in correct phase warn otherwise. */
938         msr->txcfg->reqbody_limit = limit;
939         msr->usercfg->reqbody_limit = limit;
940
941         return 1;
942     } else
943     if (strcasecmp(name, "responseBodyLimit") == 0) {
944         long int limit = strtol(value, NULL, 10);
945
946         /* ENH Accept only in correct phase warn otherwise. */
947         msr->txcfg->of_limit = limit;
948         msr->usercfg->of_limit = limit;
949
950         return 1;
951     }
952     else {
953         /* Should never happen, but log if it does. */
954         msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name);
955         return -1;
956     }
957 }
958
959 /* xmlns */
960 static char *msre_action_xmlns_validate(msre_engine *engine, msre_action *action) {
961     char *name = NULL;
962     char *value = NULL;
963
964     /* Parse first. */
965     if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) {
966         return FATAL_ERROR;
967     }
968     if (value == NULL) {
969         return apr_psprintf(engine->mp, "Missing xmlns href for prefix: %s", name);
970     }
971
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.
975      */
976
977     return NULL;
978 }
979
980 /* sanitiseArg */
981 static apr_status_t msre_action_sanitiseArg_execute(modsec_rec *msr, apr_pool_t *mptmp,
982     msre_rule *rule, msre_action *action)
983 {
984     const char *sargname = NULL;
985     const apr_array_header_t *tarr;
986     const apr_table_entry_t *telts;
987     int i;
988
989     sargname = action->param;
990
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;
995
996         if (strcasecmp(sargname, arg->name) == 0) {
997             apr_table_addn(msr->arguments_to_sanitise, arg->name, (void *)arg);
998         }
999     }
1000
1001     return 1;
1002 }
1003
1004 #define SANITISE_ARG                1
1005 #define SANITISE_REQUEST_HEADER     2
1006 #define SANITISE_RESPONSE_HEADER    3
1007
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)
1011 {
1012     const char *sargname = NULL;
1013     const apr_array_header_t *tarr;
1014     const apr_table_entry_t *telts;
1015     int i, type = 0;
1016     msc_string *mvar = msr->matched_var;
1017
1018     if (mvar->name_len == 0) return 0;
1019
1020     /* IMP1 We need to extract the variable name properly here,
1021      *      taking into account it may have been escaped.
1022      */
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;
1026     } else
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;
1030     } else
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;
1034     } else
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;
1038     } else
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;
1042     } else
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;
1046     }
1047     else {
1048         msr_log(msr, 3, "sanitiseMatched: Don't know how to handle variable: %s",
1049             mvar->name);
1050         return 0;
1051     }
1052
1053     switch(type) {
1054         case SANITISE_ARG :
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);
1061                 }
1062             }
1063             break;
1064
1065         case SANITISE_REQUEST_HEADER :
1066             apr_table_set(msr->request_headers_to_sanitise, sargname, "1");
1067             break;
1068
1069         case SANITISE_RESPONSE_HEADER :
1070             apr_table_set(msr->response_headers_to_sanitise, sargname, "1");
1071             break;
1072
1073         default :
1074             /* do nothing */
1075             break;
1076     }
1077
1078     return 1;
1079 }
1080
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)
1084 {
1085     apr_table_set(msr->request_headers_to_sanitise, action->param, "1");
1086     return 1;
1087 }
1088
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)
1092 {
1093     apr_table_set(msr->response_headers_to_sanitise, action->param, "1");
1094     return 1;
1095 }
1096
1097 /* setenv */
1098 static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptmp,
1099     msre_rule *rule, msre_action *action)
1100 {
1101     char *data = apr_pstrdup(mptmp, action->param);
1102     char *env_name = NULL, *env_value = NULL;
1103     char *s = NULL;
1104     msc_string *env = NULL;
1105
1106     /* Extract the name and the value. */
1107     /* IMP1 We have a function for this now, parse_name_eq_value? */
1108     s = strstr(data, "=");
1109     if (s == NULL) {
1110         env_name = data;
1111         env_value = "1";
1112     } else {
1113         env_name = data;
1114         env_value = s + 1;
1115         *s = '\0';
1116     }
1117
1118     if (msr->txcfg->debuglog_level >= 9) {
1119         msr_log(msr, 9, "Setting env variable: %s=%s", env_name, env_value);
1120     }
1121
1122     /* Expand and escape any macros in the name */
1123     env = apr_palloc(msr->mp, sizeof(msc_string));
1124     if (env == NULL) {
1125         msr_log(msr, 1, "Failed to allocate space to expand name macros");
1126         return -1;
1127     }
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);
1132
1133     /* Execute the requested action. */
1134     if (env_name[0] == '!') {
1135         /* Delete */
1136         apr_table_unset(msr->r->subprocess_env, env_name + 1);
1137
1138         if (msr->txcfg->debuglog_level >= 9) {
1139             msr_log(msr, 9, "Unset env variable \"%s\".", env_name);
1140         }
1141     } else {
1142         /* Set */
1143         char * val_value = NULL;
1144         msc_string *val = apr_palloc(msr->mp, sizeof(msc_string));
1145         if (val == NULL) {
1146             msr_log(msr, 1, "Failed to allocate space to expand value macros");
1147             return -1;
1148         }
1149
1150         /* Expand values in value */
1151         val->value = env_value;
1152         val->value_len = strlen(val->value);
1153         expand_macros(msr, val, rule, mptmp);
1154
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);
1157
1158         apr_table_set(msr->r->subprocess_env, env_name, val_value);
1159
1160         if (msr->txcfg->debuglog_level >= 9) {
1161             msr_log(msr, 9, "Set env variable \"%s\" to \"%s\".",
1162                 env_name,
1163                 log_escape(mptmp, val_value));
1164         }
1165     }
1166
1167     return 1;
1168 }
1169
1170 /* setvar */
1171 static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp,
1172     msre_rule *rule, msre_action *action)
1173 {
1174     char *data = apr_pstrdup(mptmp, action->param);
1175     char *col_name = NULL, *var_name = NULL, *var_value = NULL;
1176     char *s = NULL;
1177     apr_table_t *target_col = NULL;
1178     int is_negated = 0;
1179     msc_string *var = NULL;
1180
1181     /* Extract the name and the value. */
1182     /* IMP1 We have a function for this now, parse_name_eq_value? */
1183     s = strstr(data, "=");
1184     if (s == NULL) {
1185         var_name = data;
1186         var_value = "1";
1187     } else {
1188         var_name = data;
1189         var_value = s + 1;
1190         *s = '\0';
1191
1192         while ((*var_value != '\0')&&(isspace(*var_value))) var_value++;
1193     }
1194
1195     if (msr->txcfg->debuglog_level >= 9) {
1196         msr_log(msr, 9, "Setting variable: %s=%s", var_name, var_value);
1197     }
1198
1199
1200     /* Expand and escape any macros in the name */
1201     var = apr_palloc(msr->mp, sizeof(msc_string));
1202     if (var == NULL) {
1203         msr_log(msr, 1, "Failed to allocate space to expand name macros");
1204         return -1;
1205     }
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);
1210
1211     /* Handle the exclamation mark. */
1212     if (var_name[0] == '!') {
1213         var_name = var_name + 1;
1214         is_negated = 1;
1215     }
1216
1217     /* ENH Not possible to use ! and = at the same time. */
1218     /* ENH Not possible to change variable "KEY".        */
1219
1220     /* Figure out the collection name. */
1221     target_col = msr->tx_vars;
1222     s = strstr(var_name, ".");
1223     if (s == NULL) {
1224         msr_log(msr, 3, "Asked to set variable \"%s\", but no collection name specified. ",
1225             log_escape(msr->mp, var_name));
1226          return 0;
1227     }
1228     col_name = var_name;
1229     var_name = s + 1;
1230     *s = '\0';
1231
1232     /* Locate the collection. */
1233     if (strcasecmp(col_name, "tx") == 0) { /* Special case for TX variables. */
1234         target_col = msr->tx_vars;
1235     } else {
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));
1240             return 0;
1241         }
1242     }
1243
1244     if (is_negated) {
1245         /* Unset variable. */
1246
1247         /* ENH Refuse to remove certain variables, e.g. TIMEOUT, internal variables, etc... */
1248
1249         apr_table_unset(target_col, var_name);
1250
1251         if (msr->txcfg->debuglog_level >= 9) {
1252             msr_log(msr, 9, "Unset variable \"%s.%s\".", col_name, var_name);
1253         }
1254     }
1255     else {
1256         /* Set or change variable. */
1257
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));
1262             int value = 0;
1263
1264             if (val == NULL) {
1265                 msr_log(msr, 1, "Failed to allocate space to expand value macros");
1266                 return -1;
1267             }
1268
1269             /* Retrieve  variable or generate (if it does not exist). */
1270             rec = (msc_string *)apr_table_get(target_col, var_name);
1271             if (rec == NULL) {
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);
1275                 value = 0;
1276                 rec->value = apr_psprintf(msr->mp, "%d", value);
1277                 rec->value_len = strlen(rec->value);
1278             }
1279             else {
1280                 value = atoi(rec->value);
1281             }
1282
1283             /* Record the original value before we change it */
1284             collection_original_setvar(msr, col_name, rec);
1285
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;
1291
1292             if (msr->txcfg->debuglog_level >= 9) {
1293                 msr_log(msr, 9, "Relative change: %s=%d%s", var_name, value, var_value);
1294             }
1295
1296             /* Change value. */
1297             value += atoi(var_value);
1298             if (value < 0) value = 0; /* Counters never go below zero. */
1299
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);
1304
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));
1309             }
1310         }
1311         else {
1312             /* Absolute change. */
1313
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);
1320
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));
1326             }
1327         }
1328     }
1329
1330     /* Make note of the change so that we know later
1331      * we need to persist the collection.
1332      */
1333     apr_table_set(msr->collections_dirty, col_name, "1");
1334
1335     return 1;
1336 }
1337
1338 /* expirevar */
1339 static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *mptmp,
1340     msre_rule *rule, msre_action *action)
1341 {
1342     char *data = apr_pstrdup(mptmp, action->param);
1343     char *col_name = NULL, *var_name = NULL, *var_value = NULL;
1344     char *s = NULL;
1345     apr_table_t *target_col = NULL;
1346     msc_string *var = NULL;
1347
1348     /* Extract the name and the value. */
1349     /* IMP1 We have a function for this now, parse_name_eq_value? */
1350     s = strstr(data, "=");
1351     if (s == NULL) {
1352         var_name = data;
1353         var_value = "1";
1354     } else {
1355         var_name = data;
1356         var_value = s + 1;
1357         *s = '\0';
1358     }
1359
1360     if (msr->txcfg->debuglog_level >= 9) {
1361         msr_log(msr, 9, "Expiring variable: %s=%s", var_name, var_value);
1362     }
1363
1364     /* Expand and escape any macros in the name */
1365     var = apr_palloc(msr->mp, sizeof(msc_string));
1366     if (var == NULL) {
1367         msr_log(msr, 1, "Failed to allocate space to expand name macros");
1368         return -1;
1369     }
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);
1374
1375     /* Choose the collection to work with. */
1376     s = strstr(var_name, ".");
1377     if (s != NULL) {
1378         col_name = var_name;
1379         var_name = s + 1;
1380         *s = '\0';
1381
1382         /* IMP1 No need to handle TX here because TX variables cannot expire,
1383          *      but we definitely need to have a better error message.
1384          */
1385
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));
1390             return 0;
1391         }
1392     } else {
1393         msr_log(msr, 3, "Asked to expire variable \"%s\", but no collection name specified. ",
1394             log_escape(msr->mp, var_name));
1395         return 0;
1396     }
1397
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.
1401      */
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);
1405
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;
1411
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);
1416
1417     apr_table_setn(target_col, var->name, (void *)var);
1418
1419     msr_log(msr, 4, "Variable \"%s.%s\" set to expire in %s seconds.", col_name,
1420         var_name, var_value);
1421
1422     apr_table_set(msr->collections_dirty, col_name, "1");
1423
1424     return 1;
1425 }
1426
1427 /* deprecatevar */
1428 static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t *mptmp,
1429     msre_rule *rule, msre_action *action)
1430 {
1431     char *data = apr_pstrdup(mptmp, action->param);
1432     char *col_name = NULL, *var_name = NULL, *var_value = NULL;
1433     char *s = 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;
1438
1439     /* Extract the name and the value. */
1440     /* IMP1 We have a function for this now, parse_name_eq_value? */
1441     s = strstr(data, "=");
1442     if (s == NULL) {
1443         var_name = data;
1444         var_value = "1";
1445     } else {
1446         var_name = data;
1447         var_value = s + 1;
1448         *s = '\0';
1449     }
1450
1451     if (msr->txcfg->debuglog_level >= 9) {
1452         msr_log(msr, 9, "Deprecating variable: %s=%s", var_name, var_value);
1453     }
1454
1455     /* Expand and escape any macros in the name */
1456     var = apr_palloc(msr->mp, sizeof(msc_string));
1457     if (var == NULL) {
1458         msr_log(msr, 1, "Failed to allocate space to expand name macros");
1459         return -1;
1460     }
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);
1465
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;
1471
1472     /* Choose the collection to work with. */
1473     s = strstr(var_name, ".");
1474     if (s != NULL) {
1475         col_name = var_name;
1476         var_name = s + 1;
1477         *s = '\0';
1478
1479         /* IMP1 Add message TX variables cannot deprecate in value. */
1480
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));
1485             return 0;
1486         }
1487     } else {
1488         msr_log(msr, 3, "Asked to deprecate variable \"%s\", but no collection name specified. ",
1489             log_escape(msr->mp, var_name));
1490         return 0;
1491     }
1492
1493     /* Find the current value. */
1494     var = (msc_string *)apr_table_get(target_col, var_name);
1495     if (var == NULL) {
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));
1499         }
1500         return 0;
1501     }
1502     current_value = atoi(var->value);
1503
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.
1509          */
1510         return 0;
1511     }
1512
1513     current_time = apr_time_sec(apr_time_now());
1514     last_update_time = atoi(var_last_update_time->value);
1515
1516     s = strstr(var_value, "/");
1517     if (s == NULL) {
1518         msr_log(msr, 3, "Incorrect format for the deprecatevar argument: \"%s\"",
1519             log_escape(msr->mp, var_value));
1520         return 0;
1521     }
1522     *s = '\0';
1523     s++;
1524
1525     /* Deprecate the value using the given speed and the
1526      * time elapsed since the last update.
1527      */
1528     new_value = current_value -
1529         (atol(var_value) * ((current_time - last_update_time) / atol(s)));
1530     if (new_value < 0) new_value = 0;
1531
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);
1536
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));
1540
1541         apr_table_set(msr->collections_dirty, col_name, "1");
1542     } else {
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));
1548         }
1549     }
1550
1551     return 1;
1552 }
1553
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)
1556 {
1557     apr_table_t *table = NULL;
1558     msc_string *var = NULL;
1559
1560     /* IMP1 Cannot initialise the built-in collections this way. */
1561
1562     /* Does the collection exist already? */
1563     if (apr_table_get(msr->collections, col_name) != NULL) {
1564         /* ENH Warn about this. */
1565         return 0;
1566     }
1567
1568     /* Init collection from storage. */
1569     table = collection_retrieve(msr, real_col_name, col_key, col_key_len);
1570
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);
1575
1576         table = apr_table_make(msr->mp, 24);
1577         if (table == NULL) return -1;
1578
1579         /* IMP1 Is the timeout hard-coded to 3600? */
1580
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);
1588
1589         /* Remember the key. */
1590         var = apr_pcalloc(msr->mp, sizeof(msc_string));
1591         var->name = "KEY";
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);
1596
1597         /* The timeout. */
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);
1604
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.
1608          */
1609
1610         /* IMP1 Actually I want a better way to delete collections,
1611          *      perhaps a dedicated action.
1612          */
1613
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);
1620
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);
1628
1629         /* Create time. */
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);
1636
1637         /* Update counter. */
1638         var = apr_pcalloc(msr->mp, sizeof(msc_string));
1639         var->name = "UPDATE_COUNTER";
1640         var->name_len = strlen(var->name);
1641         var->value = "0";
1642         var->value_len = strlen(var->value);
1643         apr_table_setn(table, var->name, (void *)var);
1644
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);
1649         var->value = "1";
1650         var->value_len = strlen(var->value);
1651         apr_table_setn(table, var->name, (void *)var);
1652     }
1653
1654     /* Record the original counter value before we change it */
1655     var = (msc_string *)apr_table_get(table, "UPDATE_COUNTER");
1656     if (var != NULL) {
1657         collection_original_setvar(msr, col_name, var);
1658     }
1659
1660     /* Add the collection to the list. */
1661     apr_table_setn(msr->collections, apr_pstrdup(msr->mp, col_name), (void *)table);
1662
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));
1666     } else {
1667         msr_log(msr, 4, "Added collection \"%s\" to the list.",
1668             log_escape(msr->mp, real_col_name));
1669     }
1670
1671     return 1;
1672 }
1673
1674 /* initcol */
1675 static apr_status_t msre_action_initcol_execute(modsec_rec *msr, apr_pool_t *mptmp,
1676     msre_rule *rule, msre_action *action)
1677 {
1678     char *data = apr_pstrdup(msr->mp, action->param);
1679     char *col_name = NULL, *col_key = NULL;
1680     unsigned int col_key_len;
1681
1682     msc_string *var = NULL;
1683     char *s = NULL;
1684
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;
1689     col_name = data;
1690     col_key = s + 1;
1691     *s = '\0';
1692
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);
1698
1699     col_key = var->value;
1700     col_key_len = var->value_len;
1701
1702     return init_collection(msr, col_name, col_name, col_key, col_key_len);
1703 }
1704
1705 /* setsid */
1706 static apr_status_t msre_action_setsid_execute(modsec_rec *msr, apr_pool_t *mptmp,
1707     msre_rule *rule, msre_action *action)
1708 {
1709     msc_string *var = NULL;
1710     char *real_col_name = NULL, *col_key = NULL;
1711     unsigned int col_key_len;
1712
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);
1719
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);
1724
1725     /* Initialise collection. */
1726     return init_collection(msr, real_col_name, "SESSION", col_key, col_key_len);
1727 }
1728
1729 /* setuid */
1730 static apr_status_t msre_action_setuid_execute(modsec_rec *msr, apr_pool_t *mptmp,
1731     msre_rule *rule, msre_action *action)
1732 {
1733     msc_string *var = NULL;
1734     char *real_col_name = NULL, *col_key = NULL;
1735     unsigned int col_key_len;
1736
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);
1743
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);
1748
1749     /* Initialise collection. */
1750     return init_collection(msr, real_col_name, "USER", col_key, col_key_len);
1751 }
1752
1753 /* exec */
1754 static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) {
1755     #if defined(WITH_LUA)
1756     char *filename = (char *)action->param;
1757
1758     /* TODO Support relative filenames. */
1759
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;
1766
1767             /* Compile script. */
1768             char *msg = lua_compile(&script, filename, engine->mp);
1769             if (msg != NULL) return msg;
1770
1771             action->param_data = script;
1772         }
1773     }
1774     #endif
1775
1776     return NULL;
1777 }
1778
1779 static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp,
1780     msre_rule *rule, msre_action *action)
1781 {
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;
1786
1787         if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) {
1788             msr_log(msr, 1, "%s", my_error_msg);
1789             return 0;
1790         }
1791     } else
1792     #endif
1793     { /* Execute as shell script. */
1794         char *script_output = NULL;
1795
1796         int rc = apache2_exec(msr, action->param, NULL, &script_output);
1797         if (rc != 1) {
1798             msr_log(msr, 1, "Failed to execute: %s", action->param);
1799             return 0;
1800         }
1801     }
1802
1803     return 1;
1804 }
1805
1806 /* prepend */
1807 static apr_status_t msre_action_prepend_execute(modsec_rec *msr, apr_pool_t *mptmp,
1808     msre_rule *rule, msre_action *action)
1809 {
1810     msc_string *var = NULL;
1811
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);
1818
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;
1822
1823     return 1;
1824 }
1825
1826 /* append */
1827 static apr_status_t msre_action_append_execute(modsec_rec *msr, apr_pool_t *mptmp,
1828     msre_rule *rule, msre_action *action)
1829 {
1830     msc_string *var = NULL;
1831
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);
1838
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;
1842
1843     return 1;
1844 }
1845
1846 /* -- */
1847
1848 /**
1849  *
1850  */
1851 void msre_engine_register_default_actions(msre_engine *engine) {
1852
1853     /* id */
1854     msre_engine_action_register(engine,
1855         "id",
1856         ACTION_METADATA,
1857         1, 1,
1858         NO_PLUS_MINUS,
1859         ACTION_CARDINALITY_ONE,
1860         ACTION_CGROUP_NONE,
1861         NULL,
1862         msre_action_id_init,
1863         NULL
1864     );
1865
1866     /* rev */
1867     msre_engine_action_register(engine,
1868         "rev",
1869         ACTION_METADATA,
1870         1, 1,
1871         NO_PLUS_MINUS,
1872         ACTION_CARDINALITY_ONE,
1873         ACTION_CGROUP_NONE,
1874         NULL,
1875         msre_action_rev_init,
1876         NULL
1877     );
1878
1879     /* msg */
1880     msre_engine_action_register(engine,
1881         "msg",
1882         ACTION_METADATA,
1883         1, 1,
1884         NO_PLUS_MINUS,
1885         ACTION_CARDINALITY_ONE,
1886         ACTION_CGROUP_NONE,
1887         NULL,
1888         msre_action_msg_init,
1889         NULL
1890     );
1891
1892     /* logdata */
1893     msre_engine_action_register(engine,
1894         "logdata",
1895         ACTION_METADATA,
1896         1, 1,
1897         NO_PLUS_MINUS,
1898         ACTION_CARDINALITY_ONE,
1899         ACTION_CGROUP_NONE,
1900         NULL,
1901         msre_action_logdata_init,
1902         NULL
1903     );
1904
1905     /* severity */
1906     msre_engine_action_register(engine,
1907         "severity",
1908         ACTION_METADATA,
1909         1, 1,
1910         NO_PLUS_MINUS,
1911         ACTION_CARDINALITY_ONE,
1912         ACTION_CGROUP_NONE,
1913         NULL,
1914         msre_action_severity_init,
1915         NULL
1916     );
1917
1918     /* chain */
1919     msre_engine_action_register(engine,
1920         "chain",
1921         ACTION_FLOW,
1922         0, 0,
1923         NO_PLUS_MINUS,
1924         ACTION_CARDINALITY_ONE,
1925         ACTION_CGROUP_NONE,
1926         NULL,
1927         msre_action_chain_init,
1928         NULL
1929     );
1930
1931     /* log */
1932     msre_engine_action_register(engine,
1933         "log",
1934         ACTION_NON_DISRUPTIVE,
1935         0, 0,
1936         NO_PLUS_MINUS,
1937         ACTION_CARDINALITY_ONE,
1938         ACTION_CGROUP_LOG,
1939         NULL,
1940         msre_action_log_init,
1941         NULL
1942     );
1943
1944     /* nolog */
1945     msre_engine_action_register(engine,
1946         "nolog",
1947         ACTION_NON_DISRUPTIVE,
1948         0, 0,
1949         NO_PLUS_MINUS,
1950         ACTION_CARDINALITY_ONE,
1951         ACTION_CGROUP_LOG,
1952         NULL,
1953         msre_action_nolog_init,
1954         NULL
1955     );
1956
1957     /* auditlog */
1958     msre_engine_action_register(engine,
1959         "auditlog",
1960         ACTION_NON_DISRUPTIVE,
1961         0, 0,
1962         NO_PLUS_MINUS,
1963         ACTION_CARDINALITY_ONE,
1964         ACTION_CGROUP_AUDITLOG,
1965         NULL,
1966         msre_action_auditlog_init,
1967         NULL
1968     );
1969
1970     /* noauditlog */
1971     msre_engine_action_register(engine,
1972         "noauditlog",
1973         ACTION_NON_DISRUPTIVE,
1974         0, 0,
1975         NO_PLUS_MINUS,
1976         ACTION_CARDINALITY_ONE,
1977         ACTION_CGROUP_AUDITLOG,
1978         NULL,
1979         msre_action_noauditlog_init,
1980         NULL
1981     );
1982
1983     /* deny */
1984     msre_engine_action_register(engine,
1985         "block",
1986         ACTION_DISRUPTIVE,
1987         0, 0,
1988         NO_PLUS_MINUS,
1989         ACTION_CARDINALITY_ONE,
1990         ACTION_CGROUP_DISRUPTIVE,
1991         NULL,
1992         msre_action_block_init,
1993         NULL
1994     );
1995
1996     /* deny */
1997     msre_engine_action_register(engine,
1998         "deny",
1999         ACTION_DISRUPTIVE,
2000         0, 0,
2001         NO_PLUS_MINUS,
2002         ACTION_CARDINALITY_ONE,
2003         ACTION_CGROUP_DISRUPTIVE,
2004         NULL,
2005         msre_action_deny_init,
2006         NULL
2007     );
2008
2009     /* status */
2010     msre_engine_action_register(engine,
2011         "status",
2012         ACTION_DISRUPTIVE,
2013         1, 1,
2014         NO_PLUS_MINUS,
2015         ACTION_CARDINALITY_ONE,
2016         ACTION_CGROUP_NONE,
2017         msre_action_status_validate,
2018         msre_action_status_init,
2019         NULL
2020     );
2021
2022     /* drop */
2023     msre_engine_action_register(engine,
2024         "drop",
2025         ACTION_DISRUPTIVE,
2026         0, 0,
2027         NO_PLUS_MINUS,
2028         ACTION_CARDINALITY_ONE,
2029         ACTION_CGROUP_DISRUPTIVE,
2030         NULL,
2031         msre_action_drop_init,
2032         NULL
2033     );
2034
2035     /* pause */
2036     msre_engine_action_register(engine,
2037         "pause",
2038         ACTION_DISRUPTIVE,
2039         1, 1,
2040         NO_PLUS_MINUS,
2041         ACTION_CARDINALITY_ONE,
2042         ACTION_CGROUP_NONE,
2043         msre_action_pause_validate,
2044         msre_action_pause_init,
2045         NULL
2046     );
2047
2048     /* redirect */
2049     msre_engine_action_register(engine,
2050         "redirect",
2051         ACTION_DISRUPTIVE,
2052         1, 1,
2053         NO_PLUS_MINUS,
2054         ACTION_CARDINALITY_ONE,
2055         ACTION_CGROUP_DISRUPTIVE,
2056         msre_action_redirect_validate,
2057         msre_action_redirect_init,
2058         msre_action_redirect_execute
2059     );
2060
2061     /* proxy */
2062     msre_engine_action_register(engine,
2063         "proxy",
2064         ACTION_DISRUPTIVE,
2065         1, 1,
2066         NO_PLUS_MINUS,
2067         ACTION_CARDINALITY_ONE,
2068         ACTION_CGROUP_DISRUPTIVE,
2069         msre_action_proxy_validate,
2070         msre_action_proxy_init,
2071         msre_action_proxy_execute
2072     );
2073
2074     /* pass */
2075     msre_engine_action_register(engine,
2076         "pass",
2077         ACTION_DISRUPTIVE,
2078         0, 0,
2079         NO_PLUS_MINUS,
2080         ACTION_CARDINALITY_ONE,
2081         ACTION_CGROUP_DISRUPTIVE,
2082         NULL,
2083         msre_action_pass_init,
2084         NULL
2085     );
2086
2087     /* skip */
2088     msre_engine_action_register(engine,
2089         "skip",
2090         ACTION_DISRUPTIVE,
2091         1, 1,
2092         NO_PLUS_MINUS,
2093         ACTION_CARDINALITY_ONE,
2094         ACTION_CGROUP_DISRUPTIVE,
2095         msre_action_skip_validate,
2096         msre_action_skip_init,
2097         NULL
2098     );
2099
2100     /* skipAfter */
2101     msre_engine_action_register(engine,
2102         "skipAfter",
2103         ACTION_DISRUPTIVE,
2104         1, 1,
2105         NO_PLUS_MINUS,
2106         ACTION_CARDINALITY_ONE,
2107         ACTION_CGROUP_DISRUPTIVE,
2108         msre_action_skipAfter_validate,
2109         msre_action_skipAfter_init,
2110         NULL
2111     );
2112
2113     /* allow */
2114     msre_engine_action_register(engine,
2115         "allow",
2116         ACTION_DISRUPTIVE,
2117         0, 1,
2118         NO_PLUS_MINUS,
2119         ACTION_CARDINALITY_ONE,
2120         ACTION_CGROUP_DISRUPTIVE,
2121         msre_action_allow_validate,
2122         msre_action_allow_init,
2123         NULL
2124     );
2125
2126     /* phase */
2127     /* ENH: This should be ACTION_NON_DISRUPTIVE or ACTION_FLOW??? */
2128     msre_engine_action_register(engine,
2129         "phase",
2130         ACTION_DISRUPTIVE,
2131         1, 1,
2132         NO_PLUS_MINUS,
2133         ACTION_CARDINALITY_ONE,
2134         ACTION_CGROUP_NONE,
2135         msre_action_phase_validate,
2136         msre_action_phase_init,
2137         NULL
2138     );
2139
2140     /* t */
2141     msre_engine_action_register(engine,
2142         "t",
2143         ACTION_NON_DISRUPTIVE,
2144         1, 1,
2145         ALLOW_PLUS_MINUS,
2146         ACTION_CARDINALITY_MANY,
2147         ACTION_CGROUP_NONE,
2148         msre_action_t_validate,
2149         msre_action_t_init,
2150         NULL
2151     );
2152
2153     /* ctl */
2154     msre_engine_action_register(engine,
2155         "ctl",
2156         ACTION_NON_DISRUPTIVE,
2157         1, 1,
2158         NO_PLUS_MINUS,
2159         ACTION_CARDINALITY_MANY,
2160         ACTION_CGROUP_NONE,
2161         msre_action_ctl_validate,
2162         msre_action_ctl_init,
2163         msre_action_ctl_execute
2164     );
2165
2166     /* xmlns */
2167     msre_engine_action_register(engine,
2168         "xmlns",
2169         ACTION_NON_DISRUPTIVE,
2170         1, 1,
2171         NO_PLUS_MINUS,
2172         ACTION_CARDINALITY_MANY,
2173         ACTION_CGROUP_NONE,
2174         msre_action_xmlns_validate,
2175         NULL,
2176         NULL
2177     );
2178
2179     /* capture */
2180     msre_engine_action_register(engine,
2181         "capture",
2182         ACTION_NON_DISRUPTIVE,
2183         0, 0,
2184         NO_PLUS_MINUS,
2185         ACTION_CARDINALITY_ONE,
2186         ACTION_CGROUP_NONE,
2187         NULL,
2188         NULL,
2189         NULL
2190     );
2191
2192     /* sanitiseArg */
2193     msre_engine_action_register(engine,
2194         "sanitiseArg",
2195         ACTION_NON_DISRUPTIVE,
2196         1, 1,
2197         NO_PLUS_MINUS,
2198         ACTION_CARDINALITY_MANY,
2199         ACTION_CGROUP_NONE,
2200         NULL,
2201         NULL,
2202         msre_action_sanitiseArg_execute
2203     );
2204
2205     /* sanitiseMatched */
2206     msre_engine_action_register(engine,
2207         "sanitiseMatched",
2208         ACTION_NON_DISRUPTIVE,
2209         0, 0,
2210         NO_PLUS_MINUS,
2211         ACTION_CARDINALITY_MANY,
2212         ACTION_CGROUP_NONE,
2213         NULL,
2214         NULL,
2215         msre_action_sanitiseMatched_execute
2216     );
2217
2218     /* sanitiseRequestHeader */
2219     msre_engine_action_register(engine,
2220         "sanitiseRequestHeader",
2221         ACTION_NON_DISRUPTIVE,
2222         1, 1,
2223         NO_PLUS_MINUS,
2224         ACTION_CARDINALITY_MANY,
2225         ACTION_CGROUP_NONE,
2226         NULL,
2227         NULL,
2228         msre_action_sanitiseRequestHeader_execute
2229     );
2230
2231     /* sanitiseResponseHeader */
2232     msre_engine_action_register(engine,
2233         "sanitiseResponseHeader",
2234         ACTION_NON_DISRUPTIVE,
2235         1, 1,
2236         NO_PLUS_MINUS,
2237         ACTION_CARDINALITY_MANY,
2238         ACTION_CGROUP_NONE,
2239         NULL,
2240         NULL,
2241         msre_action_sanitiseResponseHeader_execute
2242     );
2243
2244     /* setenv */
2245     msre_engine_action_register(engine,
2246         "setenv",
2247         ACTION_NON_DISRUPTIVE,
2248         1, 1,
2249         NO_PLUS_MINUS,
2250         ACTION_CARDINALITY_MANY,
2251         ACTION_CGROUP_NONE,
2252         NULL,
2253         NULL,
2254         msre_action_setenv_execute
2255     );
2256
2257     /* setvar */
2258     msre_engine_action_register(engine,
2259         "setvar",
2260         ACTION_NON_DISRUPTIVE,
2261         1, 1,
2262         NO_PLUS_MINUS,
2263         ACTION_CARDINALITY_MANY,
2264         ACTION_CGROUP_NONE,
2265         NULL,
2266         NULL,
2267         msre_action_setvar_execute
2268     );
2269
2270     /* expirevar */
2271     msre_engine_action_register(engine,
2272         "expirevar",
2273         ACTION_NON_DISRUPTIVE,
2274         1, 1,
2275         NO_PLUS_MINUS,
2276         ACTION_CARDINALITY_MANY,
2277         ACTION_CGROUP_NONE,
2278         NULL,
2279         NULL,
2280         msre_action_expirevar_execute
2281     );
2282
2283     /* deprecatevar */
2284     msre_engine_action_register(engine,
2285         "deprecatevar",
2286         ACTION_NON_DISRUPTIVE,
2287         1, 1,
2288         NO_PLUS_MINUS,
2289         ACTION_CARDINALITY_MANY,
2290         ACTION_CGROUP_NONE,
2291         NULL,
2292         NULL,
2293         msre_action_deprecatevar_execute
2294     );
2295
2296     /* initcol */
2297     msre_engine_action_register(engine,
2298         "initcol",
2299         ACTION_NON_DISRUPTIVE,
2300         1, 1,
2301         NO_PLUS_MINUS,
2302         ACTION_CARDINALITY_MANY,
2303         ACTION_CGROUP_NONE,
2304         NULL,
2305         NULL,
2306         msre_action_initcol_execute
2307     );
2308
2309     /* setsid */
2310     msre_engine_action_register(engine,
2311         "setsid",
2312         ACTION_NON_DISRUPTIVE,
2313         1, 1,
2314         NO_PLUS_MINUS,
2315         ACTION_CARDINALITY_ONE,
2316         ACTION_CGROUP_NONE,
2317         NULL,
2318         NULL,
2319         msre_action_setsid_execute
2320     );
2321
2322     /* setuid */
2323     msre_engine_action_register(engine,
2324         "setuid",
2325         ACTION_NON_DISRUPTIVE,
2326         1, 1,
2327         NO_PLUS_MINUS,
2328         ACTION_CARDINALITY_ONE,
2329         ACTION_CGROUP_NONE,
2330         NULL,
2331         NULL,
2332         msre_action_setuid_execute
2333     );
2334
2335     /* exec */
2336     msre_engine_action_register(engine,
2337         "exec",
2338         ACTION_NON_DISRUPTIVE,
2339         1, 1,
2340         NO_PLUS_MINUS,
2341         ACTION_CARDINALITY_MANY,
2342         ACTION_CGROUP_NONE,
2343         msre_action_exec_validate,
2344         NULL,
2345         msre_action_exec_execute
2346     );
2347
2348     /* multiMatch */
2349     msre_engine_action_register(engine,
2350         "multiMatch",
2351         ACTION_NON_DISRUPTIVE,
2352         0, 0,
2353         NO_PLUS_MINUS,
2354         ACTION_CARDINALITY_ONE,
2355         ACTION_CGROUP_NONE,
2356         NULL,
2357         NULL,
2358         NULL
2359     );
2360
2361     /* tag */
2362     /* ENH: This should be ACTION_METADATA??? */
2363     msre_engine_action_register(engine,
2364         "tag",
2365         ACTION_NON_DISRUPTIVE,
2366         1, 1,
2367         NO_PLUS_MINUS,
2368         ACTION_CARDINALITY_MANY,
2369         ACTION_CGROUP_NONE,
2370         NULL,
2371         NULL,
2372         NULL
2373     );
2374
2375     /* prepend */
2376     msre_engine_action_register(engine,
2377         "prepend",
2378         ACTION_NON_DISRUPTIVE,
2379         1, 1,
2380         NO_PLUS_MINUS,
2381         ACTION_CARDINALITY_ONE,
2382         ACTION_CGROUP_NONE,
2383         NULL,
2384         NULL,
2385         msre_action_prepend_execute
2386     );
2387
2388     /* append */
2389     msre_engine_action_register(engine,
2390         "append",
2391         ACTION_NON_DISRUPTIVE,
2392         1, 1,
2393         NO_PLUS_MINUS,
2394         ACTION_CARDINALITY_ONE,
2395         ACTION_CGROUP_NONE,
2396         NULL,
2397         NULL,
2398         msre_action_append_execute
2399     );
2400 }