1 /*
2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4 *
5 * You may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * If any of the files related to licensing are missing or if you have any
11 * other questions related to licensing please contact Trustwave Holdings, Inc.
12 * directly using the email address security@modsecurity.org.
13 */
14 
15 #include <limits.h>
16 
17 #include "modsecurity.h"
18 #include "msc_logging.h"
19 #include "msc_util.h"
20 #include "http_log.h"
21 #include "apr_lib.h"
22 #include "acmp.h"
23 #include "msc_crypt.h"
24 
25 #if defined(WITH_LUA)
26 #include "msc_lua.h"
27 #endif
28 
29 
30 /* -- Directory context creation and initialisation -- */
31 
32 /**
33  * Creates a fresh directory configuration.
34  */
create_directory_config(apr_pool_t * mp,char * path)35 void *create_directory_config(apr_pool_t *mp, char *path)
36 {
37     directory_config *dcfg = (directory_config *)apr_pcalloc(mp, sizeof(directory_config));
38     if (dcfg == NULL) return NULL;
39 
40     #ifdef DEBUG_CONF
41     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Created directory config %pp path %s", dcfg, path);
42     #endif
43 
44     dcfg->mp = mp;
45     dcfg->is_enabled = NOT_SET;
46 
47     dcfg->reqbody_access = NOT_SET;
48     dcfg->reqintercept_oe = NOT_SET;
49     dcfg->reqbody_buffering = NOT_SET;
50     dcfg->reqbody_inmemory_limit = NOT_SET;
51     dcfg->reqbody_limit = NOT_SET;
52     dcfg->reqbody_no_files_limit = NOT_SET;
53     dcfg->resbody_access = NOT_SET;
54 
55     dcfg->debuglog_name = NOT_SET_P;
56     dcfg->debuglog_level = NOT_SET;
57     dcfg->debuglog_fd = NOT_SET_P;
58 
59     dcfg->of_limit = NOT_SET;
60     dcfg->if_limit_action = NOT_SET;
61     dcfg->of_limit_action = NOT_SET;
62     dcfg->of_mime_types = NOT_SET_P;
63     dcfg->of_mime_types_cleared = NOT_SET;
64 
65     dcfg->cookie_format = NOT_SET;
66     dcfg->argument_separator = NOT_SET;
67     dcfg->cookiev0_separator = NOT_SET_P;
68 
69     dcfg->rule_inheritance = NOT_SET;
70     dcfg->rule_exceptions = apr_array_make(mp, 16, sizeof(rule_exception *));
71     dcfg->hash_method = apr_array_make(mp, 16, sizeof(hash_method *));
72 
73     /* audit log variables */
74     dcfg->auditlog_flag = NOT_SET;
75     dcfg->auditlog_type = NOT_SET;
76     #ifdef WITH_YAJL
77     dcfg->auditlog_format = NOT_SET;
78     #endif
79     dcfg->max_rule_time = NOT_SET;
80     dcfg->auditlog_dirperms = NOT_SET;
81     dcfg->auditlog_fileperms = NOT_SET;
82     dcfg->auditlog_name = NOT_SET_P;
83     dcfg->auditlog2_name = NOT_SET_P;
84     dcfg->auditlog_fd = NOT_SET_P;
85     dcfg->auditlog2_fd = NOT_SET_P;
86     dcfg->auditlog_storage_dir = NOT_SET_P;
87     dcfg->auditlog_parts = NOT_SET_P;
88     dcfg->auditlog_relevant_regex = NOT_SET_P;
89 
90     dcfg->ruleset = NULL;
91 
92     /* Upload */
93     dcfg->tmp_dir = NOT_SET_P;
94     dcfg->upload_dir = NOT_SET_P;
95     dcfg->upload_keep_files = NOT_SET;
96     dcfg->upload_validates_files = NOT_SET;
97     dcfg->upload_filemode = NOT_SET;
98     dcfg->upload_file_limit = NOT_SET;
99 
100     /* These are only used during the configuration process. */
101     dcfg->tmp_chain_starter = NULL;
102     dcfg->tmp_default_actionset = NULL;
103     dcfg->tmp_rule_placeholders = NULL;
104 
105     /* Misc */
106     dcfg->data_dir = NOT_SET_P;
107     dcfg->webappid = NOT_SET_P;
108     dcfg->sensor_id = NOT_SET_P;
109     dcfg->httpBlkey = NOT_SET_P;
110 
111     /* Content injection. */
112     dcfg->content_injection_enabled = NOT_SET;
113 
114     /* Stream inspection */
115     dcfg->stream_inbody_inspection = NOT_SET;
116     dcfg->stream_outbody_inspection = NOT_SET;
117 
118     /* Geo Lookups */
119     dcfg->geo = NOT_SET_P;
120 
121     /* Gsb Lookups */
122     dcfg->gsb = NOT_SET_P;
123 
124     /* Unicode Map */
125     dcfg->u_map = NOT_SET_P;
126 
127     /* Cache */
128     dcfg->cache_trans = NOT_SET;
129     dcfg->cache_trans_incremental = NOT_SET;
130     dcfg->cache_trans_min = NOT_SET;
131     dcfg->cache_trans_max = NOT_SET;
132     dcfg->cache_trans_maxitems = NOT_SET;
133 
134     /* Rule ids */
135     dcfg->rule_id_htab = apr_hash_make(mp);
136     dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *));
137 
138     dcfg->request_encoding = NOT_SET_P;
139     dcfg->disable_backend_compression = NOT_SET;
140 
141     /* Collection timeout */
142     dcfg->col_timeout = NOT_SET;
143 
144     dcfg->crypto_key = NOT_SET_P;
145     dcfg->crypto_key_len = NOT_SET;
146     dcfg->crypto_key_add = NOT_SET;
147     dcfg->crypto_param_name = NOT_SET_P;
148     dcfg->hash_is_enabled = NOT_SET;
149     dcfg->hash_enforcement = NOT_SET;
150     dcfg->crypto_hash_href_rx = NOT_SET;
151     dcfg->crypto_hash_faction_rx = NOT_SET;
152     dcfg->crypto_hash_location_rx = NOT_SET;
153     dcfg->crypto_hash_iframesrc_rx = NOT_SET;
154     dcfg->crypto_hash_framesrc_rx = NOT_SET;
155     dcfg->crypto_hash_href_pm = NOT_SET;
156     dcfg->crypto_hash_faction_pm = NOT_SET;
157     dcfg->crypto_hash_location_pm = NOT_SET;
158     dcfg->crypto_hash_iframesrc_pm = NOT_SET;
159     dcfg->crypto_hash_framesrc_pm = NOT_SET;
160 
161 
162     /* xml external entity */
163     dcfg->xml_external_entity = NOT_SET;
164 
165     return dcfg;
166 }
167 
168 /**
169  * Copies rules between one phase of two configuration contexts,
170  * taking exceptions into account.
171  */
copy_rules_phase(apr_pool_t * mp,apr_array_header_t * parent_phase_arr,apr_array_header_t * child_phase_arr,apr_array_header_t * exceptions_arr)172 static void copy_rules_phase(apr_pool_t *mp,
173                              apr_array_header_t *parent_phase_arr,
174                              apr_array_header_t *child_phase_arr,
175                              apr_array_header_t *exceptions_arr)
176 {
177     rule_exception **exceptions;
178     msre_rule **rules;
179     int i, j;
180     int mode = 0;
181 
182     rules = (msre_rule **)parent_phase_arr->elts;
183     for(i = 0; i < parent_phase_arr->nelts; i++) {
184         msre_rule *rule = (msre_rule *)rules[i];
185         int copy = 1;
186 
187         if (mode == 0) {
188             /* First rule in the chain. */
189             exceptions = (rule_exception **)exceptions_arr->elts;
190             for(j = 0; j < exceptions_arr->nelts; j++) {
191 
192                 /* Process exceptions. */
193                 switch(exceptions[j]->type) {
194                     case RULE_EXCEPTION_REMOVE_ID :
195                         if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) {
196                             int ruleid = atoi(rule->actionset->id);
197                             if (rule_id_in_range(ruleid, exceptions[j]->param)) copy--;
198                         }
199                         break;
200                     case RULE_EXCEPTION_REMOVE_MSG :
201                         if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) {
202                             char *my_error_msg = NULL;
203 
204                             int rc = msc_regexec(exceptions[j]->param_data,
205                                     rule->actionset->msg, strlen(rule->actionset->msg),
206                                     &my_error_msg);
207                             if (rc >= 0) copy--;
208                         }
209                         break;
210                     case RULE_EXCEPTION_REMOVE_TAG :
211                         if ((rule->actionset != NULL)&&(apr_is_empty_table(rule->actionset->actions) == 0)) {
212                             char *my_error_msg = NULL;
213                             const apr_array_header_t *tarr = NULL;
214                             const apr_table_entry_t *telts = NULL;
215                             int c;
216 
217                             tarr = apr_table_elts(rule->actionset->actions);
218                             telts = (const apr_table_entry_t*)tarr->elts;
219 
220                             for (c = 0; c < tarr->nelts; c++) {
221                                 msre_action *action = (msre_action *)telts[c].val;
222                                 if(strcmp("tag", action->metadata->name) == 0)  {
223 
224                                     int rc = msc_regexec(exceptions[j]->param_data,
225                                             action->param, strlen(action->param),
226                                             &my_error_msg);
227                                     if (rc >= 0) copy--;
228                                 }
229                             }
230                         }
231                         break;
232                 }
233             }
234 
235             if (copy > 0) {
236 #ifdef DEBUG_CONF
237                 ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, rule->actionset->id);
238 #endif
239 
240                 /* Copy the rule. */
241                 *(msre_rule **)apr_array_push(child_phase_arr) = rule;
242                 if (rule->actionset && rule->actionset->is_chained) mode = 2;
243             } else {
244                 if (rule->actionset && rule->actionset->is_chained) mode = 1;
245             }
246         } else {
247             if (mode == 2) {
248 #ifdef DEBUG_CONF
249                 ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy chain %pp for rule %pp [id \"%s\"]", rule, rule->chain_starter, rule->chain_starter->actionset->id);
250 #endif
251 
252                 /* Copy the rule (it belongs to the chain we want to include. */
253                 *(msre_rule **)apr_array_push(child_phase_arr) = rule;
254             }
255 
256             if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0;
257         }
258     }
259 }
260 
261 /**
262  * @brief Copies rules between one phase of two configuration contexts.
263  *
264  * Copies rules between one phase of two configuration contexts,
265  * taking exceptions into account.
266  *
267  * @param mp apr pool structure
268  * @param parent_ruleset Parent's msre_ruleset
269  * @param child_ruleset Child's msre_ruleset
270  * @param exceptions_arr Exceptions' apr_array_header_t
271  * @retval 0 Everything went well.
272  * @retval -1 Something went wrong.
273  *
274  */
copy_rules(apr_pool_t * mp,msre_ruleset * parent_ruleset,msre_ruleset * child_ruleset,apr_array_header_t * exceptions_arr)275 static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset,
276                       msre_ruleset *child_ruleset,
277                       apr_array_header_t *exceptions_arr)
278 {
279     int ret = 0;
280 
281     if (parent_ruleset == NULL || child_ruleset == NULL ||
282             exceptions_arr == NULL) {
283         ret = -1;
284         goto failed;
285     }
286 
287     copy_rules_phase(mp, parent_ruleset->phase_request_headers,
288         child_ruleset->phase_request_headers, exceptions_arr);
289     copy_rules_phase(mp, parent_ruleset->phase_request_body,
290         child_ruleset->phase_request_body, exceptions_arr);
291     copy_rules_phase(mp, parent_ruleset->phase_response_headers,
292         child_ruleset->phase_response_headers, exceptions_arr);
293     copy_rules_phase(mp, parent_ruleset->phase_response_body,
294         child_ruleset->phase_response_body, exceptions_arr);
295     copy_rules_phase(mp, parent_ruleset->phase_logging,
296         child_ruleset->phase_logging, exceptions_arr);
297 
298 failed:
299     return ret;
300 }
301 
302 /**
303  * Merges two directory configurations.
304  */
merge_directory_configs(apr_pool_t * mp,void * _parent,void * _child)305 void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child)
306 {
307     directory_config *parent = (directory_config *)_parent;
308     directory_config *child = (directory_config *)_child;
309     directory_config *merged = create_directory_config(mp, NULL);
310 
311     #ifdef DEBUG_CONF
312     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Merge parent %pp child %pp RESULT %pp", _parent, _child, merged);
313     #endif
314 
315     if (merged == NULL) return NULL;
316 
317     /* Use values from the child configuration where possible,
318      * otherwise use the parent's.
319      */
320 
321     merged->is_enabled = (child->is_enabled == NOT_SET
322         ? parent->is_enabled : child->is_enabled);
323 
324     /* IO parameters */
325     merged->reqbody_access = (child->reqbody_access == NOT_SET
326         ? parent->reqbody_access : child->reqbody_access);
327     merged->reqbody_buffering = (child->reqbody_buffering == NOT_SET
328         ? parent->reqbody_buffering : child->reqbody_buffering);
329     merged->reqbody_inmemory_limit = (child->reqbody_inmemory_limit == NOT_SET
330         ? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit);
331     merged->reqbody_limit = (child->reqbody_limit == NOT_SET
332         ? parent->reqbody_limit : child->reqbody_limit);
333     merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET
334         ? parent->reqbody_no_files_limit : child->reqbody_no_files_limit);
335     merged->resbody_access = (child->resbody_access == NOT_SET
336         ? parent->resbody_access : child->resbody_access);
337 
338     merged->of_limit = (child->of_limit == NOT_SET
339         ? parent->of_limit : child->of_limit);
340     merged->if_limit_action = (child->if_limit_action == NOT_SET
341         ? parent->if_limit_action : child->if_limit_action);
342     merged->of_limit_action = (child->of_limit_action == NOT_SET
343         ? parent->of_limit_action : child->of_limit_action);
344     merged->reqintercept_oe = (child->reqintercept_oe == NOT_SET
345         ? parent->reqintercept_oe : child->reqintercept_oe);
346 
347     if (child->of_mime_types != NOT_SET_P) {
348         /* Child added to the table */
349 
350         if (child->of_mime_types_cleared == 1) {
351             /* The list of MIME types was cleared in the child,
352              * which means the parent's MIME types went away and
353              * we should not take them into consideration here.
354              */
355             merged->of_mime_types = child->of_mime_types;
356             merged->of_mime_types_cleared = 1;
357         } else {
358             /* Add MIME types defined in the child to those
359              * defined in the parent context.
360              */
361             if (parent->of_mime_types == NOT_SET_P) {
362                 merged->of_mime_types = child->of_mime_types;
363                 merged->of_mime_types_cleared = NOT_SET;
364             } else {
365                 merged->of_mime_types = apr_table_overlay(mp, parent->of_mime_types,
366                     child->of_mime_types);
367                 if (merged->of_mime_types == NULL) return NULL;
368             }
369         }
370     } else {
371         /* Child did not add to the table */
372 
373         if (child->of_mime_types_cleared == 1) {
374             merged->of_mime_types_cleared = 1;
375         } else {
376             merged->of_mime_types = parent->of_mime_types;
377             merged->of_mime_types_cleared = parent->of_mime_types_cleared;
378         }
379     }
380 
381     /* debug log */
382     if (child->debuglog_fd == NOT_SET_P) {
383         merged->debuglog_name = parent->debuglog_name;
384         merged->debuglog_fd = parent->debuglog_fd;
385     } else {
386         merged->debuglog_name = child->debuglog_name;
387         merged->debuglog_fd = child->debuglog_fd;
388     }
389 
390     merged->debuglog_level = (child->debuglog_level == NOT_SET
391         ? parent->debuglog_level : child->debuglog_level);
392 
393     merged->cookie_format = (child->cookie_format == NOT_SET
394         ? parent->cookie_format : child->cookie_format);
395     merged->argument_separator = (child->argument_separator == NOT_SET
396         ? parent->argument_separator : child->argument_separator);
397     merged->cookiev0_separator = (child->cookiev0_separator == NOT_SET_P
398         ? parent->cookiev0_separator : child->cookiev0_separator);
399 
400 
401     /* rule inheritance */
402     if ((child->rule_inheritance == NOT_SET)||(child->rule_inheritance == 1)) {
403         merged->rule_inheritance = parent->rule_inheritance;
404         if ((child->ruleset == NULL)&&(parent->ruleset == NULL)) {
405             #ifdef DEBUG_CONF
406             ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "No rules in this context.");
407             #endif
408 
409             /* Do nothing, there are no rules in either context. */
410         } else
411         if (child->ruleset == NULL) {
412             #ifdef DEBUG_CONF
413             ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent rules in this context.");
414             #endif
415 
416             /* Copy the rules from the parent context. */
417             merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp);
418             /* TODO: copy_rules return code should be taken into consideration. */
419             copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions);
420         } else
421         if (parent->ruleset == NULL) {
422             #ifdef DEBUG_CONF
423             ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using child rules in this context.");
424             #endif
425 
426             /* Copy child rules. */
427             merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp);
428             merged->ruleset->phase_request_headers = apr_array_copy(mp,
429                 child->ruleset->phase_request_headers);
430             merged->ruleset->phase_request_body = apr_array_copy(mp,
431                 child->ruleset->phase_request_body);
432             merged->ruleset->phase_response_headers = apr_array_copy(mp,
433                 child->ruleset->phase_response_headers);
434             merged->ruleset->phase_response_body = apr_array_copy(mp,
435                 child->ruleset->phase_response_body);
436             merged->ruleset->phase_logging = apr_array_copy(mp,
437                 child->ruleset->phase_logging);
438         } else {
439             #ifdef DEBUG_CONF
440             ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent then child rules in this context.");
441             #endif
442 
443             /* Copy parent rules, then add child rules to it. */
444             merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp);
445             /* TODO: copy_rules return code should be taken into consideration. */
446             copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions);
447 
448             apr_array_cat(merged->ruleset->phase_request_headers,
449                 child->ruleset->phase_request_headers);
450             apr_array_cat(merged->ruleset->phase_request_body,
451                 child->ruleset->phase_request_body);
452             apr_array_cat(merged->ruleset->phase_response_headers,
453                 child->ruleset->phase_response_headers);
454             apr_array_cat(merged->ruleset->phase_response_body,
455                 child->ruleset->phase_response_body);
456             apr_array_cat(merged->ruleset->phase_logging,
457                 child->ruleset->phase_logging);
458         }
459     } else {
460         merged->rule_inheritance = 0;
461         if (child->ruleset != NULL) {
462             /* Copy child rules. */
463             merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp);
464             merged->ruleset->phase_request_headers = apr_array_copy(mp,
465                 child->ruleset->phase_request_headers);
466             merged->ruleset->phase_request_body = apr_array_copy(mp,
467                 child->ruleset->phase_request_body);
468             merged->ruleset->phase_response_headers = apr_array_copy(mp,
469                 child->ruleset->phase_response_headers);
470             merged->ruleset->phase_response_body = apr_array_copy(mp,
471                 child->ruleset->phase_response_body);
472             merged->ruleset->phase_logging = apr_array_copy(mp,
473                 child->ruleset->phase_logging);
474         }
475     }
476 
477     /* Merge rule exceptions. */
478     merged->rule_exceptions = apr_array_append(mp, parent->rule_exceptions,
479         child->rule_exceptions);
480 
481     merged->hash_method = apr_array_append(mp, parent->hash_method,
482         child->hash_method);
483 
484     /* audit log variables */
485     merged->auditlog_flag = (child->auditlog_flag == NOT_SET
486         ? parent->auditlog_flag : child->auditlog_flag);
487     merged->auditlog_type = (child->auditlog_type == NOT_SET
488         ? parent->auditlog_type : child->auditlog_type);
489     merged->max_rule_time = (child->max_rule_time == NOT_SET
490         ? parent->max_rule_time : child->max_rule_time);
491     merged->auditlog_dirperms = (child->auditlog_dirperms == NOT_SET
492         ? parent->auditlog_dirperms : child->auditlog_dirperms);
493     merged->auditlog_fileperms = (child->auditlog_fileperms == NOT_SET
494         ? parent->auditlog_fileperms : child->auditlog_fileperms);
495     if (child->auditlog_fd != NOT_SET_P) {
496         merged->auditlog_fd = child->auditlog_fd;
497         merged->auditlog_name = child->auditlog_name;
498     } else {
499         merged->auditlog_fd = parent->auditlog_fd;
500         merged->auditlog_name = parent->auditlog_name;
501     }
502     if (child->auditlog2_fd != NOT_SET_P) {
503         merged->auditlog2_fd = child->auditlog2_fd;
504         merged->auditlog2_name = child->auditlog2_name;
505     } else {
506         merged->auditlog2_fd = parent->auditlog2_fd;
507         merged->auditlog2_name = parent->auditlog2_name;
508     }
509     #ifdef WITH_YAJL
510     merged->auditlog_format = (child->auditlog_format == NOT_SET
511         ? parent->auditlog_format : child->auditlog_format);
512     #endif
513     merged->auditlog_storage_dir = (child->auditlog_storage_dir == NOT_SET_P
514         ? parent->auditlog_storage_dir : child->auditlog_storage_dir);
515     merged->auditlog_parts = (child->auditlog_parts == NOT_SET_P
516         ? parent->auditlog_parts : child->auditlog_parts);
517     merged->auditlog_relevant_regex = (child->auditlog_relevant_regex == NOT_SET_P
518         ? parent->auditlog_relevant_regex : child->auditlog_relevant_regex);
519 
520     /* Upload */
521     merged->tmp_dir = (child->tmp_dir == NOT_SET_P
522         ? parent->tmp_dir : child->tmp_dir);
523     merged->upload_dir = (child->upload_dir == NOT_SET_P
524         ? parent->upload_dir : child->upload_dir);
525     merged->upload_keep_files = (child->upload_keep_files == NOT_SET
526         ? parent->upload_keep_files : child->upload_keep_files);
527     merged->upload_validates_files = (child->upload_validates_files == NOT_SET
528         ? parent->upload_validates_files : child->upload_validates_files);
529     merged->upload_filemode = (child->upload_filemode == NOT_SET
530         ? parent->upload_filemode : child->upload_filemode);
531     merged->upload_file_limit = (child->upload_file_limit == NOT_SET
532         ? parent->upload_file_limit : child->upload_file_limit);
533 
534     /* Misc */
535     merged->data_dir = (child->data_dir == NOT_SET_P
536         ? parent->data_dir : child->data_dir);
537     merged->webappid = (child->webappid == NOT_SET_P
538         ? parent->webappid : child->webappid);
539     merged->sensor_id = (child->sensor_id == NOT_SET_P
540         ? parent->sensor_id : child->sensor_id);
541     merged->httpBlkey = (child->httpBlkey == NOT_SET_P
542         ? parent->httpBlkey : child->httpBlkey);
543 
544     /* Content injection. */
545     merged->content_injection_enabled = (child->content_injection_enabled == NOT_SET
546         ? parent->content_injection_enabled : child->content_injection_enabled);
547 
548     /* Stream inspection */
549     merged->stream_inbody_inspection = (child->stream_inbody_inspection == NOT_SET
550         ? parent->stream_inbody_inspection : child->stream_inbody_inspection);
551     merged->stream_outbody_inspection = (child->stream_outbody_inspection == NOT_SET
552         ? parent->stream_outbody_inspection : child->stream_outbody_inspection);
553 
554     /* Geo Lookup */
555     merged->geo = (child->geo == NOT_SET_P
556         ? parent->geo : child->geo);
557 
558     /* Gsb Lookup */
559     merged->gsb = (child->gsb == NOT_SET_P
560         ? parent->gsb : child->gsb);
561 
562     /* Unicode Map */
563     merged->u_map = (child->u_map == NOT_SET_P
564         ? parent->u_map : child->u_map);
565 
566     /* Cache */
567     merged->cache_trans = (child->cache_trans == NOT_SET
568         ? parent->cache_trans : child->cache_trans);
569     merged->cache_trans_incremental = (child->cache_trans_incremental == NOT_SET
570         ? parent->cache_trans_incremental : child->cache_trans_incremental);
571     merged->cache_trans_min = (child->cache_trans_min == (apr_size_t)NOT_SET
572         ? parent->cache_trans_min : child->cache_trans_min);
573     merged->cache_trans_max = (child->cache_trans_max == (apr_size_t)NOT_SET
574         ? parent->cache_trans_max : child->cache_trans_max);
575     merged->cache_trans_maxitems = (child->cache_trans_maxitems == (apr_size_t)NOT_SET
576         ? parent->cache_trans_maxitems : child->cache_trans_maxitems);
577 
578     /* Merge component signatures. */
579     merged->component_signatures = apr_array_append(mp, parent->component_signatures,
580         child->component_signatures);
581 
582     merged->request_encoding = (child->request_encoding == NOT_SET_P
583         ? parent->request_encoding : child->request_encoding);
584 
585     merged->disable_backend_compression = (child->disable_backend_compression == NOT_SET
586         ? parent->disable_backend_compression : child->disable_backend_compression);
587 
588     merged->col_timeout = (child->col_timeout == NOT_SET
589         ? parent->col_timeout : child->col_timeout);
590 
591     /* Hash */
592     merged->crypto_key = (child->crypto_key == NOT_SET_P
593         ? parent->crypto_key : child->crypto_key);
594     merged->crypto_key_len = (child->crypto_key_len == NOT_SET
595         ? parent->crypto_key_len : child->crypto_key_len);
596     merged->crypto_key_add = (child->crypto_key_add == NOT_SET
597         ? parent->crypto_key_add : child->crypto_key_add);
598     merged->crypto_param_name = (child->crypto_param_name == NOT_SET_P
599         ? parent->crypto_param_name : child->crypto_param_name);
600     merged->hash_is_enabled = (child->hash_is_enabled == NOT_SET
601         ? parent->hash_is_enabled : child->hash_is_enabled);
602     merged->hash_enforcement = (child->hash_enforcement == NOT_SET
603         ? parent->hash_enforcement : child->hash_enforcement);
604     merged->crypto_hash_href_rx = (child->crypto_hash_href_rx == NOT_SET
605         ? parent->crypto_hash_href_rx : child->crypto_hash_href_rx);
606     merged->crypto_hash_faction_rx = (child->crypto_hash_faction_rx == NOT_SET
607         ? parent->crypto_hash_faction_rx : child->crypto_hash_faction_rx);
608     merged->crypto_hash_location_rx = (child->crypto_hash_location_rx == NOT_SET
609         ? parent->crypto_hash_location_rx : child->crypto_hash_location_rx);
610     merged->crypto_hash_iframesrc_rx = (child->crypto_hash_iframesrc_rx == NOT_SET
611         ? parent->crypto_hash_iframesrc_rx : child->crypto_hash_iframesrc_rx);
612     merged->crypto_hash_framesrc_rx = (child->crypto_hash_framesrc_rx == NOT_SET
613         ? parent->crypto_hash_framesrc_rx : child->crypto_hash_framesrc_rx);
614     merged->crypto_hash_href_pm = (child->crypto_hash_href_pm == NOT_SET
615         ? parent->crypto_hash_href_pm : child->crypto_hash_href_pm);
616     merged->crypto_hash_faction_pm = (child->crypto_hash_faction_pm == NOT_SET
617         ? parent->crypto_hash_faction_pm : child->crypto_hash_faction_pm);
618     merged->crypto_hash_location_pm = (child->crypto_hash_location_pm == NOT_SET
619         ? parent->crypto_hash_location_pm : child->crypto_hash_location_pm);
620     merged->crypto_hash_iframesrc_pm = (child->crypto_hash_iframesrc_pm == NOT_SET
621         ? parent->crypto_hash_iframesrc_pm : child->crypto_hash_iframesrc_pm);
622     merged->crypto_hash_framesrc_pm = (child->crypto_hash_framesrc_pm == NOT_SET
623         ? parent->crypto_hash_framesrc_pm : child->crypto_hash_framesrc_pm);
624 
625     /* xml external entity */
626     merged->xml_external_entity = (child->xml_external_entity == NOT_SET
627         ? parent->xml_external_entity : child->xml_external_entity);
628 
629     return merged;
630 }
631 
632 /**
633  * Initialise directory configuration. This function is *not* meant
634  * to be called for directory configuration instances created during
635  * the configuration phase. It can only be called on copies of those
636  * (created fresh for every transaction).
637  */
init_directory_config(directory_config * dcfg)638 void init_directory_config(directory_config *dcfg)
639 {
640     if (dcfg == NULL) return;
641 
642     if (dcfg->is_enabled == NOT_SET) dcfg->is_enabled = 0;
643 
644     if (dcfg->reqbody_access == NOT_SET) dcfg->reqbody_access = 0;
645     if (dcfg->reqintercept_oe == NOT_SET) dcfg->reqintercept_oe = 0;
646     if (dcfg->reqbody_buffering == NOT_SET) dcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF;
647     if (dcfg->reqbody_inmemory_limit == NOT_SET)
648         dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT;
649     if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT;
650     if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT;
651     if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0;
652     if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT;
653     if (dcfg->if_limit_action == NOT_SET) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT;
654     if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
655 
656     if (dcfg->of_mime_types == NOT_SET_P) {
657         dcfg->of_mime_types = apr_table_make(dcfg->mp, 3);
658         if (dcfg->of_mime_types_cleared != 1) {
659             apr_table_setn(dcfg->of_mime_types, "text/plain", "1");
660             apr_table_setn(dcfg->of_mime_types, "text/html", "1");
661         }
662     }
663 
664     if (dcfg->debuglog_fd == NOT_SET_P) dcfg->debuglog_fd = NULL;
665     if (dcfg->debuglog_name == NOT_SET_P) dcfg->debuglog_name = NULL;
666     if (dcfg->debuglog_level == NOT_SET) dcfg->debuglog_level = 0;
667 
668     if (dcfg->cookie_format == NOT_SET) dcfg->cookie_format = 0;
669     if (dcfg->argument_separator == NOT_SET) dcfg->argument_separator = '&';
670     if (dcfg->cookiev0_separator == NOT_SET_P) dcfg->cookiev0_separator = NULL;
671 
672     if (dcfg->rule_inheritance == NOT_SET) dcfg->rule_inheritance = 1;
673 
674     /* audit log variables */
675     if (dcfg->auditlog_flag == NOT_SET) dcfg->auditlog_flag = 0;
676     if (dcfg->auditlog_type == NOT_SET) dcfg->auditlog_type = AUDITLOG_SERIAL;
677     #ifdef WITH_YAJL
678     if (dcfg->auditlog_format == NOT_SET) dcfg->auditlog_format = AUDITLOGFORMAT_NATIVE;
679     #endif
680     if (dcfg->max_rule_time == NOT_SET) dcfg->max_rule_time = 0;
681     if (dcfg->auditlog_dirperms == NOT_SET) dcfg->auditlog_dirperms = CREATEMODE_DIR;
682     if (dcfg->auditlog_fileperms == NOT_SET) dcfg->auditlog_fileperms = CREATEMODE;
683     if (dcfg->auditlog_fd == NOT_SET_P) dcfg->auditlog_fd = NULL;
684     if (dcfg->auditlog2_fd == NOT_SET_P) dcfg->auditlog2_fd = NULL;
685     if (dcfg->auditlog_name == NOT_SET_P) dcfg->auditlog_name = NULL;
686     if (dcfg->auditlog2_name == NOT_SET_P) dcfg->auditlog2_name = NULL;
687     if (dcfg->auditlog_storage_dir == NOT_SET_P) dcfg->auditlog_storage_dir = NULL;
688     if (dcfg->auditlog_parts == NOT_SET_P) dcfg->auditlog_parts = "ABCFHZ";
689     if (dcfg->auditlog_relevant_regex == NOT_SET_P) dcfg->auditlog_relevant_regex = NULL;
690 
691     /* Upload */
692     if (dcfg->tmp_dir == NOT_SET_P) dcfg->tmp_dir = guess_tmp_dir(dcfg->mp);
693     if (dcfg->upload_dir == NOT_SET_P) dcfg->upload_dir = NULL;
694     if (dcfg->upload_keep_files == NOT_SET) dcfg->upload_keep_files = KEEP_FILES_OFF;
695     if (dcfg->upload_validates_files == NOT_SET) dcfg->upload_validates_files = 0;
696     if (dcfg->upload_filemode == NOT_SET) dcfg->upload_filemode = 0600;
697     if (dcfg->upload_file_limit == NOT_SET) dcfg->upload_file_limit = 100;
698 
699     /* Misc */
700     if (dcfg->data_dir == NOT_SET_P) dcfg->data_dir = NULL;
701     if (dcfg->webappid == NOT_SET_P) dcfg->webappid = "default";
702     if (dcfg->sensor_id == NOT_SET_P) dcfg->sensor_id = "default";
703     if (dcfg->httpBlkey == NOT_SET_P) dcfg->httpBlkey = NULL;
704 
705     /* Content injection. */
706     if (dcfg->content_injection_enabled == NOT_SET) dcfg->content_injection_enabled = 0;
707 
708     /* Stream inspection */
709     if (dcfg->stream_inbody_inspection == NOT_SET) dcfg->stream_inbody_inspection = 0;
710     if (dcfg->stream_outbody_inspection == NOT_SET) dcfg->stream_outbody_inspection = 0;
711 
712     /* Geo Lookup */
713     if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL;
714 
715     /* Gsb Lookup */
716     if (dcfg->gsb == NOT_SET_P) dcfg->gsb = NULL;
717 
718     /* Unicode Map */
719     if (dcfg->u_map == NOT_SET_P) dcfg->u_map = NULL;
720 
721     /* Cache */
722     if (dcfg->cache_trans == NOT_SET) dcfg->cache_trans = MODSEC_CACHE_DISABLED;
723     if (dcfg->cache_trans_incremental == NOT_SET) dcfg->cache_trans_incremental = 0;
724     if (dcfg->cache_trans_min == (apr_size_t)NOT_SET) dcfg->cache_trans_min = 32;
725     if (dcfg->cache_trans_max == (apr_size_t)NOT_SET) dcfg->cache_trans_max = 1024;
726     if (dcfg->cache_trans_maxitems == (apr_size_t)NOT_SET) dcfg->cache_trans_maxitems = 512;
727 
728     if (dcfg->request_encoding == NOT_SET_P) dcfg->request_encoding = NULL;
729 
730     if (dcfg->disable_backend_compression == NOT_SET) dcfg->disable_backend_compression = 0;
731 
732     if (dcfg->col_timeout == NOT_SET) dcfg->col_timeout = 3600;
733 
734     /* Hash */
735     if (dcfg->hash_is_enabled == HASH_ENABLED) {
736         if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = getkey(dcfg->mp);
737         if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = strlen(dcfg->crypto_key);
738     } else {
739         if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = "";
740         if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = 0;
741     }
742     if (dcfg->crypto_key_add == NOT_SET) dcfg->crypto_key_add = HASH_KEYONLY;
743     if (dcfg->crypto_param_name == NOT_SET_P) dcfg->crypto_param_name = "crypt";
744     if (dcfg->hash_is_enabled == NOT_SET) dcfg->hash_is_enabled = HASH_DISABLED;
745     if (dcfg->hash_enforcement == NOT_SET) dcfg->hash_enforcement = HASH_DISABLED;
746     if (dcfg->crypto_hash_href_rx == NOT_SET) dcfg->crypto_hash_href_rx = 0;
747     if (dcfg->crypto_hash_faction_rx == NOT_SET) dcfg->crypto_hash_faction_rx = 0;
748     if (dcfg->crypto_hash_location_rx == NOT_SET) dcfg->crypto_hash_location_rx = 0;
749     if (dcfg->crypto_hash_iframesrc_rx == NOT_SET) dcfg->crypto_hash_iframesrc_rx = 0;
750     if (dcfg->crypto_hash_framesrc_rx == NOT_SET) dcfg->crypto_hash_framesrc_rx = 0;
751     if (dcfg->crypto_hash_href_pm == NOT_SET) dcfg->crypto_hash_href_pm = 0;
752     if (dcfg->crypto_hash_faction_pm == NOT_SET) dcfg->crypto_hash_faction_pm = 0;
753     if (dcfg->crypto_hash_location_pm == NOT_SET) dcfg->crypto_hash_location_pm = 0;
754     if (dcfg->crypto_hash_iframesrc_pm == NOT_SET) dcfg->crypto_hash_iframesrc_pm = 0;
755     if (dcfg->crypto_hash_framesrc_pm == NOT_SET) dcfg->crypto_hash_framesrc_pm = 0;
756 
757     /* xml external entity */
758     if (dcfg->xml_external_entity == NOT_SET) dcfg->xml_external_entity = 0;
759 
760 }
761 
762 /**
763  *
764  */
add_rule(cmd_parms * cmd,directory_config * dcfg,int type,const char * p1,const char * p2,const char * p3)765 static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type,
766                             const char *p1, const char *p2, const char *p3)
767 {
768     char *my_error_msg = NULL;
769     //msre_rule *rule = NULL, *tmp_rule = NULL;
770     char *rid = NULL;
771     msre_rule *rule = NULL;
772     extern msc_engine *modsecurity;
773     int type_with_lua = 1;
774     int type_rule;
775     int rule_actionset;
776     int offset = 0;
777 
778     #ifdef DEBUG_CONF
779     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
780         "Rule: type=%d p1='%s' p2='%s' p3='%s'", type, p1, p2, p3);
781     #endif
782 
783     /* Create a ruleset if one does not exist. */
784     if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) {
785         dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool);
786         if (dcfg->ruleset == NULL) return FATAL_ERROR;
787     }
788 
789     /* Create the rule now. */
790     switch(type) {
791         #if defined(WITH_LUA)
792         case RULE_TYPE_LUA :
793             rule = msre_rule_lua_create(dcfg->ruleset, cmd->directive->filename,
794                 cmd->directive->line_num, p1, p2, &my_error_msg);
795             break;
796         #endif
797         default :
798             rule = msre_rule_create(dcfg->ruleset, type, cmd->directive->filename,
799                 cmd->directive->line_num, p1, p2, p3, &my_error_msg);
800             break;
801     }
802 
803     if (rule == NULL) {
804         return my_error_msg;
805     }
806 
807 #ifndef ALLOW_ID_NOT_UNIQUE
808 	/* Rules must have uniq ID */
809     type_rule = (dcfg->tmp_chain_starter == NULL);
810 #if defined(WITH_LUA)
811             type_rule = (type != RULE_TYPE_LUA && type_rule);
812 #endif
813             if (type_rule)
814                 if(rule->actionset == NULL)
815                     return "ModSecurity: Rules must have at least id action";
816 
817     if(rule->actionset != NULL && (dcfg->tmp_chain_starter == NULL))    {
818         rule_actionset = (rule->actionset->id == NOT_SET_P);
819 #if defined(WITH_LUA)
820         rule_actionset = (rule_actionset && (type != RULE_TYPE_LUA));
821 #endif
822         if (rule_actionset)
823           return "ModSecurity: No action id present within the rule";
824 #if defined(WITH_LUA)
825         type_with_lua = (type != RULE_TYPE_LUA);
826 #endif
827         if (type_with_lua){
828             rid = apr_hash_get(dcfg->rule_id_htab, rule->actionset->id, APR_HASH_KEY_STRING);
829             if(rid != NULL) {
830                 return "ModSecurity: Found another rule with the same id";
831             } else    {
832                 apr_hash_set(dcfg->rule_id_htab, apr_pstrdup(dcfg->mp, rule->actionset->id), APR_HASH_KEY_STRING, apr_pstrdup(dcfg->mp, "1"));
833             }
834 
835             //tmp_rule = msre_ruleset_fetch_rule(dcfg->ruleset, rule->actionset->id, offset);
836             //if(tmp_rule != NULL)
837             //    return "ModSecurity: Found another rule with the same id";
838         }
839     }
840 #endif
841 
842     /* Create default actionset if one does not already exist. */
843     if (dcfg->tmp_default_actionset == NULL) {
844         dcfg->tmp_default_actionset = msre_actionset_create_default(modsecurity->msre);
845         if (dcfg->tmp_default_actionset == NULL) return FATAL_ERROR;
846     }
847 
848     /* Check some cases prior to merging so we know where it came from */
849 
850     /* Check syntax for chained rules */
851     if ((rule->actionset != NULL) && (dcfg->tmp_chain_starter != NULL)) {
852         /* Must NOT specify a disruptive action. */
853         if (rule->actionset->intercept_action != NOT_SET) {
854             return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions can only "
855                 "be specified by chain starter rules.");
856         }
857 
858         /* Must NOT specify a skipafter action. */
859         if (rule->actionset->skip_after != NOT_SET_P) {
860             return apr_psprintf(cmd->pool, "ModSecurity: SkipAfter actions can only "
861                 "be specified by chain starter rules.");
862         }
863 
864         /* Must NOT specify a phase. */
865         if (rule->actionset->phase != NOT_SET) {
866             return apr_psprintf(cmd->pool, "ModSecurity: Execution phases can only be "
867                 "specified by chain starter rules.");
868         }
869 
870         /* Must NOT use metadata actions. */
871         /* ENH: loop through to check for tags */
872         if ((rule->actionset->id != NOT_SET_P)
873             ||(rule->actionset->rev != NOT_SET_P)
874             ||(rule->actionset->msg != NOT_SET_P)
875             ||(rule->actionset->severity != NOT_SET)
876             ||(rule->actionset->version != NOT_SET_P)
877             ||(rule->actionset->accuracy != NOT_SET)
878             ||(rule->actionset->maturity != NOT_SET)
879             ||(rule->actionset->logdata != NOT_SET_P))
880         {
881             return apr_psprintf(cmd->pool, "ModSecurity: Metadata actions (id, rev, msg, tag, severity, ver, accuracy, maturity, logdata) "
882                 " can only be specified by chain starter rules.");
883         }
884 
885         /* Must NOT use skip. */
886         if (rule->actionset->skip_count != NOT_SET) {
887             return apr_psprintf(cmd->pool, "ModSecurity: The skip action can only be used "
888                 " by chain starter rules. ");
889         }
890     }
891 
892     /* Merge actions with the parent.
893      *
894      * ENH Probably do not want this done fully for chained rules.
895      */
896     rule->actionset = msre_actionset_merge(modsecurity->msre, cmd->pool, dcfg->tmp_default_actionset,
897         rule->actionset, 1);
898 
899     /* Keep track of the parent action for "block" */
900     if (rule->actionset) {
901         rule->actionset->parent_intercept_action_rec = dcfg->tmp_default_actionset->intercept_action_rec;
902         rule->actionset->parent_intercept_action = dcfg->tmp_default_actionset->intercept_action;
903     }
904 
905     /* Must NOT specify a disruptive action in logging phase. */
906     if ((rule->actionset != NULL)
907         && (rule->actionset->phase == PHASE_LOGGING)
908         && (rule->actionset->intercept_action != ACTION_ALLOW)
909         && (rule->actionset->intercept_action != ACTION_ALLOW_REQUEST)
910         && (rule->actionset->intercept_action != ACTION_NONE)
911     ) {
912         return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions "
913             "cannot be specified in the logging phase.");
914     }
915 
916     if (dcfg->tmp_chain_starter != NULL) {
917         rule->chain_starter = dcfg->tmp_chain_starter;
918         if (rule->actionset) {
919             rule->actionset->phase = rule->chain_starter->actionset->phase;
920         }
921     }
922 
923     if (rule->actionset->is_chained != 1) {
924         /* If this rule is part of the chain but does
925          * not want more rules to follow in the chain
926          * then cut it (the chain).
927          */
928         dcfg->tmp_chain_starter = NULL;
929     } else {
930         /* On the other hand, if this rule wants other
931          * rules to follow it, then start a new chain
932          * if there isn't one already.
933          */
934         if (dcfg->tmp_chain_starter == NULL) {
935             dcfg->tmp_chain_starter = rule;
936         }
937     }
938 
939     /* Create skip table if one does not already exist. */
940     if (dcfg->tmp_rule_placeholders == NULL) {
941         dcfg->tmp_rule_placeholders = apr_table_make(cmd->pool, 10);
942         if (dcfg->tmp_rule_placeholders == NULL) return FATAL_ERROR;
943     }
944 
945     /* Keep track of any rule IDs we need to skip after */
946     if (rule->actionset->skip_after != NOT_SET_P) {
947         char *tmp_id = apr_pstrdup(cmd->pool, rule->actionset->skip_after);
948         apr_table_setn(dcfg->tmp_rule_placeholders, tmp_id, tmp_id);
949 
950         #ifdef DEBUG_CONF
951         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
952             "Watching for skipafter target rule id=\"%s\".", tmp_id);
953         #endif
954 
955     }
956 
957     #ifdef DEBUG_CONF
958     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
959         "Adding rule %pp phase=%d id=\"%s\".", rule, rule->actionset->phase, (rule->actionset->id == NOT_SET_P
960         ? "(none)" : rule->actionset->id));
961     #endif
962 
963     /* Add rule to the recipe. */
964     if (msre_ruleset_rule_add(dcfg->ruleset, rule, rule->actionset->phase) < 0) {
965         return "Internal Error: Failed to add rule to the ruleset.";
966     }
967 
968     /* Add an additional placeholder if this rule ID is on the list */
969     if ((rule->actionset->id != NULL) && apr_table_get(dcfg->tmp_rule_placeholders, rule->actionset->id)) {
970         msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule));
971         if (phrule == NULL) {
972             return FATAL_ERROR;
973         }
974 
975         #ifdef DEBUG_CONF
976         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
977             "Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id);
978         #endif
979 
980         /* shallow copy of original rule with placeholder marked as target */
981         memcpy(phrule, rule, sizeof(msre_rule));
982         phrule->placeholder = RULE_PH_SKIPAFTER;
983 
984         /* Add placeholder. */
985         if (msre_ruleset_rule_add(dcfg->ruleset, phrule, phrule->actionset->phase) < 0) {
986             return "Internal Error: Failed to add placeholder to the ruleset.";
987         }
988 
989         /* No longer need to search for the ID */
990         apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id);
991     }
992 
993     /* Update the unparsed rule */
994     rule->unparsed = msre_rule_generate_unparsed(dcfg->ruleset->mp, rule, NULL, NULL, NULL);
995 
996     return NULL;
997 }
998 
999 /**
1000  *
1001  */
add_marker(cmd_parms * cmd,directory_config * dcfg,const char * p1,const char * p2,const char * p3)1002 static const char *add_marker(cmd_parms *cmd, directory_config *dcfg,
1003                               const char *p1, const char *p2, const char *p3)
1004 {
1005     char *my_error_msg = NULL;
1006     msre_rule *rule = NULL;
1007     extern msc_engine *modsecurity;
1008     int p;
1009 
1010     #ifdef DEBUG_CONF
1011     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1012         "Rule: type=%d p1='%s' p2='%s' p3='%s'", RULE_TYPE_MARKER, p1, p2, p3);
1013     #endif
1014 
1015     /* Create a ruleset if one does not exist. */
1016     if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) {
1017         dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool);
1018         if (dcfg->ruleset == NULL) return FATAL_ERROR;
1019     }
1020 
1021     /* Create the rule now. */
1022     rule = msre_rule_create(dcfg->ruleset, RULE_TYPE_MARKER, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg);
1023     if (rule == NULL) {
1024         return my_error_msg;
1025     }
1026 
1027     /* This is a marker */
1028     rule->placeholder = RULE_PH_MARKER;
1029 
1030     /* Add placeholder to each phase */
1031     for (p = PHASE_FIRST; p <= PHASE_LAST; p++) {
1032         #ifdef DEBUG_CONF
1033         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1034             "Adding marker %pp phase=%d id=\"%s\".", rule, p, (rule->actionset->id == NOT_SET_P
1035             ? "(none)" : rule->actionset->id));
1036         #endif
1037 
1038         if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) {
1039             return "Internal Error: Failed to add marker to the ruleset.";
1040         }
1041     }
1042 
1043     /* No longer need to search for the ID */
1044     if (dcfg->tmp_rule_placeholders != NULL) {
1045         apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id);
1046     }
1047 
1048     return NULL;
1049 }
1050 
1051 /**
1052  *
1053  */
update_rule_action(cmd_parms * cmd,directory_config * dcfg,const char * p1,const char * p2,int offset)1054 static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg,
1055                                       const char *p1, const char *p2, int offset)
1056 {
1057     char *my_error_msg = NULL;
1058     msre_rule *rule = NULL;
1059     msre_actionset *new_actionset = NULL;
1060     msre_ruleset *ruleset = dcfg->ruleset;
1061     extern msc_engine *modsecurity;
1062 
1063     /* Get the ruleset if one exists */
1064     if ((ruleset == NULL)||(ruleset == NOT_SET_P)) {
1065         return NULL;
1066     }
1067 
1068     #ifdef DEBUG_CONF
1069     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1070         "Update rule id=\"%s\" with action \"%s\".", p1, p2);
1071     #endif
1072 
1073     /* Fetch the rule */
1074     rule = msre_ruleset_fetch_rule(ruleset, p1, offset);
1075     if (rule == NULL) {
1076         #ifdef DEBUG_CONF
1077         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1078             "Update rule id=\"%s\" with action \"%s\" failed: Rule not found.", p1, p2);
1079         #endif
1080         return NULL;
1081     }
1082 
1083     /* Check the rule actionset */
1084     /* ENH: Can this happen? */
1085     if (rule->actionset == NULL) {
1086         return apr_psprintf(cmd->pool, "ModSecurity: Attempt to update action for rule \"%s\" failed: Rule does not have an actionset.", p1);
1087     }
1088 
1089     /* Create a new actionset */
1090     new_actionset = msre_actionset_create(modsecurity->msre, cmd->pool, p2, &my_error_msg);
1091     if (new_actionset == NULL) return FATAL_ERROR;
1092     if (my_error_msg != NULL) return my_error_msg;
1093 
1094     /* Must NOT change an id */
1095     if ((new_actionset->id != NOT_SET_P) && (rule->actionset->id != NULL) && (strcmp(rule->actionset->id, new_actionset->id) != 0)) {
1096         return apr_psprintf(cmd->pool, "ModSecurity: Rule IDs cannot be updated via SecRuleUpdateActionById.");
1097     }
1098 
1099     /* Must NOT alter the phase */
1100     if ((new_actionset->phase != NOT_SET) && (rule->actionset->phase != new_actionset->phase)) {
1101         return apr_psprintf(cmd->pool, "ModSecurity: Rule phases cannot be updated via SecRuleUpdateActionById.");
1102     }
1103 
1104     #ifdef DEBUG_CONF
1105     {
1106         char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset);
1107         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1108             "Update rule %pp id=\"%s\" old action: \"%s\"",
1109             rule,
1110             (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id),
1111             actions);
1112     }
1113     #endif
1114 
1115     /* Merge new actions with the rule */
1116     /* ENH: Will this leak the old actionset? */
1117     rule->actionset = msre_actionset_merge(modsecurity->msre, cmd->pool, rule->actionset,
1118         new_actionset, 1);
1119     msre_actionset_set_defaults(rule->actionset);
1120 
1121     /* Update the unparsed rule */
1122     rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, NULL, NULL);
1123 
1124     #ifdef DEBUG_CONF
1125     {
1126         char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset);
1127         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1128             "Update rule %pp id=\"%s\" new action: \"%s\"",
1129             rule,
1130             (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id),
1131             actions);
1132     }
1133     #endif
1134 
1135     return NULL;
1136 }
1137 
1138 /* -- Configuration directives -- */
1139 
cmd_action(cmd_parms * cmd,void * _dcfg,const char * p1)1140 static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1)
1141 {
1142     return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_ACTION, SECACTION_TARGETS, SECACTION_ARGS, p1);
1143 }
1144 
cmd_marker(cmd_parms * cmd,void * _dcfg,const char * p1)1145 static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1)
1146 {
1147     directory_config *dcfg = (directory_config *)_dcfg;
1148     const char *action = apr_pstrcat(dcfg->mp, SECMARKER_BASE_ACTIONS, p1, NULL);
1149     return add_marker(cmd, (directory_config *)_dcfg, SECMARKER_TARGETS, SECMARKER_ARGS, action);
1150 }
1151 
cmd_cookiev0_separator(cmd_parms * cmd,void * _dcfg,const char * p1)1152 static const char *cmd_cookiev0_separator(cmd_parms *cmd, void *_dcfg,
1153         const char *p1)
1154 {
1155     directory_config *dcfg = (directory_config *)_dcfg;
1156 
1157     if (strlen(p1) != 1) {
1158         return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie v0 separator: %s", p1);
1159     }
1160 
1161     dcfg->cookiev0_separator = p1;
1162 
1163     return NULL;
1164 }
1165 
cmd_argument_separator(cmd_parms * cmd,void * _dcfg,const char * p1)1166 static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg,
1167         const char *p1)
1168 {
1169     directory_config *dcfg = (directory_config *)_dcfg;
1170 
1171     if (strlen(p1) != 1) {
1172         return apr_psprintf(cmd->pool, "ModSecurity: Invalid argument separator: %s", p1);
1173     }
1174 
1175     dcfg->argument_separator = p1[0];
1176 
1177     return NULL;
1178 }
1179 
cmd_audit_engine(cmd_parms * cmd,void * _dcfg,const char * p1)1180 static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1)
1181 {
1182     directory_config *dcfg = _dcfg;
1183 
1184     if (strcasecmp(p1, "On") == 0) dcfg->auditlog_flag = AUDITLOG_ON;
1185     else
1186         if (strcasecmp(p1, "Off") == 0) dcfg->auditlog_flag = AUDITLOG_OFF;
1187         else
1188             if (strcasecmp(p1, "RelevantOnly") == 0) dcfg->auditlog_flag = AUDITLOG_RELEVANT;
1189             else
1190                 return (const char *)apr_psprintf(cmd->pool,
1191                         "ModSecurity: Unrecognised parameter value for SecAuditEngine: %s", p1);
1192 
1193     return NULL;
1194 }
1195 
cmd_audit_log(cmd_parms * cmd,void * _dcfg,const char * p1)1196 static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1)
1197 {
1198     directory_config *dcfg = _dcfg;
1199 
1200     dcfg->auditlog_name = (char *)p1;
1201 
1202     if (dcfg->auditlog_name[0] == '|') {
1203         const char *pipe_name = dcfg->auditlog_name + 1;
1204         piped_log *pipe_log;
1205 
1206         pipe_log = ap_open_piped_log(cmd->pool, pipe_name);
1207         if (pipe_log == NULL) {
1208             return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log pipe: %s",
1209                     pipe_name);
1210         }
1211         dcfg->auditlog_fd = ap_piped_log_write_fd(pipe_log);
1212     }
1213     else {
1214         const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog_name);
1215         apr_status_t rc;
1216 
1217         if (dcfg->auditlog_fileperms == NOT_SET) {
1218             dcfg->auditlog_fileperms = CREATEMODE;
1219         }
1220         rc = apr_file_open(&dcfg->auditlog_fd, file_name,
1221                 APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
1222                 dcfg->auditlog_fileperms, cmd->pool);
1223 
1224         if (rc != APR_SUCCESS) {
1225             return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log file: %s",
1226                     file_name);
1227         }
1228     }
1229 
1230     return NULL;
1231 }
1232 
cmd_audit_log2(cmd_parms * cmd,void * _dcfg,const char * p1)1233 static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1)
1234 {
1235     directory_config *dcfg = _dcfg;
1236 
1237     if (dcfg->auditlog_name == NOT_SET_P) {
1238         return apr_psprintf(cmd->pool, "ModSecurity: Cannot configure a secondary audit log without a primary defined: %s", p1);
1239     }
1240 
1241     dcfg->auditlog2_name = (char *)p1;
1242 
1243     if (dcfg->auditlog2_name[0] == '|') {
1244         const char *pipe_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name + 1);
1245         piped_log *pipe_log;
1246 
1247         pipe_log = ap_open_piped_log(cmd->pool, pipe_name);
1248         if (pipe_log == NULL) {
1249             return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log pipe: %s",
1250                     pipe_name);
1251         }
1252         dcfg->auditlog2_fd = ap_piped_log_write_fd(pipe_log);
1253     }
1254     else {
1255         const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name);
1256         apr_status_t rc;
1257 
1258         if (dcfg->auditlog_fileperms == NOT_SET) {
1259             dcfg->auditlog_fileperms = CREATEMODE;
1260         }
1261         rc = apr_file_open(&dcfg->auditlog2_fd, file_name,
1262                 APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
1263                 dcfg->auditlog_fileperms, cmd->pool);
1264 
1265         if (rc != APR_SUCCESS) {
1266             return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log file: %s",
1267                     file_name);
1268         }
1269     }
1270 
1271     return NULL;
1272 }
1273 
cmd_audit_log_parts(cmd_parms * cmd,void * _dcfg,const char * p1)1274 static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg,
1275         const char *p1)
1276 {
1277     directory_config *dcfg = _dcfg;
1278 
1279     if (is_valid_parts_specification((char *)p1) != 1) {
1280         return apr_psprintf(cmd->pool, "Invalid parts specification for SecAuditLogParts: %s", p1);
1281     }
1282 
1283     dcfg->auditlog_parts = (char *)p1;
1284     return NULL;
1285 }
1286 
cmd_audit_log_relevant_status(cmd_parms * cmd,void * _dcfg,const char * p1)1287 static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg,
1288         const char *p1)
1289 {
1290     directory_config *dcfg = _dcfg;
1291 
1292     dcfg->auditlog_relevant_regex = msc_pregcomp(cmd->pool, p1, PCRE_DOTALL, NULL, NULL);
1293     if (dcfg->auditlog_relevant_regex == NULL) {
1294         return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
1295     }
1296 
1297     return NULL;
1298 }
1299 
cmd_audit_log_type(cmd_parms * cmd,void * _dcfg,const char * p1)1300 static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg,
1301         const char *p1)
1302 {
1303     directory_config *dcfg = _dcfg;
1304 
1305     if (strcasecmp(p1, "Serial") == 0) dcfg->auditlog_type = AUDITLOG_SERIAL;
1306     else
1307         if (strcasecmp(p1, "Concurrent") == 0) dcfg->auditlog_type = AUDITLOG_CONCURRENT;
1308         else
1309             return (const char *)apr_psprintf(cmd->pool,
1310                     "ModSecurity: Unrecognised parameter value for SecAuditLogType: %s", p1);
1311 
1312     return NULL;
1313 }
1314 
1315 #ifdef WITH_YAJL
cmd_audit_log_mode(cmd_parms * cmd,void * _dcfg,const char * p1)1316 static const char *cmd_audit_log_mode(cmd_parms *cmd, void *_dcfg,
1317         const char *p1)
1318 {
1319     directory_config *dcfg = _dcfg;
1320 
1321     if (strcasecmp(p1, "JSON") == 0) dcfg->auditlog_format = AUDITLOGFORMAT_JSON;
1322     else
1323         if (strcasecmp(p1, "Native") == 0) dcfg->auditlog_format = AUDITLOGFORMAT_NATIVE;
1324         else
1325             return (const char *)apr_psprintf(cmd->pool,
1326                     "ModSecurity: Unrecognised parameter value for SecAuditLogFormat: %s", p1);
1327 
1328     return NULL;
1329 }
1330 #endif
1331 
cmd_audit_log_dirmode(cmd_parms * cmd,void * _dcfg,const char * p1)1332 static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg,
1333         const char *p1)
1334 {
1335     directory_config *dcfg = (directory_config *)_dcfg;
1336 
1337     if (dcfg == NULL) return NULL;
1338 
1339     if (strcasecmp(p1, "default") == 0) {
1340         dcfg->auditlog_dirperms = NOT_SET;
1341     }
1342     else {
1343         long int mode = strtol(p1, NULL, 8); /* expects octal mode */
1344         if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) {
1345             return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogDirMode: %s", p1);
1346         }
1347 
1348         dcfg->auditlog_dirperms = mode2fileperms(mode);
1349     }
1350 
1351     return NULL;
1352 }
1353 
cmd_audit_log_filemode(cmd_parms * cmd,void * _dcfg,const char * p1)1354 static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg,
1355         const char *p1)
1356 {
1357     directory_config *dcfg = (directory_config *)_dcfg;
1358 
1359     if (dcfg == NULL) return NULL;
1360 
1361     if (strcasecmp(p1, "default") == 0) {
1362         dcfg->auditlog_fileperms = NOT_SET;
1363     }
1364     else {
1365         long int mode = strtol(p1, NULL, 8); /* expects octal mode */
1366         if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) {
1367             return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogFileMode: %s", p1);
1368         }
1369 
1370         dcfg->auditlog_fileperms = mode2fileperms(mode);
1371     }
1372 
1373     return NULL;
1374 }
1375 
cmd_audit_log_storage_dir(cmd_parms * cmd,void * _dcfg,const char * p1)1376 static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg,
1377                                              const char *p1)
1378 {
1379     directory_config *dcfg = _dcfg;
1380 
1381     dcfg->auditlog_storage_dir = ap_server_root_relative(cmd->pool, p1);
1382 
1383     return NULL;
1384 }
1385 
cmd_cookie_format(cmd_parms * cmd,void * _dcfg,const char * p1)1386 static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg,
1387                                      const char *p1)
1388 {
1389     directory_config *dcfg = (directory_config *)_dcfg;
1390 
1391     if (strcmp(p1, "0") == 0) dcfg->cookie_format = COOKIES_V0;
1392     else
1393     if (strcmp(p1, "1") == 0) dcfg->cookie_format = COOKIES_V1;
1394     else {
1395         return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie format: %s", p1);
1396     }
1397 
1398     return NULL;
1399 }
1400 
cmd_chroot_dir(cmd_parms * cmd,void * _dcfg,const char * p1)1401 static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
1402 {
1403     char cwd[1025] = "";
1404 
1405     if (cmd->server->is_virtual) {
1406         return "ModSecurity: SecChrootDir not allowed in VirtualHost";
1407     }
1408 
1409     chroot_dir = (char *)p1;
1410 
1411     if (getcwd(cwd, 1024) == NULL) {
1412         return "ModSecurity: Failed to get the current working directory";
1413     }
1414 
1415     if (chdir(chroot_dir) < 0) {
1416         return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)",
1417             chroot_dir, errno, strerror(errno));
1418     }
1419 
1420     if (chdir(cwd) < 0) {
1421         return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)",
1422             cwd, errno, strerror(errno));
1423     }
1424 
1425     return NULL;
1426 }
1427 
1428 /**
1429  * Adds component signature to the list of signatures kept in configuration.
1430  */
cmd_component_signature(cmd_parms * cmd,void * _dcfg,const char * p1)1431 static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg,
1432                                            const char *p1)
1433 {
1434     directory_config *dcfg = (directory_config *)_dcfg;
1435 
1436     /* ENH Enforce "Name/VersionX.Y.Z (comment)" format. */
1437     *(char **)apr_array_push(dcfg->component_signatures) = (char *)p1;
1438 
1439     return NULL;
1440 }
1441 
cmd_content_injection(cmd_parms * cmd,void * _dcfg,int flag)1442 static const char *cmd_content_injection(cmd_parms *cmd, void *_dcfg, int flag)
1443 {
1444     directory_config *dcfg = (directory_config *)_dcfg;
1445     if (dcfg == NULL) return NULL;
1446     dcfg->content_injection_enabled = flag;
1447     return NULL;
1448 }
1449 
cmd_data_dir(cmd_parms * cmd,void * _dcfg,const char * p1)1450 static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
1451 {
1452     directory_config *dcfg = (directory_config *)_dcfg;
1453 
1454     if (cmd->server->is_virtual) {
1455         return "ModSecurity: SecDataDir not allowed in VirtualHost.";
1456     }
1457 
1458     dcfg->data_dir = ap_server_root_relative(cmd->pool, p1);
1459 
1460     return NULL;
1461 }
1462 
cmd_debug_log(cmd_parms * cmd,void * _dcfg,const char * p1)1463 static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1)
1464 {
1465     directory_config *dcfg = (directory_config *)_dcfg;
1466     apr_status_t rc;
1467 
1468     dcfg->debuglog_name = ap_server_root_relative(cmd->pool, p1);
1469 
1470     rc = apr_file_open(&dcfg->debuglog_fd, dcfg->debuglog_name,
1471                    APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
1472                    CREATEMODE, cmd->pool);
1473 
1474     if (rc != APR_SUCCESS) {
1475         return apr_psprintf(cmd->pool, "ModSecurity: Failed to open debug log file: %s",
1476             dcfg->debuglog_name);
1477     }
1478 
1479     return NULL;
1480 }
1481 
1482 /**
1483 * \brief Add SecCollectionTimeout configuration option
1484 *
1485 * \param cmd Pointer to configuration data
1486 * \param _dcfg Pointer to directory configuration
1487 * \param p1 Pointer to configuration option
1488 *
1489 * \retval NULL On failure
1490 * \retval apr_psprintf On Success
1491 */
cmd_collection_timeout(cmd_parms * cmd,void * _dcfg,const char * p1)1492 static const char *cmd_collection_timeout(cmd_parms *cmd, void *_dcfg,
1493                                        const char *p1)
1494 {
1495     directory_config *dcfg = (directory_config *)_dcfg;
1496 
1497     dcfg->col_timeout = atoi(p1);
1498     /* max 30 days */
1499     if ((dcfg->col_timeout >= 0)&&(dcfg->col_timeout <= 2592000)) return NULL;
1500 
1501     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCollectionTimeout: %s", p1);
1502 }
1503 
cmd_debug_log_level(cmd_parms * cmd,void * _dcfg,const char * p1)1504 static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg,
1505                                        const char *p1)
1506 {
1507     directory_config *dcfg = (directory_config *)_dcfg;
1508 
1509     dcfg->debuglog_level = atoi(p1);
1510     if ((dcfg->debuglog_level >= 0)&&(dcfg->debuglog_level <= 9)) return NULL;
1511 
1512     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecDebugLogLevel: %s", p1);
1513 }
1514 
cmd_default_action(cmd_parms * cmd,void * _dcfg,const char * p1)1515 static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg,
1516                                       const char *p1)
1517 {
1518     directory_config *dcfg = (directory_config *)_dcfg;
1519     extern msc_engine *modsecurity;
1520     char *my_error_msg = NULL;
1521 
1522     dcfg->tmp_default_actionset = msre_actionset_create(modsecurity->msre, cmd->pool, p1, &my_error_msg);
1523     if (dcfg->tmp_default_actionset == NULL) {
1524         if (my_error_msg != NULL) return my_error_msg;
1525         else return FATAL_ERROR;
1526     }
1527 
1528     /* Must specify a disruptive action. */
1529     /* ENH: Remove this requirement? */
1530     if (dcfg->tmp_default_actionset->intercept_action == NOT_SET) {
1531         return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a disruptive action.");
1532     }
1533 
1534     /* Must specify a phase. */
1535     /* ENH: Remove this requirement? */
1536     if (dcfg->tmp_default_actionset->phase == NOT_SET) {
1537         return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a phase.");
1538     }
1539 
1540     /* Must not use metadata actions. */
1541     /* ENH: loop through to check for tags */
1542     if ((dcfg->tmp_default_actionset->id != NOT_SET_P)
1543         ||(dcfg->tmp_default_actionset->rev != NOT_SET_P)
1544         ||(dcfg->tmp_default_actionset->version != NOT_SET_P)
1545         ||(dcfg->tmp_default_actionset->maturity != NOT_SET)
1546         ||(dcfg->tmp_default_actionset->accuracy != NOT_SET)
1547         ||(dcfg->tmp_default_actionset->msg != NOT_SET_P))
1548     {
1549         return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
1550             "contain any metadata actions (id, rev, msg, tag, severity, ver, accuracy, maturity, logdata).");
1551     }
1552     /* These are just a warning for now. */
1553     if ((dcfg->tmp_default_actionset->severity != NOT_SET)
1554         ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P))
1555     {
1556         ap_log_perror(APLOG_MARK,
1557             APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool,
1558             "ModSecurity: WARNING Using \"severity\" or \"logdata\" in "
1559             "SecDefaultAction is deprecated (%s:%d).",
1560             cmd->directive->filename, cmd->directive->line_num);
1561     }
1562 
1563     if (apr_table_get(dcfg->tmp_default_actionset->actions, "t")) {
1564         ap_log_perror(APLOG_MARK,
1565             APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool,
1566             "ModSecurity: WARNING Using transformations in "
1567             "SecDefaultAction is deprecated (%s:%d).",
1568             cmd->directive->filename, cmd->directive->line_num);
1569     }
1570 
1571     /* Must not use chain. */
1572     if (dcfg->tmp_default_actionset->is_chained != NOT_SET) {
1573         return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
1574             "contain a chain action.");
1575     }
1576 
1577     /* Must not use skip. */
1578     if (dcfg->tmp_default_actionset->skip_count != NOT_SET) {
1579         return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
1580             "contain a skip action.");
1581     }
1582 
1583     /* Must not use skipAfter. */
1584     if (dcfg->tmp_default_actionset->skip_after != NOT_SET_P) {
1585         return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
1586             "contain a skipAfter action.");
1587     }
1588 
1589     return NULL;
1590 }
1591 
cmd_disable_backend_compression(cmd_parms * cmd,void * _dcfg,int flag)1592 static const char *cmd_disable_backend_compression(cmd_parms *cmd, void *_dcfg, int flag)
1593 {
1594     directory_config *dcfg = (directory_config *)_dcfg;
1595     if (dcfg == NULL) return NULL;
1596     dcfg->disable_backend_compression = flag;
1597     return NULL;
1598 }
1599 
cmd_guardian_log(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)1600 static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg,
1601                                     const char *p1, const char *p2)
1602 {
1603     extern char *guardianlog_name;
1604     extern apr_file_t *guardianlog_fd;
1605     extern char *guardianlog_condition;
1606 
1607     if (cmd->server->is_virtual) {
1608         return "ModSecurity: SecGuardianLog not allowed in VirtualHost";
1609     }
1610 
1611     if (p2 != NULL) {
1612         if (strncmp(p2, "env=", 4) != 0) {
1613             return "ModSecurity: Error in condition clause";
1614         }
1615         if ( (p2[4] == '\0') || ((p2[4] == '!')&&(p2[5] == '\0')) ) {
1616             return "ModSecurity: Missing variable name";
1617         }
1618         guardianlog_condition = apr_pstrdup(cmd->pool, p2 + 4);
1619     }
1620 
1621     guardianlog_name = (char *)p1;
1622 
1623     if (guardianlog_name[0] == '|') {
1624         const char *pipe_name = ap_server_root_relative(cmd->pool, guardianlog_name + 1);
1625         piped_log *pipe_log;
1626 
1627         pipe_log = ap_open_piped_log(cmd->pool, pipe_name);
1628         if (pipe_log == NULL) {
1629             return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log pipe: %s",
1630                 pipe_name);
1631         }
1632         guardianlog_fd = ap_piped_log_write_fd(pipe_log);
1633     }
1634     else {
1635         const char *file_name = ap_server_root_relative(cmd->pool, guardianlog_name);
1636         apr_status_t rc;
1637 
1638         rc = apr_file_open(&guardianlog_fd, file_name,
1639             APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
1640             CREATEMODE, cmd->pool);
1641 
1642         if (rc != APR_SUCCESS) {
1643             return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log file: %s",
1644                 file_name);
1645         }
1646     }
1647 
1648     return NULL;
1649 }
1650 
1651 /**
1652 * \brief Add SecStreamInBodyInspection configuration option
1653 *
1654 * \param cmd Pointer to configuration data
1655 * \param _dcfg Pointer to directory configuration
1656 * \param p1 Pointer to configuration option
1657 *
1658 * \retval NULL On failure
1659 * \retval apr_psprintf On Success
1660 */
cmd_stream_inbody_inspection(cmd_parms * cmd,void * _dcfg,int flag)1661 static const char *cmd_stream_inbody_inspection(cmd_parms *cmd, void *_dcfg, int flag)
1662 {
1663     directory_config *dcfg = (directory_config *)_dcfg;
1664     if (dcfg == NULL) return NULL;
1665     dcfg->stream_inbody_inspection = flag;
1666     return NULL;
1667 }
1668 
1669 
1670 /**
1671 * \brief Add SecStreamOutBodyInspection configuration option
1672 *
1673 * \param cmd Pointer to configuration data
1674 * \param _dcfg Pointer to directory configuration
1675 * \param p1 Pointer to configuration option
1676 *
1677 * \retval NULL On failure
1678 * \retval apr_psprintf On Success
1679 */
cmd_stream_outbody_inspection(cmd_parms * cmd,void * _dcfg,int flag)1680 static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, int flag)
1681 {
1682     directory_config *dcfg = (directory_config *)_dcfg;
1683     if (dcfg == NULL) return NULL;
1684     dcfg->stream_outbody_inspection = flag;
1685     return NULL;
1686 }
1687 /**
1688 * \brief Add SecRulePerfTime configuration option
1689 *
1690 * \param cmd Pointer to configuration data
1691 * \param _dcfg Pointer to directory configuration
1692 * \param p1 Pointer to configuration option
1693 *
1694 * \retval NULL On failure
1695 * \retval apr_psprintf On Success
1696 */
cmd_rule_perf_time(cmd_parms * cmd,void * _dcfg,const char * p1)1697 static const char *cmd_rule_perf_time(cmd_parms *cmd, void *_dcfg,
1698         const char *p1)
1699 {
1700     directory_config *dcfg = (directory_config *)_dcfg;
1701     long int limit;
1702 
1703     if (dcfg == NULL) return NULL;
1704 
1705     limit = strtol(p1, NULL, 10);
1706     if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
1707         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRulePerfTime: %s", p1);
1708     }
1709 
1710     dcfg->max_rule_time = limit;
1711 
1712     return NULL;
1713 }
1714 
parser_conn_limits_operator(apr_pool_t * mp,const char * p2,TreeRoot ** whitelist,TreeRoot ** suspicious_list,const char * filename)1715 char *parser_conn_limits_operator(apr_pool_t *mp, const char *p2,
1716     TreeRoot **whitelist, TreeRoot **suspicious_list,
1717     const char *filename)
1718 {
1719     int res = 0;
1720     char *config_orig_path;
1721     char *param = strchr(p2, ' ');
1722     char *file = NULL;
1723     char *error_msg = NULL;
1724     param++;
1725 
1726     config_orig_path = apr_pstrndup(mp, filename,
1727         strlen(filename) - strlen(apr_filepath_name_get(filename)));
1728 
1729     apr_filepath_merge(&file, config_orig_path, param, APR_FILEPATH_TRUENAME,
1730         mp);
1731 
1732     if ((strncasecmp(p2, "!@ipMatchFromFile", strlen("!@ipMatchFromFile")) == 0) ||
1733         (strncasecmp(p2, "!@ipMatchF", strlen("!@ipMatchF")) == 0)) {
1734 
1735         res = ip_tree_from_file(whitelist, file, mp, &error_msg);
1736     }
1737     else if (strncasecmp(p2, "!@ipMatch", strlen("!@ipMatch")) == 0) {
1738         res = ip_tree_from_param(mp, param, whitelist, &error_msg);
1739     }
1740     else if ((strncasecmp(p2, "@ipMatchFromFile", strlen("@ipMatchFromFile")) == 0) ||
1741         (strncasecmp(p2, "@ipMatchF", strlen("@ipMatchF")) == 0)) {
1742 
1743         res = ip_tree_from_file(suspicious_list, file, mp, &error_msg);
1744     }
1745     else if (strncasecmp(p2, "@ipMatch", strlen("@ipMatch")) == 0) {
1746         res = ip_tree_from_param(mp, param, suspicious_list, &error_msg);
1747     }
1748     else {
1749         return apr_psprintf(mp, "ModSecurity: Invalid operator for " \
1750            "SecConnReadStateLimit: %s, expected operators: @ipMatch, @ipMatchF " \
1751            "or @ipMatchFromFile with or without !", p2);
1752     }
1753 
1754     if (res) {
1755         char *error;
1756         error = apr_psprintf(mp, "ModSecurity: failed to load IPs " \
1757             "from: %s", param);
1758 
1759         if (*error_msg) {
1760             error = apr_psprintf(mp, "%s %s", error, error_msg);
1761         }
1762 
1763         return error;
1764     }
1765 
1766     return NULL;
1767 }
1768 
1769 
1770 /**
1771 * \brief Add SecConnReadStateLimit configuration option
1772 *
1773 * \param cmd Pointer to configuration data
1774 * \param _dcfg Pointer to directory configuration
1775 * \param p1 Pointer to configuration option
1776 * \param p2 Pointer to configuration option
1777 *
1778 * \retval NULL On failure
1779 * \retval apr_psprintf On Success
1780 */
cmd_conn_read_state_limit(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)1781 static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg,
1782         const char *p1, const char *p2)
1783 {
1784     directory_config *dcfg = (directory_config *)_dcfg;
1785     long int limit;
1786 
1787     if (dcfg == NULL) return NULL;
1788 
1789     limit = strtol(p1, NULL, 10);
1790     if ((limit == LONG_MAX) || (limit == LONG_MIN) || (limit <= 0)) {
1791         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
1792             "SecConnReadStateLimit: %s", p1);
1793     }
1794 
1795     if (p2 != NULL) {
1796         char *param = parser_conn_limits_operator(cmd->pool, p2,
1797             &conn_read_state_whitelist, &conn_read_state_suspicious_list,
1798             cmd->directive->filename);
1799 
1800         if (param)
1801             return param;
1802     }
1803 
1804     conn_read_state_limit = limit;
1805 
1806     return NULL;
1807 }
1808 
cmd_read_state_limit(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)1809 static const char *cmd_read_state_limit(cmd_parms *cmd, void *_dcfg,
1810         const char *p1, const char *p2)
1811 {
1812     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1813             "SecReadStateLimit is depricated, use SecConnReadStateLimit " \
1814             "instead.");
1815 
1816     return cmd_conn_read_state_limit(cmd, _dcfg, p1, p2);
1817 }
1818 
1819 /**
1820 * \brief Add SecConnWriteStateLimit configuration option
1821 *
1822 * \param cmd Pointer to configuration data
1823 * \param _dcfg Pointer to directory configuration
1824 * \param p1 Pointer to configuration option
1825 * \param p2 Pointer to configuration option
1826 *
1827 * \retval NULL On failure
1828 * \retval apr_psprintf On Success
1829 */
cmd_conn_write_state_limit(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)1830 static const char *cmd_conn_write_state_limit(cmd_parms *cmd, void *_dcfg,
1831         const char *p1, const char *p2)
1832 {
1833     directory_config *dcfg = (directory_config *)_dcfg;
1834     long int limit;
1835 
1836     if (dcfg == NULL) return NULL;
1837 
1838     limit = strtol(p1, NULL, 10);
1839     if ((limit == LONG_MAX) || (limit == LONG_MIN) || (limit <= 0)) {
1840         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
1841             "SecConnWriteStateLimit: %s", p1);
1842     }
1843 
1844     if (p2 != NULL) {
1845         char *param = parser_conn_limits_operator(cmd->pool, p2,
1846             &conn_write_state_whitelist, &conn_write_state_suspicious_list,
1847             cmd->directive->filename);
1848 
1849         if (param)
1850             return param;
1851     }
1852 
1853     conn_write_state_limit = limit;
1854 
1855     return NULL;
1856 }
cmd_write_state_limit(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)1857 static const char *cmd_write_state_limit(cmd_parms *cmd, void *_dcfg,
1858         const char *p1, const char *p2)
1859 {
1860     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
1861             "SecWriteStateLimit is depricated, use SecConnWriteStateLimit " \
1862             "instead.");
1863 
1864     return cmd_conn_write_state_limit(cmd, _dcfg, p1, p2);
1865 }
1866 
1867 
1868 
cmd_request_body_inmemory_limit(cmd_parms * cmd,void * _dcfg,const char * p1)1869 static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg,
1870                                                    const char *p1)
1871 {
1872     directory_config *dcfg = (directory_config *)_dcfg;
1873     long int limit;
1874 
1875     if (dcfg == NULL) return NULL;
1876 
1877     limit = strtol(p1, NULL, 10);
1878     if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
1879         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyInMemoryLimit: %s", p1);
1880     }
1881 
1882     dcfg->reqbody_inmemory_limit = limit;
1883 
1884     return NULL;
1885 }
1886 
cmd_request_body_limit(cmd_parms * cmd,void * _dcfg,const char * p1)1887 static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg,
1888                                           const char *p1)
1889 {
1890     directory_config *dcfg = (directory_config *)_dcfg;
1891     long int limit;
1892 
1893     if (dcfg == NULL) return NULL;
1894 
1895     limit = strtol(p1, NULL, 10);
1896     if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
1897         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimit: %s", p1);
1898     }
1899 
1900     dcfg->reqbody_limit = limit;
1901 
1902     return NULL;
1903 }
1904 
cmd_request_body_no_files_limit(cmd_parms * cmd,void * _dcfg,const char * p1)1905 static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg,
1906                                                    const char *p1)
1907 {
1908     directory_config *dcfg = (directory_config *)_dcfg;
1909     long int limit;
1910 
1911     if (dcfg == NULL) return NULL;
1912 
1913     limit = strtol(p1, NULL, 10);
1914     if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
1915         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyNoFilesLimit: %s", p1);
1916     }
1917 
1918     dcfg->reqbody_no_files_limit = limit;
1919 
1920     return NULL;
1921 }
1922 
cmd_request_body_access(cmd_parms * cmd,void * _dcfg,const char * p1)1923 static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg,
1924                                            const char *p1)
1925 {
1926     directory_config *dcfg = (directory_config *)_dcfg;
1927     if (dcfg == NULL) return NULL;
1928 
1929     if (strcasecmp(p1, "on") == 0) dcfg->reqbody_access = 1;
1930     else
1931     if (strcasecmp(p1, "off") == 0) dcfg->reqbody_access = 0;
1932     else
1933     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyAccess: %s", p1);
1934 
1935     return NULL;
1936 }
1937 
1938 /**
1939 * \brief Add SecInterceptOnError configuration option
1940 *
1941 * \param cmd Pointer to configuration data
1942 * \param _dcfg Pointer to directory configuration
1943 * \param p1 Pointer to configuration option
1944 *
1945 * \retval NULL On failure
1946 * \retval apr_psprintf On success
1947 */
cmd_request_intercept_on_error(cmd_parms * cmd,void * _dcfg,const char * p1)1948 static const char *cmd_request_intercept_on_error(cmd_parms *cmd, void *_dcfg,
1949                                            const char *p1)
1950 {
1951     directory_config *dcfg = (directory_config *)_dcfg;
1952     if (dcfg == NULL) return NULL;
1953 
1954     if (strcasecmp(p1, "on") == 0) dcfg->reqintercept_oe = 1;
1955     else
1956     if (strcasecmp(p1, "off") == 0) dcfg->reqintercept_oe = 0;
1957     else
1958     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecInterceptOnError: %s", p1);
1959 
1960     return NULL;
1961 }
1962 
1963 
cmd_request_encoding(cmd_parms * cmd,void * _dcfg,const char * p1)1964 static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg,
1965                                         const char *p1)
1966 {
1967     directory_config *dcfg = (directory_config *)_dcfg;
1968     if (dcfg == NULL) return NULL;
1969 
1970     /* ENH Validate encoding */
1971 
1972     dcfg->request_encoding = p1;
1973 
1974     return NULL;
1975 }
1976 
cmd_response_body_access(cmd_parms * cmd,void * _dcfg,const char * p1)1977 static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg,
1978                                             const char *p1)
1979 {
1980     directory_config *dcfg = (directory_config *)_dcfg;
1981     if (dcfg == NULL) return NULL;
1982 
1983     if (strcasecmp(p1, "on") == 0) dcfg->resbody_access = 1;
1984     else
1985     if (strcasecmp(p1, "off") == 0) dcfg->resbody_access = 0;
1986     else
1987     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyAccess: %s", p1);
1988 
1989     return NULL;
1990 }
1991 
cmd_response_body_limit(cmd_parms * cmd,void * _dcfg,const char * p1)1992 static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg,
1993                                            const char *p1)
1994 {
1995     directory_config *dcfg = (directory_config *)_dcfg;
1996     long int limit;
1997 
1998     limit = strtol(p1, NULL, 10);
1999     if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
2000         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimit: %s", p1);
2001     }
2002 
2003     if (limit > RESPONSE_BODY_HARD_LIMIT) {
2004         return apr_psprintf(cmd->pool, "ModSecurity: Response size limit can not exceed the hard limit: %li", RESPONSE_BODY_HARD_LIMIT);
2005     }
2006 
2007     dcfg->of_limit = limit;
2008 
2009     return NULL;
2010 }
2011 
cmd_response_body_limit_action(cmd_parms * cmd,void * _dcfg,const char * p1)2012 static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg,
2013                                                   const char *p1)
2014 {
2015     directory_config *dcfg = (directory_config *)_dcfg;
2016     if (dcfg == NULL) return NULL;
2017 
2018     if (dcfg->is_enabled == MODSEC_DETECTION_ONLY)  {
2019         dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL;
2020         return NULL;
2021     }
2022 
2023     if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL;
2024     else
2025     if (strcasecmp(p1, "Reject") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
2026     else
2027     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimitAction: %s", p1);
2028 
2029     return NULL;
2030 }
2031 
2032 /**
2033 * \brief Add SecRequestBodyLimitAction configuration option
2034 *
2035 * \param cmd Pointer to configuration data
2036 * \param _dcfg Pointer to directory configuration
2037 * \param p1 Pointer to configuration option
2038 *
2039 * \retval NULL On failure
2040 * \retval apr_psprintf On success
2041 */
cmd_resquest_body_limit_action(cmd_parms * cmd,void * _dcfg,const char * p1)2042 static const char *cmd_resquest_body_limit_action(cmd_parms *cmd, void *_dcfg,
2043                                                   const char *p1)
2044 {
2045     directory_config *dcfg = (directory_config *)_dcfg;
2046     if (dcfg == NULL) return NULL;
2047 
2048     if (dcfg->is_enabled == MODSEC_DETECTION_ONLY)  {
2049         dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL;
2050         return NULL;
2051     }
2052 
2053     if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL;
2054     else
2055     if (strcasecmp(p1, "Reject") == 0) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT;
2056     else
2057     return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimitAction: %s", p1);
2058 
2059     return NULL;
2060 }
2061 
cmd_response_body_mime_type(cmd_parms * cmd,void * _dcfg,const char * _p1)2062 static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg,
2063                                                const char *_p1)
2064 {
2065     directory_config *dcfg = (directory_config *)_dcfg;
2066     char *p1 = apr_pstrdup(cmd->pool, _p1);
2067 
2068     /* TODO check whether the parameter is a valid MIME type of "???" */
2069 
2070     if ((dcfg->of_mime_types == NULL)||(dcfg->of_mime_types == NOT_SET_P)) {
2071         dcfg->of_mime_types = apr_table_make(cmd->pool, 10);
2072     }
2073 
2074     strtolower_inplace((unsigned char *)p1);
2075     apr_table_setn(dcfg->of_mime_types, p1, "1");
2076 
2077     return NULL;
2078 }
2079 
cmd_response_body_mime_types_clear(cmd_parms * cmd,void * _dcfg)2080 static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd,
2081                                                       void *_dcfg)
2082 {
2083     directory_config *dcfg = (directory_config *)_dcfg;
2084     if (dcfg == NULL) return NULL;
2085 
2086     dcfg->of_mime_types_cleared = 1;
2087 
2088     if ((dcfg->of_mime_types != NULL)&&(dcfg->of_mime_types != NOT_SET_P)) {
2089         apr_table_clear(dcfg->of_mime_types);
2090     }
2091 
2092     return NULL;
2093 }
2094 
2095 /**
2096  * \brief Add SecRuleUpdateTargetById
2097  *
2098  * \param cmd Pointer to configuration data
2099  * \param _dcfg Pointer to directory configuration
2100  * \param p1 Pointer to configuration option
2101  * \param p2 Pointer to configuration option
2102  * \param p3 Pointer to configuration option
2103  *
2104  * \retval NULL On failure|Success
2105  */
cmd_rule_update_target_by_id(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2,const char * p3)2106 static const char *cmd_rule_update_target_by_id(cmd_parms *cmd, void *_dcfg,
2107         const char *p1, const char *p2, const char *p3)
2108 {
2109     directory_config *dcfg = (directory_config *)_dcfg;
2110     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception));
2111     if (dcfg == NULL) return NULL;
2112 
2113     if(p1 == NULL)  {
2114         return apr_psprintf(cmd->pool, "Updating target by ID with no ID");
2115     }
2116 
2117     re->type = RULE_EXCEPTION_REMOVE_ID;
2118     /* TODO: Validate the range here, while we can still tell the user if it's invalid */
2119     re->param = p1;
2120 
2121     if(dcfg->ruleset == NULL) {
2122         return apr_psprintf(cmd->pool, "Updating target by ID with no ruleset in this context");
2123     }
2124 
2125     return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3);
2126 }
2127 
2128 /**
2129  * \brief Add SecRuleUpdateTargetByTag  configuration option
2130  *
2131  * \param cmd Pointer to configuration data
2132  * \param _dcfg Pointer to directory configuration
2133  * \param p1 Pointer to configuration option RULETAG
2134  * \param p2 Pointer to configuration option TARGET
2135  * \param p3 Pointer to configuration option REPLACED_TARGET
2136  * \todo Finish documenting
2137  *
2138  * \retval NULL On success
2139  * \retval apr_psprintf On failure
2140  *
2141  * \todo Figure out error checking
2142  */
cmd_rule_update_target_by_tag(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2,const char * p3)2143 static const char *cmd_rule_update_target_by_tag(cmd_parms *cmd, void *_dcfg,
2144         const char *p1, const char *p2, const char *p3)
2145 {
2146     directory_config *dcfg = (directory_config *)_dcfg;
2147     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception));
2148     if (dcfg == NULL) return NULL;
2149 
2150     if(p1 == NULL)  {
2151         return apr_psprintf(cmd->pool, "Updating target by tag with no tag");
2152     }
2153 
2154     re->type = RULE_EXCEPTION_REMOVE_TAG;
2155     re->param = p1;
2156     re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
2157     if (re->param_data == NULL) {
2158         return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
2159     }
2160 
2161     return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3);
2162 }
2163 /**
2164  * \brief Add SecRuleUpdateTargetByMsg configuration option
2165  *
2166  * \param cmd Pointer to configuration data
2167  * \param _dcfg Pointer to directory configuration
2168  * \param p1 Pointer to configuration option RULEMSG
2169  * \param p2 Pointer to configuration option TARGET
2170  * \param p3 Pointer to configuration option REPLACED_TARGET
2171  * \todo Finish documenting
2172  *
2173  * \retval NULL On success
2174  * \retval apr_psprintf On failure
2175  *
2176  * \todo Figure out error checking
2177  */
cmd_rule_update_target_by_msg(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2,const char * p3)2178 static const char *cmd_rule_update_target_by_msg(cmd_parms *cmd, void *_dcfg,
2179         const char *p1, const char *p2, const char *p3)
2180 {
2181     directory_config *dcfg = (directory_config *)_dcfg;
2182     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception));
2183     if (dcfg == NULL) return NULL;
2184 
2185     if(p1 == NULL)  {
2186         return apr_psprintf(cmd->pool, "Updating target by message with no message");
2187     }
2188 
2189     re->type = RULE_EXCEPTION_REMOVE_MSG;
2190     re->param = p1;
2191     re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
2192     if (re->param_data == NULL) {
2193         return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
2194     }
2195 
2196     return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3);
2197 }
2198 
2199 
cmd_rule(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2,const char * p3)2200 static const char *cmd_rule(cmd_parms *cmd, void *_dcfg,
2201         const char *p1, const char *p2, const char *p3)
2202 {
2203     return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, p1, p2, p3);
2204 }
2205 
cmd_sever_conn_filters_engine(cmd_parms * cmd,void * _dcfg,const char * p1)2206 static const char *cmd_sever_conn_filters_engine(cmd_parms *cmd, void *_dcfg,
2207     const char *p1)
2208 {
2209     directory_config *dcfg = (directory_config *)_dcfg;
2210 
2211     if (dcfg == NULL) return NULL;
2212 
2213     if (strcasecmp(p1, "on") == 0)
2214     {
2215         conn_limits_filter_state = MODSEC_ENABLED;
2216     }
2217     else if (strcasecmp(p1, "off") == 0)
2218     {
2219         conn_limits_filter_state = MODSEC_DISABLED;
2220     }
2221     else if (strcasecmp(p1, "detectiononly") == 0)
2222     {
2223         conn_limits_filter_state = MODSEC_DETECTION_ONLY;
2224     }
2225     else
2226     {
2227         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
2228                 "SecConnEngine: %s", p1);
2229     }
2230 
2231     return NULL;
2232 }
2233 
cmd_rule_engine(cmd_parms * cmd,void * _dcfg,const char * p1)2234 static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1)
2235 {
2236     directory_config *dcfg = (directory_config *)_dcfg;
2237 
2238     if (dcfg == NULL) return NULL;
2239 
2240     if (strcasecmp(p1, "on") == 0)
2241     {
2242         dcfg->is_enabled = MODSEC_ENABLED;
2243     }
2244     else if (strcasecmp(p1, "off") == 0)
2245     {
2246         dcfg->is_enabled = MODSEC_DISABLED;
2247     }
2248     else if (strcasecmp(p1, "detectiononly") == 0)
2249     {
2250         dcfg->is_enabled = MODSEC_DETECTION_ONLY;
2251         dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL;
2252         dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL;
2253     }
2254     else
2255     {
2256         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
2257                 "SecRuleEngine: %s", p1);
2258     }
2259 
2260     return NULL;
2261 }
2262 
cmd_remote_rules_fail(cmd_parms * cmd,void * _dcfg,const char * p1)2263 static const char *cmd_remote_rules_fail(cmd_parms *cmd, void *_dcfg, const char *p1)
2264 {
2265     directory_config *dcfg = (directory_config *)_dcfg;
2266     if (dcfg == NULL) return NULL;
2267     if (strncasecmp(p1, "warn", 4) == 0)
2268     {
2269         remote_rules_fail_action = REMOTE_RULES_WARN_ON_FAIL;
2270     }
2271     else if (strncasecmp(p1, "abort", 5) == 0)
2272     {
2273         remote_rules_fail_action = REMOTE_RULES_ABORT_ON_FAIL;
2274     }
2275     else
2276     {
2277         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
2278                 "SecRemoteRulesFailAction, expected: Abort or Warn.");
2279     }
2280 
2281     return NULL;
2282 }
2283 
cmd_remote_rules(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2,const char * p3)2284 static const char *cmd_remote_rules(cmd_parms *cmd, void *_dcfg, const char *p1,
2285         const char *p2, const char *p3)
2286 {
2287     char *error_msg = NULL;
2288     directory_config *dcfg = (directory_config *)_dcfg;
2289 #ifdef WITH_REMOTE_RULES
2290     int crypto = 0;
2291     const char *uri = p2;
2292     const char *key = p1;
2293 #endif
2294 
2295     if (dcfg == NULL) return NULL;
2296 
2297 #ifdef WITH_REMOTE_RULES
2298     if (strncasecmp(p1, "crypto", 6) == 0)
2299     {
2300 #ifdef WITH_APU_CRYPTO
2301         uri = p3;
2302         key = p2;
2303         crypto = 1;
2304 #else
2305         return apr_psprintf(cmd->pool, "ModSecurity: SecRemoteRule using " \
2306                 "`crypto' but ModSecurity was not compiled with crypto " \
2307                 "support.");
2308 #endif
2309     }
2310 
2311     if (uri == NULL || key == NULL)
2312     {
2313         return apr_psprintf(cmd->pool, "ModSecurity: Use SecRemoteRule with " \
2314                 "Key and URI");
2315     }
2316 
2317     if (strncasecmp(uri, "https", 5) != 0) {
2318         return apr_psprintf(cmd->pool, "ModSecurity: Invalid URI:" \
2319                 " '%s'. Expected HTTPS.", uri);
2320     }
2321 
2322     // FIXME: Should we handle more then one server at once?
2323     if (remote_rules_server != NULL)
2324     {
2325         return apr_psprintf(cmd->pool, "ModSecurity: " \
2326                 "SecRemoteRules cannot be used more than once.");
2327     }
2328 
2329     remote_rules_server = apr_pcalloc(cmd->pool, sizeof(msc_remote_rules_server));
2330     if (remote_rules_server == NULL)
2331     {
2332         return apr_psprintf(cmd->pool, "ModSecurity: " \
2333                 "SecRemoteRules: Internal failure. Not enougth memory.");
2334     }
2335 
2336     remote_rules_server->context = dcfg;
2337     remote_rules_server->context_label = apr_pstrdup(cmd->pool, "Unkwon context");
2338     remote_rules_server->key = key;
2339     remote_rules_server->uri = uri;
2340     remote_rules_server->amount_of_rules = 0;
2341     remote_rules_server->crypto = crypto;
2342 
2343     msc_remote_add_rules_from_uri(cmd, remote_rules_server, &error_msg);
2344     if (error_msg != NULL)
2345     {
2346         return error_msg;
2347     }
2348 #else
2349     return apr_psprintf(cmd->pool, "ModSecurity: SecRemoteRules: " \
2350             "ModSecurity was not compiled with SecRemoteRules support.");
2351 #endif
2352 
2353     return NULL;
2354 }
2355 
2356 
cmd_status_engine(cmd_parms * cmd,void * _dcfg,const char * p1)2357 static const char *cmd_status_engine(cmd_parms *cmd, void *_dcfg, const char *p1)
2358 {
2359     if (strcasecmp(p1, "on") == 0) {
2360         status_engine_state = STATUS_ENGINE_ENABLED;
2361     }
2362     else if (strcasecmp(p1, "off") == 0) {
2363         status_engine_state = STATUS_ENGINE_DISABLED;
2364     }
2365     else {
2366         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
2367                 "SecStatusEngine: %s", p1);
2368     }
2369 
2370     return NULL;
2371 }
2372 
2373 
cmd_rule_inheritance(cmd_parms * cmd,void * _dcfg,int flag)2374 static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag)
2375 {
2376     directory_config *dcfg = (directory_config *)_dcfg;
2377     if (dcfg == NULL) return NULL;
2378     dcfg->rule_inheritance = flag;
2379     return NULL;
2380 }
2381 
cmd_rule_script(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)2382 static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg,
2383                                    const char *p1, const char *p2)
2384 {
2385     #if defined(WITH_LUA)
2386     const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1);
2387     return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL);
2388     #else
2389     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Ignoring SecRuleScript \"%s\" directive (%s:%d): No Lua scripting support.", p1, cmd->directive->filename, cmd->directive->line_num);
2390     return NULL;
2391     #endif
2392 }
2393 
cmd_rule_remove_by_id(cmd_parms * cmd,void * _dcfg,const char * p1)2394 static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg,
2395                                          const char *p1)
2396 {
2397     directory_config *dcfg = (directory_config *)_dcfg;
2398     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception));
2399     if (dcfg == NULL) return NULL;
2400 
2401     re->type = RULE_EXCEPTION_REMOVE_ID;
2402     re->param = p1;
2403     *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re;
2404 
2405     /* Remove the corresponding rules from the context straight away. */
2406     msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re);
2407 
2408     return NULL;
2409 }
2410 
2411 /**
2412 * \brief Add SecRuleRemoveByTag  configuration option
2413 *
2414 * \param cmd Pointer to configuration data
2415 * \param _dcfg Pointer to directory configuration
2416 * \param p1 Pointer to configuration option
2417 *
2418 * \retval NULL On failure
2419 * \retval apr_psprintf On success
2420 */
cmd_rule_remove_by_tag(cmd_parms * cmd,void * _dcfg,const char * p1)2421 static const char *cmd_rule_remove_by_tag(cmd_parms *cmd, void *_dcfg,
2422                                           const char *p1)
2423 {
2424     directory_config *dcfg = (directory_config *)_dcfg;
2425     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception));
2426     if (dcfg == NULL) return NULL;
2427 
2428     re->type = RULE_EXCEPTION_REMOVE_TAG;
2429     re->param = p1;
2430     re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
2431     if (re->param_data == NULL) {
2432         return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
2433     }
2434     *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re;
2435 
2436     /* Remove the corresponding rules from the context straight away. */
2437     msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re);
2438 
2439     #ifdef DEBUG_CONF
2440     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg);
2441     #endif
2442 
2443     return NULL;
2444 }
2445 
cmd_rule_remove_by_msg(cmd_parms * cmd,void * _dcfg,const char * p1)2446 static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg,
2447                                           const char *p1)
2448 {
2449     directory_config *dcfg = (directory_config *)_dcfg;
2450     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception));
2451     if (dcfg == NULL) return NULL;
2452 
2453     re->type = RULE_EXCEPTION_REMOVE_MSG;
2454     re->param = p1;
2455     re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
2456     if (re->param_data == NULL) {
2457         return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
2458     }
2459     *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re;
2460 
2461     /* Remove the corresponding rules from the context straight away. */
2462     msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re);
2463 
2464     #ifdef DEBUG_CONF
2465     ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg);
2466     #endif
2467 
2468     return NULL;
2469 }
2470 
cmd_rule_update_action_by_id(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)2471 static const char *cmd_rule_update_action_by_id(cmd_parms *cmd, void *_dcfg,
2472         const char *p1, const char *p2)
2473 {
2474     int offset = 0, rule_id = atoi(p1);
2475     char *opt = strchr(p1,':');
2476     char *savedptr = NULL;
2477     char *param = apr_pstrdup(cmd->pool, p1);
2478 
2479     if ((rule_id == LONG_MAX)||(rule_id == LONG_MIN)||(rule_id <= 0)) {
2480         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for ID for update action: %s", p1);
2481     }
2482 
2483     if(opt != NULL) {
2484         opt++;
2485         offset = atoi(opt);
2486         opt = apr_strtok(param,":", &savedptr);
2487         return update_rule_action(cmd, (directory_config *)_dcfg, (const char *)opt, p2, offset);
2488     }
2489 
2490     return update_rule_action(cmd, (directory_config *)_dcfg, p1, p2, offset);
2491 }
2492 
cmd_server_signature(cmd_parms * cmd,void * _dcfg,const char * p1)2493 static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg,
2494                                         const char *p1)
2495 {
2496     if (cmd->server->is_virtual) {
2497         return "ModSecurity: SecServerSignature not allowed in VirtualHost";
2498     }
2499     new_server_signature = (char *)p1;
2500     return NULL;
2501 }
2502 
cmd_tmp_dir(cmd_parms * cmd,void * _dcfg,const char * p1)2503 static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
2504 {
2505     directory_config *dcfg = (directory_config *)_dcfg;
2506 
2507     if (dcfg == NULL) return NULL;
2508 
2509     if (strcasecmp(p1, "none") == 0) dcfg->tmp_dir = NULL;
2510     else dcfg->tmp_dir = ap_server_root_relative(cmd->pool, p1);
2511 
2512     return NULL;
2513 }
2514 
cmd_upload_dir(cmd_parms * cmd,void * _dcfg,const char * p1)2515 static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1)
2516 {
2517     directory_config *dcfg = (directory_config *)_dcfg;
2518 
2519     if (dcfg == NULL) return NULL;
2520 
2521     if (strcasecmp(p1, "none") == 0) dcfg->upload_dir = NULL;
2522     else dcfg->upload_dir = ap_server_root_relative(cmd->pool, p1);
2523 
2524     return NULL;
2525 }
2526 
cmd_upload_file_limit(cmd_parms * cmd,void * _dcfg,const char * p1)2527 static const char *cmd_upload_file_limit(cmd_parms *cmd, void *_dcfg,
2528                                          const char *p1)
2529 {
2530     directory_config *dcfg = (directory_config *)_dcfg;
2531 
2532     if (dcfg == NULL) return NULL;
2533 
2534     if (strcasecmp(p1, "default") == 0) {
2535         dcfg->upload_file_limit = NOT_SET;
2536     }
2537     else {
2538         dcfg->upload_file_limit = atoi(p1);
2539     }
2540 
2541     return NULL;
2542 }
2543 
cmd_upload_filemode(cmd_parms * cmd,void * _dcfg,const char * p1)2544 static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg,
2545                                        const char *p1)
2546 {
2547     directory_config *dcfg = (directory_config *)_dcfg;
2548 
2549     if (dcfg == NULL) return NULL;
2550 
2551     if (strcasecmp(p1, "default") == 0) {
2552         dcfg->upload_filemode = NOT_SET;
2553     }
2554     else {
2555         long int mode = strtol(p1, NULL, 8); /* expects octal mode */
2556         if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) {
2557             return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecUploadFileMode: %s", p1);
2558         }
2559 
2560         dcfg->upload_filemode = (int)mode;
2561     }
2562 
2563     return NULL;
2564 }
2565 
cmd_upload_keep_files(cmd_parms * cmd,void * _dcfg,const char * p1)2566 static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg,
2567                                          const char *p1)
2568 {
2569     directory_config *dcfg = (directory_config *)_dcfg;
2570 
2571     if (dcfg == NULL) return NULL;
2572 
2573     if (strcasecmp(p1, "on") == 0) {
2574         dcfg->upload_keep_files = KEEP_FILES_ON;
2575     } else
2576     if (strcasecmp(p1, "off") == 0) {
2577         dcfg->upload_keep_files = KEEP_FILES_OFF;
2578     } else
2579     if (strcasecmp(p1, "relevantonly") == 0) {
2580         dcfg->upload_keep_files = KEEP_FILES_RELEVANT_ONLY;
2581     } else {
2582         return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for SecUploadKeepFiles: %s",
2583             p1);
2584     }
2585     return NULL;
2586 }
2587 
cmd_upload_save_tmp_files(cmd_parms * cmd,void * _dcfg,const char * p1)2588 static const char *cmd_upload_save_tmp_files(cmd_parms *cmd, void *_dcfg,
2589     const char *p1)
2590 {
2591     directory_config *dcfg = (directory_config *)_dcfg;
2592 
2593     if (dcfg == NULL) return NULL;
2594 
2595     if (strcasecmp(p1, "on") == 0)
2596     {
2597         dcfg->upload_validates_files = 1;
2598     }
2599     else if (strcasecmp(p1, "off") == 0)
2600     {
2601         dcfg->upload_validates_files = 0;
2602     }
2603     else
2604     {
2605         return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for SecTmpSaveUploadedFiles: %s",
2606             p1);
2607     }
2608 
2609     return NULL;
2610 }
2611 
cmd_web_app_id(cmd_parms * cmd,void * _dcfg,const char * p1)2612 static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1)
2613 {
2614     directory_config *dcfg = (directory_config *)_dcfg;
2615 
2616     /* ENH enforce format (letters, digits, ., _, -) */
2617     dcfg->webappid = p1;
2618 
2619     return NULL;
2620 }
2621 
cmd_sensor_id(cmd_parms * cmd,void * _dcfg,const char * p1)2622 static const char *cmd_sensor_id(cmd_parms *cmd, void *_dcfg, const char *p1)
2623 {
2624     directory_config *dcfg = (directory_config *)_dcfg;
2625 
2626     /* ENH enforce format (letters, digits, ., _, -) */
2627     dcfg->sensor_id = p1;
2628 
2629     return NULL;
2630 }
2631 
2632 /**
2633 * \brief Add SecXmlExternalEntity configuration option
2634 *
2635 * \param cmd Pointer to configuration data
2636 * \param _dcfg Pointer to directory configuration
2637 * \param p1 Pointer to configuration option
2638 *
2639 * \retval NULL On failure
2640 * \retval apr_psprintf On Success
2641 */
cmd_xml_external_entity(cmd_parms * cmd,void * _dcfg,const char * p1)2642 static const char *cmd_xml_external_entity(cmd_parms *cmd, void *_dcfg, const char *p1)
2643 {
2644     directory_config *dcfg = (directory_config *)_dcfg;
2645     if (dcfg == NULL) return NULL;
2646 
2647     if (strcasecmp(p1, "on") == 0)  {
2648         dcfg->xml_external_entity = 1;
2649     }
2650     else if (strcasecmp(p1, "off") == 0)    {
2651         dcfg->xml_external_entity = 0;
2652     }
2653     else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecXmlExternalEntity: %s", p1);
2654 
2655     return NULL;
2656 }
2657 
2658 
2659 /**
2660 * \brief Add SecHashEngine configuration option
2661 *
2662 * \param cmd Pointer to configuration data
2663 * \param _dcfg Pointer to directory configuration
2664 * \param p1 Pointer to configuration option
2665 *
2666 * \retval NULL On failure
2667 * \retval apr_psprintf On Success
2668 */
cmd_hash_engine(cmd_parms * cmd,void * _dcfg,const char * p1)2669 static const char *cmd_hash_engine(cmd_parms *cmd, void *_dcfg, const char *p1)
2670 {
2671     directory_config *dcfg = (directory_config *)_dcfg;
2672     if (dcfg == NULL) return NULL;
2673 
2674     if (strcasecmp(p1, "on") == 0)  {
2675         dcfg->hash_is_enabled = HASH_ENABLED;
2676         dcfg->hash_enforcement = HASH_ENABLED;
2677     }
2678     else if (strcasecmp(p1, "off") == 0)    {
2679         dcfg->hash_is_enabled = HASH_DISABLED;
2680         dcfg->hash_enforcement = HASH_DISABLED;
2681     }
2682     else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecHashEngine: %s", p1);
2683 
2684     return NULL;
2685 }
2686 
2687 /**
2688 * \brief Add SecHashPram configuration option
2689 *
2690 * \param cmd Pointer to configuration data
2691 * \param _dcfg Pointer to directory configuration
2692 * \param p1 Pointer to configuration option
2693 *
2694 * \retval NULL On success
2695 */
cmd_hash_param(cmd_parms * cmd,void * _dcfg,const char * p1)2696 static const char *cmd_hash_param(cmd_parms *cmd, void *_dcfg, const char *p1)
2697 {
2698     directory_config *dcfg = (directory_config *)_dcfg;
2699 
2700     if (dcfg == NULL) return NULL;
2701 
2702     if (p1 == NULL) return NULL;
2703     dcfg->crypto_param_name = p1;
2704 
2705     return NULL;
2706 }
2707 
2708 /**
2709 * \brief Add SecHashKey configuration option
2710 *
2711 * \param cmd Pointer to configuration data
2712 * \param _dcfg Pointer to directory configuration
2713 * \param _p1 Pointer to configuration option
2714 * \param _p2 Pointer to configuration option
2715 *
2716 * \retval NULL On success
2717 */
cmd_hash_key(cmd_parms * cmd,void * _dcfg,const char * _p1,const char * _p2)2718 static const char *cmd_hash_key(cmd_parms *cmd, void *_dcfg, const char *_p1, const char *_p2)
2719 {
2720     directory_config *dcfg = (directory_config *)_dcfg;
2721     char *p1 = NULL;
2722 
2723     if (dcfg == NULL) return NULL;
2724     if (_p1 == NULL) return NULL;
2725 
2726     if (strcasecmp(_p1, "Rand") == 0)    {
2727         p1 = apr_pstrdup(cmd->pool, getkey(cmd->pool));
2728         dcfg->crypto_key = p1;
2729         dcfg->crypto_key_len = strlen(dcfg->crypto_key);
2730     } else    {
2731         p1 = apr_pstrdup(cmd->pool, _p1);
2732         dcfg->crypto_key = p1;
2733         dcfg->crypto_key_len = strlen(p1);
2734     }
2735 
2736     if(_p2 == NULL)  {
2737         return NULL;
2738     } else    {
2739         if (strcasecmp(_p2, "KeyOnly") == 0)
2740             dcfg->crypto_key_add = HASH_KEYONLY;
2741         else if (strcasecmp(_p2, "SessionID") == 0)
2742             dcfg->crypto_key_add = HASH_SESSIONID;
2743         else if (strcasecmp(_p2, "RemoteIP") == 0)
2744             dcfg->crypto_key_add = HASH_REMOTEIP;
2745     }
2746     return NULL;
2747 }
2748 
2749 /**
2750 * \brief Add SecHashMethodPm configuration option
2751 *
2752 * \param cmd Pointer to configuration data
2753 * \param _dcfg Pointer to directory configuration
2754 * \param p1 Pointer to configuration option
2755 * \param p2 Pointer to configuration option
2756 *
2757 * \retval NULL On failure
2758 * \retval apr_psprintf On Success
2759 */
cmd_hash_method_pm(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)2760 static const char *cmd_hash_method_pm(cmd_parms *cmd, void *_dcfg,
2761         const char *p1, const char *p2)
2762 {
2763     directory_config *dcfg = (directory_config *)_dcfg;
2764     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(hash_method));
2765     const char *_p2 = apr_pstrdup(cmd->pool, p2);
2766     ACMP *p = NULL;
2767     const char *phrase = NULL;
2768     const char *next = NULL;
2769 
2770     if (dcfg == NULL) return NULL;
2771 
2772     p = acmp_create(0, cmd->pool);
2773     if (p == NULL) return NULL;
2774 
2775     if(phrase == NULL)
2776         phrase = apr_pstrdup(cmd->pool, _p2);
2777 
2778     for (;;) {
2779         while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++;
2780         if (*phrase == '\0') break;
2781         next = phrase;
2782         while((apr_isspace(*next) == 0) && (*next != 0)) next++;
2783         acmp_add_pattern(p, phrase, NULL, NULL, next - phrase);
2784         phrase = next;
2785     }
2786 
2787     acmp_prepare(p);
2788 
2789     if (strcasecmp(p1, "HashHref") == 0) {
2790         re->type = HASH_URL_HREF_HASH_PM;
2791         re->param = _p2;
2792         re->param_data = (void *)p;
2793         if (re->param_data == NULL) {
2794             return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
2795         }
2796         dcfg->crypto_hash_href_pm = 1;
2797     }
2798     else if (strcasecmp(p1, "HashFormAction") == 0) {
2799         re->type = HASH_URL_FACTION_HASH_PM;
2800         re->param = _p2;
2801         re->param_data = (void *)p;
2802         if (re->param_data == NULL) {
2803             return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
2804         }
2805         dcfg->crypto_hash_faction_pm = 1;
2806     }
2807     else if (strcasecmp(p1, "HashLocation") == 0) {
2808         re->type = HASH_URL_LOCATION_HASH_PM;
2809         re->param = _p2;
2810         re->param_data = (void *)p;
2811         if (re->param_data == NULL) {
2812             return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
2813         }
2814         dcfg->crypto_hash_location_pm = 1;
2815     }
2816     else if (strcasecmp(p1, "HashIframeSrc") == 0) {
2817         re->type = HASH_URL_IFRAMESRC_HASH_PM;
2818         re->param = _p2;
2819         re->param_data = (void *)p;
2820         if (re->param_data == NULL) {
2821             return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
2822         }
2823         dcfg->crypto_hash_iframesrc_pm = 1;
2824     }
2825     else if (strcasecmp(p1, "HashFrameSrc") == 0) {
2826         re->type = HASH_URL_FRAMESRC_HASH_PM;
2827         re->param = _p2;
2828         re->param_data = (void *)p;
2829         if (re->param_data == NULL) {
2830             return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
2831         }
2832         dcfg->crypto_hash_framesrc_pm = 1;
2833     }
2834 
2835     *(hash_method **)apr_array_push(dcfg->hash_method) = re;
2836 
2837     return NULL;
2838 }
2839 
2840 /**
2841  * \brief Add SecHashMethodRx configuration option
2842  *
2843  * \param cmd Pointer to configuration data
2844  * \param _dcfg Pointer to directory configuration
2845  * \param p1 Pointer to configuration option
2846  * \param p2 Pointer to configuration option
2847  *
2848  * \retval NULL On failure
2849  * \retval apr_psprintf On Success
2850  */
cmd_hash_method_rx(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)2851 static const char *cmd_hash_method_rx(cmd_parms *cmd, void *_dcfg,
2852         const char *p1, const char *p2)
2853 {
2854     directory_config *dcfg = (directory_config *)_dcfg;
2855     rule_exception *re = apr_pcalloc(cmd->pool, sizeof(hash_method));
2856     const char *_p2 = apr_pstrdup(cmd->pool, p2);
2857     if (dcfg == NULL) return NULL;
2858 
2859     if (strcasecmp(p1, "HashHref") == 0) {
2860         re->type = HASH_URL_HREF_HASH_RX;
2861         re->param = _p2;
2862         re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
2863         if (re->param_data == NULL) {
2864             return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
2865         }
2866         dcfg->crypto_hash_href_rx = 1;
2867     }
2868     else if (strcasecmp(p1, "HashFormAction") == 0) {
2869         re->type = HASH_URL_FACTION_HASH_RX;
2870         re->param = _p2;
2871         re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
2872         if (re->param_data == NULL) {
2873             return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
2874         }
2875         dcfg->crypto_hash_faction_rx = 1;
2876     }
2877     else if (strcasecmp(p1, "HashLocation") == 0) {
2878         re->type = HASH_URL_LOCATION_HASH_RX;
2879         re->param = _p2;
2880         re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
2881         if (re->param_data == NULL) {
2882             return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
2883         }
2884         dcfg->crypto_hash_location_rx = 1;
2885     }
2886     else if (strcasecmp(p1, "HashIframeSrc") == 0) {
2887         re->type = HASH_URL_IFRAMESRC_HASH_RX;
2888         re->param = _p2;
2889         re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
2890         if (re->param_data == NULL) {
2891             return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
2892         }
2893         dcfg->crypto_hash_iframesrc_rx = 1;
2894     }
2895     else if (strcasecmp(p1, "HashFrameSrc") == 0) {
2896         re->type = HASH_URL_FRAMESRC_HASH_RX;
2897         re->param = _p2;
2898         re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
2899         if (re->param_data == NULL) {
2900             return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
2901         }
2902         dcfg->crypto_hash_framesrc_rx = 1;
2903     }
2904 
2905     *(hash_method **)apr_array_push(dcfg->hash_method) = re;
2906 
2907     return NULL;
2908 }
2909 
2910 /**
2911 * \brief Add SecHttpBlKey configuration option
2912 *
2913 * \param cmd Pointer to configuration data
2914 * \param _dcfg Pointer to directory configuration
2915 * \param p1 Pointer to configuration option
2916 *
2917 * \retval NULL On success
2918 */
cmd_httpBl_key(cmd_parms * cmd,void * _dcfg,const char * p1)2919 static const char *cmd_httpBl_key(cmd_parms *cmd, void *_dcfg, const char *p1)
2920 {
2921     directory_config *dcfg = (directory_config *)_dcfg;
2922 
2923     if (dcfg == NULL) return NULL;
2924 
2925     if (p1 == NULL) return NULL;
2926     dcfg->httpBlkey = p1;
2927 
2928     return NULL;
2929 }
2930 
2931 /* PCRE Limits */
2932 
cmd_pcre_match_limit(cmd_parms * cmd,void * _dcfg,const char * p1)2933 static const char *cmd_pcre_match_limit(cmd_parms *cmd,
2934         void *_dcfg, const char *p1)
2935 {
2936     long val;
2937 
2938     if (cmd->server->is_virtual) {
2939         return "ModSecurity: SecPcreMatchLimit not allowed in VirtualHost";
2940     }
2941 
2942     val = atol(p1);
2943     if (val <= 0) {
2944         return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
2945                 "SecPcreMatchLimit: %s", p1);
2946     }
2947     msc_pcre_match_limit = (unsigned long int)val;
2948 
2949     return NULL;
2950 }
2951 
cmd_pcre_match_limit_recursion(cmd_parms * cmd,void * _dcfg,const char * p1)2952 static const char *cmd_pcre_match_limit_recursion(cmd_parms *cmd,
2953         void *_dcfg, const char *p1)
2954 {
2955     long val;
2956 
2957     if (cmd->server->is_virtual) {
2958         return "ModSecurity: SecPcreMatchLimitRecursion not allowed in VirtualHost";
2959     }
2960 
2961     val = atol(p1);
2962     if (val <= 0) {
2963         return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
2964                 "SecPcreMatchLimitRecursion: %s", p1);
2965     }
2966     msc_pcre_match_limit_recursion = (unsigned long int)val;
2967 
2968     return NULL;
2969 }
2970 
2971 
2972 /* -- Geo Lookup configuration -- */
2973 
cmd_geo_lookup_db(cmd_parms * cmd,void * _dcfg,const char * p1)2974 static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg,
2975         const char *p1)
2976 {
2977     const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1);
2978     char *error_msg;
2979     directory_config *dcfg = (directory_config *)_dcfg;
2980     if (dcfg == NULL) return NULL;
2981 
2982     if (geo_init(dcfg, filename, &error_msg) <= 0) {
2983         return error_msg;
2984     }
2985 
2986     return NULL;
2987 }
2988 
2989 /**
2990 * \brief Add SecUnicodeCodePage configuration option
2991 *
2992 * Depcrecated
2993 *
2994 * \param cmd Pointer to configuration data
2995 * \param _dcfg Pointer to directory configuration
2996 * \param p1 Pointer to configuration option
2997 *
2998 * \retval NULL On success
2999 */
cmd_unicode_codepage(cmd_parms * cmd,void * _dcfg,const char * p1)3000 static const char *cmd_unicode_codepage(cmd_parms *cmd,
3001                                         void *_dcfg, const char *p1)
3002 {
3003     long val;
3004 
3005     val = atol(p1);
3006     if (val <= 0) {
3007         return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
3008                                        "SecUnicodeCodePage: %s", p1);
3009     }
3010 
3011     unicode_codepage = (unsigned long int)val;
3012 
3013     return NULL;
3014 }
3015 
3016 /**
3017 * \brief Add SecUnicodeMapFile configuration option
3018 *
3019 * \param cmd Pointer to configuration data
3020 * \param _dcfg Pointer to directory configuration
3021 * \param p1 Pointer to configuration option
3022 *
3023 * \retval NULL On success
3024 */
cmd_unicode_map(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)3025 static const char *cmd_unicode_map(cmd_parms *cmd, void *_dcfg,
3026                                      const char *p1, const char *p2)
3027 {
3028     const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1);
3029     char *error_msg;
3030     long val = 0;
3031     directory_config *dcfg = (directory_config *)_dcfg;
3032     if (dcfg == NULL) return NULL;
3033 
3034     if(p2 != NULL)  {
3035         val = atol(p2);
3036         if (val <= 0) {
3037             return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
3038                     "SecUnicodeMapFile: %s", p2);
3039         }
3040 
3041         unicode_codepage = (unsigned long int)val;
3042     }
3043 
3044     if (unicode_map_init(dcfg, filename, &error_msg) <= 0) {
3045         return error_msg;
3046     }
3047 
3048     return NULL;
3049 }
3050 
3051 /**
3052 * \brief Add SecGsbLookupDb configuration option
3053 *
3054 * \param cmd Pointer to configuration data
3055 * \param _dcfg Pointer to directory configuration
3056 * \param p1 Pointer to configuration option
3057 *
3058 * \retval NULL On success
3059 */
cmd_gsb_lookup_db(cmd_parms * cmd,void * _dcfg,const char * p1)3060 static const char *cmd_gsb_lookup_db(cmd_parms *cmd, void *_dcfg,
3061                                      const char *p1)
3062 {
3063     const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1);
3064     char *error_msg;
3065     directory_config *dcfg = (directory_config *)_dcfg;
3066     if (dcfg == NULL) return NULL;
3067 
3068     if (gsb_db_init(dcfg, filename, &error_msg) <= 0) {
3069         return error_msg;
3070     }
3071 
3072     return NULL;
3073 }
3074 
3075 /* -- Cache -- */
3076 
cmd_cache_transformations(cmd_parms * cmd,void * _dcfg,const char * p1,const char * p2)3077 static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg,
3078                                              const char *p1, const char *p2)
3079 {
3080     directory_config *dcfg = (directory_config *)_dcfg;
3081 
3082     if (dcfg == NULL) return NULL;
3083 
3084     if (strcasecmp(p1, "on") == 0)
3085         dcfg->cache_trans = MODSEC_CACHE_ENABLED;
3086     else if (strcasecmp(p1, "off") == 0)
3087         dcfg->cache_trans = MODSEC_CACHE_DISABLED;
3088     else
3089         return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCacheTransformations: %s", p1);
3090 
3091     /* Process options */
3092     if (p2 != NULL) {
3093         apr_table_t *vartable = apr_table_make(cmd->pool, 4);
3094         apr_status_t rc;
3095         char *error_msg = NULL;
3096         const char *charval = NULL;
3097         apr_int64_t intval = 0;
3098 
3099         if (vartable == NULL) {
3100             return apr_psprintf(cmd->pool, "ModSecurity: Unable to process options for SecCacheTransformations");
3101         }
3102         rc = msre_parse_generic(cmd->pool, p2, vartable, &error_msg);
3103         if (rc < 0) {
3104             return apr_psprintf(cmd->pool, "ModSecurity: Unable to parse options for SecCacheTransformations: %s", error_msg);
3105         }
3106 
3107         /* incremental */
3108         charval = apr_table_get(vartable, "incremental");
3109         if (charval != NULL) {
3110             if (strcasecmp(charval, "on") == 0)
3111                 dcfg->cache_trans_incremental = 1;
3112             else if (strcasecmp(charval, "off") == 0)
3113                 dcfg->cache_trans_incremental = 0;
3114             else
3115                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations invalid incremental value: %s", charval);
3116         }
3117 
3118         /* minlen */
3119         charval = apr_table_get(vartable, "minlen");
3120         if (charval != NULL) {
3121             intval = apr_atoi64(charval);
3122             if (errno == ERANGE) {
3123                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen out of range: %s", charval);
3124             }
3125             if (intval < 0) {
3126                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be positive: %s", charval);
3127             }
3128 
3129             /* The NOT_SET indicator is -1, a signed long, and therfore
3130              * we cannot be >= the unsigned value of NOT_SET.
3131              */
3132             if ((unsigned long)intval >= (unsigned long)NOT_SET) {
3133                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET);
3134             }
3135             dcfg->cache_trans_min = (apr_size_t)intval;
3136         }
3137 
3138         /* maxlen */
3139         charval = apr_table_get(vartable, "maxlen");
3140         if (charval != NULL) {
3141             intval = apr_atoi64(charval);
3142             if (errno == ERANGE) {
3143                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen out of range: %s", charval);
3144             }
3145             if (intval < 0) {
3146                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be positive: %s", charval);
3147             }
3148 
3149             /* The NOT_SET indicator is -1, a signed long, and therfore
3150              * we cannot be >= the unsigned value of NOT_SET.
3151              */
3152             if ((unsigned long)intval >= (unsigned long)NOT_SET) {
3153                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET);
3154             }
3155             if ((intval != 0) && ((apr_size_t)intval < dcfg->cache_trans_min)) {
3156                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %lu < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min);
3157             }
3158             dcfg->cache_trans_max = (apr_size_t)intval;
3159 
3160         }
3161 
3162         /* maxitems */
3163         charval = apr_table_get(vartable, "maxitems");
3164         if (charval != NULL) {
3165             intval = apr_atoi64(charval);
3166             if (errno == ERANGE) {
3167                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems out of range: %s", charval);
3168             }
3169             if (intval < 0) {
3170                 return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems must be positive: %s", charval);
3171             }
3172             dcfg->cache_trans_maxitems = (apr_size_t)intval;
3173         }
3174     }
3175 
3176     return NULL;
3177 }
3178 
3179 
3180 /* -- Configuration directives definitions -- */
3181 
3182 #define CMD_SCOPE_MAIN  (RSRC_CONF)
3183 #define CMD_SCOPE_ANY   (RSRC_CONF | ACCESS_CONF)
3184 
3185 #if defined(HTACCESS_CONFIG)
3186 #define CMD_SCOPE_HTACCESS  (OR_OPTIONS)
3187 #endif
3188 
3189 const command_rec module_directives[] = {
3190 
3191 #ifdef HTACCESS_CONFIG
3192     AP_INIT_TAKE1 (
3193         "SecAction",
3194         cmd_action,
3195         NULL,
3196         CMD_SCOPE_HTACCESS,
3197         "an action list"
3198     ),
3199 #else
3200     AP_INIT_TAKE1 (
3201         "SecAction",
3202         cmd_action,
3203         NULL,
3204         CMD_SCOPE_ANY,
3205         "an action list"
3206     ),
3207 #endif
3208 
3209     AP_INIT_TAKE1 (
3210         "SecArgumentSeparator",
3211         cmd_argument_separator,
3212         NULL,
3213         CMD_SCOPE_ANY,
3214         "character that will be used as separator when parsing application/x-www-form-urlencoded content."
3215     ),
3216 
3217     AP_INIT_TAKE1 (
3218         "SecCookiev0Separator",
3219         cmd_cookiev0_separator,
3220         NULL,
3221         CMD_SCOPE_ANY,
3222         "character that will be used as separator when parsing cookie v0 content."
3223     ),
3224 
3225     AP_INIT_TAKE1 (
3226         "SecAuditEngine",
3227         cmd_audit_engine,
3228         NULL,
3229         CMD_SCOPE_ANY,
3230         "On, Off or RelevantOnly to determine the level of audit logging"
3231     ),
3232 
3233     AP_INIT_TAKE1 (
3234         "SecAuditLog",
3235         cmd_audit_log,
3236         NULL,
3237         CMD_SCOPE_ANY,
3238         "filename of the primary audit log file"
3239     ),
3240 
3241     AP_INIT_TAKE1 (
3242         "SecAuditLog2",
3243         cmd_audit_log2,
3244         NULL,
3245         CMD_SCOPE_ANY,
3246         "filename of the secondary audit log file"
3247     ),
3248 
3249     AP_INIT_TAKE1 (
3250         "SecAuditLogParts",
3251         cmd_audit_log_parts,
3252         NULL,
3253         CMD_SCOPE_ANY,
3254         "list of audit log parts that go into the log."
3255     ),
3256 
3257     AP_INIT_TAKE1 (
3258         "SecAuditLogRelevantStatus",
3259         cmd_audit_log_relevant_status,
3260         NULL,
3261         CMD_SCOPE_ANY,
3262         "regular expression that will be used to determine if the response status is relevant for audit logging"
3263     ),
3264 
3265     AP_INIT_TAKE1 (
3266         "SecAuditLogType",
3267         cmd_audit_log_type,
3268         NULL,
3269         CMD_SCOPE_ANY,
3270         "whether to use the old audit log format (Serial) or new (Concurrent)"
3271     ),
3272 
3273 #ifdef WITH_YAJL
3274     AP_INIT_TAKE1 (
3275         "SecAuditLogFormat",
3276         cmd_audit_log_mode,
3277         NULL,
3278         CMD_SCOPE_ANY,
3279         "whether to emit audit log data in native format or JSON"
3280     ),
3281 #endif
3282 
3283     AP_INIT_TAKE1 (
3284         "SecAuditLogStorageDir",
3285         cmd_audit_log_storage_dir,
3286         NULL,
3287         CMD_SCOPE_ANY,
3288         "path to the audit log storage area; absolute, or relative to the root of the server"
3289     ),
3290 
3291     AP_INIT_TAKE1 (
3292         "SecAuditLogDirMode",
3293         cmd_audit_log_dirmode,
3294         NULL,
3295         CMD_SCOPE_ANY,
3296         "octal permissions mode for concurrent audit log directories"
3297     ),
3298 
3299     AP_INIT_TAKE1 (
3300         "SecAuditLogFileMode",
3301         cmd_audit_log_filemode,
3302         NULL,
3303         CMD_SCOPE_ANY,
3304         "octal permissions mode for concurrent audit log files"
3305     ),
3306 
3307     AP_INIT_TAKE12 (
3308         "SecCacheTransformations",
3309         cmd_cache_transformations,
3310         NULL,
3311         CMD_SCOPE_ANY,
3312         "whether or not to cache transformations. Defaults to true."
3313     ),
3314 
3315     AP_INIT_TAKE1 (
3316         "SecChrootDir",
3317         cmd_chroot_dir,
3318         NULL,
3319         CMD_SCOPE_MAIN,
3320         "path of the directory to which server will be chrooted"
3321     ),
3322 
3323     AP_INIT_TAKE1 (
3324         "SecComponentSignature",
3325         cmd_component_signature,
3326         NULL,
3327         CMD_SCOPE_MAIN,
3328         "component signature to add to ModSecurity signature."
3329     ),
3330 
3331     AP_INIT_FLAG (
3332         "SecContentInjection",
3333         cmd_content_injection,
3334         NULL,
3335         CMD_SCOPE_ANY,
3336         "On or Off"
3337     ),
3338 
3339     AP_INIT_FLAG (
3340         "SecStreamOutBodyInspection",
3341         cmd_stream_outbody_inspection,
3342         NULL,
3343         CMD_SCOPE_ANY,
3344         "On or Off"
3345     ),
3346 
3347     AP_INIT_FLAG (
3348         "SecStreamInBodyInspection",
3349         cmd_stream_inbody_inspection,
3350         NULL,
3351         CMD_SCOPE_ANY,
3352         "On or Off"
3353     ),
3354 
3355     AP_INIT_TAKE1 (
3356         "SecCookieFormat",
3357         cmd_cookie_format,
3358         NULL,
3359         CMD_SCOPE_ANY,
3360         "version of the Cookie specification to use for parsing. Possible values are 0 and 1."
3361     ),
3362 
3363     AP_INIT_TAKE1 (
3364         "SecDataDir",
3365         cmd_data_dir,
3366         NULL,
3367         CMD_SCOPE_MAIN,
3368         "path to the persistent data storage area" // TODO
3369     ),
3370 
3371     AP_INIT_TAKE1 (
3372         "SecDebugLog",
3373         cmd_debug_log,
3374         NULL,
3375         CMD_SCOPE_ANY,
3376         "path to the debug log file"
3377     ),
3378 
3379     AP_INIT_TAKE1 (
3380         "SecDebugLogLevel",
3381         cmd_debug_log_level,
3382         NULL,
3383         CMD_SCOPE_ANY,
3384         "debug log level, which controls the verbosity of logging."
3385         " Use values from 0 (no logging) to 9 (a *lot* of logging)."
3386     ),
3387 
3388     AP_INIT_TAKE1 (
3389         "SecCollectionTimeout",
3390         cmd_collection_timeout,
3391         NULL,
3392         CMD_SCOPE_ANY,
3393         "set default collections timeout. default it 3600"
3394     ),
3395 
3396     AP_INIT_TAKE1 (
3397         "SecDefaultAction",
3398         cmd_default_action,
3399         NULL,
3400         CMD_SCOPE_ANY,
3401         "default action list"
3402     ),
3403 
3404     AP_INIT_FLAG (
3405         "SecDisableBackendCompression",
3406         cmd_disable_backend_compression,
3407         NULL,
3408         CMD_SCOPE_ANY,
3409         "When set to On, removes the compression headers from the backend requests."
3410     ),
3411 
3412     AP_INIT_TAKE1 (
3413         "SecGsbLookupDB",
3414         cmd_gsb_lookup_db,
3415         NULL,
3416         RSRC_CONF,
3417         "database google safe browsing"
3418     ),
3419 
3420     AP_INIT_TAKE1 (
3421         "SecUnicodeCodePage",
3422         cmd_unicode_codepage,
3423         NULL,
3424         CMD_SCOPE_MAIN,
3425         "Unicode CodePage"
3426     ),
3427 
3428     AP_INIT_TAKE12 (
3429         "SecUnicodeMapFile",
3430         cmd_unicode_map,
3431         NULL,
3432         CMD_SCOPE_MAIN,
3433         "Unicode Map file"
3434     ),
3435 
3436     AP_INIT_TAKE1 (
3437         "SecGeoLookupDB",
3438         cmd_geo_lookup_db,
3439         NULL,
3440         RSRC_CONF,
3441         "database for geographical lookups module."
3442     ),
3443 
3444     AP_INIT_TAKE12 (
3445         "SecGuardianLog",
3446         cmd_guardian_log,
3447         NULL,
3448         CMD_SCOPE_MAIN,
3449         "The filename of the filter debugging log file"
3450     ),
3451 
3452     AP_INIT_TAKE1 (
3453         "SecMarker",
3454         cmd_marker,
3455         NULL,
3456         CMD_SCOPE_ANY,
3457         "marker for a skipAfter target"
3458     ),
3459 
3460     AP_INIT_TAKE1 (
3461         "SecPcreMatchLimit",
3462         cmd_pcre_match_limit,
3463         NULL,
3464         CMD_SCOPE_MAIN,
3465         "PCRE match limit"
3466     ),
3467 
3468     AP_INIT_TAKE1 (
3469         "SecPcreMatchLimitRecursion",
3470         cmd_pcre_match_limit_recursion,
3471         NULL,
3472         CMD_SCOPE_MAIN,
3473         "PCRE match limit recursion"
3474     ),
3475 
3476     AP_INIT_TAKE1 (
3477         "SecRequestBodyAccess",
3478         cmd_request_body_access,
3479         NULL,
3480         CMD_SCOPE_ANY,
3481         "On or Off"
3482     ),
3483 
3484     AP_INIT_TAKE1 (
3485         "SecInterceptOnError",
3486         cmd_request_intercept_on_error,
3487         NULL,
3488         CMD_SCOPE_ANY,
3489         "On or Off"
3490     ),
3491 
3492     AP_INIT_TAKE1 (
3493         "SecRulePerfTime",
3494         cmd_rule_perf_time,
3495         NULL,
3496         CMD_SCOPE_ANY,
3497         "Threshold to log slow rules in usecs."
3498     ),
3499 
3500     AP_INIT_TAKE12 (
3501         "SecConnReadStateLimit",
3502         cmd_conn_read_state_limit,
3503         NULL,
3504         CMD_SCOPE_ANY,
3505         "maximum number of threads in READ_BUSY state per ip address"
3506     ),
3507 
3508     AP_INIT_TAKE12 (
3509         "SecReadStateLimit",
3510         cmd_read_state_limit,
3511         NULL,
3512         CMD_SCOPE_ANY,
3513         "maximum number of threads in READ_BUSY state per ip address"
3514     ),
3515 
3516     AP_INIT_TAKE12 (
3517         "SecConnWriteStateLimit",
3518         cmd_conn_write_state_limit,
3519         NULL,
3520         CMD_SCOPE_ANY,
3521         "maximum number of threads in WRITE_BUSY state per ip address"
3522     ),
3523 
3524     AP_INIT_TAKE12 (
3525         "SecWriteStateLimit",
3526         cmd_write_state_limit,
3527         NULL,
3528         CMD_SCOPE_ANY,
3529         "maximum number of threads in WRITE_BUSY state per ip address"
3530     ),
3531 
3532     AP_INIT_TAKE1 (
3533         "SecRequestBodyInMemoryLimit",
3534         cmd_request_body_inmemory_limit,
3535         NULL,
3536         CMD_SCOPE_ANY,
3537         "maximum request body size that will be placed in memory (except for POST urlencoded requests)."
3538     ),
3539 
3540     AP_INIT_TAKE1 (
3541         "SecRequestBodyLimit",
3542         cmd_request_body_limit,
3543         NULL,
3544         CMD_SCOPE_ANY,
3545         "maximum request body size ModSecurity will accept."
3546     ),
3547 
3548     AP_INIT_TAKE1 (
3549         "SecRequestBodyNoFilesLimit",
3550         cmd_request_body_no_files_limit,
3551         NULL,
3552         CMD_SCOPE_ANY,
3553         "maximum request body size ModSecurity will accept, but excluding the size of uploaded files."
3554     ),
3555 
3556     AP_INIT_TAKE1 (
3557         "SecRequestEncoding",
3558         cmd_request_encoding,
3559         NULL,
3560         CMD_SCOPE_ANY,
3561         "character encoding used in request."
3562     ),
3563 
3564     AP_INIT_TAKE1 (
3565         "SecResponseBodyAccess",
3566         cmd_response_body_access,
3567         NULL,
3568         CMD_SCOPE_ANY,
3569         "On or Off"
3570     ),
3571 
3572     AP_INIT_TAKE1 (
3573         "SecResponseBodyLimit",
3574         cmd_response_body_limit,
3575         NULL,
3576         CMD_SCOPE_ANY,
3577         "byte limit for response body"
3578     ),
3579 
3580     AP_INIT_TAKE1 (
3581         "SecResponseBodyLimitAction",
3582         cmd_response_body_limit_action,
3583         NULL,
3584         CMD_SCOPE_ANY,
3585         "what happens when the response body limit is reached"
3586     ),
3587 
3588     AP_INIT_TAKE1 (
3589         "SecRequestBodyLimitAction",
3590         cmd_resquest_body_limit_action,
3591         NULL,
3592         CMD_SCOPE_ANY,
3593         "what happens when the request body limit is reached"
3594     ),
3595 
3596     AP_INIT_ITERATE (
3597         "SecResponseBodyMimeType",
3598         cmd_response_body_mime_type,
3599         NULL,
3600         CMD_SCOPE_ANY,
3601         "adds given MIME types to the list of types that will be buffered on output"
3602     ),
3603 
3604     AP_INIT_NO_ARGS (
3605         "SecResponseBodyMimeTypesClear",
3606         cmd_response_body_mime_types_clear,
3607         NULL,
3608         CMD_SCOPE_ANY,
3609         "clears the list of MIME types that will be buffered on output"
3610     ),
3611 
3612 #ifdef HTACCESS_CONFIG
3613     AP_INIT_TAKE23 (
3614         "SecRule",
3615         cmd_rule,
3616         NULL,
3617         CMD_SCOPE_HTACCESS,
3618         "rule target, operator and optional action list"
3619     ),
3620 #else
3621     AP_INIT_TAKE23 (
3622         "SecRule",
3623         cmd_rule,
3624         NULL,
3625         CMD_SCOPE_ANY,
3626         "rule target, operator and optional action list"
3627     ),
3628 #endif
3629 
3630     AP_INIT_TAKE1 (
3631         "SecRuleEngine",
3632         cmd_rule_engine,
3633         NULL,
3634         CMD_SCOPE_ANY,
3635         "On or Off"
3636     ),
3637 
3638     AP_INIT_TAKE1 (
3639         "SecStatusEngine",
3640         cmd_status_engine,
3641         NULL,
3642         CMD_SCOPE_ANY,
3643         "On or Off"
3644     ),
3645 
3646     AP_INIT_TAKE1 (
3647         "SecConnEngine",
3648         cmd_sever_conn_filters_engine,
3649         NULL,
3650         CMD_SCOPE_ANY,
3651         "On or Off"
3652     ),
3653 
3654     AP_INIT_TAKE23 (
3655         "SecRemoteRules",
3656         cmd_remote_rules,
3657         NULL,
3658         CMD_SCOPE_ANY,
3659         "key and URI to the remote rules"
3660     ),
3661 
3662     AP_INIT_TAKE1 (
3663         "SecRemoteRulesFailAction",
3664         cmd_remote_rules_fail,
3665         NULL,
3666         CMD_SCOPE_ANY,
3667         "Abort or Warn"
3668     ),
3669 
3670 
3671     AP_INIT_TAKE1 (
3672         "SecXmlExternalEntity",
3673         cmd_xml_external_entity,
3674         NULL,
3675         CMD_SCOPE_ANY,
3676         "On or Off"
3677     ),
3678 
3679     AP_INIT_FLAG (
3680         "SecRuleInheritance",
3681         cmd_rule_inheritance,
3682         NULL,
3683         CMD_SCOPE_ANY,
3684         "On or Off"
3685     ),
3686 
3687     AP_INIT_TAKE12 (
3688         "SecRuleScript",
3689         cmd_rule_script,
3690         NULL,
3691         CMD_SCOPE_ANY,
3692         "rule script and optional actionlist"
3693     ),
3694 
3695 #ifdef HTACCESS_CONFIG
3696     AP_INIT_ITERATE (
3697         "SecRuleRemoveById",
3698         cmd_rule_remove_by_id,
3699         NULL,
3700         CMD_SCOPE_HTACCESS,
3701         "rule ID for removal"
3702     ),
3703 
3704     AP_INIT_ITERATE (
3705         "SecRuleRemoveByTag",
3706         cmd_rule_remove_by_tag,
3707         NULL,
3708         CMD_SCOPE_HTACCESS,
3709         "rule tag for removal"
3710     ),
3711 
3712     AP_INIT_ITERATE (
3713         "SecRuleRemoveByMsg",
3714         cmd_rule_remove_by_msg,
3715         NULL,
3716         CMD_SCOPE_HTACCESS,
3717         "rule message for removal"
3718     ),
3719 #else
3720     AP_INIT_ITERATE (
3721         "SecRuleRemoveById",
3722         cmd_rule_remove_by_id,
3723         NULL,
3724         CMD_SCOPE_ANY,
3725         "rule ID for removal"
3726     ),
3727 
3728     AP_INIT_ITERATE (
3729         "SecRuleRemoveByTag",
3730         cmd_rule_remove_by_tag,
3731         NULL,
3732         CMD_SCOPE_ANY,
3733         "rule tag for removal"
3734     ),
3735 
3736     AP_INIT_ITERATE (
3737         "SecRuleRemoveByMsg",
3738         cmd_rule_remove_by_msg,
3739         NULL,
3740         CMD_SCOPE_ANY,
3741         "rule message for removal"
3742     ),
3743 #endif
3744 
3745     AP_INIT_TAKE2 (
3746         "SecHashMethodPm",
3747         cmd_hash_method_pm,
3748         NULL,
3749         CMD_SCOPE_ANY,
3750         "Hash method and pattern"
3751     ),
3752 
3753     AP_INIT_TAKE2 (
3754         "SecHashMethodRx",
3755         cmd_hash_method_rx,
3756         NULL,
3757         CMD_SCOPE_ANY,
3758         "Hash method and regex"
3759     ),
3760 
3761 #ifdef HTACCESS_CONFIG
3762     AP_INIT_TAKE2 (
3763         "SecRuleUpdateActionById",
3764         cmd_rule_update_action_by_id,
3765         NULL,
3766         CMD_SCOPE_HTACCESS,
3767         "updated action list"
3768     ),
3769 
3770     AP_INIT_TAKE23 (
3771         "SecRuleUpdateTargetById",
3772         cmd_rule_update_target_by_id,
3773         NULL,
3774         CMD_SCOPE_HTACCESS,
3775         "updated target list"
3776     ),
3777 
3778     AP_INIT_TAKE23 (
3779         "SecRuleUpdateTargetByTag",
3780         cmd_rule_update_target_by_tag,
3781         NULL,
3782         CMD_SCOPE_HTACCESS,
3783         "rule tag pattern and updated target list"
3784     ),
3785 
3786     AP_INIT_TAKE23 (
3787         "SecRuleUpdateTargetByMsg",
3788         cmd_rule_update_target_by_msg,
3789         NULL,
3790         CMD_SCOPE_HTACCESS,
3791         "rule message pattern and updated target list"
3792     ),
3793 #else
3794     AP_INIT_TAKE2 (
3795         "SecRuleUpdateActionById",
3796         cmd_rule_update_action_by_id,
3797         NULL,
3798         CMD_SCOPE_ANY,
3799         "updated action list"
3800     ),
3801 
3802     AP_INIT_TAKE23 (
3803         "SecRuleUpdateTargetById",
3804         cmd_rule_update_target_by_id,
3805         NULL,
3806         CMD_SCOPE_ANY,
3807         "updated target list"
3808     ),
3809 
3810     AP_INIT_TAKE23 (
3811         "SecRuleUpdateTargetByTag",
3812         cmd_rule_update_target_by_tag,
3813         NULL,
3814         CMD_SCOPE_ANY,
3815         "rule tag pattern and updated target list"
3816     ),
3817 
3818     AP_INIT_TAKE23 (
3819         "SecRuleUpdateTargetByMsg",
3820         cmd_rule_update_target_by_msg,
3821         NULL,
3822         CMD_SCOPE_ANY,
3823         "rule message pattern and updated target list"
3824     ),
3825 #endif
3826 
3827     AP_INIT_TAKE1 (
3828         "SecServerSignature",
3829         cmd_server_signature,
3830         NULL,
3831         CMD_SCOPE_MAIN,
3832         "the new signature of the server"
3833     ),
3834 
3835     AP_INIT_TAKE1 (
3836         "SecTmpDir",
3837         cmd_tmp_dir,
3838         NULL,
3839         CMD_SCOPE_ANY,
3840         "path to the temporary storage area"
3841     ),
3842 
3843     AP_INIT_TAKE1 (
3844         "SecUploadDir",
3845         cmd_upload_dir,
3846         NULL,
3847         CMD_SCOPE_ANY,
3848         "path to the file upload area"
3849     ),
3850 
3851     AP_INIT_TAKE1 (
3852         "SecUploadFileLimit",
3853         cmd_upload_file_limit,
3854         NULL,
3855         CMD_SCOPE_ANY,
3856         "limit the number of uploaded files processed"
3857     ),
3858 
3859     AP_INIT_TAKE1 (
3860         "SecUploadFileMode",
3861         cmd_upload_filemode,
3862         NULL,
3863         CMD_SCOPE_ANY,
3864         "octal permissions mode for uploaded files"
3865     ),
3866 
3867     AP_INIT_TAKE1 (
3868         "SecUploadKeepFiles",
3869         cmd_upload_keep_files,
3870         NULL,
3871         CMD_SCOPE_ANY,
3872         "On or Off"
3873     ),
3874 
3875     AP_INIT_TAKE1 (
3876         "SecTmpSaveUploadedFiles",
3877         cmd_upload_save_tmp_files,
3878         NULL,
3879         CMD_SCOPE_ANY,
3880         "On or Off"
3881     ),
3882 
3883     AP_INIT_TAKE1 (
3884         "SecWebAppId",
3885         cmd_web_app_id,
3886         NULL,
3887         CMD_SCOPE_ANY,
3888         "id"
3889     ),
3890 
3891     AP_INIT_TAKE1 (
3892         "SecSensorId",
3893         cmd_sensor_id,
3894         NULL,
3895         CMD_SCOPE_MAIN,
3896         "sensor id"
3897     ),
3898 
3899     AP_INIT_TAKE1 (
3900         "SecHttpBlKey",
3901         cmd_httpBl_key,
3902         NULL,
3903         CMD_SCOPE_ANY,
3904         "httpBl access key"
3905     ),
3906 
3907     AP_INIT_TAKE1 (
3908         "SecHashEngine",
3909         cmd_hash_engine,
3910         NULL,
3911         CMD_SCOPE_ANY,
3912         "On or Off"
3913     ),
3914 
3915     AP_INIT_TAKE2 (
3916         "SecHashKey",
3917         cmd_hash_key,
3918         NULL,
3919         CMD_SCOPE_ANY,
3920         "Set Hash key"
3921     ),
3922 
3923     AP_INIT_TAKE1 (
3924         "SecHashParam",
3925         cmd_hash_param,
3926         NULL,
3927         CMD_SCOPE_ANY,
3928         "Set Hash parameter"
3929     ),
3930 
3931     { NULL }
3932 };
3933