From 08edc0c26ffd684e332bdfe52a697f3f4b707e96 Mon Sep 17 00:00:00 2001 From: b1v1r Date: Fri, 5 Feb 2010 19:05:20 +0000 Subject: [PATCH] Merge 2.5.x (2.5.12) changes into trunk. --- CHANGES | 48 +- README.TXT | 2 +- apache2/acmp.c | 2 +- apache2/acmp.h | 2 +- apache2/apache2.h | 2 +- apache2/apache2_config.c | 313 ++-- apache2/apache2_io.c | 2 +- apache2/apache2_util.c | 2 +- apache2/configure | 69 +- apache2/configure.in | 54 +- apache2/mod_security2.c | 20 +- apache2/mod_security2_config.h.in | 3 + apache2/modsecurity.c | 2 +- apache2/modsecurity.h | 8 +- apache2/msc_geo.c | 2 +- apache2/msc_geo.h | 2 +- apache2/msc_logging.c | 2 +- apache2/msc_logging.h | 2 +- apache2/msc_lua.c | 2 +- apache2/msc_lua.h | 2 +- apache2/msc_multipart.c | 56 +- apache2/msc_multipart.h | 7 +- apache2/msc_parsers.c | 20 +- apache2/msc_parsers.h | 2 +- apache2/msc_pcre.c | 76 +- apache2/msc_pcre.h | 33 +- apache2/msc_release.c | 2 +- apache2/msc_release.h | 2 +- apache2/msc_reqbody.c | 2 +- apache2/msc_test.c | 7 +- apache2/msc_util.c | 235 ++- apache2/msc_util.h | 9 +- apache2/msc_xml.c | 2 +- apache2/msc_xml.h | 2 +- apache2/persist_dbm.c | 2 +- apache2/persist_dbm.h | 2 +- apache2/re.c | 5 +- apache2/re.h | 2 +- apache2/re_operators.c | 135 +- apache2/re_tfns.c | 2 +- apache2/re_variables.c | 50 +- apache2/t/regression/action/10-logging.t | 368 ++++- .../t/regression/config/10-misc-directives.t | 2 +- .../t/regression/misc/00-multipart-parser.t | 1346 ++++++++++++++++- apache2/t/regression/misc/10-pcre.t | 38 + apache2/t/tfn/normalisePath.t | 162 +- apache2/t/tfn/normalisePathWin.t | 162 +- apache2/utf8tables.h | 2 +- doc/html-chunked.xsl | 2 +- doc/html.xsl | 2 +- doc/migration-matrix.xml | 2 +- doc/modsecurity2-apache-reference.xml | 200 ++- doc/modsecurity2-data-formats.xml | 2 +- modsecurity.conf-minimal | 18 +- 54 files changed, 3170 insertions(+), 330 deletions(-) create mode 100644 apache2/t/regression/misc/10-pcre.t diff --git a/CHANGES b/CHANGES index 6c50bfb1..5727fbf7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,4 @@ - -2 Feb 2010 - trunk +04 Feb 2010 - trunk ------------------- * Add REQUEST_BODY_LENGTH, which contains the number of request body @@ -27,6 +26,51 @@ pave support for allowing access to all ModSecurity variables from mog_log_config. [Ivan Ristic] + +04 Feb 2010 - 2.5.12 +-------------------- + + * Fixed SecUploadFileMode to set the correct mode. + + * Fixed nolog,auditlog/noauditlog/nolog controls for disruptive actions. + + * Added additional file info definitions introduced in APR 0.9.5 so that + build will work with older APRs (IBM HTTP Server v6). + + * Added SecUploadFileLimit to limit the number of uploaded file parts that + will be processed in a multipart POST. The default is 100. + + * Fixed path normalization to better handle backreferences that extend + above root directories. Reported by Sogeti/ESEC R&D. + + * Trim whitespace around phrases used with @pmFromFile and allow + for both LF and CRLF terminated lines. + + * Allow for more robust parsing for multipart header folding. Reported + by Sogeti/ESEC R&D. + + * Fixed failure to match internally set TX variables with regex + (TX:/.../) syntax. + + * Fixed failure to log full internal TX variable names and populate + MATCHED_VAR* vars. + + * Enabled PCRE "studying" by default. This is now a configure-time option. + + * Added PCRE match limits (SecPcreMatchLimit/SecPcreMatchLimitRecursion) to + aide in REDoS type attacks. A rule that goes over the limits will set + TX:MSC_PCRE_LIMITS_EXCEEDED. It is intended that the next major release + of ModSecurity (2.6.x) will move these flags to a dedicated collection. + + * Reduced default PCRE match limits reducing impact of REDoS on poorly + written regex rules. Reported by Sogeti/ESEC R&D. + + * Fixed memory leak in v1 cookie parser. Reported by Sogeti/ESEC R&D. + + * Now support macro expansion in numeric operators (@eq, @ge, @lt, etc.) + + * Update copyright to 2010. + * Reserved 700,000-799,999 IDs for Ivan Ristic. * Fixed SecAction not working when CONNECT request method is used diff --git a/README.TXT b/README.TXT index 188e629f..d6ede95f 100644 --- a/README.TXT +++ b/README.TXT @@ -1,5 +1,5 @@ ModSecurity for Apache 2.x, http://www.modsecurity.org/ -Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) +Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) ModSecurity for Apache is an open source product, released under terms of the General Public Licence, Version 2 (GPLv2). Please refer to the diff --git a/apache2/acmp.c b/apache2/acmp.c index d8c0c0aa..f52a48a5 100644 --- a/apache2/acmp.c +++ b/apache2/acmp.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/acmp.h b/apache2/acmp.h index 761e3a47..c4652223 100644 --- a/apache2/acmp.h +++ b/apache2/acmp.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/apache2.h b/apache2/apache2.h index 264fcf44..b6f8edf7 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index a2713efa..c06fe122 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -33,7 +33,8 @@ /** * Creates a fresh directory configuration. */ -void *create_directory_config(apr_pool_t *mp, char *path) { +void *create_directory_config(apr_pool_t *mp, char *path) +{ directory_config *dcfg = (directory_config *)apr_pcalloc(mp, sizeof(directory_config)); if (dcfg == NULL) return NULL; @@ -87,6 +88,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->upload_keep_files = NOT_SET; dcfg->upload_validates_files = NOT_SET; dcfg->upload_filemode = NOT_SET; + dcfg->upload_file_limit = NOT_SET; /* These are only used during the configuration process. */ dcfg->tmp_chain_starter = NULL; @@ -121,8 +123,10 @@ void *create_directory_config(apr_pool_t *mp, char *path) { * Copies rules between one phase of two configuration contexts, * taking exceptions into account. */ -static void 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) +static void 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) { rule_exception **exceptions; msre_rule **rules; @@ -190,8 +194,9 @@ static void copy_rules_phase(apr_pool_t *mp, apr_array_header_t *parent_phase_ar * Copies rules between two configuration contexts, * taking exceptions into account. */ -static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, msre_ruleset *child_ruleset, - apr_array_header_t *exceptions_arr) +static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, + msre_ruleset *child_ruleset, + apr_array_header_t *exceptions_arr) { copy_rules_phase(mp, parent_ruleset->phase_request_headers, child_ruleset->phase_request_headers, exceptions_arr); @@ -210,7 +215,8 @@ static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, msre_ruleset /** * Merges two directory configurations. */ -void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { +void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) +{ directory_config *parent = (directory_config *)_parent; directory_config *child = (directory_config *)_child; directory_config *merged = create_directory_config(mp, NULL); @@ -418,6 +424,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { ? parent->upload_validates_files : child->upload_validates_files); merged->upload_filemode = (child->upload_filemode == NOT_SET ? parent->upload_filemode : child->upload_filemode); + merged->upload_file_limit = (child->upload_file_limit == NOT_SET + ? parent->upload_file_limit : child->upload_file_limit); /* Misc */ merged->data_dir = (child->data_dir == NOT_SET_P @@ -461,7 +469,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { * the configuration phase. It can only be called on copies of those * (created fresh for every transaction). */ -void init_directory_config(directory_config *dcfg) { +void init_directory_config(directory_config *dcfg) +{ if (dcfg == NULL) return; if (dcfg->is_enabled == NOT_SET) dcfg->is_enabled = 0; @@ -511,7 +520,8 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->upload_dir == NOT_SET_P) dcfg->upload_dir = NULL; if (dcfg->upload_keep_files == NOT_SET) dcfg->upload_keep_files = KEEP_FILES_OFF; if (dcfg->upload_validates_files == NOT_SET) dcfg->upload_validates_files = 0; - if (dcfg->upload_filemode == NOT_SET) dcfg->upload_filemode = mode2fileperms(0600); + if (dcfg->upload_filemode == NOT_SET) dcfg->upload_filemode = 0600; + if (dcfg->upload_file_limit == NOT_SET) dcfg->upload_file_limit = 100; /* Misc */ if (dcfg->data_dir == NOT_SET_P) dcfg->data_dir = NULL; @@ -531,13 +541,14 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->cache_trans_maxitems == (apr_size_t)NOT_SET) dcfg->cache_trans_maxitems = 512; if (dcfg->request_encoding == NOT_SET_P) dcfg->request_encoding = NULL; + } /** * */ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, - const char *p1, const char *p2, const char *p3) + const char *p1, const char *p2, const char *p3) { char *my_error_msg = NULL; msre_rule *rule = NULL; @@ -724,8 +735,8 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, /** * */ -static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, const char *p1, - const char *p2, const char *p3) +static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, + const char *p1, const char *p2, const char *p3) { char *my_error_msg = NULL; msre_rule *rule = NULL; @@ -777,7 +788,7 @@ static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, const char * */ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, - const char *p1, const char *p2) + const char *p1, const char *p2) { char *my_error_msg = NULL; msre_rule *rule = NULL; @@ -862,17 +873,21 @@ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, /* -- Configuration directives -- */ -static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) +{ return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_ACTION, SECACTION_TARGETS, SECACTION_ARGS, p1); } -static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; const char *action = apr_pstrcat(dcfg->mp, SECMARKER_BASE_ACTIONS, p1, NULL); return add_marker(cmd, (directory_config *)_dcfg, SECMARKER_TARGETS, SECMARKER_ARGS, action); } -static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (strlen(p1) != 1) { @@ -884,7 +899,8 @@ static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, const cha return NULL; } -static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = _dcfg; if (strcasecmp(p1, "On") == 0) dcfg->auditlog_flag = AUDITLOG_ON; @@ -899,7 +915,8 @@ static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) return NULL; } -static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = _dcfg; dcfg->auditlog_name = (char *)p1; @@ -932,7 +949,8 @@ static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } -static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = _dcfg; if (dcfg->auditlog_name == NOT_SET_P) { @@ -969,7 +987,9 @@ static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } -static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = _dcfg; if (is_valid_parts_specification((char *)p1) != 1) { @@ -980,7 +1000,9 @@ static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, const char * return NULL; } -static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = _dcfg; dcfg->auditlog_relevant_regex = msc_pregcomp(cmd->pool, p1, PCRE_DOTALL, NULL, NULL); @@ -991,7 +1013,9 @@ static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, co return NULL; } -static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = _dcfg; if (strcasecmp(p1, "Serial") == 0) dcfg->auditlog_type = AUDITLOG_SERIAL; @@ -1004,7 +1028,9 @@ static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, const char *p return NULL; } -static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1024,7 +1050,9 @@ static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, const char return NULL; } -static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1044,7 +1072,9 @@ static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, const cha return NULL; } -static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = _dcfg; dcfg->auditlog_storage_dir = ap_server_root_relative(cmd->pool, p1); @@ -1052,7 +1082,9 @@ static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg, const return NULL; } -static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (strcmp(p1, "0") == 0) dcfg->cookie_format = COOKIES_V0; @@ -1065,7 +1097,8 @@ static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg, const char *p1 return NULL; } -static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ char cwd[1025] = ""; if (cmd->server->is_virtual) { @@ -1094,7 +1127,9 @@ static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { /** * Adds component signature to the list of signatures kept in configuration. */ -static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; /* ENH Enforce "Name/VersionX.Y.Z (comment)" format. */ @@ -1103,14 +1138,16 @@ static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, const ch return NULL; } -static const char *cmd_content_injection(cmd_parms *cmd, void *_dcfg, int flag) { +static const char *cmd_content_injection(cmd_parms *cmd, void *_dcfg, int flag) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; dcfg->content_injection_enabled = flag; return NULL; } -static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (cmd->server->is_virtual) { @@ -1122,7 +1159,8 @@ static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } -static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; apr_status_t rc; @@ -1140,7 +1178,9 @@ static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } -static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; dcfg->debuglog_level = atoi(p1); @@ -1149,7 +1189,9 @@ static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg, const char * return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecDebugLogLevel: %s", p1); } -static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; extern msc_engine *modsecurity; char *my_error_msg = NULL; @@ -1213,7 +1255,9 @@ static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, const char *p return NULL; } -static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { +static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) +{ extern char *guardianlog_name; extern apr_file_t *guardianlog_fd; extern char *guardianlog_condition; @@ -1262,7 +1306,9 @@ static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg, const char *p1, return NULL; } -static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; long int limit; @@ -1278,7 +1324,9 @@ static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, return NULL; } -static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; long int limit; @@ -1294,7 +1342,9 @@ static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, const cha return NULL; } -static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; long int limit; @@ -1310,7 +1360,9 @@ static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, return NULL; } -static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1323,7 +1375,9 @@ static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, const ch return NULL; } -static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1334,7 +1388,9 @@ static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, const char return NULL; } -static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1347,7 +1403,9 @@ static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, const c return NULL; } -static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; long int limit; @@ -1365,7 +1423,9 @@ static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, const ch return NULL; } -static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1378,7 +1438,9 @@ static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, c return NULL; } -static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, const char *_p1) { +static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, + const char *_p1) +{ directory_config *dcfg = (directory_config *)_dcfg; char *p1 = apr_pstrdup(cmd->pool, _p1); @@ -1394,7 +1456,9 @@ static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, cons return NULL; } -static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, void *_dcfg) { +static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, + void *_dcfg) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1407,13 +1471,14 @@ static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, void *_dcf return NULL; } -static const char *cmd_rule(cmd_parms *cmd, void *_dcfg, const char *p1, - const char *p2, const char *p3) +static const char *cmd_rule(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2, const char *p3) { return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, p1, p2, p3); } -static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1428,43 +1493,16 @@ static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) return NULL; } -/* -static const char *cmd_rule_import_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - re->type = RULE_EXCEPTION_IMPORT_ID; - // TODO verify p1 - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - return NULL; -} - -static const char *cmd_rule_import_by_msg(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - re->type = RULE_EXCEPTION_IMPORT_MSG; - // TODO verify p1 - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - return NULL; -} -*/ - -static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) { +static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; dcfg->rule_inheritance = flag; return NULL; } -static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, const char *p1, - const char *p2) +static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) { #if defined(WITH_LUA) const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); @@ -1475,7 +1513,9 @@ static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, const char *p1, #endif } -static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); if (dcfg == NULL) return NULL; @@ -1490,7 +1530,9 @@ static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, const char return NULL; } -static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); if (dcfg == NULL) return NULL; @@ -1514,12 +1556,14 @@ static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, const cha } static const char *cmd_rule_update_action_by_id(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2) + const char *p1, const char *p2) { return update_rule_action(cmd, (directory_config *)_dcfg, p1, p2); } -static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ if (cmd->server->is_virtual) { return "ModSecurity: SecServerSignature not allowed in VirtualHost"; } @@ -1527,7 +1571,8 @@ static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg, const char return NULL; } -static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1538,7 +1583,8 @@ static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } -static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1549,7 +1595,26 @@ static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } -static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_upload_file_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "default") == 0) { + dcfg->upload_file_limit = NOT_SET; + } + else { + dcfg->upload_file_limit = atoi(p1); + } + + return NULL; +} + +static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1569,7 +1634,9 @@ static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg, const char * return NULL; } -static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1589,7 +1656,8 @@ static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg, const char return NULL; } -static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1) { +static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1) +{ directory_config *dcfg = (directory_config *)_dcfg; /* ENH enforce format (letters, digits, ., _, -) */ @@ -1598,10 +1666,51 @@ static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } +/* PCRE Limits */ + +static const char *cmd_pcre_match_limit(cmd_parms *cmd, + void *_dcfg, const char *p1) +{ + long val; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecPcreMatchLimit not allowed in VirtualHost"; + } + + val = atol(p1); + if (val <= 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for " + "SecPcreMatchLimit: %s", p1); + } + msc_pcre_match_limit = (unsigned long int)val; + + return NULL; +} + +static const char *cmd_pcre_match_limit_recursion(cmd_parms *cmd, + void *_dcfg, const char *p1) +{ + long val; + + if (cmd->server->is_virtual) { + return "ModSecurity: SecPcreMatchLimitRecursion not allowed in VirtualHost"; + } + + val = atol(p1); + if (val <= 0) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for " + "SecPcreMatchLimitRecursion: %s", p1); + } + msc_pcre_match_limit_recursion = (unsigned long int)val; + + return NULL; +} + + /* -- Geo Lookup configuration -- */ static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); char *error_msg; @@ -1618,7 +1727,9 @@ static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, /* -- Cache -- */ -static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { +static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2) +{ directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1911,6 +2022,22 @@ const command_rec module_directives[] = { "marker for a skipAfter target" ), + AP_INIT_TAKE1 ( + "SecPcreMatchLimit", + cmd_pcre_match_limit, + NULL, + CMD_SCOPE_MAIN, + "PCRE match limit" + ), + + AP_INIT_TAKE1 ( + "SecPcreMatchLimitRecursion", + cmd_pcre_match_limit_recursion, + NULL, + CMD_SCOPE_MAIN, + "PCRE match limit recursion" + ), + AP_INIT_TAKE1 ( "SecRequestBodyAccess", cmd_request_body_access, @@ -2071,6 +2198,14 @@ const command_rec module_directives[] = { "path to the file upload area" ), + AP_INIT_TAKE1 ( + "SecUploadFileLimit", + cmd_upload_file_limit, + NULL, + CMD_SCOPE_ANY, + "limit the number of uploaded files processed" + ), + AP_INIT_TAKE1 ( "SecUploadFileMode", cmd_upload_filemode, diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 7c46c8b7..8f4b1968 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 8d24f115..870e641d 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/configure b/apache2/configure index 5acc851e..a58eaf34 100755 --- a/apache2/configure +++ b/apache2/configure @@ -694,6 +694,9 @@ SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking +enable_pcre_study +enable_pcre_match_limit +enable_pcre_match_limit_recursion enable_errors enable_verbose_output enable_strict_compile @@ -1328,6 +1331,12 @@ Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-pcre-study Enable PCRE regex studying during configure. + --enable-pcre-match-limit + Enable PCRE regex match limit during configure. + --enable-pcre-match-limit-recursion + Enable PCRE regex match limit recursion during + configure. --disable-errors Disable errors during configure. --enable-verbose-output Enable more verbose configure output. --enable-strict-compile Enable strict compilation (warnings are errors). @@ -4186,7 +4195,7 @@ test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in esac -for ac_func in atexit getcwd memmove memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol +for ac_func in atexit getcwd memmove memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol fchmod do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -4221,6 +4230,62 @@ MSC_REGRESSION_DOCROOT_DIR="$MSC_REGRESSION_SERVERROOT_DIR/htdocs" ### Configure Options +# Add PCRE Studying + +# Check whether --enable-pcre-study was given. +if test "${enable_pcre_study+set}" = set; then : + enableval=$enable_pcre_study; + if test "$enableval" != "no"; then + pcre_study='-DWITH_PCRE_STUDY' + else + pcre_study='' + fi + +else + + pcre_study='-DWITH_PCRE_STUDY' + +fi + + +# Limit PCRE matching +# Check whether --enable-pcre-match-limit was given. +if test "${enable_pcre_match_limit+set}" = set; then : + enableval=$enable_pcre_match_limit; + if test "$enableval" = "yes"; then + as_fn_error "PCRE match limits require a numeric value" "$LINENO" 5 + elif test "$enableval" = "no"; then + pcre_match_limit='' + else + pcre_match_limit="-DMODSEC_PCRE_MATCH_LIMIT=$enableval" + fi + +else + + pcre_match_limit='-DMODSEC_PCRE_MATCH_LIMIT=1500' + +fi + + +# Limit PCRE matching recursion +# Check whether --enable-pcre-match-limit-recursion was given. +if test "${enable_pcre_match_limit_recursion+set}" = set; then : + enableval=$enable_pcre_match_limit_recursion; + if test "$enableval" = "yes"; then + as_fn_error "PCRE match limits require a numeric value" "$LINENO" 5 + elif test "$enableval" = "no"; then + pcre_match_limit_recursion='' + else + pcre_match_limit_recursion="-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=$enableval" + fi + +else + + pcre_match_limit_recursion='-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=1500' + +fi + + # Ignore configure errors # Check whether --enable-errors was given. if test "${enable_errors+set}" = set; then : @@ -4575,7 +4640,7 @@ else EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" fi fi -MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" +MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" APXS_WRAPPER=build/apxs-wrapper APXS_EXTRA_CFLAGS="" diff --git a/apache2/configure.in b/apache2/configure.in index 19ee8a2b..1eb935c5 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -40,7 +40,7 @@ AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_MEMCMP -AC_CHECK_FUNCS([atexit getcwd memmove memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol]) +AC_CHECK_FUNCS([atexit getcwd memmove memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol fchmod]) # Some directories MSC_BASE_DIR=`pwd` @@ -63,6 +63,56 @@ AC_SUBST(MSC_REGRESSION_DOCROOT_DIR) ### Configure Options +# Add PCRE Studying + +AC_ARG_ENABLE(pcre-study, + AS_HELP_STRING([--enable-pcre-study], + [Enable PCRE regex studying during configure.]), +[ + if test "$enableval" != "no"; then + pcre_study='-DWITH_PCRE_STUDY' + else + pcre_study='' + fi +], +[ + pcre_study='-DWITH_PCRE_STUDY' +]) + +# Limit PCRE matching +AC_ARG_ENABLE(pcre-match-limit, + AS_HELP_STRING([--enable-pcre-match-limit], + [Enable PCRE regex match limit during configure.]), +[ + if test "$enableval" = "yes"; then + AC_MSG_ERROR([PCRE match limits require a numeric value]) + elif test "$enableval" = "no"; then + pcre_match_limit='' + else + pcre_match_limit="-DMODSEC_PCRE_MATCH_LIMIT=$enableval" + fi +], +[ + pcre_match_limit='-DMODSEC_PCRE_MATCH_LIMIT=1500' +]) + +# Limit PCRE matching recursion +AC_ARG_ENABLE(pcre-match-limit-recursion, + AS_HELP_STRING([--enable-pcre-match-limit-recursion], + [Enable PCRE regex match limit recursion during configure.]), +[ + if test "$enableval" = "yes"; then + AC_MSG_ERROR([PCRE match limits require a numeric value]) + elif test "$enableval" = "no"; then + pcre_match_limit_recursion='' + else + pcre_match_limit_recursion="-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=$enableval" + fi +], +[ + pcre_match_limit_recursion='-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=1500' +]) + # Ignore configure errors AC_ARG_ENABLE(errors, AS_HELP_STRING([--disable-errors], @@ -325,7 +375,7 @@ else EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" fi fi -MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" +MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" APXS_WRAPPER=build/apxs-wrapper APXS_EXTRA_CFLAGS="" diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 8910b959..1b430810 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -52,6 +52,9 @@ apr_file_t DSOLOCAL *guardianlog_fd = NULL; char DSOLOCAL *guardianlog_condition = NULL; +unsigned long int DSOLOCAL msc_pcre_match_limit = 0; + +unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0; /* -- Miscellaneous functions -- */ @@ -227,9 +230,24 @@ int perform_interception(modsec_rec *msr) { break; } + /* If the level is not high enough to add an alert message, but "auditlog" + * is enabled, then still add the message. */ + if ((log_level > 3) && (actionset->auditlog != 0)) { + *(const char **)apr_array_push(msr->alerts) = msc_alert_message(msr, actionset, NULL, message); + } + /* Log the message now. */ msc_alert(msr, log_level, actionset, message, msr->intercept_message); + /* However, this will mark the txn relevant again if it is <= 3, + * which will mess up noauditlog. We need to compensate for this + * so that we do not increment twice when auditlog is enabled and + * prevent incrementing when auditlog is disabled. + */ + if ((actionset->auditlog == 0) && (log_level <= 3)) { + msr->is_relevant--; + } + return status; } diff --git a/apache2/mod_security2_config.h.in b/apache2/mod_security2_config.h.in index 6e740e08..95957bb5 100644 --- a/apache2/mod_security2_config.h.in +++ b/apache2/mod_security2_config.h.in @@ -3,6 +3,9 @@ /* Define to 1 if you have the `atexit' function. */ #undef HAVE_ATEXIT +/* Define to 1 if you have the `fchmod' function. */ +#undef HAVE_FCHMOD + /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index c670ada6..4827525c 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index f727f7f5..b2bcab87 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -127,6 +127,10 @@ extern module AP_MODULE_DECLARE_DATA security2_module; extern DSOLOCAL const command_rec module_directives[]; +extern DSOLOCAL unsigned long int msc_pcre_match_limit; + +extern DSOLOCAL unsigned long int msc_pcre_match_limit_recursion; + #define RESBODY_STATUS_NOT_READ 0 /* we were not configured to read the body */ #define RESBODY_STATUS_ERROR 1 /* error occured while we were reading the body */ #define RESBODY_STATUS_PARTIAL 2 /* partial body content available in the brigade */ @@ -335,6 +339,7 @@ struct modsec_rec { /* upload */ int upload_extract_files; int upload_remove_files; + int upload_files_count; /* other */ apr_table_t *collections_original; @@ -441,6 +446,7 @@ struct directory_config { int upload_keep_files; int upload_validates_files; int upload_filemode; /* int only so NOT_SET works */ + int upload_file_limit; /* Used only in the configuration phase. */ msre_rule *tmp_chain_starter; diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 5e72fb9f..7909e86f 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_geo.h b/apache2/msc_geo.h index 0df47af7..26bee2ef 100644 --- a/apache2/msc_geo.h +++ b/apache2/msc_geo.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 0b4c6b50..90c15c47 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_logging.h b/apache2/msc_logging.h index ea258986..ee9b399d 100644 --- a/apache2/msc_logging.h +++ b/apache2/msc_logging.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c index f248a6d7..c278c9df 100644 --- a/apache2/msc_lua.c +++ b/apache2/msc_lua.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_lua.h b/apache2/msc_lua.h index 80c2bcf7..04e72556 100644 --- a/apache2/msc_lua.h +++ b/apache2/msc_lua.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 3f4c0588..7081d25e 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -279,12 +279,20 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { } else { /* Header line. */ - if ((msr->mpd->buf[0] == '\t') || (msr->mpd->buf[0] == ' ')) { + if (isspace(msr->mpd->buf[0])) { char *header_value, *new_value, *data; /* header folding, add data to the header we are building */ msr->mpd->flag_header_folding = 1; + /* RFC-2557 states header folding is SP / HTAB, but PHP and + * perhaps others will take any whitespace. So, we accept, + * but with a flag set. + */ + if ((msr->mpd->buf[0] != '\t') && (msr->mpd->buf[0] != ' ')) { + msr->mpd->flag_invalid_header_folding = 1; + } + if (msr->mpd->mpp->last_header_name == NULL) { /* we are not building a header at this moment */ *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (folding error)."); @@ -293,7 +301,15 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { /* locate the beginning of data */ data = msr->mpd->buf; - while((*data == '\t') || (*data == ' ')) data++; + while(isspace(*data)) { + /* Flag invalid header folding if an invalid RFC-2557 character is used anywhere + * in the folding prefix. + */ + if ((*data != '\t') && (*data != ' ')) { + msr->mpd->flag_invalid_header_folding = 1; + } + data++; + } new_value = apr_pstrdup(msr->mp, data); remove_lf_crlf_inplace(new_value); @@ -397,16 +413,32 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { /* add data to the part we are building */ if (msr->mpd->mpp->type == MULTIPART_FILE) { + int extract = msr->upload_extract_files; /* remember where we started */ if (msr->mpd->mpp->length == 0) { msr->mpd->mpp->offset = msr->mpd->buf_offset; } + /* check if the file limit has been reached */ + if (extract && (msr->mpd->nfiles >= msr->txcfg->upload_file_limit)) { + if (msr->mpd->flag_file_limit_exceeded == 0) { + *error_msg = apr_psprintf(msr->mp, + "Multipart: Upload file limit exceeded " + "SecUploadFileLimit %d.", + msr->txcfg->upload_file_limit); + msr_log(msr, 3, "%s", *error_msg); + + msr->mpd->flag_file_limit_exceeded = 1; + } + + extract = 0; + } + /* only store individual files on disk if we are going * to keep them or if we need to have them approved later */ - if (msr->upload_extract_files) { + if (extract) { /* first create a temporary file if we don't have it already */ if (msr->mpd->mpp->tmp_file_fd == 0) { /* construct temporary file name */ @@ -421,8 +453,14 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { return -1; } + /* keep track of the files count */ + msr->mpd->nfiles++; + if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Created temporary file: %s", + msr_log(msr, 4, + "Multipart: Created temporary file %d (mode %04o): %s", + msr->mpd->nfiles, + (unsigned int)msr->txcfg->upload_filemode, log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); } } @@ -879,6 +917,14 @@ int multipart_complete(modsec_rec *msr, char **error_msg) { if (msr->mpd->flag_missing_semicolon) { msr_log(msr, 4, "Multipart: Warning: missing semicolon in C-T header."); } + + if (msr->mpd->flag_invalid_quoting) { + msr_log(msr, 4, "Multipart: Warning: invalid quoting used."); + } + + if (msr->mpd->flag_invalid_header_folding) { + msr_log(msr, 4, "Multipart: Warning: invalid header folding used."); + } } if ((msr->mpd->seen_data != 0) && (msr->mpd->is_complete == 0)) { diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h index 409e86ff..ac28ee59 100644 --- a/apache2/msc_multipart.h +++ b/apache2/msc_multipart.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -68,6 +68,9 @@ struct multipart_data { /* this array keeps parts */ apr_array_header_t *parts; + /* Number of parts that are files */ + int nfiles; + /* mime boundary used to detect when * parts end and begin */ @@ -118,6 +121,8 @@ struct multipart_data { int flag_boundary_whitespace; int flag_missing_semicolon; int flag_invalid_quoting; + int flag_invalid_header_folding; + int flag_file_limit_exceeded; }; diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index 3b8e90f8..c61ebc25 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -22,7 +22,9 @@ /** * */ -int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies) { +int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, + apr_table_t *cookies) +{ char *attr_name = NULL, *attr_value = NULL; char *cookie_header; char *saveptr = NULL; @@ -85,13 +87,21 @@ int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies /** * */ -int parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies) { +int parse_cookies_v1(modsec_rec *msr, char *_cookie_header, + apr_table_t *cookies) +{ char *attr_name = NULL, *attr_value = NULL, *p = NULL; char *prev_attr_name = NULL; char *cookie_header = NULL; int cookie_count = 0; if (_cookie_header == NULL) return -1; + // XXX Should it not match _v0 parser? + //if (_cookie_header == NULL) { + // msr_log(msr, 1, "Cookie parser: Received null for argument."); + // return -1; + //} + cookie_header = strdup(_cookie_header); if (cookie_header == NULL) return -1; @@ -213,6 +223,7 @@ int parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies while( (*p != 0)&&( (*p == ',')||(*p == ';')||(isspace(*p)) ) ) p++; } + free(cookie_header); return cookie_count; } @@ -322,7 +333,8 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, /** * */ -void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg) { +void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg) +{ if (msr->txcfg->debuglog_level >= 5) { msr_log(msr, 5, "Adding request argument (%s): name \"%s\", value \"%s\"", arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len), diff --git a/apache2/msc_parsers.h b/apache2/msc_parsers.h index 9a55dd7b..a5dac5b6 100644 --- a/apache2/msc_parsers.h +++ b/apache2/msc_parsers.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c index 2281dbd2..b8a5a012 100644 --- a/apache2/msc_pcre.c +++ b/apache2/msc_pcre.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -38,17 +38,21 @@ apr_status_t msc_pcre_cleanup(msc_regex_t *regex) { } /** - * Compiles the provided regular expression pattern. The last two + * Compiles the provided regular expression pattern. The _err* * parameters are optional, but if they are provided and an error * occurs they will contain the error message and the offset in - * the pattern where the offending part of the pattern begins. + * the pattern where the offending part of the pattern begins. The + * match_limit* parameters are optional and if >0, then will set + * match limits. */ -void *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, - const char **_errptr, int *_erroffset) +void *msc_pregcomp_ex(apr_pool_t *pool, const char *pattern, int options, + const char **_errptr, int *_erroffset, + int match_limit, int match_limit_recursion) { const char *errptr = NULL; int erroffset; msc_regex_t *regex; + pcre_extra *pe = NULL; regex = apr_pcalloc(pool, sizeof(msc_regex_t)); if (regex == NULL) return NULL; @@ -62,15 +66,74 @@ void *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, if (regex->re == NULL) return NULL; #ifdef WITH_PCRE_STUDY - regex->pe = pcre_study(regex->re, 0, &errptr); + pe = pcre_study(regex->re, 0, &errptr); #endif + /* Setup the pcre_extra record if pcre_study did not already do it */ + if (pe == NULL) { + pe = malloc(sizeof(pcre_extra)); + if (pe == NULL) { + return NULL; + } + memset(pe, 0, sizeof(pcre_extra)); + } + +#ifdef PCRE_EXTRA_MATCH_LIMIT + /* If match limit is available, then use it */ + + /* Use ModSecurity runtime defaults */ + if (match_limit > 0) { + pe->match_limit = match_limit; + pe->flags |= PCRE_EXTRA_MATCH_LIMIT; + } +#ifdef MODSEC_PCRE_MATCH_LIMIT + /* Default to ModSecurity compiled defaults */ + else { + pe->match_limit = MODSEC_PCRE_MATCH_LIMIT; + pe->flags |= PCRE_EXTRA_MATCH_LIMIT; + } +#endif /* MODSEC_PCRE_MATCH_LIMIT */ +#else +#warning This PCRE version does not support match limits! Upgrade to at least PCRE v6.5. +#endif /* PCRE_EXTRA_MATCH_LIMIT */ + +#ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION + /* If match limit recursion is available, then use it */ + + /* Use ModSecurity runtime defaults */ + if (match_limit_recursion > 0) { + pe->match_limit_recursion = match_limit_recursion; + pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + } +#ifdef MODSEC_PCRE_MATCH_LIMIT_RECURSION + /* Default to ModSecurity compiled defaults */ + else { + pe->match_limit_recursion = MODSEC_PCRE_MATCH_LIMIT_RECURSION; + pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + } +#endif /* MODSEC_PCRE_MATCH_LIMIT_RECURSION */ +#else +#warning This PCRE version does not support match recursion limits! Upgrade to at least PCRE v6.5. +#endif /* PCRE_EXTRA_MATCH_LIMIT_RECURSION */ + + regex->pe = pe; + apr_pool_cleanup_register(pool, (void *)regex, (apr_status_t (*)(void *))msc_pcre_cleanup, apr_pool_cleanup_null); return regex; } +/** + * Compiles the provided regular expression pattern. Calls msc_pregcomp_ex() + * with default limits. + */ +void *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, + const char **_errptr, int *_erroffset) +{ + return msc_pregcomp_ex(pool, pattern, options, _errptr, _erroffset, 0, 0); +} + /** * Executes regular expression with extended options. * Returns PCRE_ERROR_NOMATCH when there is no match, error code < -1 @@ -119,3 +182,4 @@ int msc_fullinfo(msc_regex_t *regex, int what, void *where) { return pcre_fullinfo(regex->re, regex->pe, what, where); } + diff --git a/apache2/msc_pcre.h b/apache2/msc_pcre.h index 7c0a01c6..63a8f8c5 100644 --- a/apache2/msc_pcre.h +++ b/apache2/msc_pcre.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -22,6 +22,17 @@ typedef struct msc_regex_t msc_regex_t; #include "pcre.h" + +#ifndef PCRE_ERROR_MATCHLIMIT +/* Define for compile, but not valid in this version of PCRE. */ +#define PCRE_ERROR_MATCHLIMIT (-8) +#endif /* PCRE_ERROR_MATCHLIMIT */ + +#ifndef PCRE_ERROR_RECURSIONLIMIT +/* Define for compile, but not valid in this version of PCRE. */ +#define PCRE_ERROR_RECURSIONLIMIT (-21) +#endif /* PCRE_ERROR_RECURSIONLIMIT */ + #include "apr_general.h" #include "modsecurity.h" @@ -33,17 +44,23 @@ struct msc_regex_t { apr_status_t DSOLOCAL msc_pcre_cleanup(msc_regex_t *regex); -void DSOLOCAL *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, - const char **_errptr, int *_erroffset); +void DSOLOCAL *msc_pregcomp_ex(apr_pool_t *pool, const char *pattern, int options, + const char **_errptr, int *_erroffset, + int match_limit, int match_limit_recursion); -int DSOLOCAL msc_regexec_ex(msc_regex_t *regex, const char *s, unsigned int slen, - int startoffset, int options, int *ovector, int ovecsize, char **error_msg); +void DSOLOCAL *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, + const char **_errptr, int *_erroffset); + +int DSOLOCAL msc_regexec_ex(msc_regex_t *regex, const char *s, + unsigned int slen, int startoffset, int options, + int *ovector, int ovecsize, char **error_msg); int DSOLOCAL msc_regexec_capture(msc_regex_t *regex, const char *s, - unsigned int slen, int *ovector, int ovecsize, char **error_msg); + unsigned int slen, int *ovector, + int ovecsize, char **error_msg); -int DSOLOCAL msc_regexec(msc_regex_t *regex, const char *s, unsigned int slen, - char **error_msg); +int DSOLOCAL msc_regexec(msc_regex_t *regex, const char *s, + unsigned int slen, char **error_msg); int DSOLOCAL msc_fullinfo(msc_regex_t *regex, int what, void *where); diff --git a/apache2/msc_release.c b/apache2/msc_release.c index 9513f59a..2ce72334 100644 --- a/apache2/msc_release.c +++ b/apache2/msc_release.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_release.h b/apache2/msc_release.h index 1f3d949c..dc4a0388 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index b74b1aba..edb8252e 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_test.c b/apache2/msc_test.c index f83a0d61..6240ab35 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -79,7 +79,8 @@ static apr_pool_t *g_mp = NULL; static modsec_rec *g_msr = NULL; static unsigned char buf[BUFLEN]; msc_engine *modsecurity = NULL; - +unsigned long int DSOLOCAL msc_pcre_match_limit = 0; +unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0; /* Stubs */ char *format_error_log_message(apr_pool_t *mp, error_message *em) { @@ -765,7 +766,7 @@ int main(int argc, const char * const argv[]) result = RESULT_WRONGRET; } else if (param_len != out_len) { - fprintf(stderr, "Lenth %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len); + fprintf(stderr, "Length %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len); result = RESULT_WRONGSIZE; } else { diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 39bde986..f99e823e 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -16,15 +16,16 @@ * directly using the email address support@breach.com. * */ -#include "msc_release.h" -#include "msc_util.h" - #include #include #include #include #include +#include "mod_security2_config.h" +#include "msc_release.h" +#include "msc_util.h" + #include /** @@ -435,14 +436,25 @@ char *current_filetime(apr_pool_t *mp) { * */ int msc_mkstemp_ex(char *template, int mode) { + int fd = -1; + /* ENH Use apr_file_mktemp instead. */ - #if !(defined(WIN32)||defined(NETWARE)) - return mkstemp(template); - #else +#if !(defined(WIN32)||defined(NETWARE)) + fd = mkstemp(template); +#ifdef HAVE_FCHMOD + if ((fd != -1) && (mode != 0)) { + if (fchmod(fd, mode) == -1) { + return -1; + } + } +#endif /* HAVE_FCHMOD */ +#else if (mktemp(template) == NULL) return -1; - return open(template, O_WRONLY | O_APPEND | O_CREAT | O_BINARY, mode); - #endif + fd = open(template, O_WRONLY | O_APPEND | O_CREAT | O_BINARY, mode); +#endif /* !(defined(WIN32)||defined(NETWARE)) */ + + return fd; } /** @@ -1149,68 +1161,175 @@ int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) { * IMP1 Assumes NUL-terminated */ int normalize_path_inplace(unsigned char *input, int input_len, int win, int *changed) { - unsigned char *d = input; - int i, count; + unsigned char *src; + unsigned char *dst; + unsigned char *end; + int ldst = 0; + int hitroot = 0; + int done = 0; + int relative; + int trailing; *changed = 0; - i = count = 0; - while ((i < input_len)&&(count < input_len)) { - char c = input[i]; + /* Need at least one byte to normalize */ + if (input_len <= 0) return 0; + /* + * ENH: Deal with UNC and drive letters? + */ + + src = dst = input; + end = input + (input_len - 1); + ldst = 1; + + relative = ((*input == '/') || (win && (*input == '\\'))) ? 0 : 1; + trailing = ((*end == '/') || (win && (*end == '\\'))) ? 1 : 0; + + + while (!done && (src <= end) && (dst <= end)) { /* Convert backslash to forward slash on Windows only. */ - if ((win)&&(c == '\\')) { - c = '/'; - *changed = 1; - } - - if (c == '/') { - /* Is there a directory back-reference? Yes, we - * require at least 5 prior bytes here. That's on - * purpose. - */ - if ((count >= 5)&&(*(d - 1) == '.')&&(*(d - 2) == '.')&&(*(d - 3) == '/')) { - unsigned char *cd = d - 4; - int ccount = count - 4; - + if (win) { + if (*src == '\\') { + *src = '/'; *changed = 1; - - /* Go back until we reach the beginning or a forward slash. */ - while ((ccount > 0)&&(*cd != '/')) { - ccount--; - cd--; - } - - if (*cd == '/') { - d = cd; - count = ccount; - } - } else - /* Is there a directory self-reference? */ - if ((count >= 2)&&(*(d - 1) == '.')&&(*(d - 2) == '/')) { - /* Ignore the last two bytes. */ - d -= 2; - count -= 2; - *changed = 1; - } else - /* Or are there just multiple occurences of forward slash? */ - if ((count >= 1)&&(*(d - 1) == '/')) { - /* Ignore the last one byte. */ - d--; - count--; + } + if ((src < end) && (*(src + 1) == '\\')) { + *(src + 1) = '/'; *changed = 1; } } - /* Copy the byte over. */ - *d++ = c; - count++; - i++; + /* Always normalize at the end of the input. */ + if (src == end) { + done = 1; + } + + /* Skip normalization if this is NOT the end of the path segment. */ + else if (*(src + 1) != '/') { + goto copy; /* Skip normalization. */ + } + + /*** Normalize the path segment. ***/ + + /* Could it be an empty path segment? */ + if ((src != end) && *src == '/') { + /* Ignore */ + *changed = 1; + goto copy; /* Copy will take care of this. */ + } + + /* Could it be a back or self reference? */ + else if (*src == '.') { + + /* Back-reference? */ + if ((dst > input) && (*(dst - 1) == '.')) { + /* If a relative path and either our normalization has + * already hit the rootdir, or this is a backref with no + * previous path segment, then mark that the rootdir was hit + * and just copy the backref as no normilization is possible. + */ + if (relative && (hitroot || ((dst - 2) <= input))) { + hitroot = 1; + + goto copy; /* Skip normalization. */ + } + + /* Remove backreference and the previous path segment. */ + dst -= 3; + while ((dst > input) && (*dst != '/')) { + dst--; + } + + /* But do not allow going above rootdir. */ + if (dst <= input) { + hitroot = 1; + dst = input; + + /* Need to leave the root slash if this + * is not a relative path and the end was reached + * on a backreference. + */ + if (!relative && (src == end)) { + dst++; + } + } + + if (done) goto length; /* Skip the copy. */ + src++; + + *changed = 1; + } + + /* Relative Self-reference? */ + else if (dst == input) { + *changed = 1; + + /* Ignore. */ + + if (done) goto length; /* Skip the copy. */ + src++; + } + + /* Self-reference? */ + else if (*(dst - 1) == '/') { + *changed = 1; + + /* Ignore. */ + + if (done) goto length; /* Skip the copy. */ + dst--; + src++; + } + } + + /* Found a regular path segment. */ + else if (dst > input) { + hitroot = 0; + } + +copy: + /*** Copy the byte if required. ***/ + + /* Skip to the last forward slash when multiple are used. */ + if (*src == '/') { + unsigned char *oldsrc = src; + + while ( (src < end) + && ((*(src + 1) == '/') || (win && (*(src + 1) == '\\'))) ) + { + src++; + } + if (oldsrc != src) *changed = 1; + + /* Do not copy the forward slash to the root + * if it is not a relative path. Instead + * move over the slash to the next segment. + */ + if (relative && (dst == input)) { + src++; + goto length; /* Skip the copy */ + } + } + + *(dst++) = *(src++); + +length: + ldst = (dst - input); } - *d = '\0'; + /* Make sure that there is not a trailing slash in the + * normalized form if there was not one in the original form. + */ + if (!trailing && (dst > input) && *(dst - 1) == '/') { + ldst--; + dst--; + } - return count; + /* Always NUL terminate */ + *dst = '\0'; + + return ldst; } char *modsec_build(apr_pool_t *mp) { diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 909cda3c..9a34bce5 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -22,6 +22,13 @@ #include #include +#ifndef APR_WSTICKY +/* Add extra flags added to APR in 0.9.5 */ +#define APR_USETID 0x8000 /**< Set user id */ +#define APR_GSETID 0x4000 /**< Set group id */ +#define APR_WSTICKY 0x2000 /**< Sticky bit */ +#endif + #include "modsecurity.h" int DSOLOCAL normalize_path_inplace(unsigned char *input, int len, int win, int *changed); diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index 4f330140..c13d8331 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/msc_xml.h b/apache2/msc_xml.h index 34c078e3..aaf00e95 100644 --- a/apache2/msc_xml.h +++ b/apache2/msc_xml.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 1d395023..33e5934a 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/persist_dbm.h b/apache2/persist_dbm.h index 94ab1ba5..8d20a679 100644 --- a/apache2/persist_dbm.h +++ b/apache2/persist_dbm.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/re.c b/apache2/re.c index 81d76419..854da2b5 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -1050,8 +1050,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) } } } - else - if (rc == RULE_MATCH) { + else if (rc == RULE_MATCH) { if (msr->rule_was_intercepted) { /* If the transaction was intercepted by this rule we will * go back. Do note that we are relying on the diff --git a/apache2/re.h b/apache2/re.h index cdd57610..663d4bb9 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 0b7767f3..a1684a19 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -84,7 +84,7 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { *error_msg = NULL; /* Compile pattern */ - regex = msc_pregcomp(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); + regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -143,8 +143,29 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c * and no memory has to be allocated for any backreferences. */ rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg); - if (rc < -1) { - *error_msg = apr_psprintf(msr->mp, "Regex execution failed: %s", my_error_msg); + if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + + if (s == NULL) return -1; + s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED"); + s->name_len = strlen(s->name); + s->value = apr_pstrdup(msr->mp, "1"); + s->value_len = 1; + if ((s->name == NULL)||(s->value == NULL)) return -1; + apr_table_setn(msr->tx_vars, s->name, (void *)s); + + *error_msg = apr_psprintf(msr->mp, + "Rule execution error - " + "PCRE limits exceeded (%d): %s", + rc, my_error_msg); + + msr_log(msr, 3, "%s.", *error_msg); + + return 0; /* No match. */ + } + else if (rc < -1) { + *error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s", + rc, my_error_msg); return -1; } @@ -170,6 +191,7 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c if (s == NULL) return -1; s->name = apr_psprintf(msr->mp, "%d", i); + s->name_len = strlen(s->name); s->value = apr_pstrmemdup(msr->mp, target + ovector[2 * i], ovector[2 * i + 1] - ovector[2 * i]); s->value_len = (ovector[2 * i + 1] - ovector[2 * i]); @@ -243,7 +265,8 @@ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { char buf[HUGE_STRING_LEN + 1]; char *fn; char *next; - char *ptr; + char *start; + char *end; const char *rulefile_path; apr_status_t rc; apr_file_t *fd; @@ -289,7 +312,7 @@ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { } /* Open file and read */ - rc = apr_file_open(&fd, fn, APR_READ | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); + rc = apr_file_open(&fd, fn, APR_READ | APR_BUFFERED | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); if (rc != APR_SUCCESS) { *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open phrase file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); return 0; @@ -309,21 +332,24 @@ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { return 0; } - /* Remove newline */ - ptr = buf; - while(*ptr != '\0') ptr++; - if ((ptr > buf) && (*(ptr - 1) == '\n')) *(ptr - 1) = '\0'; + /* Trim Whitespace */ + start = buf; + while ((apr_isspace(*start) != 0) && (*start != '\0')) start++; + end = buf + strlen(buf); + if (end > start) end--; + while ((end > start) && (apr_isspace(*end) != 0)) end--; + if (end > start) { + *(++end) = '\0'; + } /* Ignore empty lines and comments */ - ptr = buf; - while((*ptr != '\0') && apr_isspace(*ptr)) ptr++; - if ((*ptr == '\0') || (*ptr == '#')) continue; + if ((start == end) || (*start == '#')) continue; #ifdef DEBUG_CONF fprintf(stderr, "Adding phrase file pattern: \"%s\"\n", buf); #endif - acmp_add_pattern(p, buf, NULL, NULL, strlen(buf)); + acmp_add_pattern(p, start, NULL, NULL, (end - start)); } fn = next; } @@ -369,6 +395,7 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c if (s == NULL) return -1; s->name = "0"; + s->name_len = strlen(s->name); s->value = apr_pstrdup(msr->mp, match); if (s->value == NULL) return -1; s->value_len = strlen(s->value); @@ -1067,7 +1094,7 @@ static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { *error_msg = NULL; /* Compile rule->op_param */ - regex = msc_pregcomp(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset); + regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -1159,6 +1186,7 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; s->name = apr_psprintf(msr->mp, "%d", i); + s->name_len = strlen(s->name); s->value = apr_pstrmemdup(msr->mp, match, length); s->value_len = length; if ((s->name == NULL)||(s->value == NULL)) return -1; @@ -1796,18 +1824,27 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string str; int left, right; char *target = NULL; + if (error_msg == NULL) return -1; + *error_msg = NULL; + if ((var->value == NULL)||(rule->op_param == NULL)) { /* NULL values do not match anything. */ return 0; } + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); if (target == NULL) return -1; left = atoi(target); - right = atoi(rule->op_param); + right = atoi(str.value); if (left != right) { /* No match. */ @@ -1825,6 +1862,7 @@ static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string str; int left, right; char *target = NULL; @@ -1833,10 +1871,23 @@ static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); if (target == NULL) return -1; left = atoi(target); - right = atoi(rule->op_param); + right = atoi(str.value); if (left <= right) { /* No match. */ @@ -1854,6 +1905,7 @@ static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string str; int left, right; char *target = NULL; @@ -1862,10 +1914,23 @@ static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); if (target == NULL) return -1; left = atoi(target); - right = atoi(rule->op_param); + right = atoi(str.value); if (left >= right) { /* No match. */ @@ -1883,6 +1948,7 @@ static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string str; int left, right; char *target = NULL; @@ -1891,10 +1957,23 @@ static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); if (target == NULL) return -1; left = atoi(target); - right = atoi(rule->op_param); + right = atoi(str.value); if (left < right) { /* No match. */ @@ -1912,6 +1991,7 @@ static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string str; int left, right; char *target = NULL; @@ -1920,10 +2000,23 @@ static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if ((var->value == NULL)||(rule->op_param == NULL)) { + /* NULL values do not match anything. */ + return 0; + } + + str.value = (char *)rule->op_param; + str.value_len = strlen(str.value); + + expand_macros(msr, &str, rule, msr->mp); + target = apr_pstrmemdup(msr->mp, var->value, var->value_len); if (target == NULL) return -1; left = atoi(target); - right = atoi(rule->op_param); + right = atoi(str.value); if (left > right) { /* No match. */ @@ -1936,7 +2029,7 @@ static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, } } -/* ------------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ /** * diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 9f045289..18bb9dad 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 18365937..68ee861e 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -1379,6 +1379,30 @@ static int var_multipart_invalid_quoting_generate(modsec_rec *msr, msre_var *var } } +/* MULTIPART_INVALID_HEADER_FOLDING */ + +static int var_multipart_invalid_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_invalid_header_folding != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + +/* MULTIPART_FILE_LIMIT_EXCEEDED */ + +static int var_multipart_file_limit_exceeded_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + if ((msr->mpd != NULL)&&(msr->mpd->flag_file_limit_exceeded != 0)) { + return var_simple_generate(var, vartab, mptmp, "1"); + } else { + return var_simple_generate(var, vartab, mptmp, "0"); + } +} + /* MULTIPART_STRICT_ERROR */ static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, @@ -1395,6 +1419,8 @@ static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, m ||(msr->mpd->flag_lf_line != 0) ||(msr->mpd->flag_missing_semicolon != 0) ||(msr->mpd->flag_invalid_quoting != 0) + ||(msr->mpd->flag_invalid_header_folding != 0) + ||(msr->mpd->flag_file_limit_exceeded != 0) ) { return var_simple_generate(var, vartab, mptmp, "1"); } @@ -2643,6 +2669,28 @@ void msre_engine_register_default_variables(msre_engine *engine) { PHASE_REQUEST_BODY ); + /* MULTIPART_INVALID_HEADER_FOLDING */ + msre_engine_variable_register(engine, + "MULTIPART_INVALID_HEADER_FOLDING", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_invalid_header_folding_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + + /* MULTIPART_FILE_LIMIT_EXCEEDED */ + msre_engine_variable_register(engine, + "MULTIPART_FILE_LIMIT_EXCEEDED", + VAR_SIMPLE, + 0, 0, + NULL, + var_multipart_file_limit_exceeded_generate, + VAR_DONT_CACHE, /* flag */ + PHASE_REQUEST_BODY + ); + /* MULTIPART_STRICT_ERROR */ msre_engine_variable_register(engine, "MULTIPART_STRICT_ERROR", diff --git a/apache2/t/regression/action/10-logging.t b/apache2/t/regression/action/10-logging.t index 0a199eab..3c1d42b2 100644 --- a/apache2/t/regression/action/10-logging.t +++ b/apache2/t/regression/action/10-logging.t @@ -1,17 +1,21 @@ ### Logging tests -# log/nolog +# log/nolog (pass) { type => "action", - comment => "log", + comment => "log (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,log" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], }, match_response => { status => qr/^200$/, @@ -22,9 +26,12 @@ }, { type => "action", - comment => "nolog", + comment => "nolog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,nolog" @@ -41,19 +48,70 @@ ), }, -# auditlog/noauditlog +# log/nolog (deny) { type => "action", - comment => "auditlog", + comment => "log (deny)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,log" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# auditlog/noauditlog (pass) +{ + type => "action", + comment => "auditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,auditlog" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], - audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], }, match_response => { status => qr/^200$/, @@ -64,15 +122,18 @@ }, { type => "action", - comment => "noauditlog", + comment => "noauditlog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,noauditlog" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], -audit => [ qr/./, 1 ], }, match_response => { @@ -83,19 +144,70 @@ ), }, -# All log/nolog auditlog/noauditlog combos +# auditlog/noauditlog (deny) { type => "action", - comment => "log,auditlog", + comment => "auditlog (deny)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# All log/nolog auditlog/noauditlog combos (pass) +{ + type => "action", + comment => "log,auditlog (pass)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,log,auditlog" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], - audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], }, match_response => { status => qr/^200$/, @@ -106,15 +218,18 @@ }, { type => "action", - comment => "log,noauditlog", + comment => "log,noauditlog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,log,noauditlog" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], -audit => [ qr/./, 1 ], }, match_response => { @@ -126,9 +241,12 @@ }, { type => "action", - comment => "nolog,auditlog", + comment => "nolog,auditlog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,nolog,auditlog" @@ -145,9 +263,12 @@ }, { type => "action", - comment => "nolog,noauditlog", + comment => "nolog,noauditlog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,nolog,noauditlog" @@ -165,16 +286,19 @@ }, { type => "action", - comment => "auditlog,log", + comment => "auditlog,log (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,auditlog,log" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], - audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning\. Unconditional match in SecAction\./, 1 ], }, match_response => { status => qr/^200$/, @@ -185,9 +309,12 @@ }, { type => "action", - comment => "auditlog,nolog", + comment => "auditlog,nolog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,auditlog,nolog" @@ -205,15 +332,18 @@ }, { type => "action", - comment => "noauditlog,log", + comment => "noauditlog,log (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,noauditlog,log" ), match_log => { - error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + error => [ qr/ModSecurity: Warning\. Unconditional match in SecAction\./, 1 ], -audit => [ qr/./, 1 ], }, match_response => { @@ -225,9 +355,12 @@ }, { type => "action", - comment => "noauditlog,nolog", + comment => "noauditlog,nolog (pass)", conf => qq( SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx SecAuditEngine RelevantOnly SecAuditLog "$ENV{AUDIT_LOG}" SecAction "phase:1,pass,noauditlog,nolog" @@ -244,3 +377,188 @@ ), }, +# All log/nolog auditlog/noauditlog combos (deny) +{ + type => "action", + comment => "log,auditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,log,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "log,noauditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,log,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,auditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,nolog,auditlog" + ), + match_log => { + audit => [ qr/-H--\s+Message: .*Stopwatch: /s, 1 ], + }, + match_response => { + -error => [ qr/ModSecurity: /, 1 ], + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,noauditlog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,nolog,noauditlog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,log (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,auditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,nolog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,auditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,log (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,noauditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 1\)\. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,nolog (deny)", + conf => qq( + SecRuleEngine On + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAuditLogRelevantStatus xxx + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,deny,status:403,noauditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/apache2/t/regression/config/10-misc-directives.t b/apache2/t/regression/config/10-misc-directives.t index 7968785e..0dffcaf1 100644 --- a/apache2/t/regression/config/10-misc-directives.t +++ b/apache2/t/regression/config/10-misc-directives.t @@ -96,7 +96,7 @@ return 1; }, match_log => { - debug => [ qr/Created temporary file: $ENV{TEMP_DIR}/, 1 ], + debug => [ qr/Created temporary file.*$ENV{TEMP_DIR}/, 1 ], -debug => [ qr/Failed to /, 1 ], }, match_response => { diff --git a/apache2/t/regression/misc/00-multipart-parser.t b/apache2/t/regression/misc/00-multipart-parser.t index 5d156e15..5ed94ebc 100644 --- a/apache2/t/regression/misc/00-multipart-parser.t +++ b/apache2/t/regression/misc/00-multipart-parser.t @@ -1,5 +1,51 @@ ### Multipart parser tests +# Normal +{ + type => "misc", + comment => "multipart parser (normal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Added file part [0-9a-h]+ to the list: name "image" file name "image.jpg" \(offset 258, length 10\).*Adding request argument \(BODY\): name "name", value "Brian Rectanus".*Adding request argument \(BODY\): name "email", value "brian.rectanus\@breach.com"/s, 1 ], + -debug => [ qr/Multipart.*(?i:error|warning)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + # Final CRLF or not, we should still work { type => "misc", @@ -41,6 +87,8 @@ ), ), }, + +# No final CRLF { type => "misc", comment => "multipart parser (no final CRLF)", @@ -362,6 +410,7 @@ ), ), }, + # Zero length part name should not crash { type => "misc", @@ -371,16 +420,15 @@ SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRequestBodyAccess On - #SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:403" - SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny,status:403" - SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "!\@eq 1" "phase:2,deny" ), match_log => { debug => [ qr/name: a.*variable: 1.*Invalid part header \(header name missing\)/s, 1 ], -debug => [ qr/Adding request argument \(BODY\): name "b"/s, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^200$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -404,6 +452,211 @@ ), ), }, + +# Part header folding +{ + type => "misc", + comment => "multipart parser (part header folding - space)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 0" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - tab)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 0" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - mixed)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 0" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - invalid)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (part header folding - mixed invalid)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule MULTIPART_INVALID_HEADER_FOLDING "!\@eq 1" "phase:2,deny,status:403" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:403" + ), + match_log => { + debug => [ qr/name: a.*variable: 1.*name: b.*variable: 2/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + # Data following final boundary should set flag { type => "misc", @@ -443,6 +696,7 @@ ), ), }, + # Single quoted data is invalid { type => "misc", @@ -452,16 +706,16 @@ SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRequestBodyAccess On - #SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:403" - SecRule MULTIPART_INVALID_QUOTING "\@eq 1" "chain,phase:2,deny,status:403" - SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny" + SecRule MULTIPART_INVALID_QUOTING "!\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "chain,phase:2,deny" ), match_log => { debug => [ qr/name: a.*variable: 1.*Duplicate Content-Disposition name/s, 1 ], -debug => [ qr/Adding request argument \(BODY\): name "b/s, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^200$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -483,3 +737,1079 @@ ), ), }, + +# Invalid boundary separators +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary separator - comma)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + error => [ qr/Invalid boundary in C-T \(malformed\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data, boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary separator - space)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Invalid boundary parameter names +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary parameter name - case)", + note => q( + PHP 5.2.3 - does not recognise boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + error => [ qr/Invalid boundary in C-T \(case sensitivity\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; bOundAry=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (invalid C-T boundary parameter name - trailing chars)", + note => q( + PHP 5.2.3 - does not recognise boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + error => [ qr/Invalid boundary in C-T \(parameter name\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary123=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Invalid boundaries +{ + type => "misc", + comment => "multipart parser (multiple C-T boundaries - first quoted)", + note => q( + PHP 5.2.3 - uses first boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Multiple boundary parameters in C-T/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000"; boundary=1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (multiple C-T boundaries - comma separated)", + note => q( + PHP 5.2.3 - uses first boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Multiple boundary parameters in C-T/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000, boundary=1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary whitespace in C-T - after name)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary =0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary whitespace in C-T - before value)", + note => q( + PHP 5.2.3 - uses " 0000" as boundary. + We should probably interpret it as "0000" and just flag a strict error. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary whitespace in C-T header/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary= 0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary whitespace in C-T - after value)", + note => q( + PHP 5.2.3 - no effect. + Apache just silently removes the trailing whitespace for us. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Added file part [0-9a-h]+ to the list: name "image" file name "image.jpg" \(offset 258, length 10\).*Adding request argument \(BODY\): name "name", value "Brian Rectanus".*Adding request argument \(BODY\): name "email", value "brian.rectanus\@breach.com"/s, 1 ], + -debug => [ qr/Multipart.*(?i:error|warning)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000 ), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Special chars +{ + type => "misc", + comment => "multipart parser (boundary special char - trailing whitespace+token)", + note => q( + PHP 5.2.3 - uses "0000 1111" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/No boundaries found in payload/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000 1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary special char - trailing comma+token)", + note => q( + PHP 5.2.3 - uses "0000" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000,1111), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Quoting +{ + type => "misc", + comment => "multipart parser (quoted boundary - normal)", + note => q( + PHP 5.2.3 - no effect. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted/, 1 ], + + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000"), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - whitespace before)", + note => q( + PHP 5.2.3 - uses " 0000" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted.*No boundaries found in payload/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=" 0000"), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - whitespace after)", + note => q( + PHP 5.2.3 - uses "0000 " as boundary. + Trailing whitespace violates RFC as well. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted.*No boundaries found in payload/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000 "), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - whitespace between)", + note => q( + PHP 5.2.3 - uses "0000 " as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/boundary was quoted/s, 1 ], + -debug => [ qr/No boundaries found in payload/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000 1111"), + ], + normalize_raw_request_data( + q( + --0000 1111 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 1111 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 1111 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000 1111-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - contained quote)", + note => q( + PHP 5.2.3 - uses "0000 " as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="00\"00"), + ], + normalize_raw_request_data( + q( + --00"00 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --00"00 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --00"00 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --00"00-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (quoted boundary value - two quoted values)", + note => q( + PHP 5.2.3 - does not work, uses "00" as boundary. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="00""00"), + ], + normalize_raw_request_data( + q( + --00"00 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --00"00 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --00"00 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --00"00-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (partial quoted boundary value - only start quote)", + note => q( + PHP 5.2.3 - breaks parsing. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(quote\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary="0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (partial quoted boundary value - only end quote)", + note => q( + PHP 5.2.3 - breaks parsing. + ), + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid boundary in C-T \(quote\)/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000"), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + +# Multipart mixed +{ + type => "misc", + comment => "multipart parser (multipart mixed - normal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Invalid Content-Disposition header/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: attachment + Content-Type: multipart/mixed; boundary=BbC04y + + --BbC04y + Content-Disposition: file; filename="file1.txt" + Content-Type: text/plain + + ... contents of file1.txt ... + --BbC04y + Content-Disposition: file; filename="file2.gif + Content-Type: image/jpeg + Content-Transfer-Encoding: binary + + ...contents of file2.gif... + --0000-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (multipart mixed - missing disposition)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Part missing Content-Disposition header/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Type: multipart/mixed; boundary=BbC04y + + --BbC04y + Content-Disposition: file; filename="file1.txt" + Content-Type: text/plain + + ... contents of file1.txt ... + --BbC04y + Content-Disposition: file; filename="file2.gif + Content-Type: image/jpeg + Content-Transfer-Encoding: binary + + ...contents of file2.gif... + --0000-- + ), + ), + ), +}, + +# File limits +{ + type => "misc", + comment => "multipart parser (normal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecTmpDir "$ENV{TEMP_DIR}" + SecUploadDir "$ENV{UPLOAD_DIR}" + SecUploadKeepFiles On + SecUploadFileLimit 2 + + # These should be set + SecRule MULTIPART_STRICT_ERROR "!\@eq 1" "phase:2,deny" + SecRule MULTIPART_FILE_LIMIT_EXCEEDED "!\@eq 1" "phase:2,deny" + + # This should not be set + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + + # Theses should still be accurate + SecRule &FILES "!\@eq 3" "phase:2,deny" + SecRule &FILES_NAMES "!\@eq 3" "phase:2,deny" + SecRule &FILES_SIZES "!\@eq 3" "phase:2,deny" + SecRule FILES_SIZES:/^image/ "\@eq 0" "phase:2,deny" + + # This should be the SecUploadFileLimit + SecRule &FILES_TMPNAMES "!\@eq 2" "phase:2,deny" + ), + match_log => { + debug => [ qr/Multipart: Upload file limit exceeded.*name: test.*variable: This is test data/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="name" + + Brian Rectanus + --0000 + Content-Disposition: form-data; name="email" + + brian.rectanus@breach.com + --0000 + Content-Disposition: form-data; name="image1"; filename="image1.jpg" + Content-Type: image/jpeg + + BINARYDATA1 + --0000 + Content-Disposition: form-data; name="image2"; filename="image2.jpg" + Content-Type: image/jpeg + + BINARYDATA2 + --0000 + Content-Disposition: form-data; name="image3"; filename="image3.jpg" + Content-Type: image/jpeg + + BINARYDATA3 + --0000 + Content-Disposition: form-data; name="test" + + This is test data. + --0000-- + ), + ), + ), +}, + diff --git a/apache2/t/regression/misc/10-pcre.t b/apache2/t/regression/misc/10-pcre.t new file mode 100644 index 00000000..c66f4f9e --- /dev/null +++ b/apache2/t/regression/misc/10-pcre.t @@ -0,0 +1,38 @@ +### PCRE Limits + +{ + type => "misc", + comment => "PCRE limits", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + SecRequestBodyAccess On + SecRequestBodyLimit 1000000 + SecRequestBodyInMemoryLimit 1000000 + + # Set Limits low + SecPcreMatchLimit 100 + SecPcreMatchLimitRecursion 100 + + # Poor REGEX + SecRule ARGS "(?:(.{2,})\\1{32,})" "phase:2,deny,capture,msg:'REDoS'" + # Detect PCRE limits exceeded + SecRule TX:MSC_PCRE_LIMITS_EXCEEDED "!\@streq 0" "phase:2,deny,msg:'ModSecurity Internal Error Flagged: %{MATCHED_VAR_NAME}'" + ), + match_log => { + debug => [ qr/PCRE limits exceeded/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # Args + "test=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", + ), +}, diff --git a/apache2/t/tfn/normalisePath.t b/apache2/t/tfn/normalisePath.t index 851e6679..987aaaa1 100644 --- a/apache2/t/tfn/normalisePath.t +++ b/apache2/t/tfn/normalisePath.t @@ -27,64 +27,190 @@ { type => "tfn", name => "normalisePath", - input => "/foo/bar//baz", - output => "/foo/bar/baz", + input => "x", + output => "x", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => ".", + output => "", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "/foo/bar baz/././././boo//eek/././../whoa", - output => "/foo/bar baz/boo/whoa", + input => "./", + output => "", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "./foo/bar baz/././././boo//eek/././../whoa", - output => "./foo/bar baz/boo/whoa", + input => "./..", + output => "..", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "/./foo/bar baz/././././boo//eek/././../whoa", - output => "/foo/bar baz/boo/whoa", + input => "./../", + output => "../", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "//foo/bar baz/././././boo//eek/././../whoa", - output => "/foo/bar baz/boo/whoa", + input => "..", + output => "..", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../", + output => "../", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../.", + output => "..", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "//foo/bar baz/././././boo//eek/././../whoa/./", - output => "/foo/bar baz/boo/whoa/", + input => ".././", + output => "../", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "/./foo/bar baz/././././boo//eek/././../whoa//", - output => "/foo/bar baz/boo/whoa/", + input => "../..", + output => "../..", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "../../", + output => "../../", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePath", + input => "/dir/foo//bar", + output => "/dir/foo/bar", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "/./../../../../../../../../etc/passwd", - output => "/etc/passwd", + input => "dir/foo//bar/", + output => "dir/foo/bar/", ret => 1, }, { type => "tfn", name => "normalisePath", - input => "/./.././../../../../../../../etc/../etc/./passwd", - output => "/etc/passwd", + input => "dir/../foo", + output => "foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/../../foo", + output => "../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/.", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/./", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/..", + output => "../../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/../", + output => "../../foo/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./.././../../foo/bar/", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir//.//..//.//..//..//foo//bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir//.//..//.//..//..//foo//bar//", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/subdir/subsubdir/subsubsubdir/../../..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./subdir/./subsubdir/./subsubsubdir/../../..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "dir/./subdir/../subsubdir/../subsubsubdir/..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePath", + input => "/dir/./subdir/../subsubdir/../subsubsubdir/../", + output => "/dir/", ret => 1, }, diff --git a/apache2/t/tfn/normalisePathWin.t b/apache2/t/tfn/normalisePathWin.t index a3845bf4..dc0f0c3e 100644 --- a/apache2/t/tfn/normalisePathWin.t +++ b/apache2/t/tfn/normalisePathWin.t @@ -27,64 +27,190 @@ { type => "tfn", name => "normalisePathWin", - input => "\\foo\\bar\\\\baz", - output => "/foo/bar/baz", + input => "x", + output => "x", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => ".", + output => "", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\foo\\bar baz\\.\\.\\.\\.\\boo\\\\eek\\.\\.\\..\\whoa", - output => "/foo/bar baz/boo/whoa", + input => ".\\", + output => "", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => ".\\foo\\bar baz\\.\\.\\.\\.\\boo\\\\eek\\.\\.\\..\\whoa", - output => "./foo/bar baz/boo/whoa", + input => ".\\..", + output => "..", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\.\\foo\\bar baz\\.\\.\\.\\.\\boo\\\\eek\\.\\.\\..\\whoa", - output => "/foo/bar baz/boo/whoa", + input => ".\\..\\", + output => "../", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\\\foo\\bar baz\\.\\.\\.\\.\\boo\\\\eek\\.\\.\\..\\whoa", - output => "/foo/bar baz/boo/whoa", + input => "..", + output => "..", + ret => 0, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "..\\", + output => "../", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\\\foo\\bar baz\\.\\.\\.\\.\\boo\\\\eek\\.\\.\\..\\whoa\\.\\", - output => "/foo/bar baz/boo/whoa/", + input => "..\\.", + output => "..", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\.\\foo\\bar baz\\.\\.\\.\\.\\boo\\\\eek\\.\\.\\..\\whoa\\\\", - output => "/foo/bar baz/boo/whoa/", + input => "..\\.\\", + output => "../", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\.\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd", - output => "/etc/passwd", + input => "..\\..", + output => "../..", ret => 1, }, { type => "tfn", name => "normalisePathWin", - input => "\\.\\..\\.\\..\\..\\..\\..\\..\\..\\..\\etc\\..\\etc\\.\\passwd", - output => "/etc/passwd", + input => "..\\..\\", + output => "../../", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "\\dir\\foo\\\\bar", + output => "/dir/foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\foo\\\\bar\\", + output => "dir/foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\..\\foo", + output => "foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\..\\..\\foo", + output => "../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\.", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\.\\", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\..", + output => "../../foo", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\..\\", + output => "../../foo/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\..\\.\\..\\..\\foo\\bar\\", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\\\.\\\\..\\\\.\\\\..\\\\..\\\\foo\\\\bar", + output => "../../foo/bar", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\\\.\\\\..\\\\.\\\\..\\\\..\\\\foo\\\\bar\\\\", + output => "../../foo/bar/", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\subdir\\subsubdir\\subsubsubdir\\..\\..\\..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\subdir\\.\\subsubdir\\.\\subsubsubdir\\..\\..\\..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "dir\\.\\subdir\\..\\subsubdir\\..\\subsubsubdir\\..", + output => "dir", + ret => 1, +}, +{ + type => "tfn", + name => "normalisePathWin", + input => "\\dir\\.\\subdir\\..\\subsubdir\\..\\subsubsubdir\\..\\", + output => "/dir/", ret => 1, }, diff --git a/apache2/utf8tables.h b/apache2/utf8tables.h index d18f2f36..80a20902 100644 --- a/apache2/utf8tables.h +++ b/apache2/utf8tables.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2010 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/doc/html-chunked.xsl b/doc/html-chunked.xsl index 2c42ecf1..8b250969 100644 --- a/doc/html-chunked.xsl +++ b/doc/html-chunked.xsl @@ -13,7 +13,7 @@ - + diff --git a/doc/html.xsl b/doc/html.xsl index a3b50930..4d389ee6 100644 --- a/doc/html.xsl +++ b/doc/html.xsl @@ -9,7 +9,7 @@ - + diff --git a/doc/migration-matrix.xml b/doc/migration-matrix.xml index 343e881e..6bd5f049 100644 --- a/doc/migration-matrix.xml +++ b/doc/migration-matrix.xml @@ -6,7 +6,7 @@ Version 1.0 / (April 10, 2007) - 2004-2009 + 2004-2010 Breach Security, Inc. (http://www.breach.com) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 26d6955c..158eb83f 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,9 +4,9 @@
<trademark class="registered">ModSecurity</trademark> Reference Manual - Version 2.6.0-trunk (Nov 12, 2009) + Version 2.6.0-trunk (Feb 3, 2009) - 2004-2009 + 2004-2010 Breach Security, Inc. (http://www.breach.com) @@ -218,10 +218,11 @@ central repository, then you will also need the curl library. http://curl.haxx.se/libcurl/ + - Many have had issues with libcurl linked with the GnuTLS library for SSL/TLS - support. It is recommended that the openssl library be used for SSL/TLS support in - libcurl. + Many have had issues with libcurl linked with the GnuTLS + library for SSL/TLS support. It is recommended that the openssl + library be used for SSL/TLS support in libcurl. @@ -999,6 +1000,61 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ SecMarker 99 +
+ <literal>SecPcreMatchLimit</literal> + + Description:Sets the the match limit in the + PCRE library. See the pcre_extra field in the pcreapi man page. + + Syntax: SecPcreMatchLimit value + + Example Usage: SecPcreMatchLimit 1500 + + Processing Phase: N/A + + Scope: Global + + Version: 2.5.12 + + Dependencies/Notes: Default is set at compile + (1500 by default) + + The --enable-pcre-match-limit=val configure + option will set a custom default and the + --disable-pcre-match-limit option will resort to the + compiled PCRE library default. +
+ +
+ <literal>SecPcreMatchLimitRecursion</literal> + + Description:Sets the the match limit + recursion in the PCRE library. See the pcre_extra field in the pcreapi + man page. + + Syntax: SecPcreMatchLimitRecursion value + + Example Usage: SecPcreMatchLimitRecursion 1500 + + Processing Phase: N/A + + Scope: Global + + Version: 2.5.12 + + Dependencies/Notes: Default is set at compile + (1500 by default) + + The --enable-pcre-match-limit-recursion=val + configure option will set a custom default and the + --disable-pcre-match-limit-recursion option will + resort to the compiled PCRE library default. +
+
<literal>SecPdfProtect</literal> Description: Enables the PDF XSS protection functionality. Once @@ -1670,6 +1726,42 @@ SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new m as the temporary directory defined with SecTmpDir. This directive is used with SecUploadKeepFiles.
+
+ <literal>SecUploadFileLimit</literal> + + Description: Configures the maximum number of + file uploads processed in a multipart POST. + + Syntax: SecUploadFileLimit number + + Example Usage: SecUploadFileLimit 10 + + Processing Phase: N/A + + Scope: Any + + Version: 2.5.12 + + Dependencies/Notes: The default is set to 100 + files, but you are encouraged to reduce this value. Any file over the + limit will not be extracted and the MULTIPART_FILE_LIMIT_EXCEEDED and MULTIPART_STRICT_ERROR flags will be set. To + prevent bypassing any file checks, you must check for one of these + flags. + + + If the limit is exceeded, the part name and file name will still + be recorded in FILES_NAME and + FILES, the file size will be + recorded in FILES_SIZES, but there + will be no record in FILES_TMPNAMES + as a temporary file was not created. + +
+
<literal>SecUploadFileMode</literal> Description: Configures the mode (permissions) of any uploaded @@ -2111,19 +2203,30 @@ SecRule ARGS "@pm some key words" id:12345,deny,status:500
<literal>MULTIPART_STRICT_ERROR</literal> - MULTIPART_STRICT_ERROR will be set to 1 when any - of the following variables is also set to 1: REQBODY_PROCESSOR_ERROR, MULTIPART_BOUNDARY_QUOTED, MULTIPART_BOUNDARY_WHITESPACE, MULTIPART_DATA_BEFORE, - MULTIPART_DATA_AFTER, MULTIPART_HEADER_FOLDING, - MULTIPART_LF_LINE, MULTIPART_SEMICOLON_MISSING - MULTIPART_INVALID_QUOTING. Each of these variables covers one unusual - (although sometimes legal) aspect of the request body in multipart/form-data - format. Your policies should always contain a rule to check - either this variable (easier) or one or more individual variables (if you know exactly what - you want to accomplish). Depending on the rate of false positives and your default policy - you should decide whether to block or just warn when the rule is triggered. - The best way to use this variable is as in the example below: + MULTIPART_STRICT_ERROR will be set to + 1 when any of the following variables is also set to + 1: REQBODY_PROCESSOR_ERROR, + MULTIPART_BOUNDARY_QUOTED, + MULTIPART_BOUNDARY_WHITESPACE, + MULTIPART_DATA_BEFORE, + MULTIPART_DATA_AFTER, + MULTIPART_HEADER_FOLDING, + MULTIPART_LF_LINE, + MULTIPART_SEMICOLON_MISSING + MULTIPART_INVALID_QUOTING + MULTIPART_INVALID_HEADER_FOLDING + MULTIPART_FILE_LIMIT_EXCEEDED. Each of these + variables covers one unusual (although sometimes legal) aspect of the + request body in multipart/form-data format. Your + policies should always contain a rule to check + either this variable (easier) or one or more individual variables (if + you know exactly what you want to accomplish). Depending on the rate of + false positives and your default policy you should decide whether to + block or just warn when the rule is triggered. + + The best way to use this variable is as in the example + below: + SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart request body \ failed strict validation: \ @@ -2135,7 +2238,9 @@ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_SEMICOLON_MISSING}, \ -IQ %{MULTIPART_INVALID_QUOTING}'" +IQ %{MULTIPART_INVALID_QUOTING}, \ +IQ %{MULTIPART_INVALID_HEADER_FOLDING}, \ +FE %{MULTIPART_FILE_LIMIT_EXCEEDED}'" The multipart/form-data parser was upgraded in ModSecurity v2.1.3 to actively look for signs of evasion. Many variables (as listed above) were added to expose various facts discovered during the parsing process. The using the @rx operator with capturing parens and the capture action. + + + TX:MSC_.* - ModSecurity + processing flags. + + + + MSC_PCRE_LIMITS_EXCEEDED - Set + non-zero if PCRE match limits are exceeded. See SecPcreMatchLimit and SecPcreMatchLimitRecursion. + + + SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" SecRule TX:SCORE "@gt 20" deny,log @@ -3256,7 +3376,7 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600430,000-699,999; unused (available for reservation). - 700,000-799,999; reserved for Ivan Ristic. + 700,000-799,999; reserved for Ivan Ristic. @@ -3731,6 +3851,9 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny "equal to." Example: SecRule &REQUEST_HEADERS_NAMES "@eq 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc.
<literal>ge</literal> @@ -3738,6 +3861,9 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny "greater than or equal to." Example: SecRule &REQUEST_HEADERS_NAMES "@ge 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc.
<literal>geoLookup</literal> @@ -3766,6 +3892,9 @@ SecRule &GEO "@eq 0" "deny,status:403,msg:'Failed to lookup IP'" Example: SecRule &REQUEST_HEADERS_NAMES "@gt 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc.
<literal>inspectFile</literal> @@ -3800,6 +3929,9 @@ end "less than or equal to." Example: SecRule &REQUEST_HEADERS_NAMES "@le 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc.
<literal>lt</literal> @@ -3807,6 +3939,9 @@ end "less than." Example: SecRule &REQUEST_HEADERS_NAMES "@lt 15" + + Macro expansion is performed so you may use variable names such as + %{TX.1}, etc.
<literal>pm</literal> @@ -3827,15 +3962,34 @@ end Notes: - The contents of the files should be one phrase per line. End of line markers will be - stripped from the phrases, however, whitespace will not be trimmed from phrases in the - file. Empty lines and comment lines (beginning with a '#') are ignored. + The contents of the files should be one phrase per line. End + of line markers will be stripped from the phrases (LF and CRLF), and + whitespace is trimmed from both sides of the phrases. Empty lines + and comment lines (beginning with a '#') are ignored. To allow easier inclusion of phrase files with rulesets, relative paths may be used to the phrase files. In this case, the path of the file containing the rule is prepended to the phrase file path. + + + To allow easier matching of whole IP addresses, you can add + boundary characters to the phrases. For example, use "/1.2.3.4/" + instead of "1.2.3.4". You can then insert these characters into the + target prior to a match: + + SecAction "phase:1,pass,nolog,setvar:tx.remote_addr=/%{REMOTE_ADDR}/" +SecRule TX:REMOTE_ADDR "@pmFromFile ip-blacklist.txt" "deny,status:403 + +# ip-blacklist.txt contents: +# NOTE: All IPs must be prefixed/suffixed with "/" as the rules +# will add in this character as a boundary to ensure +# the entire IP is matched. +# SecAction "phase:1,pass,nolog,setvar:tx.remote_addr='/%{REMOTE_ADDR}/'" +/1.2.3.4/ +/5.6.7.8/ + Example: SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "deny,status:403 diff --git a/doc/modsecurity2-data-formats.xml b/doc/modsecurity2-data-formats.xml index 464caced..32e1ddc2 100644 --- a/doc/modsecurity2-data-formats.xml +++ b/doc/modsecurity2-data-formats.xml @@ -6,7 +6,7 @@ 2.6.0-trunk (April 22, 2009) - 2004-2009 + 2004-2010 Breach Security, Inc. (http://www.breach.com) diff --git a/modsecurity.conf-minimal b/modsecurity.conf-minimal index bc78d2f3..091da85d 100644 --- a/modsecurity.conf-minimal +++ b/modsecurity.conf-minimal @@ -4,10 +4,15 @@ SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess Off +# PCRE Tuning +SecPcreMatchLimit 1000 +SecPcreMatchLimitRecursion 1000 + # Handling of file uploads # TODO Choose a folder private to Apache. # SecUploadDir /opt/apache-frontend/tmp/ SecUploadKeepFiles Off +SecUploadFileLimit 10 # Debug log SecDebugLog logs/modsec_debug.log @@ -53,8 +58,19 @@ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_SEMICOLON_MISSING}, \ -IQ %{MULTIPART_INVALID_QUOTING}'" +IQ %{MULTIPART_INVALID_QUOTING}, \ +IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ +IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'" # Did we see anything that might be a boundary? SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" + +# Some internal errors will set flags in TX and we will need to look for these. +# All of these are prefixed with "MSC_". The following flags currently exist: +# +# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. +# +SecRule TX:/^MSC_/ "!@streq 0" \ + "phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" +