diff --git a/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md b/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md new file mode 100644 index 0000000000..b39db8633f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md @@ -0,0 +1,46 @@ +--- +name: Bug report for version 2.x +about: Create a report to help us improve + +--- + +**Describe the bug** + +A clear and concise description of what the bug is. + +**Logs and dumps** + +Output of: + 1. DebugLogs (level 9) + 2. AuditLogs + 3. Error logs + 4. If there is a crash, the core dump file. + +_Notice:_ Be carefully to not leak any confidential information. + +**To Reproduce** + +Steps to reproduce the behavior: + +A **curl** command line that mimics the original request and reproduces the problem. Or a ModSecurity v3 test case. + +[e.g: curl "modsec-full/ca/..\\..\\..\\..\\..\\..\\/\\etc/\\passwd" or [issue-394.json](https://github.com/SpiderLabs/ModSecurity/blob/v3/master/test/test-cases/regression/issue-394.json)] + + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Server (please complete the following information):** + - ModSecurity version (and connector): [e.g. ModSecurity v3.0.1 with nginx-connector v1.0.0] + - WebServer: [e.g. nginx-1.15.5] + - OS (and distro): [e.g. Linux, archlinux] + + +**Rule Set (please complete the following information):** + - Running any public or commercial rule set? [e.g. SpiderLabs commercial rules] + - What is the version number? [e.g. 2018-08-11] + +**Additional context** + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md b/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md new file mode 100644 index 0000000000..58874dfb42 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md @@ -0,0 +1,47 @@ +--- +name: Bug report for version 3.x +about: Create a report to help us improve. If you don't know a specific detail or + piece of information leave it blank, if necessary we will help you to figure out. + +--- + +**Describe the bug** + +A clear and concise description of what the bug is. + +**Logs and dumps** + +Output of: + 1. DebugLogs (level 9) + 2. AuditLogs + 3. Error logs + 4. If there is a crash, the core dump file. + +_Notice:_ Be carefully to not leak any confidential information. + +**To Reproduce** + +Steps to reproduce the behavior: + +A **curl** command line that mimics the original request and reproduces the problem. Or a ModSecurity v3 test case. + +[e.g: curl "modsec-full/ca/..\\..\\..\\..\\..\\..\\/\\etc/\\passwd" or [issue-394.json](https://github.com/SpiderLabs/ModSecurity/blob/v3/master/test/test-cases/regression/issue-394.json)] + + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Server (please complete the following information):** + - ModSecurity version (and connector): [e.g. ModSecurity v3.0.1 with nginx-connector v1.0.0] + - WebServer: [e.g. nginx-1.15.5] + - OS (and distro): [e.g. Linux, archlinux] + + +**Rule Set (please complete the following information):** + - Running any public or commercial rule set? [e.g. SpiderLabs commercial rules] + - What is the version number? [e.g. 2018-08-11] + +**Additional context** + +Add any other context about the problem here. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..390c0e6066 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +*.o +*.lo +*.la +**/Makefile +**/Makefile.in +aclocal.m4 +ar-lib +autom4te.cache/ +build/libtool.m4 +build/ltoptions.m4 +build/ltsugar.m4 +build/ltversion.m4 +build/lt~obsolete.m4 +compile +config.guess +config.log +config.status +config.sub +configure +depcomp +.deps +.libs +.dirstamp +src/config.h +src/config.h.in +src/location.hh +src/position.hh +src/stack.hh +src/stamp-h1 +/test/rules_optimization +/test/regression_tests +/test/unit_tests +/test-driver +/test/massif.out.* +/test/benchmark/benchmark +/test/benchmark/owasp-v3/ +/test/test-cases/regression/*.trs +/test/test-cases/regression/*.log +/test-suite.log +ylwrap +missing +install-sh +libtool +ltmain.sh +examples/simple_example_using_c/test +/tools/rules-check/modsec-rules-check +examples/multiprocess_c/multi +examples/reading_logs_via_rule_message/simple_request +examples/reading_logs_with_offset/read +examples/using_bodies_in_chunks/simple_request diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..acee72d162 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "test/test-cases/secrules-language-tests"] + path = test/test-cases/secrules-language-tests + url = https://github.com/SpiderLabs/secrules-language-tests +[submodule "others/libinjection"] + path = others/libinjection + url = https://github.com/client9/libinjection.git +[submodule "bindings/python"] + path = bindings/python + url = https://github.com/SpiderLabs/ModSecurity-Python-bindings.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..4b6fde61d6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,63 @@ +dist: bionic +sudo: true + +addons: + apt: + packages: + - libfuzzy-dev + - libyajl-dev + - libgeoip-dev + - liblua5.2-dev + - liblmdb-dev + +language: cpp + +compiler: + - clang + - gcc + +os: + - linux + - osx + +env: + - OPTS="--enable-parser-generation $OPTS" + - OPTS="--without-curl $OPTS" + - OPTS="--without-yajl $OPTS" + - OPTS="--without-geoip $OPTS" + - OPTS="--with-lmdb $OPTS" + - OPTS="--without-ssdeep $OPTS" + - OPTS="--without-lua $OPTS" + - OPTS="--without-maxmind $OPTS" + +before_script: + - echo $TRAVIS_OS_NAME + - '[ "$TRAVIS_OS_NAME" != osx ] || brew update' + - '[ "$TRAVIS_OS_NAME" != osx ] || brew install cppcheck' + - '[ "$TRAVIS_OS_NAME" != osx ] || brew install libmaxminddb' + - '[ "$TRAVIS_OS_NAME" != osx ] || brew install lmdb' + - '[ "$TRAVIS_OS_NAME" != linux ] || sudo add-apt-repository --yes ppa:maxmind/ppa' + - '[ "$TRAVIS_OS_NAME" != linux ] || sudo add-apt-repository --yes ppa:grochefort/cppcheck' + - '[ "$TRAVIS_OS_NAME" != linux ] || sudo apt-get update' + - '[ "$TRAVIS_OS_NAME" != linux ] || sudo apt-cache search maxmind' + - '[ "$TRAVIS_OS_NAME" != linux ] || sudo apt-get install -y libmaxminddb-dev' + - '[ "$TRAVIS_OS_NAME" != linux ] || sudo apt-get install -y cppcheck' + - '[ "$TRAVIS_OS_NAME" != linux ] || cppcheck --version' + +script: + - ./build.sh + - ./configure $OPTS + - make parser + - make parser + - make -j$(getconf _NPROCESSORS_ONLN) || make -j$(getconf _NPROCESSORS_ONLN) || make +# +# Temporarily disabled. +# - make -j$(getconf _NPROCESSORS_ONLN) +# Leading build to crash with parser enabled. +# Not sure why. +# + - make check -j$(getconf _NPROCESSORS_ONLN) + - make check-static + + + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000..47c636965c --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +zimmerle = Felipe Zimmerle +rbarnett = Ryan C. Barnett +csanders-git = Chaim Sanders +victorhora = Victor Hora diff --git a/CHANGES b/CHANGES index 2fd265d4b2..e75b38422d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,1377 +1,463 @@ -23 Jul 2013 - 2.7.5 -------------------- -Improvements: - - * SecUnicodeCodePage is deprecated. SecUnicodeMapFile now accepts the code page as a second parameter. - - * Updated Libinjection to version 3.4.1. Many improvements were made. - - * Severity action now supports strings (emergency, alert, critical, error, warning, notice, info, debug). - -Bug Fixes: - - * Fixed utf8toUnicode tfn null byte conversion. - - * Fixed NGINX crash when issue reload command. - - * Fixed flush output buffer before inject modified hashed response body. - - * Fixed url normalization for Hash Engine. - - * Fixed NGINX ap_unixd_set_global_perms_mutex compilation error with apache 2.4 devel files. - -Security Issues: - -10 May 2013 - 2.7.4 -------------------- -Improvements: - - * Added Libinjection project http://www.client9.com/projects/libinjection/ as a new operator @detectSQLi. (Thanks Nick Galbreath). - - * Added new variable SDBM_DELETE_ERROR that will be set to 1 when sdbm engine fails to delete entries. - - * NGINX is now set to STABLE. Thanks chaizhenhua and all the people in community who help the project testing, sending feedback and patches. - -Bug Fixes: - - * Fixed SecRulePerfTime storing unnecessary rules performance times. - - * Fixed Possible SDBM deadlock condition. - - * Fixed Possible @rsub memory leak. - - * Fixed REMOTE_ADDR content will receive the client ip address when mod_remoteip.c is present. - - * Fixed NGINX Audit engine in Concurrent mode was overwriting existing alert files because a issue with UNIQUE_ID. - - * Fixed CPU 100% issue in NGINX port. This is also related to an memory leak when loading response body. - -Security Issues: - - * Fixed Remote Null Pointer DeReference (CVE-2013-2765). When forceRequestBodyVariable action is triggered and a unknown Content-Type is used, - mod_security will crash trying to manipulate msr->msc_reqbody_chunks->elts however msr->msc_reqbody_chunks is NULL. (Thanks Younes JAAIDI). - -28 Mar 2013 - 2.7.3 -------------------- - - * Fixed IIS version race condition when module is initialized. - - * Fixed IIS version failing config commands in libapr. - - * Nginx version is now RC quality. The rule engine should works for all phases. - We fixed many issues and missing features (for more information please check jira). - Code is running well with latest Nginx 1.2.7 stable. - Thanks chaizhenhua for your help. - - * Added MULTIPART_NAME and MULTIPART_FILENAME. Should be used soon by CRS - and will help prevent attacks using multipart data. - - * Added --enable-htaccess-config configure option. It will allow the follow directives - to be used into .htaccess files when AllowOverride Options is set: - - - SecAction - - SecRule - - - SecRuleRemoveByMsg - - SecRuleRemoveByTag - - SecRuleRemoveById - - - SecRuleUpdateActionById - - SecRuleUpdateTargetById - - SecRuleUpdateTargetByTag - - SecRuleUpdateTargetByMsg - - * Improvements in the ID duplicate code checking. Should be faster now. - - * SECURITY: Added SecXmlExternalEntity (On|Off - default it Off) that will disable - by default the external entity load task executed by LibXml2. This is a security issue - [CVE-2013-1915] reported by Timur Yunusov, Alexey Osipov (Positive Technologies). - -21 Jan 2013 - 2.7.2 -------------------- - - * IIS version is now stable. - - * Fixed IIS version does not pass through POST data to ASP.NET when SecRequestBodyAccess - is set to On (MODSEC-372). - - * Fixed IIS version HTTP Request Smuggling protection does not work (MODSEC-344). - - * Fixed IIS version PHP Injection Attack (958976) protection does not work (MODSEC-346). - - * Fixed IIS version Request limit protections are not working (MODSEC-349). - - * Fixed IIS version Outbound protections are not working (MODSEC-350). - - * Added IIS version better installer. - - * NGINX version removed ModSecurityPassCommand (Thanks chaizhenhua). - - * Fixed NGINX version ngx_http_read_client_request_body returned unexpected buffer type (Thanks chaizhenhua). - - * Fixed NGINX version INCS config directories on fedora (Thanks chaizhenhua). - - * Added NGINX version Added drop action for nginx (Thanks chaizhenhua). - - * Fixed bug in cpf_verify operator (Thanks Hideaki Hayashi). - - * Fixed build modsecurity under Arch Linux. - - * Fixed make test crashing when JIT pcre is enabled. - - * Fixed better cookie separator detection code. - - * Fixed mod_security displaying wrong ip address in error.log using apache 2.4 and mod_remoteip. - - * Fixed mod_security was not compiling when use apr without ipv6 support. - - * Fixed mod_security was not compiling when use lua 5.2. - - * Fixed issue when execute make install under Solaris. - - * Fixed ipmatchf operator was not working as expected. - -01 Nov 2012 - 2.7.1 -------------------- - - * Changed "Encryption" name of directives and options related to hmac feature to "Hash". - - SecEncryptionEngine to SecHashEngine - SecEncryptionKey to SecHashKey - SecEncryptionParam to SecHashParam - SecEncryptionMethodRx to SecHashMethodRx - SecEncryptionMethodPm to SecHashMethodPm - @validateEncryption to @validateHash - ctl:EncryptionEnforcement to ctl:HashEnforcement - ctl:EncryptionEngine to ctl:HashEngine - - * Added a better random bytes generator using apr_generate_random_bytes() to create - the HMAC key. - - * Fixed byte conversion issue during logging under Linux s390x platform. - - * Fixed compilation bug with LibXML2 2.9.0 (Thanks Athmane Madjoudj). - - * Fixed parsing error with modsecurity-recommended.conf and Apache 2.4. - - * Fixed DROP action was disabled for Apache 2 module by mistake. - - * Fixed bug when use ctl:ruleRemoveTargetByTag. - - * Fixed IIS and NGINX modules bugs. - - * Fixed bug when @strmatch patterns use invalid escape sequence (Thanks Hideaki Hayashi). - - * Fixed bugs in @verifySSN (Thanks Hideaki Hayashi). - - * The doc/ directory now contains the instructions to access online documentation. - -15 Oct 2012 - 2.7.0 -------------------- - - * Fixed Pause action should work as a disruptive action (MODSEC-297). - - * Fixed Problem loading mod_env variables in phase 2 (MODSEC-226). - - * Fixed Detect cookie v0 separator and use it for parsing (MODSEC-261). - - * Fixed Variable REMOTE_ADDR with wrong IP address in NGINX version (MODSEC-337). - - * Fixed Errors compiling NGINX version. - - * Added Include directive into standalone module. IIS and NGINX module should - support Include directive like Apache2. - - * Added MULTIPART_INVALID_PART flag. Also used in rule id 200002 for multipart strict - validation. https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20121017-0_mod_security_ruleset_bypass.txt). - - * Updated Reference Manual. - -25 Sep 2012 - 2.6.8 -------------------- - - * Fixed ctl:ruleRemoveTargetByID order issue (MODSEC-333). Thanks to Armadillo Dasypodidae. - - * Fixed variable HIGHEST_SEVERITY incorrectly gets reset in a chain rule (MODSEC-315). Thanks to Valery Reznic. - -10 Sep 2012 - 2.7.0-rc3 -------------------- - - * Fixed requests bigger than SecRequestBodyNoFilesLimit were truncated even engine mode was detection only. - - * Fixed double close() for multipart temporary files (Thanks Seema Deepak). - - * Fixed many small issues reported by Coverity Scanner (Thanks Peter Vrabek). - - * Fixed format string issue in ngnix experimental code. (Thanks Eldar Zaitov). - - * Added ctl:ruleRemoveTargetByTag/Msg and removed ctl:ruleUpdateTargetByTag/Msg. - - * Added IIS and Ngnix platform code. - - * Added new transformation utf8toUnicode. - -23 Jul 2012 - 2.6.7 -------------------- - - * Fixed explicit target replacement using SecUpdateTargetById was broken. - - * The ctl:ruleUpdateTargetById is deprecated and will be removed for future versions since - there is no safe way to use it per-request. - - * Added ctl:ruleRemoveTargetById that can be used to exclude targets to be processed per-request. - -22 Jun 2012 - 2.7.0-rc2 -------------------- - - * Fixed compilation errors and warnings under Windows platform. - - * Fixed SecEncryptionKey was not working as expected. - -08 Jun 2012 - 2.7.0-rc1 -------------------- - - * Added SecEncryptionEngine. Initial crypt engine support, at the momment it will sign some Html - and Response Header options. - - * Added SecEncryptionKey to define the a rand or static key for crypt engine. - - * Added SecEncryptionParam to define the new parameter name. - - * Added SecEncryptionMethodRx used with a regular expression to inspect the html in response - body/header and decide what to protect. - - * Added SecEncryptionMethodPm used with multiple or single strings to inspect the html in response - body/header and decide what to protect. - - * Added ctl encryptionEngine as a per transaction version of SecEncryptionEgine diretive. - - * Added ctl encryptionEnforcement that will allow the engine to sign the data but the enforcement is - disabled. - - * Added validateEncryption operator to enforce the signed elements. - - * Added rsub operator supports the syntax |hex| allowing users to use special chars like \n \r. - - * Added SecRuleUpdateTargetById now supports id range. - - * Added SecRuleUpdateTargetByMsg and its ctl version (Thanks Scott Gifford). - - * Added SecRuleUpdateTargetByTag and its ctl version (Thanks Scott Gifford). - - * Added SecRulePerfTime when greater than zero it will fill rule id's execution time into PERF_RULE - and log id=usec information in the new Perf-rule-info: line in part H. - - * Added PERF_RULES variable that contains rule execution time. - - * Added Engine-mode: section in part H. - - * Added ruleRemoveByMsg ctl version. - - * Added removeCommentsChar and removeComments now can work with style. - - * Added SecArgumentSeparator and SecCookieFormat can be used in different scope locations. - - * Added Rules must have ID action and must be numeric. - - * Added The use of tfns are deprecated in SecDefaultAction. Should be forbid in the future. - - * Added Macro expansion support to the action pause. - - * Added IpmatchFromFile/IpmatchF operator. - - * Added New setrsc action, the RESOURCE collection used SecWebAppId Name Space - - * Added Configure option --enable-cache-lua that allows reuse of Lua VM per transaction. - It will only take any effect when ModSecurity has multiple scripts to run per transaction. - - * Added Configure option --enable-pcre-jit that allows ModSecurity regex engine to use PCRE Jit support. - - * Added Configure option --enable-request-early that allows ModSecurity run phase 1 in post_read_request hook. - - * Added RBL operator now support the httpBl api (http://www.projecthoneypot.org/httpbl_api.php). - - * Added SecHttpBlKey to be used with httpBl api. - - * Added SecSensorId will specify the modsecurity sensor name into audit log part H. - - * Added aliases to phase:2 (phase:request), phase:4 (phase:response) and phase:5 (phase:logging). - - * Added USERAGENT_IP variable. Created when Apache24 is used with mod_remoteip to know the real - client ip address. - - ^ Added new rule metadata actions ver, maturity and accuracy. Also included into RULE collection. - - * Updated Reference manual into doc/ directory. - - * Fixed Variable DURATION contains the elapsed time in microseconds for compatible reasons with apache and - other variables. - - * Fixed Preserve names/identity of the variables going into MATCHED_VARS. - - * Fixed Redirect macro expansion does not work in SecDefaultAction when SecRule uses block action. - - * Fixed rsub operator does not work as expect if regex contains parentheses (Thanks Jerome Freilinger). - - * Current Google Safe Browsing implementation is deprecated. Google changed the API and does not allow - anymore the malware database for download. - -08 Jun 2012 - 2.6.6 -------------------- - - * Added build system support for KfreeBSD and HURD. - - * Fixed a multipart bypass issue related to quote parsing - Credits to Qualys Vulnerability & Malware Research Labs (VMRL). - -20 Mar 2012 - 2.6.5 -------------------- - - * Fixed increased a specific message debug level in SBDM code (MODSEC-293). - - * Cleanup build system. - -09 Mar 2012 - 2.6.4 -------------------- - - * Fixed Mlogc 100% CPU consume (Thanks Klaubert Herr and Ebrahim Khalilzadeh). - - * Fixed ModSecurity cannot load session and user sdbm data. - - * Fixed updateTargetById was creating rule unparsed content making apache memory grow. - - * Code cleanup. - -23 Feb 2012 - 2.6.4-rc1 -------------------- - - * Fixed @rsub adding garbage data into stream variables. - - * Fixed regex for section A into mlogc-batch-load.pl (Thanks Ebrahim Khalilzadeh). - - * Fixed logdata cuts message without closing it with final chars. - - * Added sanitizeMatchedBytes support to verifyCPF, verifyCC and verifySSN. - - -06 Dec 2011 - 2.6.3-rc1 -------------------- - -* Fixed MATCHED_VARS does not correctly handle multiple VARS with the same name. - -* Fixed SDBM garbage collection was not working as expected, increasing the size of files. - -* Fixed wrong timestamp calculation for some time zones in log files. - -* Fixed SecUpdateTargetById failed to load multiple VARS (MODSEC-270). - -* Fixed Reverted hexDecode for hexEncode compatibility reason. - -* Added SecCollectionTimeout to set collection timeout, default is 3600. - -* Added sqlHexDecode transformation to decode sql hex data. Thanks Marc Stern. - -30 Sep 2011 - 2.6.2 -------------------- - - * Fixed hexDecode test during make. - - * Updated the reference manual into doc/ directory. - -5 Sep 2011 - 2.6.2-rc1 -------------------- - - * Added support to macro expansion for rx operator. - - * Added new transformations removeComments and removeCommentsChars - - * Fixed colletion names are not case-sensitive anymore. - - * Fixed compilation errors with apache 2.0. - - * Fixed build system was not using some libraries CFLAGS. - - * Fixed check for valid hex values into hexDecode transformation. - - * Fixed ctl:ruleUpdateTargetById appending multiple targets. - -18 Jun 2011 - 2.6.1 -------------------- - - * Updated the reference manual into doc/ directory. - -11 Jul 2011 - trunk -------------------- - - * Add HttpBl support to rbl operator. - -30 Jun 2011 - 2.6.1-rc1 -------------------- - - * Fixed SecUploadFileMode doesn't work with the new build system. - - * Fixed building with Lua library (Thanks Diego Elio). - - * Fixed some ./configure --enable* features not being enabled in compilation time. - - * Improvements on GSB database add/search operations. - - * Log part K was removed from modsecurity.conf-recommended. - - * Added SecUnicodeMapFile directive. Must be use to load the unicode.mapping file. - - * Added SecUnicodeCodePage directive. Used to define the unicode code page. There are a few already available: - - 1250 (ANSI - Central Europe) - 1251 (ANSI - Cyrillic) - 1252 (ANSI - Latin I) - 1253 (ANSI - Greek) - 1254 (ANSI - Turkish) - 1255 (ANSI - Hebrew) - 1256 (ANSI - Arabic) - 1257 (ANSI - Baltic) - 1258 (ANSI/OEM - Viet Nam) - 20127 (US-ASCII) - 20261 (T.61) - 20866 (Russian - KOI8) - 28591 (ISO 8859-1 Latin I) - 28592 (ISO 8859-2 Central Europe) - 28605 (ISO 8859-15 Latin 9) - 37 (IBM EBCDIC - U.S./Canada) - 437 (OEM - United States) - 500 (IBM EBCDIC - International) - 850 (OEM - Multilingual Latin I) - 860 (OEM - Portuguese) - 861 (OEM - Icelandic) - 863 (OEM - Canadian French) - 865 (OEM - Nordic) - 874 (ANSI/OEM - Thai) - 932 (ANSI/OEM - Japanese Shift-JIS) - 936 (ANSI/OEM - Simplified Chinese GBK) - 949 (ANSI/OEM - Korean) - 950 (ANSI/OEM - Traditional Chinese Big5) - - Also mapping some extra unicode chars defined at http://tools.ietf.org/html/rfc3490#section-3.1 - - * Fixed SecRequestBodyLimit was truncating the real request body. - -18 May 2011 - 2.6.0 -------------------- - - * Added SecWriteStateLimit for Slow Post DoS mitigation. - - * Fix problem when buffering in input filter. - - * Fix memory leak when use MATCHED_VAR_NAMES. - - -2 May 2011 - 2.6.0-rc2 -------------------- - - * Added code optimizations - thanks Diego Elio. - - * Added support to AIX and HPUX in the build system (untested). - - * Renamed decodeBase64Ext to base64DecodeExt. - - * Build system improvements - thanks Diego Elio. - - * Improvements on gsblookup parser. - - * Fixed input filter bug when upload files and SecStreamInBodyInspect is enabled. - - * Logging improvements and bug fix. - - * Remove extra useless files when make clean and maintainer-clean - -18 Apr 2011 - 2.6.0-rc1 -------------------- - - * Replaced previous GPLv2 License to Apachev2. - - * Added Google Safe Browsing lookups operator and directive. It should be - used to extract and lookup urls from http packets. - - * Added Data Modification operator. It must be used with STREAM_* variables - to replace/add/edit any data from http bodies. - - * Added STREAM_OUPUT_BODY and STREAM_INPUT_BODY variables to work with data - modification operators. - - * Added fast ip address operator. It supports partial ip address, cidr for - IPv4 and IPv6. Thanks Tom Donovan. - - * Added new sensitive data tracking verifyCPF and verifySSN. - - * Added MATCHED_VARS and MATCHED_VARS_NAMES. It is similiar to MATCHED_VAR, - but now we should see all matched variables. - - * Added UNIQUE_ID variable. It holds the data created my mod_unique_id. - - * Added new tranformation cmdline. Thanks Marc Stern. - - * Added new exception handling operators and directives. It should help users - reduce FN and FPs. The directives SecRuleUpdateTargetById, SecRuleRemoveByTag - and its ctl actions were included. - - * Added SecStreamOutBodyInspection and SecStreamInBodyInspection to enable STREAM_* - variables. - - * Added SecGsbLookupDB used to load Google Safe Browsing malware databse into - memory. - - * Added the directive SecInterceptOnError to control what to do if a rule returns - values less than zero. - - * Improvements in DetectionOnly engine mode. Also added SecRequestBodyLimitAction - to control what to do if the engine receive a http request over a hard limit. - Note that there is now many combinations with SecRuleEngine and the limit action - directives for response and request data. Please see the reference manual. - - * Improvements under RBL operator. It now will parse return code values for some - RBL lists. - - * Added new Log Part J. It should log some informations about uploaded files. - - * Added new sanitizeMatchedBytes action. It will give more flexibilty for user to sanitize - logged data, also improving peformance when sanitize big amount of data. - - * Improvements on Logging phase. It is possible now see full chains, distinguish between - simple rules, chain starters and chain nodes. - - * Improvements on AutoTools usage. - - * Improvements on pattern matching operators, pmf, pm and strmatch now supports more flexible - input data allowing any kind of special char. - - * Improvements on SecRuleUpdateActionById to update chain nodes. - - * Many bugs were fixed. Please see the ModSecurity Jira for more details - - -19 Mar 2010 - trunk -------------------- - - * Added SecDisableBackendCompression, which disabled backend compression - while keeping the frontend compression enabled (assuming mod_deflate - in installed and configured in the proxy). [Ivan Ristic] - - * Added REQUEST_BODY_LENGTH, which contains the number of request body - bytes read. [Ivan Ristic] - - * Integrate with mod_log_config using the %{VARNAME}M format string. - (MODSEC-108) [Ivan Ristic] - - * Replaced the previous time-measuring mechanism with a new one, which - provides the following information: request time, request duration, - phase duration (for all 5 phases), time spent dealing with persistent - storage, and time spent on audit logging. The new information is now - available in the Stopwatch2 audit log header. The Stopwatch header - remains for backward compatiblity, although it now only includes - the request time and request duration values. Added the following - variables: PERF_COMBINED, PERF_PHASE1, PERF_PHASE2, PERF_PHASE3, - PERF_PHASE4, PERF_PHASE5, PERF_SREAD, PERF_SWRITE, PERF_LOGGING, - PERF_GC. [Ivan Ristic] - - * Added DURATION, which contains the time ellapsed since the beginning - of the current transaction, in milliseconds. [Ivan Ristic] - - * Adjusted phase 5 to execute just prior to mod_log_config. This should - allow phase 5 rules to to implement conditional logging, as well as - pave support for allowing access to all ModSecurity variables from - mog_log_config. [Ivan Ristic] - - * Added the URLENCODED_ERROR flag, which is raised whenever invalid URL - encoding is encountered in the query string or in the request body - (but only if URLENCODED request body processor is used). (MODSEC-111) - [Ivan Ristic] - - * Removed the obsolete PDF UXSS functionality. (MODSEC-96) [Ivan Ristic] - - * Renamed normalisePath to normalizePath and normalisePathWin to - normalizePathWin. Kept the previous names for backward compatibility. - (MODSEC-103) [Ivan Ristic] - - * Moved phase 1 to be run in the same Apache hook as phase 2. This means - that you can now have phase 1 rules in tags and, more - importantly, override server configuration in and others. - (MODSEC-98) [Ivan Ristic] - - * Renamed the sanitise family of actions to sanitize. Kept the old variants - for backward compatibility. (MODSEC-95) [Ivan Ristic] - - * Improve the logging of the ctl action. (MODSEC-99) [Ivan Ristic] - - * Cleanup build files that were from the Apache source. - - -14 Feb 2010 - 2.5.13-dev1 -------------------------- - - * Cleaned up some mlogc code and debugging output. - - * Remove the ability to use a relative path to a piped audit logger - (i.e. mlogc) as Apache does not support it in their piped loggers - and it was breaking Windows and probably other platforms that - use spaces in filesystem paths. Discovered by Tom Donovan. - - * Fix memory leak freeing regex. Discovered by Tom Donovan. - - * Fix some portability issues on Windows. - - -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 - (MODSEC-110). [Ivan Ristic] - - * Do not escape quotes in macro resolution and only escape NUL in setenv - values. - - -04 Nov 2009 - 2.5.11 --------------------- - - * Added a new multipart flag, MULTIPART_INVALID_QUOTING, which will be - set true if any invalid quoting is found during multipart parsing. - - * Fixed parsing quoted strings in multipart Content-Disposition headers. - Discovered by Stefan Esser. - - * Cleanup persistence database locking code. - - * Added warning during configure if libcurl is found linked against - gnutls for SSL. The openssl lib is recommended as gnutls has - proven to cause issues with mutexes and may crash. - - * Cleanup some mlogc (over)logging. - - * Do not log output filter errors in the error log. - - * Moved output filter to run before other stock filters (mod_deflate, - mod_cache, mod_expires, mod_filter) to avoid analyzing modified data - in the response. Patch originally submitted by Ivan Ristic. - - -18 Sep 2009 - 2.5.10 --------------------- - - * Cleanup mlogc so that it builds on Windows. - - * Added more detailed messages to replace "Unknown error" in filters. - - * Added SecAuditLogDirMode and SecAuditLogFileMode to allow fine tuning - auditlog permissions (especially with mpm-itk). - - * Cleanup SecUploadFileMode implementation. - - * Cleanup build scripts. - - * Fixed crash on configuration if SecMarker is used before any rules. - - * Fixed SecRuleUpdateActionById so that it will work on chain starters. - - * Cleanup build system for mlogc. - - * Allow mlogc to periodically flush memory pools. - - * Using nolog,auditlog will now log the "Message:" line to the auditlog, but - nothing to the error log. Prior versions dropped the "Message:" line from - both logs. To do this now, just use "nolog" or "nolog,noauditlog". - - * Forced mlogc to use SSLv3 to avoid some potential auto negotiation - issues with some libcurl versions. - - * Fixed mlogc issue seen on big endian machines where content type - could be listed as zero. - - * Removed extra newline from audit log message line when logging XML errors. - This was causing problems parsing audit logs. - - * Fixed @pm/@pmFromFile case insensitivity. - - * Truncate long parameters in log message for "Match of ... against ... - required" messages. - - * Correctly resolve chained rule actions in logs. - - * Cleanup some code for portability. - - * AIX does not support hidden visibility with xlc compiler. - - * Allow specifying EXTRA_CFLAGS during configure to override gcc specific - values for non-gcc compilers. - - * Populate GEO:COUNTRY_NAME and GEO:COUNTRY_CONTINENT as documented. - - * Handle a newer geo database more gracefully, avoiding a potential crash for - new countries that ModSecurity is not yet aware. - - * Allow checking &GEO "@eq 0" for a failed @geoLookup. - - * Fixed mlogc global mutex locking issue and added more debugging output. - - * Cleaned up build dependencies and configure options. - - -05 Mar 2009 - 2.5.9 -------------------- - - * Fixed parsing multipart content with a missing part header name which - would crash Apache. Discovered by "Internet Security Auditors" - (isecauditors.com). - - * Added ability to specify the config script directly using --with-apr - and --with-apu. - - * Updated copyright year to 2009. - - * Added macro expansion for append/prepend action. - - * Fixed race condition in concurrent updates of persistent counters. Updates - are now atomic. - - * Cleaned up build, adding an option for verbose configure output and making - the mlogc build more portable. - - -21 Nov 2008 - 2.5.8 -------------------- - - * Fixed PDF XSS issue where a non-GET request for a PDF file would crash the - Apache httpd process. Discovered by Steve Grubb at Red Hat. - - * Removed an invalid "Internal error: Issuing "%s" for unspecified error." - message that was logged when denying with nolog/noauditlog set and - causing the request to be audited. - - -24 Sep 2008 - 2.5.7 -------------------- - - * Fixed XML DTD/Schema validation which will now fail after request body - processing errors, even if the XML parser returns a document tree. - - * Added ctl:forceRequestBodyVariable=on|off which, when enabled, will force - the REQUEST_BODY variable to be set when a request body processor is not set. - Previously the REQUEST_BODY target was only populated by the URLENCODED - request body processor. - - * Integrated mlogc source. - - * Fixed logging the hostname in the error_log which was logging the - request hostname instead of the Apache resolved hostname. - - * Allow for disabling request body limit checks in phase:1. - - * Added transformations for processing parity for legacy protocols ported - to HTTP(S): t:parityEven7bit, t:parityOdd7bit, t:parityZero7bit - - * Added t:cssDecode transformation to decode CSS escapes. - - * Now log XML parsing/validation warnings and errors to be in the debug log - at levels 3 and 4, respectivly. - - -31 Jul 2008 - 2.5.6 -------------------- - - * Transformation caching has been deprecated, and is now off by default. We - now advise against using transformation caching in production. - - * Fixed two separate transformation caching issues that could cause incorrect - content inspection in some circumstances. - - * Fixed an issue with the transformation cache using too much RAM, potentially - crashing Apache with a large number of cache entries. Two new configuration - options have been added to allow for a finer control of caching: - - maxitems: Max number of items to cache (default 1024) - incremental: Whether to cache incrementally (default off) - - * Added an experimental regression testing suite. The regression suite may - be executed via "make test-regression", however it is strongly advised - to only be executed on a non-production machine as it will startup the - Apache web server that ModSecurity is compiled against with various - configurations in which it will run tests. - - * Added a licensing exception so that ModSecurity can be used in a derivative - work when that derivative is also under an approved open source license. - - * Updated mlogc to version 1.4.5 which adds a LockFile directive and fixes an - issue in which the configuration file may be deleted. - - -05 Jun 2008 - 2.5.5 -------------------- - - * Fixed an issue where an alert was not logged in the error log - unless "auditlog" was used. - - * Enable the "auditlog" action by default to help prevent a misconfiguration. - The new default is now: "phase:2,log,auditlog,pass" - - * Improve request body processing error messages. - - * Handle lack of a new line after the final boundary in a multipart request. - This fixes the reported WordPress Flash file uploader problem. - - * Fixed issue with multithreaded servers where concurrent XML processing - could crash the web server (at least under Windows). - - * Fixed blocking in phase 3. - - * Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before - ModSecurity so that the correct IP is used. - - -07 May 2008 - 2.5.4 -------------------- - - * Fixed issue where transformation cache was using the SecDefaultAction - value even when t:none was used within a rule. - - -24 Apr 2008 - 2.5.3 -------------------- - - * Fixed issue where the exec action may not be able to execute shell scripts. - - * Macros are now expanded in expirevar and deprecatevar. - - * Fixed crash if a persistent variable name was more than 126 characters. - - * Updated included Core Ruleset to version 1.6.1 which fixes some - false negative issues in the migration to using some 2.5 features. - - -02 Apr 2008 - 2.5.2 -------------------- - - * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. - - * Make sure temporary filehandles are closed after a transaction. - - * Make sure the apache include directory is included during build. - - -02 Apr 2008 - 2.1.7 -------------------- - - * Make sure temporary filehandles are closed after a transaction. - - -14 Mar 2008 - 2.5.1 -------------------- - - * Fixed an issue where a match would not occur if transformation caching - was enabled. - - * Using "severity" in a default action is now just a warning. - - * Cleaned up the "make test" target to better locate headers/libraries. - - * Now search /usr/lib64 and /usr/lib32 for lua libs. - - * No longer treat warnings as errors by default (use --enable-strict-compile). - - -19 Feb 2008 - 2.5.0 -------------------- - - * Updated included Core Ruleset to version 1.6.0 which uses 2.5 features. - - * Cleaned up and clarified some documentation. - - * Updated code to be more portable so it builds with MS VC++. - - * Added unit tests for most operators and transformations. - - * Fixed crash on startup when ENV is improperly used without a parameter. - - * Allow macro resolution in setenv action. - - * The default action is now a minimal "phase:2,log,pass" with no default - transformations performed. - - * Implemented SecUploadFileMode to allow setting the mode for uploaded files. - - * Implemented "block" action. - - * Implemented SecRuleUpdateActionById. - - * Fixed removal of phase 5 rules via SecRuleRemoveBy* directives. - - * No longer log the query portion of the URI in the error log as - it may contain sensitive data. - - * Build is now 'configure' based: ./configure && make && make install - - * Added support for Lua scripting in the following ways: SecRuleScript - can be used to specify a script to execute as a rule, the exec - action processes Lua scripts internally, as does the @inspectFile - operator. Refer to the documentation for more details. - - * Changed how allow works. Used on its own it now allows phases 1-4. Used - with parameter "phase" (e.g. SecAction allow:phase) it only affects - the current phase. Used with parameter "request" it allows phases - 1-2. - - * Fixed issue where only the first phase 5 rule would run when the - request was intercepted in an earlier phase. - - * Stricter configuration parsing. Disruptive actions, meta actions and - phases are no longer allowed in a chained rule. Disruptive actions, - are no longer allowed in a logging phase (phase 5) rule, including - inheriting from SecDefaultAction. - - * More efficient collection persistance. - - * Fixed t:escapeSeqDecode to better follow ANSI C escapes. - - * Added t:jsDecode to decode JavScript escape sequences. - - * Added IS_NEW built-in collection variables. - - * New audit log part 'K' logs all matching rules. - - * Implemented SecRequestBodyNoFilesLimit. - - * Enhance handling of the case where we run out of disk space while - writing to audit log entry. - - * Added SecComponentSignature to allow other components the ability - to append to the logged signature. - - * Added skipAfter: action to allow skipping all rules until a rule - with a specified ID is reached. Rule execution then continues after - the specified rule. - - * Added SecMarker directive to allow a fixed target for skipAfter. - - * Added ctl:ruleRemoveById action to allow rule removal on a match. - - * Added a @containsWord operator that will match a given string anywhere in - the target value, but only on word boundaries. - - * Added a MATCHED_VAR_NAME variable to store the last matched variable name - so that it can be more easily used by rules. - - * Added a MATCHED_VAR variable to store the last matched variable value - so that it can be more easily used by rules. - - * Fixed expansion of macros when using relative changes with setvar. In - addition, added support for expanding macros in the variable name. - - * Situations where ModSecurity will intercept, generate an error or log - a level 1-3 message to the debug log are now marked as 'relevant' and may - generate an audit log entry. - - * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds - as documented instead of decrementing by a rate. - - * Enable ModSecurity to look at partial response bodies. In previous - versions, ModSecurity would respond with status code 500 when the - response body was too long. Now, if SecResponseBodyLimitAction is - set to "ProcessPartial", it will process the part of the response - body received up until that point but send the rest without buffering. - - * ModSecurity will now process phases 3 and 4 even when request processing - is interrupted (either by Apache - e.g. by responding with 400, 401 - or 403, or by ModSecurity itself). - - * Fixed the base64decode transformation function to not return extra - characters at the end. - - * Return from the output filter with an error in addition to setting - up the HTTP error status in the output data. - - * Used new Apache API calls to get the server version/banner when available. - - * Added "logdata" meta action to allow logging of raw transaction data. - - * Added TX_SEVERITY that keeps track of the highest severity - for any matched rules so far. - - * Added ARGS_GET, ARGS_POST, ARGS_GET_NAMES, ARGS_POST_NAMES variables to - allow seperation of GET and POST arguments. - - * Added an Apache define (MODSEC_2.5) so that you can conditionally include - directives based on the ModSecurity major/minor versions with IfDefine. - - * Added MODSEC_BUILD variable that contains the numeric build value based - on the ModSecurity version. - - * Enhanced debug logging by displaying more data on rule execution. All - invoked rules are now logged in the debug log at level 5. - - * Stricter validation for @validateUtf8Encoding. - - * No longer process Apache internal subrequests. - - * Fixed warnings on Solaris and/or 64bit builds. - - * Added @within string comparison operator with support for macro expansion. - - * Do not trigger "pause" action for internal requests. - - * Added matching rule filename and line number to audit log. - - * Added new phrase matching operators, @pm and @pmFromFile. These use - an alternate set based matching engine (Aho-Corasick) to perform faster - phrase type matches such as black/white lists, spam keywords, etc. - - * Allow caching transformations per-request/phase so they are not repeated. - - * Added Solaris and Cygwin to the list of platforms not supporting the hidden - visibility attribute. - - * Fixed decoding full-width unicode in t:urlDecodeUni. - - * Add SecGeoLookupDB, @geoLookups and GEO collection to support - geographical lookups by IP/host. - - * Do not try to intercept a request after a failed rule. This fixes the - issue associated with an "Internal Error: Asked to intercept request - but was_intercepted is zero" error message. - - * Removed extraneous exported symbols. - - * Merged the PDF XSS protection functionality into ModSecurity. - - * Exported API for registering custom variables. Example in api directory. - - * Added experimental support for content injection. Directive - SecContentInjection (On|Off) controls whether injection is taking place. - Actions "prepend" and "append" inject content when executed. Do note that - it is your responsibility to make sure the response is of the appropriate - content type (e.g. HTML, plain text, etc). - - * Added string comparison operators with support for macro expansion: - @contains, @streq, @beginsWith and @endsWith. - - * Enhanced debug log output to log macro expansion, quote values and - correctly display values that contained NULs. - - * Removed support for %0 - %9 capture macros as they were incorrectly - expanding url encoded values. Use %{TX.0} - %{TX.9} instead. - - * Added t:length to transform a value to its character length. - - * Added t:trimLeft, t:trimRight, t:trim to remove whitespace - from a value on the left, right or both. - - * Added SecAuditLog2 directive to allow redundent concurrent audit log - index files. This will allow sending audit data to two consoles, etc. - - * Removed CGI style HTTP_* variables in favor of REQUEST_HEADERS:Header-Name. - - * Store filename/line for each rule and display it and the ID (if available) - in the debug log when invoking a rule. Thanks to Christian Bockermann - for the idea. - - * Do not log 'allow' action as intercepted in the debug log. - - * Fixed some collection variable names not printing with the parameter - and/or counting operator in the debug log. - - -19 Feb 2008 - 2.1.6 -------------------- - - * Fixed crash on startup when ENV is improperly used without a parameter. - - * Allow macro resolution in setenv action. - - * Implemented SecUploadFileMode to allow setting the mode for uploaded files. - - * No longer log the query portion of the URI in the error log as - it may contain sensitive data. - - -10 Jan 2008 - 2.1.5 -------------------- - - * Updated included Core Ruleset to version 1.5.1. - - * Phase 5 rules can now be removed via SecRuleRemoveBy* directives. - - * Fixed issue where only the first phase 5 rule would run when the - request was intercepted in an earlier phase. - - * Fixed configuration parsing so that disruptive actions, meta actions - and phases are not allowed in a chained rule (as originally intended). - - * Fixed t:escapeSeqDecode to better follow ANSI C escapes. - - -27 Nov 2007 - 2.1.4 -------------------- - - * Updated included Core Ruleset to version 1.5 and noted in the docs that - XML support is required to use the rules without modification. - - * Fixed an evasion FP, mistaking a multipart non-boundary for a boundary. - - * Fixed multiple warnings on Solaris and/or 64bit builds. - - * Do not process subrequests in phase 2-4, but do hand off the request data. - - * Fixed a blocking FP in the multipart parser, which affected Safari. - - -11 Sep 2007 - 2.1.3 -------------------- - - * Updated multipart parsing code adding variables to allow checking - for various parsing issues (request body abnormalities). - - * Allow mod_rpaf and mod_extract_forwarded2 to work before ModSecurity. - - * Quiet some compiler warnings. - - * Do not block internal ErrorDocument requests after blocking request. - - * Added ability to compile without an external API (use -DNO_MODSEC_API). - - -27 Jul 2007 - 2.1.2 -------------------- - - * Cleaned up and clarified some documentation. - - * Update included core rules to latest version (1.4.3). - - * Enhanced ability to alert/audit failed requests. - - * Do not trigger "pause" action for internal requests. - - * Fixed issue with requests that use internal requests. These had the - potential to be intercepted incorrectly when other Apache httpd modules - that used internal requests were used with mod_security. - - * Added Solaris and Cygwin to the list of platforms not supporting the hidden - visibility attribute. - - * Fixed decoding full-width unicode in t:urlDecodeUni. - - * Lessen some overhead of debugging messages and calculations. - - * Do not try to intercept a request after a failed rule. This fixes the - issue associated with an "Internal Error: Asked to intercept request - but was_intercepted is zero" error message. - - * Added SecAuditLog2 directive to allow redundent concurrent audit log - index files. This will allow sending audit data to two consoles, etc. - - * Small performance improvement in memory management for rule execution. - - -11 Apr 2007 - 2.1.1 -------------------- - - * Add the PCRE_DOLLAR_ENDONLY option when compiling regular expression - for the @rx operator and variables. - - * Really set PCRE_DOTALL option when compiling the regular expression - for the @rx operator as the docs state. +v3.x.y - YYYY-MMM-DD (to be released) +------------------------------------- - * Fixed potential memory corruption when expanding macros. - - * Fixed error when a collection was retrieved from storage in the same second - as creation by setting the rate to zero. - - * Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms. - - * Fixed the faulty REQUEST_FILENAME variable, which used to change - the internal Apache structures by mistake. - - * Updates to quiet some compiler warnings. - - * Fixed some casting issues for compiling on NetWare (patch from Guenter Knauf). - - -23 Feb 2007 - 2.1.0 -------------------- - - * Removed the "Connection reset by peer" message, which has nothing - to do with us. Actually the message was downgraded from ERROR to - NOTICE so it will still appear in the debug log. - - * Removed the (harmless) message mentioning LAST_UPDATE_TIME missing. - - * It was not possible to remove a rule placed in phase 4 using - SecRuleRemoveById or SecRuleRemoveByMsg. Fixed. - - * Fixed a problem with incorrectly setting requestBodyProcessor using - the ctl action. - - * Bundled Core Rules 2.1-1.3.2b4. - - * Updates to the reference manual. - - * Reversed the return values of @validateDTD and @validateSchema, to - make them consistent with other operators. - - * Added a few helpful debug messages in the XML validation area. - - * Updates to the reference manual. - - * Fixed the validateByteRange operator. - - * Default value for the status action is now 403 (as it was supposed to - be but it was effectively 500). - - * Rule exceptions (removing using an ID range or an regular expression) - is now applied to the current context too. (Previously it only worked - on rules that are inherited from the parent context.) - - * Fix of a bug with expired variables. - - * Fixed regular expression variable selectors for many collections. - - * Performance improvements - up to two times for real-life work loads! - - * Memory consumption improvements (not measured but significant). - - * The allow action did not work in phases 3 and 4. Fixed. - - * Unlocked collections GLOBAL and RESOURCE. - - * Added support for variable expansion in the msg action. - - * New feature: It is now possible to make relative changes to the - audit log parts with the ctl action. For example: "ctl:auditLogParts=+E". - - * New feature: "tag" action. To be used for event categorisation. - - * XML parser was not reporting errors that occured at the end - of XML payload. - - * Files were not extracted from request if SecUploadKeepFiles was - Off. Fixed. - - * Regular expressions that are too long are truncated to 256 - characters before used in error messages. (In order to keep - the error messages in the log at a reasonable size.) - - * Fixed the sha1 transformation function. - - * Fixed the skip action. - - * Fixed REQUEST_PROTOCOL, REMOTE_USER, and AUTH_TYPE. - - * SecRuleEngine did not work in child configuration contexts - (e.g. ). - - * Fixed base64Decode and base64Encode. - - -15 Nov 2006 - 2.0.4 -------------------- - - * Fixed the "deprecatevar" action. - - * Decreasing variable values did not work. - - * Made "nolog" do what it is supposed to do - cause a rule match to - not be logged. Also "nolog" now implies "noauditlog" but it's - possible to follow "nolog" with "auditlog" and have the match - not logged to the error log but logged to the auditlog. (Not - something that strikes me as useful but it's possible.) - - * Relative paths given to SecDataDir will now be treated as relative - to the Apache server root. - - * Added checks to make sure only correct actions are specified in - SecDefaultAction (some actions are required, some don't make any - sense) and in rules that are not chain starters (same). This should - make the unhelpful "Internal Error: Failed to add rule to the ruleset" - message go away. - - * Fixed the problem when "SecRuleInheritance Off" is used in a context - with no rules defined. - - * Fixed a problem of lost input (request body) data on some redirections, - for example when mod_rewrite is used. - - -26 Oct 2006 - 2.0.3 -------------------- - - * Fixed a memory leak (all platforms) and a concurrency control - problem that could cause a crash (multithreaded platforms only). - - * Fixed a SecAuditLogRelevantStatus problem, which would not work - properly unless the regular expression contained a subexpression. - - -19 Oct 2006 - 2.0.2 -------------------- - - * Fixed incorrect permissions on the global mutex, which prevented - the mutex from working properly. - - * Fixed incorrect actionset merging where the status was copied from - the child actionset even though it was not defined. - - * Fixed missing metadata information (in the logs) for warnings. - + - Removed unnecessary while processing the transformations. + [#2368 - @WGH-, @zimmerle] + - auditlog: Computes whether or not to save while loading the rules. + [@zimmerle] + - actions: Computes Rule association while loading the rules given a + performance boost on run time. + [@zimmerle] + - Regression: Mark the test as failed in case of segfault. + [@zimmerle] + - Replaced t:lowerCase backend for a better performance. + [@zimmerle] + - More structured rules dump. Better supporting debugging. + [@zimmerle] + - Added the basics for supporting better error/warning handling while + loading configurations. + [@zimmerle] + - IMPORTANT: SecDefaultAction behaves changing: SecDefaultAction specified + on a child configuration will overwrite the ones specified on the parent; + Previously it was concatenating. + [@zimmerle] + - Using std::shared_ptr instead of generates its own references counters + for Rules and related. + [@zimmerle] + - Better handle shared_pointers on messages aiming for better performance. + [@zimmerle] + - Better handle memory usage on transformations aiming for better + performance. + [@zimmerle] + - Coding refactoring on the Rule class. The Rule class is now refactored + into RuleWithOperator, RuleWithActions, and RuleUnconditional. + - Adds support to lua 5.4 + [@zimmerle] + - GeoIP: switch to GEOIP_MEMORY_CACHE from GEOIP_INDEX_CACHE + [Issues #2378, #2186 - @defanator] + - rx: exit after full match (remove /g emulation); ensure capture + groups occuring after unused groups still populate TX vars + [Issue #2336 - @martinhsv] + - Correct CHANGES file entry for #2234 + - Add support to test framework for audit log content verification + and add regression tests for issues #2000, #2196 + - Support configurable limit on number of arguments processed + [Issue #2234 - @jleproust, @martinhsv] + - Multipart Content-Dispostion should allow field: filename*= + [@martinhsv] + - Fix rule-update-target for non-regex + [Issue 2251 - @martinhsv] + - Fix configure script when packaging for Buildroot + [Issue 2235 - @frankvanbever] + - modsecurity.pc.in: add Libs.private + [Issue #1918, #2253 - @ffontaine, @Dridi, @victorhora] + +v3.0.4 - 2020-Jan-13 +-------------------- -16 Oct 2006 - 2.0.1 -------------------- + - Fix: audit log data omitted when nolog,auditlog + [@martinhsv] + - Fix: ModSecurity 3.x inspectFile operator does not pass + FILES_TMPNAMES parameter to lua engine + [Issue #2204, #2205 - @kadirerdogan] + - XML: Remove error messages from stderr + [Issue #2010 - @JaiHarpalani, @zimmerle] + - Filter comment or blank line for pmFromFile operator + [Issue #1645 - @LeeShan87, @victorhora, @tdoubley] + - Additional adjustment to Cookie header parsing + [@martinhsv] + - Restore chained rule part H logging to be more like 2.9 behaviour + [Issue #2196 - @martinhsv] + - Small fixes in log messages to help debugging the file upload + [Issue #2130 - @airween] + - Fix Cookie header parsing issues + [Issue #2201 - @airween, @martinhsv] + - Fix rules with nolog are logging to part H + [Issue #2196 - @martinhsv] + - Fix argument key-value pair parsing cases + [Issue #1904 - @martinhsv] + - Fix: audit log part for response body for JSON format to be E + [Issue #2066 - @martinhsv, @zimmerle] + - Make sure m_rulesMessages is filled after successfull match + [Issue #2000, #2048 - @victorhora, @defanator] + - Fix @pm lookup for possible matches on offset zero. + [@zimmerle, @afoxdavidi, @martinhsv, @marshal09] + - Regex lookup on the key name instead of COLLECTION:key + [@rdiperri-yottaa, @danbiagini-work, @mmelo-yottaa, @zimmerle] + - Missing throw in Operator::instantiate + [Issue #2106 - @marduone] + - Making block action execution dependent of the SecEngine status + [Issue #2113, #2111 - @theMiddleBlue, @airween] + - Making block action execution dependent of the SecEngine status + [Issue #1960 - @theMiddleBlue, @zimmerle, @airween, @victorhora] + - Having body limits to respect the rule engine state + [@zimmerle] + - Fix SecRuleUpdateTargetById does not match regular expressions + [Issue #1872 - @zimmerle, @anush-cr, @victorhora, @j0k2r] + - Adds missing check for runtime ctl:ruleRemoveByTag + [Issue #2102, #2099 - @airween] + - Adds a new operator verifySVNR that checks for Austrian social + security numbers. + [Issue #2063 - @Rufus125] + - Fix variables output in debug logs + [Issue #2057 - @jleproust] + - Correct typo validade in log output + [Issue #2059 - @nerrehmit] + - fix/minor: Error encoding hexa decimal. + [Issue #2068 - @tech-ozon-io] + - Limit more log variables to 200 characters. + [Issue #2073 - @jleproust] + - parser: fix parsed file names + [@zimmerle] + - Allow empty anchored variable + [Issue #2024 - @airween] + - Fixed FILES_NAMES collection after the end of multipart parsing + [Issue #2016 - @airween] + - Fixed validateByteRange parsing method + [Issue #2017 - @airween] + - Removes a memory leak on the JSON parser + [@zimmerle] + - Enables LMDB on the regression tests. + [Issue #2011, #2008 - @WGH-, @mdunc] + - Fix: Extra whitespace in some configuration directives causing error + [Issue #2006 - @porjo, @zimmerle] + - Refactoring on Regex and SMatch classes. + [@WGH-] + - Fixed buffer overflow in Utils::Md5::hexdigest() + [Issue #2002 - @defanator] + - Implemented merge() method for ConfigInt, ConfigDouble, ConfigString + [Issue #1990 - @defanator] + - Adds initially support to the drop action. + [@zimmerle] + - Complete merging of particular rule properties + [Issue #1978 - @defanator] + - Replaces AC_CHECK_FILE with 'test -f' + [Issue #1984 - @chuckwolber] + - Fix inet addr handling on 64 bit big endian systems + [Issue #1980 - @airween] + - Fix tests on FreeBSD + [Issue #1973 - @defanator] + - Changes ENV test case to read the default MODSECURTIY env var + [Issue #1969 - @zimmerle, @airween, @inittab] + - Regression: Sets MODSECURITY env var during the tests execution + [Issue #1969 - @zimmerle, @airween, @inittab] + - Fix setenv action to strdup key=variable + [@zimmerle] + - Allow 0 length JSON requests. + [Issue #1822 - @allanbomsft, @zimmerle, @victorhora, @marcstern] + - Fix "make dist" target to include default configuration + [Issue #1966 - @defanator] + - Replaced log locking using mutex with fcntl lock + [Issue #1949, #1927 - @Cloaked9000] + - Correct the usage of modsecurity::Phases::NUMBER_OF_PHASES + [Issue #1959 - @weliu] + - Adds support to multiple ranges in ctl:ruleRemoveById + [Issue #1956 - @theseion, @victorhora, @zimmerle] + - Rule variable interpolation broken + [Issue #1961 - @soonum, @zimmerle] + - Make the boundary check less strict as per RFC2046 + [Issue #1943 - @victorhora, @allanbomsft] + - Fix buffer size for utf8toUnicode transformation + [Issue #1208 - @katef, @victorhora] + + +v3.0.3 - 2018-Nov-05 +-------------------- - * Rules that used operator negation did not work. Fixed. + - Fix double macros bug + [Issue #1943 - @supplient, @zimmerle] + - Override the default status code if not suitable to redirect action + [Issue #1850 - @zimmerle, @victorhora] + - parser: Fix the support for CRLF configuration files + [Issue #1945 - @zimmerle, @defanator, @kjakub] + - Organizes the server logs + [0xb7c36 and 0x5ac20 - @zimmerle, @steven-j-wojcik] + - m_lineNumber in Rule not mapping with the correct line number in file + [Issue #1844 - @zimmerle, @victorhora, @xizeng] + - Using shared_ptr instead of unique_ptr on rules exceptions + [Issue #1697 - @zimmerle, @brianp9906, @victorhora, @LeSwiss, @defanator] + - Changes debuglogs schema to avoid unecessary str allocation + [0xb2840 - @zimmerle] + - Fix the SecUnicodeMapFile and SecUnicodeCodePage + [0x3094d - @zimmerle, @victorhora] + - Changes the timing to save the rule message + [0xca270 - @zimmerle] + - Fix crash in msc_rules_add_file() when using disruptive action in chain + [Issue #1849 - @victorhora, @zimmerle, @rperper] + - Fix memory leak in AuditLog::init() + [Issue #1897 - @weliu] + - Fix RulesProperties::appendRules() + [Issue #1901 - @steven-j-wojcik] + - Fix RULE lookup in chained rules + [0x3077c - @zimmerle] + - @ipMatch "Could not add entry" on slash/32 notation in 2.9.0 + [Issue #849 - @zimmerle, @dune73] + - Using values after transformation at MATCHED_VARS + [0x14316 - @zimmerle] + - Adds support to UpdateActionById. + [Issue #1800 - @zimmerle, @victorhora, @NisariAIT] + - Add correct C function prototypes for msc_init and msc_create_rule_set + [Issue #1922 - @steven-j-wojcik] + - Allow LuaJIT 2.1 to be used + [Issue #1909 - @victorhora, @mdunc] + - Match m_id JSON log with RuleMessage and v2 format + [Issue #1185 - @victorhora] + - Adds support to setenv action. + [Issue #1044 - @zimmerle] + - Adds new transaction constructor that accepts the transaction id + as parameter. + [Issue #1627 - @defanator, @zimmerle] + - Adds request IDs and URIs to the debug log + [Issue #1627 - @defanator, @zimmerle] + - Treating variables exception on load-time instead of run time. + [0x028e0 and 0x275a1 - @zimmerle] + - Fix: function m.setvar in Lua scripts and add testcases + [Issue #1859 - @nowaits, @victorhora] + - Fix SecResponseBodyAccess and ctl:requestBodyAccess directives + [Issue #1531 - @victorhora, @defanator] + - Fix OpenBSD build + [Issue #1841 - @victorhora, @zimmerle, @juanfra684] + - Fix parser to support GeoLookup with MaxMind + [Issue #1884, #1895 - @victorhora, @everping] + - parser: Fix simple quote setvar in the end of the line + [Issue #1831 - @zimmerle, @csanders-git] + - Fix pc file + [Issue #1847 - @gquintard] + - modsec_rules_check: uses the gnu `.la' instead of `.a' file + [Issue #1853 - @ste7677, @victorhora, @zimmerle] + - good practices: Initialize variables before use it + [Issue #1889 - Marc Stern] + - Fix utf-8 character encoding conversion + [Issue #1794 - @tinselcity, @zimmerle] + - Adds support for ctl:requestBodyProcessor=URLENCODED + [Issue #1797 - @victorhora] + - Add LUA compatibility for CentOS and try to use LuaJIT first if available + [Issue #1622 - @victorhora, @dmitryzykov] + - Allow LuaJIT to be used + [Issue #1809 - @victorhora, @p0pr0ck5] + - Implement support for Lua 5.1 + [Issue #1809 - @p0pr0ck5, @victorhora] + - Variable names must match fully, not partially. Match should be case + insensitive. + [Issue #1818, #1820, #1810, #1808 - @michaelgranzow-avi, @victorhora, + @theMiddleBlue, @airween, @zimmerle, + @LeeShan87] + - Improves the performance while loading the rules + [Issue #1735 - @zimmerle, @p0pr0ck5, @victorhora] + - Allow empty strings to be evaluated by regex::searchAll + [Issue #1799, #1785 - @victorhora, @XuanHuyDuong, @zimmerle] + - Adds basic pkg-config info + [Issue #1790 - @gquintard, @zimmerle] + - Fixed LMDB collection errors + [Issue #1787 - @airween, @zimmerle] + - Fixed false positive MULTIPART_UNMATCHED_BOUNDARY errors + [Issue #1747, #1924 - @airween, @victorhora, @defanator, @zimmerle] + - Fix ip tree lookup on netmask content + [Issue #1793 - @tinselcity, @zimmerle] + - Changes the behavior of the default sec actions + [Issue #1629 - @mirkodziadzka-avi, @zimmerle, @victorhora] + - Refactoring on {global,ip,resources,session,tx,user} collections + [Issue #1754, #1778 - @LeeShan87, @zimmerle, @victorhora, @wwd5613, + @sobigboy] + - Fix race condition in UniqueId::uniqueId() + [Issue #1786 - @weliu] + - Fix memory leak in error message for msc_rules_merge C APIs + [Issue #1765 - @weliu] + - Return false in SharedFiles::open() when an error happens + [Issue #1783 - @weliu] + - Use rvalue reference in ModSecurity::serverLog + [Issue #1769 - @weliu] + - Build System: Fix when multiple lines for curl version. + [Issue #1771 - @Artistan] + - Checks if response body inspection is enabled before process it + [Issue #1643 - @zoltan-fedor, @dennus, @defanator, @zimmerle] + - Code Cleanup. + [Issue #1757, #1755, #1756, #1761 - @p0pr0ck5] + - Fix setvar parsing of quoted data + [Issue #1733, #1759, #1775 - @victorhora, @JaiHarpalani, @defanator] + - Fix LDFLAGS for unit tests. + [Issue #1758 - @smlx] + - Adds time stamp back to the audit logs + [Issue #1762 - @Pjack, @zimmerle] + - Disables skip counter if debug log is disabled + [@zimmerle] + - Cosmetics: Represents amount of skipped rules without decimal + [Issue #1737 - @p0pr0ck5] + - Add missing escapeSeqDecode, urlEncode and trimLeft/Right tfns to parser + [Issue #1752 - @victorhora] + - Fix STATUS var parsing and accept STATUS_LINE var for v2 backward comp. + [Issue #1738 - @victorhora] + - Fix memory leak in modsecurity::utils::expandEnv() + [Issue #1750 - @defanator] + - Initialize m_dtd member in ValidateDTD class as NULL + [Issue #1751 - @airween] + - Fix broken @detectxss operator regression test case + [Issue #1739 - @p0pr0ck5] + - Fix utils::string::ssplit() to handle delimiter in the end of string + [Issue #1743, #1744 - @defanator] + - Fix variable FILES_TMPNAMES + [Issue #1646, #1610 - @victorhora, @zimmerle, @defanator] + - Fix memory leak in Collections + [Issue #1729, #1730 - @defanator] + + +v3.0.2 - 2018-Apr-03 +-------------------- - * Fixed bug that prevented invalid regular expressions from being reported. + - Fix lib version information while generating the .so file + [@gl1f1v21, @zimmerle] +v3.0.1 - 2018-Apr-02 +-------------------- -16 Oct 2006 - 2.0.0 -------------------- + - Adds support for ctl:ruleRemoveByTag + [@zimmerle, @weliu] + - Fix SecUploadDir configuration merge + [Issue #1720 - @zimmerle, @gjvanetten] + - Include all prerequisites for "make check" into dist archive + [Issue #1716 - @defanator] + - Fix: Reverse logic of checking output in @inspectFile + [Issue #1715 - @defanator] + - Adds support to libMaxMind + [Issue #1307 - @zimmerle, @defanator] + - Adds capture action to detectXSS + [Issue #1698 - @victorhora] + - Temporarily accept invalid MULTIPART_SEMICOLON_MISSING operator + [Issue #1701 - @victorhora] + - Adds capture action to detectSQLi + [Issue #1698 - @zimmerle] + - Adds capture action to rbl + [Issue #1698 - @zimmerle] + - Adds capture action to verifyCC + [Issue #1698 - @michaelgranzow-avi, @zimmerle] + - Adds capture action to verifySSN + [Issue #1698 - @zimmerle] + - Adds capture action to verifyCPF + [Issue #1698 - @zimmerle] + - Prettier error messages for unsupported configurations (UX) + [@victorhora] + - Add missing verify*** transformation statements to parser + [Issue #1006 and #1007 - @victorhora] + - Fix a set of compilation warnings + [Issue #1650 - @zimmerle, @JayCase] + - Check for disruptive action on SecDefaultAction. + [Issue #1614 - @zimmerle, @michaelgranzow-avi] + - Fix block-block infinite loop. + [Issue #1614 - @zimmerle, @michaelgranzow-avi] + - Correction remove_by_tag and remove_by_msg logic. + [Issue #1636 - @Minasu] + - Fix LMDB compile error + [Issue #1691 - @airween] + - Fix msc_who_am_i() to return pointer to a valid C string + [Issue #1640 - @defanator] + - Added some cosmetics to autoconf related code + [Issue #1652 - @airween] + - Fix "make dist" target to include necessary headers for Lua + [Issue #1678 - @defanator] + - Fix "include /foo/*.conf" for single matched object in directory + [Issue #1677 - @defanator, @zimmerle] + - Add missing Base64 transformation statements to parser + [Issue #1632 - @victorhora, @zimmerle] + - Fixed resource load on ip match from file + [#1674 - @zimmerle, @StefaanSeys] + - Fixed examples compilation while using disable-shared + [#1670 - @zimmerle, @ivanbaldo] + - Fixed compilation issue while xml is disabled + [0x243028 - @zimmerle] + - Having LDADD and LDFLAGS organized on Makefile.am + [0xd0e85e - @zimmerle] + - Checking std::deque size before use it + [0x217cbf - @zimmerle, Yaron Dayagi] + - perf improvement: Added the concept of RunTimeString and removed + all run time parser. + [0x3eae51 0x0320e0 0xb5688f 0xfe47a9 0xfa9842 0x1affc3 0x079de4 + 0xc7c04f 0x5262ea 0x01974a 0xd5ee1e - @zimmerle] + - perf improvement: Checks debuglog level before format debug msg + [0x42ee9 - @zimmerle] + - perf. improvement/rx: Only compute dynamic regex in case of macro + [0x91ff3 - @zimmerle] + - Fix uri on the benchmark utility + [0x63bec - @zimmerle] + - disable Lua on systems with liblua5.1 + [Issue #1639 - @victorhora, @defanator] + +v3.0.0 - 2017-Dec-13 +-------------------- - * First stable 2.x release. + - Improvements on LUA build scripts and support for LUA 5.2. + [Issue #1617 and #1622 - @victorhora, @zimmerle] + - Fix compilation error with disable_debug_log flag + [0xfd84e - Izik Abramov] + - Improvements on the benchmark tool. + [Issue #1615 - @zimmerle] + - Fix lua headers on the build scripts + [Issue #1621 - @Minasu] + - Refactoring on the JSON parser. + [Issue #1576, #1577 - Tobias Gutknecht, @zimmerle, @victorhora, @marcstern] + - Adds support to WEBAPPID variable. + [Issue #1027 - @zimmerle, @victorhora] + - Adds support for SecWebAppId. + [Issue #1442 - @zimmerle, @victorhora] + - Adds support for SecRuleRemoveByTag. + [Issue #1476 - @zimmerle, @victorhora] + - Adds support for update target by message. + [Issue #1474 - @zimmerle, @victorhora] + - Adds support to SecRuleScript directive. + [Issue #994 - @zimmerle] + - Adds support for the exec action. + [Issue #1050 - @zimmerle] + - Adds support for transformations inside Lua engine + [Issue #994 - @zimmerle] + - Adds initial support for Lua engine. + [Issue #994 - @zimmerle] + - Adds support for @inspectFile operator. + [Issue #999 - @zimmerle, @victorhora] + - Adds support for RESOURCE variable collection. + [Issue #1014 - @zimmerle, @victorhora] + - Adds support for @fuzzyHash operator. + [Issue #997 - @zimmerle] + - Fix build on non x86 arch build + [Issue #1598 - @athmane] + - Fix memory issue while changing rule target dynamic + [Issue #1590 - @zimmerle, @slabber] + - Fix log while displaying the name of a dict selection by regex. + [@zimmerle] + - Setting http response code on the auditlog. + [Issue #1592 - @zimmerle] + - Refactoring on RuleMessage class, now accepting http code as parameter. + [@zimmerle] + - Having disruptive msgs as disruptive [instead of warnings] on audit log + [Issue #1592 - @zimmerle, @nobodysz] + - Parser: Pipes are no longer welcomed inside regex dict element selection. + [Issue #1591 - @zimmerle, @slabber] + - Avoids unicode initialization on every rules object + [Issue #1563 - @zimmerle, @Tiki-God, @sethinsd, @Cloaked9000, @AnoopAlias, + @intelbg] + - Makes clear to the user whenever the audit log is empty due to missing + JSON support. + [Issue #1585 - @zimmerle] + - Makes auditlog more verbose on debug logs + [Issue: #1559 - @zimmerle] + - Enable support for AuditLogFormat + Issue: #1583, #1493 and #1453 - @victorhora] + - Adds macro expansion for @rx operator + [Issue: #1528, #1536 - @asterite3, @zimmerle] + - Consideres under quoted variable while loading the rules. + [Felipe Zimmerle/@zimmerle, Victor Hora/@victorhora] + - Store the connection and url parameters in std::string + [Issue: #1571 - @majordaw] + - Eliminate some reorder and sign warnings + [Issue: #1572 - Dávid Major/@majordaw] + - Makes parallel logging to work when SELinux is enabled. + [Issue: #1562 - David Buckle/@met3or] + - Adds possibility to run the pm operator inside a mutex to avoid concurrent + access while working on a thread environment. This option is a compilation + flag. + [Felipe Zimmerle/@zimmerle] + + +v3.0.0-rc1 - 2017-Aug-28 +------------------------ + + Very first public version. diff --git a/Makefile.am b/Makefile.am index 35b3797baf..75ce55d1dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,39 +1,347 @@ + +if TEST_UTILITIES +export MAYBE_TEST = test +endif + +if EXAMPLES +export MAYBE_EXAMPLES = examples +endif + + +SUBDIRS = \ + others \ + src \ + doc \ + tools \ + $(MAYBE_EXAMPLES) \ + $(MAYBE_TEST) + + + +# make clean +CLEANFILES = + ACLOCAL_AMFLAGS = -I build -SUBDIRS = @TOPLEVEL_SUBDIRS@ tests - -CLEANFILES = -MAINTAINERCLEANFILES = - -CLEANFILES += tests/regression/server_root/conf/*.t_*.conf \ -   tests/regression/server_root/logs/*.log - -MAINTAINERCLEANFILES += $(CLEANFILES) \ - Makefile.in \ - aclocal.m4 \ - alp2/Makefile.in \ - apache2/Makefile.in \ - build/config.guess \ - build/config.sub \ - build/depcomp \ - build/libtool.m4 \ - build/ltmain.sh \ - build/ltoptions.m4 \ - build/ltsugar.m4 \ - build/ltversion.m4 \ - build/lt~obsolete.m4 \ - build/missing \ - configure \ - ext/Makefile.in \ - mlogc/Makefile.in \ - modsecurity_config_auto.h.in~ \ - config.log \ - Makefile \ - config.status - -# Alias for "check" -test: check - -test-regression: - (cd tests && $(MAKE) test-regression) - -.PHONY: test + +# make maintainer-clean +MAINTAINERCLEANFILES = \ + aclocal.m4 \ + ar-lib \ + build/libtool.m4 \ + build/lt~obsolete.m4 \ + build/ltoptions.m4 \ + build/ltsugar.m4 \ + build/ltversion.m4 \ + coding-style.txt \ + compile \ + config.guess \ + config.sub \ + configure \ + cppcheck.txt \ + depcomp \ + install-sh \ + ltmain.sh \ + Makefile.in \ + missing \ + test/modsec-shared-collections \ + test/modsec-shared-collections-lock \ + test-suite-drd.log \ + test-suite-helgrind.log \ + test-suite-memcheck.log \ + ylwrap + +parser: + cat src/parser/seclang-parser.hh | sed "s/return \*new (yyas_ ()) T (t)/return *new (yyas_ ()) T (std::move((T\&)t))/g" > src/parser/seclang-parser.hh.fix && mv src/parser/seclang-parser.hh.fix src/parser/seclang-parser.hh + + + +cppcheck: + @cppcheck -U YYSTYPE -U MBEDTLS_MD5_ALT -U MBEDTLS_SHA1_ALT \ + -D MS_CPPCHECK_DISABLED_FOR_PARSER -U YY_USER_INIT \ + --suppressions-list=./test/cppcheck_suppressions.txt \ + --enable=all \ + --inconclusive \ + --template="warning: {file},{line},{severity},{id},{message}" \ + -I headers -I . -I others -I src -I others/mbedtls -I src/parser \ + --error-exitcode=1 \ + -i "src/parser/seclang-parser.cc" \ + -i "src/parser/seclang-scanner.cc" \ + -i "parser/driver.cc" \ + --force --verbose . + + +check-static: cppcheck + +check-style: check-coding-style + +check-coding-style: + @cpplint.py \ + $$(find . -name "*.h" -o -name "*.cc" | xargs) 2>&1 \ + | egrep -v $$(echo -n "catchall" ; \ + for i in $$(cat test/coding_style_suppressions.txt); do echo -n "|"$$i; done) \ + | sed 's/^\./warning: ./g' > coding-style.txt + -cat coding-style.txt + + +@VALGRIND_CHECK_RULES@ +VALGRIND_SUPPRESSIONS_FILES = valgrind_suppressions.txt + +LOG_DRIVER = env $(SHELL) $(top_srcdir)/test/custom-test-driver +AM_TESTS_ENVIRONMENT=AUTOMAKE_TESTS=true; export AUTOMAKE_TESTS; +LOG_COMPILER=test/test-suite.sh + +# for i in `find test/test-cases -iname *.json`; do echo TESTS+=$i; done +TESTS= +TESTS+=test/test-cases/regression/action-allow.json +TESTS+=test/test-cases/regression/action-block.json +TESTS+=test/test-cases/regression/action-ctl_request_body_access.json +TESTS+=test/test-cases/regression/action-ctl_request_body_processor.json +TESTS+=test/test-cases/regression/action-ctl_request_body_processor_urlencoded.json +TESTS+=test/test-cases/regression/action-ctl_rule_engine.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_by_id.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_by_tag.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_target_by_id.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_target_by_tag.json +TESTS+=test/test-cases/regression/action-disruptive.json +TESTS+=test/test-cases/regression/action-exec.json +TESTS+=test/test-cases/regression/action-id.json +TESTS+=test/test-cases/regression/action-initcol.json +TESTS+=test/test-cases/regression/action-msg.json +TESTS+=test/test-cases/regression/action-setenv.json +TESTS+=test/test-cases/regression/action-setrsc.json +TESTS+=test/test-cases/regression/action-setsid.json +TESTS+=test/test-cases/regression/action-setuid.json +TESTS+=test/test-cases/regression/actions.json +TESTS+=test/test-cases/regression/action-skip.json +TESTS+=test/test-cases/regression/action-tag.json +TESTS+=test/test-cases/regression/action-tnf-base64.json +TESTS+=test/test-cases/regression/action-xmlns.json +TESTS+=test/test-cases/regression/auditlog.json +TESTS+=test/test-cases/regression/collection-case-insensitive.json +TESTS+=test/test-cases/regression/collection-lua.json +TESTS+=test/test-cases/regression/collection-regular_expression_selection.json +TESTS+=test/test-cases/regression/collection-resource.json +TESTS+=test/test-cases/regression/collection-tx.json +TESTS+=test/test-cases/regression/collection-tx-with-macro.json +TESTS+=test/test-cases/regression/config-body_limits.json +TESTS+=test/test-cases/regression/config-calling_phases_by_name.json +TESTS+=test/test-cases/regression/config-include-bad.json +TESTS+=test/test-cases/regression/config-include.json +TESTS+=test/test-cases/regression/config-remove_by_id.json +TESTS+=test/test-cases/regression/config-remove_by_msg.json +TESTS+=test/test-cases/regression/config-remove_by_tag.json +TESTS+=test/test-cases/regression/config-response_type.json +TESTS+=test/test-cases/regression/config-secdefaultaction.json +TESTS+=test/test-cases/regression/config-secremoterules.json +TESTS+=test/test-cases/regression/config-update-action-by-id.json +TESTS+=test/test-cases/regression/config-update-target-by-id.json +TESTS+=test/test-cases/regression/config-update-target-by-msg.json +TESTS+=test/test-cases/regression/config-update-target-by-tag.json +TESTS+=test/test-cases/regression/config-xml_external_entity.json +TESTS+=test/test-cases/regression/debug_log.json +TESTS+=test/test-cases/regression/directive-sec_rule_script.json +TESTS+=test/test-cases/regression/issue-1152.json +TESTS+=test/test-cases/regression/issue-1528.json +TESTS+=test/test-cases/regression/issue-1565.json +TESTS+=test/test-cases/regression/issue-1576.json +TESTS+=test/test-cases/regression/issue-1591.json +TESTS+=test/test-cases/regression/issue-1725.json +TESTS+=test/test-cases/regression/issue-1743.json +TESTS+=test/test-cases/regression/issue-1785.json +TESTS+=test/test-cases/regression/issue-1812.json +TESTS+=test/test-cases/regression/issue-1831.json +TESTS+=test/test-cases/regression/issue-1844.json +TESTS+=test/test-cases/regression/issue-1850.json +TESTS+=test/test-cases/regression/issue-1941.json +TESTS+=test/test-cases/regression/issue-1943.json +TESTS+=test/test-cases/regression/issue-1956.json +TESTS+=test/test-cases/regression/issue-1960.json +TESTS+=test/test-cases/regression/issue-2099.json +TESTS+=test/test-cases/regression/issue-2000.json +TESTS+=test/test-cases/regression/issue-2111.json +TESTS+=test/test-cases/regression/issue-2196.json +TESTS+=test/test-cases/regression/issue-394.json +TESTS+=test/test-cases/regression/issue-849.json +TESTS+=test/test-cases/regression/issue-960.json +TESTS+=test/test-cases/regression/misc.json +TESTS+=test/test-cases/regression/misc-variable-under-quotes.json +TESTS+=test/test-cases/regression/offset-variable.json +TESTS+=test/test-cases/regression/operator-detectsqli.json +TESTS+=test/test-cases/regression/operator-detectxss.json +TESTS+=test/test-cases/regression/operator-fuzzyhash.json +TESTS+=test/test-cases/regression/operator-inpectFile.json +TESTS+=test/test-cases/regression/operator-ipMatchFromFile.json +TESTS+=test/test-cases/regression/operator-pm.json +TESTS+=test/test-cases/regression/operator-rx.json +TESTS+=test/test-cases/regression/operator-UnconditionalMatch.json +TESTS+=test/test-cases/regression/operator-validate-byte-range.json +TESTS+=test/test-cases/regression/operator-verifycc.json +TESTS+=test/test-cases/regression/operator-verifycpf.json +TESTS+=test/test-cases/regression/operator-verifyssn.json +TESTS+=test/test-cases/regression/operator-verifysvnr.json +TESTS+=test/test-cases/regression/request-body-parser-json.json +TESTS+=test/test-cases/regression/request-body-parser-multipart-crlf.json +TESTS+=test/test-cases/regression/request-body-parser-multipart.json +TESTS+=test/test-cases/regression/request-body-parser-xml.json +TESTS+=test/test-cases/regression/request-body-parser-xml-validade-dtd.json +TESTS+=test/test-cases/regression/rule-920120.json +TESTS+=test/test-cases/regression/rule-920200.json +TESTS+=test/test-cases/regression/rule-920274.json +TESTS+=test/test-cases/regression/secaction.json +TESTS+=test/test-cases/regression/secargumentslimit.json +TESTS+=test/test-cases/regression/sec_component_signature.json +TESTS+=test/test-cases/regression/secmarker.json +TESTS+=test/test-cases/regression/secruleengine.json +TESTS+=test/test-cases/regression/transformation-none.json +TESTS+=test/test-cases/regression/transformations.json +TESTS+=test/test-cases/regression/variable-ARGS_COMBINED_SIZE.json +TESTS+=test/test-cases/regression/variable-ARGS_GET.json +TESTS+=test/test-cases/regression/variable-ARGS_GET_NAMES.json +TESTS+=test/test-cases/regression/variable-ARGS.json +TESTS+=test/test-cases/regression/variable-ARGS_NAMES.json +TESTS+=test/test-cases/regression/variable-ARGS_POST.json +TESTS+=test/test-cases/regression/variable-ARGS_POST_NAMES.json +TESTS+=test/test-cases/regression/variable-AUTH_TYPE.json +TESTS+=test/test-cases/regression/variable-DURATION.json +TESTS+=test/test-cases/regression/variable-ENV.json +TESTS+=test/test-cases/regression/variable-FILES_COMBINED_SIZE.json +TESTS+=test/test-cases/regression/variable-FILES.json +TESTS+=test/test-cases/regression/variable-FILES_NAMES.json +TESTS+=test/test-cases/regression/variable-FILES_SIZES.json +TESTS+=test/test-cases/regression/variable-FULL_REQUEST.json +TESTS+=test/test-cases/regression/variable-FULL_REQUEST_LENGTH.json +TESTS+=test/test-cases/regression/variable-GEO.json +TESTS+=test/test-cases/regression/variable-HIGHEST_SEVERITY.json +TESTS+=test/test-cases/regression/variable-INBOUND_DATA_ERROR.json +TESTS+=test/test-cases/regression/variable-MATCHED_VAR.json +TESTS+=test/test-cases/regression/variable-MATCHED_VAR_NAME.json +TESTS+=test/test-cases/regression/variable-MATCHED_VARS.json +TESTS+=test/test-cases/regression/variable-MATCHED_VARS_NAMES.json +TESTS+=test/test-cases/regression/variable-MODSEC_BUILD.json +TESTS+=test/test-cases/regression/variable-MULTIPART_CRLF_LF_LINES.json +TESTS+=test/test-cases/regression/variable-MULTIPART_FILENAME.json +TESTS+=test/test-cases/regression/variable-MULTIPART_INVALID_HEADER_FOLDING.json +TESTS+=test/test-cases/regression/variable-MULTIPART_NAME.json +TESTS+=test/test-cases/regression/variable-MULTIPART_STRICT_ERROR.json +TESTS+=test/test-cases/regression/variable-MULTIPART_UNMATCHED_BOUNDARY.json +TESTS+=test/test-cases/regression/variable-OUTBOUND_DATA_ERROR.json +TESTS+=test/test-cases/regression/variable-PATH_INFO.json +TESTS+=test/test-cases/regression/variable-QUERY_STRING.json +TESTS+=test/test-cases/regression/variable-REMOTE_ADDR.json +TESTS+=test/test-cases/regression/variable-REMOTE_HOST.json +TESTS+=test/test-cases/regression/variable-REMOTE_PORT.json +TESTS+=test/test-cases/regression/variable-REMOTE_USER.json +TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR_ERROR.json +TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR.json +TESTS+=test/test-cases/regression/variable-REQUEST_BASENAME.json +TESTS+=test/test-cases/regression/variable-REQUEST_BODY.json +TESTS+=test/test-cases/regression/variable-REQUEST_BODY_LENGTH.json +TESTS+=test/test-cases/regression/variable-REQUEST_COOKIES.json +TESTS+=test/test-cases/regression/variable-REQUEST_COOKIES_NAMES.json +TESTS+=test/test-cases/regression/variable-REQUEST_FILENAME.json +TESTS+=test/test-cases/regression/variable-REQUEST_HEADERS.json +TESTS+=test/test-cases/regression/variable-REQUEST_HEADERS_NAMES.json +TESTS+=test/test-cases/regression/variable-REQUEST_LINE.json +TESTS+=test/test-cases/regression/variable-REQUEST_METHOD.json +TESTS+=test/test-cases/regression/variable-REQUEST_PROTOCOL.json +TESTS+=test/test-cases/regression/variable-REQUEST_URI.json +TESTS+=test/test-cases/regression/variable-REQUEST_URI_RAW.json +TESTS+=test/test-cases/regression/variable-RESPONSE_BODY.json +TESTS+=test/test-cases/regression/variable-RESPONSE_CONTENT_LENGTH.json +TESTS+=test/test-cases/regression/variable-RESPONSE_CONTENT_TYPE.json +TESTS+=test/test-cases/regression/variable-RESPONSE_HEADERS.json +TESTS+=test/test-cases/regression/variable-RESPONSE_HEADERS_NAMES.json +TESTS+=test/test-cases/regression/variable-RESPONSE_PROTOCOL.json +TESTS+=test/test-cases/regression/variable-RULE.json +TESTS+=test/test-cases/regression/variable-SERVER_ADDR.json +TESTS+=test/test-cases/regression/variable-SERVER_NAME.json +TESTS+=test/test-cases/regression/variable-SERVER_PORT.json +TESTS+=test/test-cases/regression/variable-SESSIONID.json +TESTS+=test/test-cases/regression/variable-STATUS.json +TESTS+=test/test-cases/regression/variable-TIME_DAY.json +TESTS+=test/test-cases/regression/variable-TIME_EPOCH.json +TESTS+=test/test-cases/regression/variable-TIME_HOUR.json +TESTS+=test/test-cases/regression/variable-TIME.json +TESTS+=test/test-cases/regression/variable-TIME_MIN.json +TESTS+=test/test-cases/regression/variable-TIME_MON.json +TESTS+=test/test-cases/regression/variable-TIME_SEC.json +TESTS+=test/test-cases/regression/variable-TIME_WDAY.json +TESTS+=test/test-cases/regression/variable-TIME_YEAR.json +TESTS+=test/test-cases/regression/variable-TX.json +TESTS+=test/test-cases/regression/variable-UNIQUE_ID.json +TESTS+=test/test-cases/regression/variable-URLENCODED_ERROR.json +TESTS+=test/test-cases/regression/variable-USERID.json +TESTS+=test/test-cases/regression/variable-variation-count.json +TESTS+=test/test-cases/regression/variable-variation-exclusion.json +TESTS+=test/test-cases/regression/variable-WEBAPPID.json +TESTS+=test/test-cases/regression/variable-WEBSERVER_ERROR_LOG.json +TESTS+=test/test-cases/secrules-language-tests/operators/beginsWith.json +TESTS+=test/test-cases/secrules-language-tests/operators/contains.json +TESTS+=test/test-cases/secrules-language-tests/operators/containsWord.json +TESTS+=test/test-cases/secrules-language-tests/operators/detectSQLi.json +TESTS+=test/test-cases/secrules-language-tests/operators/detectXSS.json +TESTS+=test/test-cases/secrules-language-tests/operators/endsWith.json +TESTS+=test/test-cases/secrules-language-tests/operators/eq.json +TESTS+=test/test-cases/secrules-language-tests/operators/ge.json +TESTS+=test/test-cases/secrules-language-tests/operators/geoLookup.json +TESTS+=test/test-cases/secrules-language-tests/operators/gt.json +TESTS+=test/test-cases/secrules-language-tests/operators/ipMatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/le.json +TESTS+=test/test-cases/secrules-language-tests/operators/lt.json +TESTS+=test/test-cases/secrules-language-tests/operators/noMatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/pmFromFile.json +TESTS+=test/test-cases/secrules-language-tests/operators/pm.json +TESTS+=test/test-cases/secrules-language-tests/operators/rx.json +TESTS+=test/test-cases/secrules-language-tests/operators/streq.json +TESTS+=test/test-cases/secrules-language-tests/operators/strmatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/unconditionalMatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/validateByteRange.json +TESTS+=test/test-cases/secrules-language-tests/operators/validateUrlEncoding.json +TESTS+=test/test-cases/secrules-language-tests/operators/validateUtf8Encoding.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifyCC.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifycpf.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifyssn.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifysvnr.json +TESTS+=test/test-cases/secrules-language-tests/operators/within.json +TESTS+=test/test-cases/secrules-language-tests/transformations/base64DecodeExt.json +TESTS+=test/test-cases/secrules-language-tests/transformations/base64Decode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/base64Encode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/cmdLine.json +TESTS+=test/test-cases/secrules-language-tests/transformations/compressWhitespace.json +TESTS+=test/test-cases/secrules-language-tests/transformations/cssDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/escapeSeqDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/hexDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/hexEncode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/htmlEntityDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/jsDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/length.json +TESTS+=test/test-cases/secrules-language-tests/transformations/lowercase.json +TESTS+=test/test-cases/secrules-language-tests/transformations/md5.json +TESTS+=test/test-cases/secrules-language-tests/transformations/normalisePath.json +TESTS+=test/test-cases/secrules-language-tests/transformations/normalisePathWin.json +TESTS+=test/test-cases/secrules-language-tests/transformations/parityEven7bit.json +TESTS+=test/test-cases/secrules-language-tests/transformations/parityOdd7bit.json +TESTS+=test/test-cases/secrules-language-tests/transformations/parityZero7bit.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeCommentsChar.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeComments.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeNulls.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeWhitespace.json +TESTS+=test/test-cases/secrules-language-tests/transformations/replaceComments.json +TESTS+=test/test-cases/secrules-language-tests/transformations/replaceNulls.json +TESTS+=test/test-cases/secrules-language-tests/transformations/sha1.json +TESTS+=test/test-cases/secrules-language-tests/transformations/sqlHexDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/trim.json +TESTS+=test/test-cases/secrules-language-tests/transformations/trimLeft.json +TESTS+=test/test-cases/secrules-language-tests/transformations/trimRight.json +TESTS+=test/test-cases/secrules-language-tests/transformations/urlDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/urlDecodeUni.json +TESTS+=test/test-cases/secrules-language-tests/transformations/urlEncode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/utf8toUnicode.json + + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = modsecurity.pc +EXTRA_DIST = modsecurity.pc.in \ + modsecurity.conf-recommended \ + unicode.mapping + diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 0501be9f03..0000000000 --- a/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ - ModSecurity (www.modsecurity.org) -    Copyright [2004-2013] Trustwave Holdings, Inc - -    This product includes software developed at -    Trustwave Holdings, Inc (http://www.trustwave.com/). diff --git a/README.TXT b/README.TXT deleted file mode 100644 index 3647d08f9d..0000000000 --- a/README.TXT +++ /dev/null @@ -1,111 +0,0 @@ -ModSecurity for Apache 2.x, http://www.modsecurity.org/ -Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) - -You may not use this file except in compliance with -the License.  You may obtain a copy of the License at - -    http://www.apache.org/licenses/LICENSE-2.0 - -If any of the files related to licensing are missing or if you have any -other questions related to licensing please contact Trustwave Holdings, Inc. -directly using the email address security@modsecurity.org. - - -DOCUMENTATION - -Please refer to the documentation folder (/doc) for -the reference manual. - - -############################################## ----------------------------------- -OWASP ModSecurity Core Rule Set (CRS) - - -Project Site: -https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Pr -oject - - -Download: -https://github.com/SpiderLabs/owasp-modsecurity-crs - ----------------------------------- - -ModSecurity™ is a web application firewall engine that provides very -little protection on its own. In order to become useful, ModSecurity™ must -be configured with rules. In order to enable users to take full advantage -of ModSecurity™ out of the box, Trustwave's SpiderLabs is providing a free -certified rule set for ModSecurity™ 2.x. Unlike intrusion detection and -prevention systems, which rely on signatures specific to known -vulnerabilities, the Core Rules provide generic protection from unknown -vulnerabilities often found in web applications, which are in most cases -custom coded. The Core Rules are heavily commented to allow it to be used -as a step-by-step deployment guide for ModSecurity™. -Core Rules Content - -In order to provide generic web applications protection, the Core Rules -use the following techniques: - -* HTTP Protection - detecting violations of the HTTP protocol and a -locally defined usage policy. -* Real-time Blacklist Lookups - utilizes 3rd Party IP Reputation -* Web-based Malware Detection - identifies malicious web content by check -against the Google Safe Browsing API. -* HTTP Denial of Service Protections - defense against HTTP Flooding and -Slow HTTP DoS Attacks. -* Common Web Attacks Protection - detecting common web application -security attack. -* Automation Detection - Detecting bots, crawlers, scanners and other -surface malicious activity. -* Integration with AV Scanning for File Uploads - detects malicious files -uploaded through the web application. -* Tracking Sensitive Data - Tracks Credit Card usage and blocks leakages. -* Trojan Protection - Detecting access to Trojans horses. -* Identification of Application Defects - alerts on application -misconfigurations. -* Error Detection and Hiding - Disguising error messages sent by the -server. - - ----------------------------------- -ModSecurity Rules from Trustwave SpiderLabs - -Project Site: -hhttps://www.trustwave.com/modsecurity-rules-support.php - -Download: -https://ssl.trustwave.com/web-application-firewall - ----------------------------------- - - - -Trustwave now provides a commercial certified rule set for ModSecurity 2.x -that protects against known attacks that target vulnerabilities in public -software and are based on intelligence gathered from real-world -investigations, honeypot data and research. - -1. More than 16,000 specific rules, broken out into the following attack -categories: - * SQL injection - * Cross-site Scripting (XSS) - * Local File Include - * Remote File Include - -2. User option for application specific rules, covering the same -vulnerability classes for applications such as: - * WordPress - * cPanel - * osCommerce - * Joomla - * For a complete listing of application coverage, please refer to this -link (which is updated daily). -https://modsecurity.org/projects/commercial/rules/application_coverage.html - -3. Complements and integrates with the OWASP Core Rule Set -4. IP Reputation capabilities which provide protection against malicious -clients identified by the Trustwave SpiderLabs Distributed Web Honeypots -5. Malware Detection capabilities which prevent your web site from -distributing malicious code to clients. -############################################## diff --git a/README.md b/README.md new file mode 100644 index 0000000000..50e09dc16d --- /dev/null +++ b/README.md @@ -0,0 +1,277 @@ + + + +[![Build Status](https://travis-ci.org/SpiderLabs/ModSecurity.svg?branch=v3/master)](https://travis-ci.org/SpiderLabs/ModSecurity) +[![](https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png)](https://zenhub.com) +[![Build Status](https://sonarcloud.io/api/project_badges/measure?project=USHvY32Uy62L&metric=alert_status)](https://sonarcloud.io/dashboard?id=USHvY32Uy62L) +[![](https://sonarcloud.io/api/project_badges/measure?project=USHvY32Uy62L&metric=sqale_rating +)](https://sonarcloud.io/dashboard?id=USHvY32Uy62L) +[![](https://sonarcloud.io/api/project_badges/measure?project=USHvY32Uy62L&metric=reliability_rating +)](https://sonarcloud.io/dashboard?id=USHvY32Uy62L) +[![](https://sonarcloud.io/api/project_badges/measure?project=USHvY32Uy62L&metric=security_rating +)](https://sonarcloud.io/dashboard?id=USHvY32Uy62L) +[![](https://sonarcloud.io/api/project_badges/measure?project=USHvY32Uy62L&metric=vulnerabilities +)](https://sonarcloud.io/dashboard?id=USHvY32Uy62L) + + + +Libmodsecurity is one component of the ModSecurity v3 project. The library +codebase serves as an interface to ModSecurity Connectors taking in web traffic +and applying traditional ModSecurity processing. In general, it provides the +capability to load/interpret rules written in the ModSecurity SecRules format +and apply them to HTTP content provided by your application via Connectors. + +If you are looking for ModSecurity for Apache (aka ModSecurity v2.x), it is still under maintenence and available: +[here](https://github.com/SpiderLabs/ModSecurity/tree/v2/master). + +### What is the difference between this project and the old ModSecurity (v2.x.x)? + +* All Apache dependences have been removed +* Higher performance +* New features +* New architecture + +Libmodsecurity is a complete rewrite of the ModSecurity platform. When it was first devised the ModSecurity project started as just an Apache module. Over time the project has been extended, due to popular demand, to support other platforms including (but not limited to) Nginx and IIS. In order to provide for the growing demand for additional platform support, it has became necessary to remove the Apache dependencies underlying this project, making it more platform independent. + +As a result of this goal we have rearchitected Libmodsecurity such that it is no longer dependent on the Apache web server (both at compilation and during runtime). One side effect of this is that across all platforms users can expect increased performance. Additionally, we have taken this opprotunity to lay the groundwork for some new features that users have been long seeking. For example we are looking to nativly support auditlogs in the JSON format, along with a host of other functionality in future versions. + + +### It is no longer just a module. + +The 'ModSecurity' branch no longer contains the traditional module logic (for Nginx, Apache, and IIS) that has traditionally been packaged all together. Instead, this branch only contains the library portion (libmodsecurity) for this project. This library is consumed by what we have termed 'Connectors' these connectors will interface with your webserver and provide the library with a common format that it undersands. Each of these connectors is maintained as a seperate GitHub project. For instance, the Nginx connector is supplied by the ModSecurity-nginx project (https://github.com/SpiderLabs/ModSecurity-nginx). + +Keeping these connectors seperated allows each project to be have different release cycles, issues and development trees. Addtionally, it means that when you install ModSecurity v3 you only get exactly what you need, no extras you won't be using. + +# Compilation + +Before starting the compilation process, make sure that you have all the +dependencies in place. Read the subsection “Dependencies” for further +information. + +After the compilation make sure that there are no issues on your +build/platform. We strongly recommend the utilization of the unit tests and +regression tests. These test utilities are located under the subfolder ‘tests’. + +As a dynamic library, don’t forget that libmodsecurity must be installed to a location (folder) where you OS will be looking for dynamic libraries. + + + +### Unix (Linux, MacOS, FreeBSD, …) + +On unix the project uses autotools to help the compilation process. + +```shell +$ ./build.sh +$ ./configure +$ make +$ sudo make install +``` + +Details on distribution specific builds can be found in our Wiki: +[Compilation Recipes](https://github.com/SpiderLabs/ModSecurity/wiki/Compilation-recipes) + +### Windows + +Windows build is not ready yet. + + +## Dependencies + +This library is written in C++ using the C++11 standards. It also uses Flex +and Yacc to produce the “Sec Rules Language” parser. Other, manditory dependencies include YAJL, as ModSecurity uses JSON for producing logs and its testing framework, libpcre (not yet manditory) for processing regular expressions in SecRules, and libXML2 (not yet manditory) which is used for parsing XML requests. + +All others dependencies are related to operators specified within SecRules or configuration directives and may not be required for compilation. A short list of such dependencies is as follows: + +* libinjection is needed for the operator @detectXSS and @detectSQL +* curl is needed for the directive SecRemoteRules. + +If those libraries are missing ModSecurity will be compiled without the support for the operator @detectXSS and the configuration directive SecRemoteRules. + +# Library documentation + +The library documentation is written within the code in Doxygen format. To generate this documentation, please use the doxygen utility with the provided configuration file, “doxygen.cfg”, located with the "doc/" subfolder. This will generate HTML formatted documentation including usage examples. + +# Library utilization + +The library provides a C++ and C interface. Some resources are currently only +available via the C++ interface, for instance, the capability to create custom logging +mechanism (see the regression test to check for how those logging mechanism works). +The objective is to have both APIs (C, C++) providing the same functionality, +if you find an aspect of the API that is missing via a perticular interface, please open an issue. + +Inside the subfolder examples, there are simple examples on how to use the API. +Below some are illustrated: + +### Simple example using C++ + +```c++ +using ModSecurity::ModSecurity; +using ModSecurity::Rules; +using ModSecurity::Transaction; + +ModSecurity *modsec; +ModSecurity::Rules *rules; + +modsec = new ModSecurity(); + +rules = new Rules(); + +rules->loadFromUri(rules_file); + +Transaction *modsecTransaction = new Transaction(modsec, rules); + +modsecTransaction->processConnection("127.0.0.1"); +if (modsecTransaction->intervention()) { + std::cout << "There is an intervention" << std::endl; +} +``` + +### Simple example using C + +```c +#include "modsecurity/modsecurity.h" +#include "modsecurity/transaction.h" + + +char main_rule_uri[] = "basic_rules.conf"; + +int main (int argc, char **argv) +{ + ModSecurity *modsec = NULL; + Transaction *transaction = NULL; + Rules *rules = NULL; + + modsec = msc_init(); + + rules = msc_create_rules_set(); + msc_rules_add_file(rules, main_rule_uri); + + transaction = msc_new_transaction(modsec, rules); + + msc_process_connection(transaction, "127.0.0.1"); + msc_process_uri(transaction, "http://www.modsecurity.org/test?key1=value1&key2=value2&key3=value3&test=args&test=test"); + msc_process_request_headers(transaction); + msc_process_request_body(transaction); + msc_process_response_headers(transaction); + msc_process_response_body(transaction); + + return 0; +} + +``` + +# Contributing + +You are more than welcome to contribute to this project and look forward to growing the community around this new version of ModSecurity. Areas of interest include: New +functionalities, fixes, bug report, support for beginning users, or anything that you +are willing to help with. + +## Providing patches + +We prefer to have your patch within the GitHub infrastructure to facilitate our +review work, and our Q.A. integration. GitHub provides excellent +documentation on how to perform “Pull Requests”, more information available +here: https://help.github.com/articles/using-pull-requests/ + +Please respect the coding style. Pull requests can include various commits, so +provide one fix or one piece of functionality per commit. Please do not change anything outside +the scope of your target work (e.g. coding style in a function that you have +passed by). For further information about the coding style used in this +project, please check: https://www.chromium.org/blink/coding-style + +Provides explanative commit messages. Your first line should give the highlights of your +patch, 3rd and on give a more detailed explanation/technical details about your +patch. Patch explanation is valuable during the review process. + +### Don’t know where to start? + +Within our code there are various items marked as TODO or FIXME that may need +your attention. Check the list of items by performing a grep: + +``` +$ cd /path/to/modsecurity-nginx +$ egrep -Rin "TODO|FIXME" -R * +``` + +A TODO list is also available as part of the Doxygen documentation. + +### Testing your patch + +Along with the manual testing, we strongly recommend you to use the our +regression tests and unit tests. If you have implemented an operator, don’t +forget to create unit tests for it. If you impliment anything else, it is encouraged that you develop complimentary regression tests for it. + +The regression test and unit test utilities are native and do not demand any +external tool or script, although you need to fetch the test cases from other +repositories, as they are shared with other versions of ModSecurity, those +others repositories git submodules. To fetch the submodules repository and run +the utilities, follow the commands listed below: + +```shell +$ cd /path/to/your/ModSecurity +$ git submodule foreach git pull +$ cd test +$ ./regression-tests +$ ./unit-tests + ``` + +### Debugging + + +Before start the debugging process, make sure of where your bug is. The problem +could be on your connector or in libmodsecurity. In order to identify where the +bug is, it is recommended that you develop a regression test that mimics the +scenario where the bug is happening. If the bug is reproducible with the +regression-test utility, then it will be far simpliar to debug and ensure that it never occurs again. On Linux it is +recommended that anyone undertaking debugging utilize gdb and/or valgrind as needed. + +During the configuration/compilation time, you may want to disable the compiler +optimization making your “back traces” populated with readable data. Use the +CFLAGS to disable the compilation optimization parameters: + +```shell +$ export CFLAGS="-g -O0" +$ ./build.sh +$ ./configure +$ make +$ sudo make install +``` + + +## Reporting Issues + +If you are facing a configuration issue or something is not working as you +expected to be, please use the ModSecurity user’s mailing list. Issues on GitHub +are also welcomed, but we prefer to have user ask questions on the mailing list first so that you can reach an entire community. Also don’t forget to look for existing issues before open a new one. + +If you are going to open a new issue on GitHub, don’t forget to tell us the +version of your libmodsecurity and the version of a specific connector if there +is one. + + +### Security issue + +Please do not make public any security issue. Contact us at: +security@modsecurity.org reporting the issue. Once the problem is fixed your +credit will be given. + +## Feature request + +We are open to discussing any new feature request with the community via the mailing lists. You can alternativly, +feel free to open GitHub issues requesting new features. Before opening a +new issue, please check if there is one already opened on the same topic. + +## Bindings + +The libModSecurity design allows the integration with bindings. There is an effort to avoid brake the API [binary] compatibility to make an easy integration with possible bindings. Currently, there are two notable projects maintained by the community: + * Python - https://github.com/actions-security/pymodsecurity + * Varnish - https://github.com/xdecock/vmod-modsecurity + +## Packaging + +Having our packages in distros on time is a desire that we have, so let us know +if there is anything we can do to facilitate your work as a packager. + + + + diff --git a/README_WINDOWS.TXT b/README_WINDOWS.TXT deleted file mode 100644 index 94c2bc9db9..0000000000 --- a/README_WINDOWS.TXT +++ /dev/null @@ -1,192 +0,0 @@ -===================================================================== -MOD_SECURITY 2.6 Command-line Build notes for Windows 4/2/2011 -by Tom Donovam -===================================================================== - -PREREQUISITES: - - Microsoft Visual Studio C++ tested with Visual Studio 2008 (aka VC9) - - CMake build system from: http://www.cmake.org/ tested with CMake v2.8.0 - - Apache 2.2.x from: http://httpd.apache.org/ tested with Apache 2.2.17 - Apache must be built from source using the same Visual Studio compiler as mod_security. - - PCRE Perl Compatible Regular Expression library from: http://www.pcre.org/ tested with PCRE v8.12 - - LibXML2 from: http://xmlsoft.org/ tested with LibXML2 v2.7.7 - Note that LibXML2 v2.7.8 does not build correctly for Windows - - Lua Scripting Language from: http://www.lua.org/ tested with Lua v5.1.4 - - cURL multiprotocol file transfer library from: http://curl.haxx.se/ tested with cURL v7.21.4 - - -BEFORE BUILDING - -The directory where you build software from source ( C:\work in this exmaple) -must contain the Apache source you used to build the Apache web serverand the mod_security source - - Apache source is in C:\work\httpd-2.2.17 in this example. - Apache has been installed to C:\Apache2217 in this example. - Mod_security source is in C:\work\mod_security in this example. - -Download and untar the prerequite library sources: - - Download pcre-8.12.tar.gz from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ - untar it into C:\work\ creating C:\work\pcre-8.12 - - Download libxml2-2.7.7.tar.gz from ftp://xmlsoft.org/libxml2/ - untar it into C:\work\ creating C:\work\libxml2-2.7.7 - - Download lua-5.1.4.tar.gz from http://www.lua.org/ftp/ - untar it into C:\work\ creating C:\work\lua-5.1.4 - - Download curl-7.21.4.tar.gz from http://curl.haxx.se/download.html - untar it into C:\work\ creating C:\work\curl-7.21.4 - -Setup your build environment: - - The PATH environment variable must include the Visual Studio variables as set by vsvars32.bat - The PATH environment variable must also include the CMAKE bin\ directory - - Set an environment variable to the Apache source code directory: - - SET HTTPD_BUILD=C:\work\httpd-2.2.17 - - If OpenSSL and Zlib support were included when you built Apache 2.2, and you want them available to LIBXML2 and CURL - - Ensure that cURL and libXML2 can find the OpenSSL and Zlib includes and libraries that Apache was built with. - - SET INCLUDE=%INCLUDE%;%HTTPD_BUILD%\srclib\openssl\inc32;%HTTPD_BUILD%\srclib\zlib - SET LIB=%LIB%;%HTTPD_BUILD%\srclib\openssl\out32dll;%HTTPD_BUILD%\srclib\zlib - - Ensure that cURL and libXML2 don't use the static zlib library: zlib.lib. - Force cURL and libXML2 to use zdll.lib instead, requiring zlib1.dll at runtime: - - IF EXIST %HTTPD_BUILD%\srclib\zlib\zlib.lib DEL %HTTPD_BUILD%\srclib\zlib\zlib.lib - -BUILD PCRE-8.12 - - CD C:\work\pcre-8.12 - CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True - NMAKE - -BUILD LIBXML2-2.7.7 (note: the more recent version: 2.7.8 does not build correctly on Windows) - - CD C:\work\libxml2-2.7.7\win32 - CSCRIPT configure.js iconv=no vcmanifest=yes zlib=yes - NMAKE -f Makefile.msvc - -BUILD LUA-5.1.4 - - CD C:\work\lua-5.1.4\src - CL /Ox /arch:SSE2 /GF /GL /Gy /FD /EHsc /MD /Zi /TC /wd4005 /D "_MBCS" /D "LUA_CORE" /D "LUA_BUILD_AS_DLL" /D "_CRT_SECURE_NO_WARNINGS" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_WIN32" /D "_WINDLL" /c *.c - DEL lua.obj luac.obj - LINK /DLL /LTCG /DEBUG /OUT:lua5.1.dll *.obj - IF EXIST lua5.1.dll.manifest MT -manifest lua5.1.dll.manifest -outputresource:lua5.1.dll;2 - -BUILD CURL-7.21.4 - - CD C:\work\curl-7.21.4 - CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True -DCURL_ZLIB=True - NMAKE - -BUILD MOD_SECURITY-2.6 - - CD C:\work\mod_security\apache2 - NMAKE -f Makefile.win APACHE=C:\Apache2217 PCRE=C:\work\pcre-8.12 LIBXML2=C:\work\libxml2-2.7.7 LUA=C:\work\lua-5.1.4\src - -INSTALL MOD_SECURITY AND RUN APACHE - -Copy these five files to C:\Apache2217\bin: - C:\work\pcre-8.12\pcre.dll C:\Apache2217\bin\ - C:\work\lua-5.1.4\src\lua5.1.dll C:\Apache2217\bin\ - C:\work\libxml2-2.7.7\win32\bin.msvc\libxml2.dll C:\Apache2217\bin\ - C:\work\curl-7.21.4\libcurl.dll C:\Apache2217\bin\ - C:\work\mod_security\apache2\mlogc-src\mlogc.exe - -Copy this one file to C:\Apache2217\modules: - - C:\work\mod_security\apache2\mod_security2.so - -You may also copy C:\work\curl-7.21.4\curl.exe to C:\Apache2217\bin, if you want to use the cURL command-line program. - -Download the core rules from http://sourceforge.net/projects/mod-security/files/modsecurity-crs/0-CURRENT/ -and unzip them into C:\Apache2217\conf\modsecurity_crs - -Add configuration directives to your Apache conf\httpd.conf: - - # mod_security requires mod_unique_id - LoadModule unique_id_module modules/mod_unique_id.so - - # mod_security - LoadModule security2_module modules/mod_security2.so - - SecRuleEngine On - SecDataDir logs - Include conf/modsecurity_crs/*.conf - Include conf/modsecurity_crs/base_rules/*.conf - SecAuditEngine RelevantOnly - SecAuditLogRelevantStatus "^(?:5|4\d[^4])" - SecAuditLogType Serial - SecAuditLogParts ABCDEFGHZ - SecAuditLog logs/modsecurity.log - - - -============================================================================================== -OPTIONAL: BUILD AND CONFIGURE THE MOD_SECURITY-2.6 MLOGC piped-logging program - -Edit the top of C:\work\mod_security\apache2\mlogc-src\Makefile.win and set your local paths - - # Path to Apache httpd installation - BASE = C:\Apache2217 - - # Paths to required libraries - PCRE = C:\work\pcre-8.12 - CURL = C:\work\curl-7.21.4 - - # Linking libraries - LIBS = $(BASE)\lib\libapr-1.lib \ - $(BASE)\lib\libaprutil-1.lib \ - $(PCRE)\pcre.lib \ - $(CURL)\libcurl_imp.lib \ - wsock32.lib - -Build the mlogc.exe program: - - CD C:\work\mod_security_trunk\mlogc - NMAKE -f Makefile.win - -Copy mlocg.exe to C:\Apache2217\bin\ - -Create a new command file C:\Apache2217\bin\mlogc.bat with one line: - - C:\Apache2217\bin\mlogc.exe C:\Apache2217\conf\mlogc.conf - -Create a new configuration file C:\Apache2217\conf\mlogc.conf to control the piped-logging program mlogc.exe. -Here is an example conf\mlogc.conf: - - CollectorRoot "C:/Apache2217/logs" - ConsoleURI "https://localhost:8888/rpc/auditLogReceiver" - SensorUsername "test" - SensorPassword "testtest" - LogStorageDir "data" - TransactionLog "mlogc-transaction.log" - QueuePath "mlogc-queue.log" - ErrorLog "mlogc-error.log" - LockFile "mlogc.lck" - KeepEntries 0 - ErrorLogLevel 2 - MaxConnections 10 - MaxWorkerRequests 1000 - TransactionDelay 50 - StartupDelay 5000 - CheckpointInterval 15 - ServerErrorTimeout 60 - -Change the SecAuditLog directive in conf\httpd.conf to pipe the log data to mlogc -instead of writing them to a file: - - SecAuditLog |C:/Apache2217/bin/mlogc.bat diff --git a/alp2/Makefile.am b/alp2/Makefile.am deleted file mode 100644 index 4b475451ec..0000000000 --- a/alp2/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -lib_LTLIBRARIES = libalp2.la -include_HEADERS = alp2.h alp2_pp.h - -libalp2_la_SOURCES = alp2.c alp2_pp.c -libalp2_la_CFLAGS = @APR_CFLAGS@ @APU_CFLAGS@ -libalp2_la_LDFLAGS = @APR_LDFLAGS@ @APU_LDFLAGS@ diff --git a/alp2/alp2.c b/alp2/alp2.c deleted file mode 100755 index 8a40272cc2..0000000000 --- a/alp2/alp2.c +++ /dev/null @@ -1,1418 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include -#include -#include -#include - -#include "alp2.h" - -#ifdef DEBUG -#define alp_debug(...) fprintf(stderr, __VA_ARGS__) -#else -#define alp_debug(...) -#endif /* DEBUG */ - -/** - * Add one error to the audit log entry. - */ -static void add_error(alp2_t *alp, int is_fatal, const char *text, ...) -{ - char *str = NULL; - va_list ap; - - if (is_fatal) { - alp->parse_error = 1; - } - - va_start(ap, text); - str = apr_pvsprintf(alp->auditlog->mp, text, ap); - va_end(ap); - - *(char **)apr_array_push(alp->errors) = str; -} - -/** - * Parse the Response-Body-Transformed trailer header. - */ -static int handle_part_H_parse_ResponseTFN(alp2_t *alp, const char *s) -{ - char *capture = NULL; - int ovector[33]; - int rc; - - // TODO This header is optional, but is not allowed to appear more than once. - - - return 1; -} - -/** - * Parse the Action trailer header. - */ -static int handle_part_H_parse_Action(alp2_t *alp, const char *s) -{ - char *capture = NULL; - int ovector[33]; - int rc; - - // TODO This header is optional, but is not allowed to appear more than once. - - alp->auditlog->was_intercepted = 1; - - rc = pcre_exec(alp->trailer_action_pattern, NULL, s, strlen(s), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part H: Failed to parse Action header"); - return -1; - } - - capture = apr_pstrmemdup(alp->auditlog->mp, s + ovector[2 * 1], - ovector[2 * 1 + 1] - ovector[2 * 1]); - - alp->auditlog->intercept_phase = atoi(capture); - - return 1; -} - -/** - * Convert two hexadecimal characters into a character. - */ -static uint8_t x2c(uint8_t *what) -{ - register uint8_t digit; - - digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); - digit *= 16; - digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); - - return digit; -} - -/** - * Remove a layer of encoding from a string. This function needs to be used - * for every piece of data ModSecurity encoded for a message. - */ -static int remove_slashes(uint8_t *s) -{ - uint8_t *d = s; - - while(*s != '\0') { - if ((*s == '\\')&&(*(s + 1) != '\0')) { - s++; - - switch(*s) { - case 'b' : - *d = '\b'; - break; - case 'n' : - *d = '\n'; - break; - case 'r' : - *d = '\r'; - break; - case 't' : - *d = '\t'; - break; - case 'v' : - *d = '\v'; - break; - case '\\' : - *d = '\\'; - break; - case '"' : - *d = '"'; - break; - case 'x' : - if ( (*(s + 1) != '\0') - &&(*(s + 2) != '\0') - &&(isxdigit(*(s + 1))) - &&(isxdigit(*(s + 2))) - ) { - *d = x2c(s + 1); - s += 2; - } - else { - /* Invalid encoding. */ - return -1; - } - break; - default : - /* Invalid encoding. */ - return -1; - break; - } - } - else { - *d = *s; - } - - s++; - d++; - } - - *d = '\0'; - - return 1; -} - -/** - * Process one (ModSecurity message) meta-data fragment. - */ -static int handle_part_H_parse_Message_meta(alp2_t *alp, alp2_msg_t *message, - const char *l, const char *string_start, const char *string_end) -{ - const char *value; - - // XXX if ((*string_start != '"')||(*string_end != '"')||(string_end <= string_start)) { - if (string_end <= string_start) { - add_error(alp, 1, "Part H: Invalid handle_part_H_parse_Message_meta invocation"); - return -1; - } - - if ((*string_start != '"')||(*string_end != '"')) { - value = apr_pstrndup(alp->auditlog->mp, string_start, (string_end - string_start) + 1); - } - else { - value = apr_pstrndup(alp->auditlog->mp, string_start + 1, (string_end - string_start - 1)); - } - if (value == NULL) { - return -1; - } - - if (remove_slashes((uint8_t *)value) < 0) { - add_error(alp, 1, "Part H: Invalid encoding in meta-data fragment"); - return -1; - } - - /* Target ( at THE_TARGET) */ - if (strncmp(l, "at ", 3) == 0) { - if (message->target != NULL) { - add_error(alp, 1, "Part H: Already seen target"); - return -1; - } - - message->target = value; - - return 1; - } - - /* id */ - if (strncmp(l, "id ", 3) == 0) { - if (message->id != NULL) { - add_error(alp, 1, "Part H: Already seen meta-data: id"); - return -1; - } - - message->id = value; - - return 1; - } - - /* rev */ - if (strncmp(l, "rev ", 4) == 0) { - if (message->rev != NULL) { - add_error(alp, 1, "Part H: Already seen meta-data: rev"); - return -1; - } - - message->rev = value; - - return 1; - } - - /* msg */ - if (strncmp(l, "msg ", 4) == 0) { - if (message->msg != NULL) { - add_error(alp, 1, "Part H: Already seen meta-data: msg"); - return -1; - } - - message->msg = value; - - return 1; - } - - /* data */ - if (strncmp(l, "data ", 4) == 0) { - if (message->data != NULL) { - add_error(alp, 1, "Part H: Already seen meta-data: data"); - return -1; - } - - message->data = value; - - return 1; - } - - /* file */ - if (strncmp(l, "file ", 5) == 0) { - if (message->file != NULL) { - add_error(alp, 1, "Part H: Already seen meta-data: file"); - return -1; - } - - message->file = value; - - return 1; - } - - /* line */ - if (strncmp(l, "line ", 5) == 0) { - if (message->file_line != (unsigned long)-1) { - add_error(alp, 1, "Part H: Already seen meta-data: line"); - return -1; - } - - // TODO Validate. - message->file_line = atoi(value); - - return 1; - } - - /* tag */ - if (strncmp(l, "tag ", 4) == 0) { - *(char **)apr_array_push(message->tags) = (char *)value; - return 1; - } - - /* severity */ - if (strncmp(l, "severity ", 9) == 0) { - if ( (strcmp(value, "0") == 0) - ||(strcasecmp(value, "EMERGENCY") == 0)) - { - message->severity = 0; - return 1; - } - - if ( (strcmp(value, "1") == 0) - ||(strcasecmp(value, "ALERT") == 0)) - { - message->severity = 1; - return 1; - } - - if ( (strcmp(value, "2") == 0) - ||(strcasecmp(value, "CRITICAL") == 0)) - { - message->severity = 2; - return 1; - } - - if ( (strcmp(value, "3") == 0) - ||(strcasecmp(value, "ERROR") == 0)) - { - message->severity = 3; - return 1; - } - - if ( (strcmp(value, "4") == 0) - ||(strcasecmp(value, "WARNING") == 0)) - { - message->severity = 4; - return 1; - } - - if ( (strcmp(value, "5") == 0) - ||(strcasecmp(value, "NOTICE") == 0)) - { - message->severity = 5; - return 1; - } - - if ( (strcmp(value, "6") == 0) - ||(strcasecmp(value, "INFO") == 0)) - { - message->severity = 6; - return 1; - } - - if ( (strcmp(value, "7") == 0) - ||(strcasecmp(value, "DEBUG") == 0)) - { - message->severity = 7; - return 1; - } - - add_error(alp, 1, "Part H: Invalid severity value: %s", value); - - return -1; - } - - /* offset */ - if (strncmp(l, "offset ", 7) == 0) { - if (message->offset != (size_t)-1) { - /* Already seen "offset". */ - add_error(alp, 1, "Part H: Already seen fragment offset"); - return -1; - } - - // TODO Validate. - message->offset = atoi(value); - - return 1; - } - - /* Ignore unknown meta-data information. */ - - return 0; -} - -/** - * Parse the Message trailer header. More than one such header - * can exist in an audit log, and each represents one ModSecurity - * message. - */ -static int handle_part_H_parse_Message(alp2_t *alp, const char *s) -{ - alp2_msg_t *message = NULL; - char *l = (char *)(s + strlen(s) - 1); - char *engine_message_start = (char *)s; - char *engine_message_end = NULL; - char *string_start = NULL, *string_end = NULL; - char *fragment_end = NULL; - char *tptr; - int in_string; - int done; - - /* Create one new message structure. */ - message = apr_pcalloc(alp->auditlog->mp, sizeof(alp2_msg_t)); - if (message == NULL) { - return -1; - } - - message->file_line = (unsigned long)-1; - message->offset = (size_t)-1; - message->severity = -1; - message->warning = 0; - - if (strncasecmp("warning. ", s, 9) == 0) { - message->warning = 1; - engine_message_start += 9; - } - - message->tags = apr_array_make(alp->auditlog->mp, 4, sizeof(const char *)); - - /* Start at the end of the message and go back identifying - * the meta-data fragments as we go. Stop when we find the - * end of the engine message. - */ - done = in_string = 0; - while ((l >= s)&&(!done)) { - if (in_string == 0) { - /* Outside string. */ - - // TODO Make sure this is not an escaped char - switch(*l) { - case ' ' : - /* Do nothing. */ - break; - case ']' : - fragment_end = l; - break; - case '[' : - if (fragment_end) { - /* Found one meta-data fragment. */ - // TODO This parser implementation allows for invalid - // meta-data fragments to be accepted. It would be - // nice to check the format of the fragment (e.g. - // by matching it against a regular expression - // pattern) before we accept any data. At this point - // l points to the first byte of the fragment, and - // fragment_end to the last. - handle_part_H_parse_Message_meta(alp, message, - l + 1, string_start, string_end); - - fragment_end = NULL; - string_start = NULL; - string_end = NULL; - } - break; - case '"' : - /* Found the end of a string. */ - in_string = 1; - string_end = l; - break; - default : - if (!fragment_end) { - /* There are no more meta-data fragments. */ - engine_message_end = l; - done = 1; - } - break; - } - } - else { - /* In string. We are only interested - * in where the string ends. - */ - if ((*l == '"')&&((l - 1) >= s)&&(*(l - 1) != '\\')) { - in_string = 0; - string_start = l; - } - } - - l--; - } - - /* Target is between " at " and "." */ - tptr = engine_message_start; - while ((tptr = strstr(tptr, " at ")) && (tptr < engine_message_end)) { - char *tend = strchr(tptr, '.'); - if ((tend <= engine_message_end) && (tend - tptr > 5)) { - int rc = handle_part_H_parse_Message_meta(alp, message, tptr + 1, - tptr + 4, tend - 1); - if (rc == 1) { - /* Remove the target data from the message */ - engine_message_end = tptr; - } - } - break; - } - - if (engine_message_end == NULL) { - add_error(alp, 1, "Part H: Failed parsing ModSecurity message: %s", s); - return -1; - } - - message->engine_message = apr_pstrndup(alp->auditlog->mp, engine_message_start, (engine_message_end - engine_message_start + 1)); - - /* Add this message to the audit log. */ - *(alp2_msg_t **)apr_array_push(alp->auditlog->messages) = message; - - return 1; -} - -/** - * Parse the Stopwatch trailer header. - */ -static int handle_part_H_parse_Stopwatch(alp2_t *alp, const char *s) -{ - int ovector[33]; - int i, rc; - - // TODO This header is required (a check for its appearance is made when - // handling the end of an H part), and is not allowed to appear - // more than once. - - rc = pcre_exec(alp->trailer_stopwatch_pattern, NULL, s, strlen(s), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part H: Failed to parse Stopwatch header"); - return -1; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, s + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch (i) { - case 1 : /* timestamp */ - // TODO Validate - alp->auditlog->timestamp = apr_atoi64(capture); - break; - case 2 : /* duration */ - // TODO Validate - alp->auditlog->duration = apr_atoi64(capture); - break; - case 3 : /* ignore (group of three further time elements)*/ - break; - case 4 : /* t1 */ - break; - case 5 : /* t2 */ - break; - case 6 : /* t3 */ - break; - } - } - - return 1; -} - -/** - * Parse the WebApp-Info trailer header. - */ -static int handle_part_H_parse_WebAppInfo(alp2_t *alp, const char *s) -{ - int ovector[33]; - int i, rc; - - // TODO This header is optional, but it is not allowed to appear more than once. - - rc = pcre_exec(alp->trailer_webappinfo_pattern, NULL, s, strlen(s), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part H: Failed to parse WebApp-Info header"); - return -1; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, s + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch (i) { - case 1 : /* application ID */ - // TODO Validate - alp->auditlog->application_id = capture; - break; - case 2 : /* session ID */ - // TODO Validate - if (strcmp(capture, "-") != 0) { - alp->auditlog->session_id = capture; - } - break; - case 3 : /* user ID */ - // TODO Validate - if (strcmp(capture, "-") != 0) { - alp->auditlog->user_id = capture; - } - break; - } - } - - return 1; -} - -/** - * Handle part H events. - */ -static void handle_part_H(alp2_t *alp, int event_type) -{ - /* Part data. */ - if (event_type == ALP2_EVENT_PART_DATA) { - char *line = alp2_pp_line_chomp(alp->pp); - - /* This part ends with an empty line. */ - if (strlen(line) == 0) { - alp->part_data_done = 1; - return; - } - - /* Extract the header information. */ - { - char *name = NULL, *value = NULL; - int ovector[33]; - int i, rc; - - /* Header line. */ - - /* Extract the fields. */ - rc = pcre_exec(alp->header_pattern, NULL, line, strlen(line), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part H: Failed to parse header: %i", rc); - return; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch(i) { - case 1 : - name = capture; - break; - case 2 : - value = capture; - break; - } - } - - /* Add header to the table. */ - apr_table_addn(alp->auditlog->trailer_headers, name, value); - } - - return; - } - - /* Part end. */ - if (event_type == ALP2_EVENT_PART_END) { - const apr_array_header_t *tarr = apr_table_elts(alp->auditlog->trailer_headers); - apr_table_entry_t *te = NULL; - const char *s = NULL; - int stopwatch = 0; - int rc = 0; - int i; - - if ((tarr == NULL) || (tarr->nelts == 0)) { - return; - } - - /* Here we are going to extract certain headers and - * parse them to populate the corresponding fields in - * the auditlog structure. - */ - - te = (apr_table_entry_t *)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - const char *key = te[i].key; - const char *val = te[i].val; - - if ((key == NULL) || (val == NULL)) { - continue; - } - - /* Action: optional */ - else if (strcmp("Action", key) == 0) { - rc = handle_part_H_parse_Action(alp, val); - } - - /* Message: optional */ - else if (strcmp("Message", key) == 0) { - rc = handle_part_H_parse_Message(alp, val); - } - - /* Apache-Handler: optional */ - else if (strcmp("Apache-Handler", key) == 0) { - rc = 0; - // TODO Only one allowed - alp->auditlog->handler = apr_pstrdup(alp->auditlog->mp, val); - } - - /* Producer: optional */ - else if (strcmp("Producer", key) == 0) { - rc = 0; - // TODO Only one allowed - alp->auditlog->producer = apr_pstrdup(alp->auditlog->mp, val); - } - - /* Server: optional */ - else if (strcmp("Server", key) == 0) { - rc = 0; - // TODO Only one allowed - alp->auditlog->server = apr_pstrdup(alp->auditlog->mp, val); - } - - /* Response-Body-Transformed: optional */ - else if (strcmp("Response-Body-Transformed", key) == 0) { - rc = 0; - // TODO Only one allowed - alp->auditlog->response_tfn = apr_pstrdup(alp->auditlog->mp, val); - } - - /* Stopwatch: required */ - else if (strcmp("Stopwatch", key) == 0) { - stopwatch = 1; - rc = handle_part_H_parse_Stopwatch(alp, val); - } - - /* WebApp-Info: optional */ - else if (strcmp("WebApp-Info", key) == 0) { - rc = handle_part_H_parse_WebAppInfo(alp, val); - } - - if (rc < 0) { - /* No need to report anything, it's already been reported. */ - } - } - - if (stopwatch == 0) { - add_error(alp, 1, "Part H: Stopwatch header missing"); - } - - return; - } -} - -/** - * Handle part F events. - */ -static void handle_part_F(alp2_t *alp, int event_type) -{ - /* Part data. */ - if (event_type == ALP2_EVENT_PART_DATA) { - char *line = alp2_pp_line_chomp(alp->pp); - - /* This part ends with an empty line. */ - if (strlen(line) == 0) { - alp->part_data_done = 1; - return; - } - - /* The first line should be the response line. */ - if (alp->part_line_counter == 1) { - int ovector[33]; - int i, rc; - - /* Response line. */ - - /* Extract the fields. */ - rc = pcre_exec(alp->response_line_pattern, NULL, line, strlen(line), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part F: Failed to parse response line: %i", rc); - return; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch(i) { - case 1 : - alp->auditlog->response_protocol = capture; - break; - case 2 : - alp->auditlog->response_status = atoi(capture); - break; - case 4 : - alp->auditlog->response_message = capture; - break; - break; - } - } - } - else { - char *name = NULL, *value = NULL; - int ovector[33]; - int i, rc; - - /* Response header line. */ - - /* Extract the fields. */ - rc = pcre_exec(alp->header_pattern, NULL, line, strlen(line), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part F: Failed to parse response header: %i", rc); - return; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch(i) { - case 1 : - name = capture; - break; - case 2 : - value = capture; - break; - } - } - - /* Add header to the table. */ - apr_table_addn(alp->auditlog->response_headers, name, value); - } - - return; - } - - /* Part end. */ - if (event_type == ALP2_EVENT_PART_END) { - /* If any of the response headers need - * special handling, place the code here. - */ - return; - } -} - -/** - * Parse the URI. APR-Util does most of the work here. - */ -static int handle_part_B_parse_uri(alp2_t *alp) -{ - char *u = (char *)alp->auditlog->request_uri; - apr_uri_t *uri = NULL; - - if (( alp->auditlog->request_method == NULL) - ||(alp->auditlog->request_uri == NULL)) - { - return 0; - } - - /* Since this is not a proper URI but a path, handle - * the leading double slash. - */ - while ((u[0] == '/') && (u[1] == '/')) { - u++; - } - - uri = apr_pcalloc(alp->auditlog->mp, sizeof(apr_uri_t)); - - if (strcasecmp(alp->auditlog->request_method, "CONNECT") == 0) { - if (apr_uri_parse_hostinfo(alp->auditlog->mp, u, uri) != APR_SUCCESS) { - add_error(alp, 0, "Info: Failed to parse request URI (hostinfo)"); - return -1; - } - } - else { - if (apr_uri_parse(alp->auditlog->mp, u, uri) != APR_SUCCESS) { - add_error(alp, 0, "Info: Failed to parse request URI"); - return -1; - } - } - - alp->auditlog->parsed_uri = uri; - - return 1; -} - -/** - * Handle part B events. - */ -static void handle_part_B(alp2_t *alp, int event_type) -{ - /* Part data. */ - if (event_type == ALP2_EVENT_PART_DATA) { - char *line = alp2_pp_line_chomp(alp->pp); - - /* This part ends with an empty line. */ - if (strlen(line) == 0) { - alp->part_data_done = 1; - return; - } - - /* The first line should be the request line. */ - if (alp->part_line_counter == 1) { - int ovector[33]; - int i, rc; - - /* Request line. */ - - /* Extract the fields. */ - rc = pcre_exec(alp->request_line_pattern, NULL, line, strlen(line), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part B: Failed to parse request line: %i", rc); - return; - } - - alp->auditlog->request_line_valid = 1; - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch(i) { - case 0 : - alp->auditlog->request_line = capture; - break; - case 1 : - alp->auditlog->request_method = capture; - break; - case 2 : - alp->auditlog->request_uri = capture; - if (handle_part_B_parse_uri(alp) != 1) { - // TODO Do we want to do anything on error? - } - break; - case 3 : - alp->auditlog->request_protocol = capture; - break; - } - } - } - else { - char *name = NULL, *value = NULL; - int ovector[33]; - int i, rc; - - /* Header line. */ - - /* Extract the fields. */ - rc = pcre_exec(alp->header_pattern, NULL, line, strlen(line), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part B: Failed to parse request header: %i", rc); - return; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch(i) { - case 1 : - name = capture; - break; - case 2 : - value = capture; - break; - } - } - - /* ModSecurity 1.9.x adds some requests headers of - * its own, and we don't want them. - */ - if (strncmp(name, "mod_security-", 13) != 0) { - /* Add header to the table. */ - apr_table_addn(alp->auditlog->request_headers, name, value); - } - } - - return; - } - - /* Part end. */ - if (event_type == ALP2_EVENT_PART_END) { - /* Determine hostname. */ - - // TODO I think the right thing to do is use the port numbers - // only when the host itself is a numerical IP. - - /* Try the URI first. */ - if ( (alp->auditlog->parsed_uri != NULL) - &&(alp->auditlog->parsed_uri->hostname != NULL)) - { - if ( (alp->auditlog->parsed_uri->port != 0) - && (alp->auditlog->parsed_uri->port != 80) - && (alp->auditlog->parsed_uri->port != 443) ) - { - // TODO Do not use the port number if the hostname - // is not numeric. - alp->auditlog->hostname = apr_psprintf(alp->auditlog->mp, "%s:%i", - alp->auditlog->parsed_uri->hostname, alp->auditlog->parsed_uri->port); - } - else { - // TODO Always use the port number if the hostname - // is numeric. - alp->auditlog->hostname = alp->auditlog->parsed_uri->hostname; - } - } - else { - /* Try the Host header. */ - char *s = (char *)apr_table_get(alp->auditlog->request_headers, "Host"); - if (s != NULL) { - // TODO If the hostname is not numeric, remove the port - // numbers if present. - alp->auditlog->hostname = s; - } - else { - /* Use the destination IP and port. */ - alp->auditlog->hostname = apr_psprintf(alp->auditlog->mp, "%s:%i", - alp->auditlog->dst_ip, alp->auditlog->dst_port); - } - } - - return; - } -} - -/** - * Handle part A events. - */ -static void handle_part_A(alp2_t *alp, int event_type) -{ - /* Part data. */ - if (event_type == ALP2_EVENT_PART_DATA) { - char *line = alp2_pp_line_chomp(alp->pp); - int ovector[33]; - int i, rc; - - /* This part can have only one line, - * so we don't expect to be here again. - */ - alp->part_data_done = 1; - - /* Extract the fields. */ - rc = pcre_exec(alp->part_a_pattern, NULL, line, strlen(line), 0, 0, ovector, 30); - if (rc < 0) { - add_error(alp, 1, "Part A: Parsing failed: %i", rc); - return; - } - - /* Loop through the captures. */ - for (i = 0; i < rc; i++) { - char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i], - ovector[2 * i + 1] - ovector[2 * i]); - - switch(i) { - case 1 : /* timestamp in Apache format */ - /* We don't need it as we use the one from the H part. */ - break; - case 2 : /* transaction ID */ - alp->auditlog->id = capture; - break; - case 3 : /* source address */ - // TODO Validate - alp->auditlog->src_ip = capture; - break; - case 4 : /* source port */ - // TODO Validate - alp->auditlog->src_port = atoi(capture); - break; - case 5 : /* destination address */ - // TODO Validate - alp->auditlog->dst_ip = capture; - break; - case 6 : /* destinatio port */ - // TODO Validate - alp->auditlog->dst_port = atoi(capture); - break; - } - } - - return; - } - - /* Part end. */ - if (event_type == ALP2_EVENT_PART_END) { - /* Place part post-validation here. */ - return; - } -} - -/** - * Create a new audit log data structure, allocating - * memory from the provided memory pool. - */ -auditlog2_t *alp2_auditlog_create(apr_pool_t *mp) -{ - auditlog2_t *al; - - /* Create a new memory pool and the - * auditlog structure in it. We will use the - * parent pool of the parser pool, in order to - * ensure the auditlog memory structures survive - * the death of the parser. - */ - al = apr_pcalloc(mp, sizeof(auditlog2_t)); - al->mp = mp; - - - al->request_headers = apr_table_make(al->mp, 20); - al->response_headers = apr_table_make(al->mp, 20); - al->trailer_headers = apr_table_make(al->mp, 20); - al->messages = apr_array_make(al->mp, 10, sizeof(const alp2_msg_t *)); - al->intercept_phase = -1; - - return al; -} - -/** - * Destroy the provided audit log entry. - */ -void alp2_auditlog_destroy(auditlog2_t *al) -{ - apr_pool_destroy(al->mp); -} - -/** - * Handle ALP2_EVENT_ENTRY_START. - */ -static void handle_entry_start(alp2_t *alp) -{ - /* Create a new data structure to hold the entry. */ - alp->auditlog = alp2_auditlog_create(alp->pp->current_entry->mp); - alp->auditlog->pp_entry = alp->pp->current_entry; - - /* Reset entry flags. */ - alp->previous_part_id = 0; - alp->seen_part_h = 0; - alp->parse_error = 0; - alp->errors = apr_array_make(alp->auditlog->mp, 4, sizeof(const char *)); -} - -/** - * Handle ALP2_EVENT_ENTRY_END. - */ -static void handle_entry_end(alp2_t *alp) -{ - if (alp->parse_error) { - /* No need to validate the entry since we've - * previously encountered a problem with it. - */ - } - else { - /* Final entry validation. */ - - /* Have we seen the H part? (We must have seen the A - * part, otherwise the entry would have begain in - * the first place. - */ - if (alp->seen_part_h == 0) { - add_error(alp, 1, "Entry does not have part H."); - } - } - - /* Invoke the upstream callback to handle the entry. */ - if (alp->user_callback(alp) == 0) { - alp->done = 1; - } - - /* Upstream owns the audit log entry now. */ - alp->auditlog = NULL; -} - -/** - * Handle ALP2_EVENT_PART_START. - */ -static void handle_part_start(alp2_t *alp) -{ - if (alp->parse_error) { - return; - } - - /* Reset part flags. */ - alp->part_line_counter = 0; - alp->part_data_done = 0; - - /* Is this part allowed/expected? */ - if (alp->previous_part_id == 0) { - if (alp->pp->current_part->id != 'A') { - add_error(alp, 1, "Expected part A but got %c.", alp->pp->current_part->id); - return; - } - } - - /* Invoke the appropriate part handler. */ - switch(alp->pp->current_part->id) { - case 'A' : - handle_part_A(alp, ALP2_EVENT_PART_START); - break; - case 'B' : - handle_part_B(alp, ALP2_EVENT_PART_START); - break; - case 'F' : - handle_part_F(alp, ALP2_EVENT_PART_START); - break; - case 'H' : - alp->seen_part_h = 1; - handle_part_H(alp, ALP2_EVENT_PART_START); - break; - default : - /* Ignore unknown part. */ - break; - } -} - -/* - * Handle ALP2_EVENT_PART_END. - */ -static void handle_part_end(alp2_t *alp) -{ - if (alp->parse_error) { - return; - } - - /* Invoke the appropriate part handler. */ - switch(alp->pp->current_part->id) { - case 'A' : - handle_part_A(alp, ALP2_EVENT_PART_END); - break; - case 'B' : - handle_part_B(alp, ALP2_EVENT_PART_END); - case 'F' : - handle_part_F(alp, ALP2_EVENT_PART_END); - break; - case 'H' : - handle_part_H(alp, ALP2_EVENT_PART_END); - break; - default : - /* Ignore unknown part. */ - break; - } - - /* Remember the last part processed. */ - alp->previous_part_id = alp->pp->current_part->id; -} - -/* - * Handle ALP2_EVENT_PART_DATA. - */ -static void handle_part_data(alp2_t *alp) -{ - if (alp->parse_error) { - return; - } - - alp->part_line_counter++; - - if (alp->part_data_done) { - add_error(alp, 1, "Unexpected data for part %c.", alp->pp->current_part->id); - return; - } - - /* Invoke the appropriate part handler. */ - switch(alp->pp->current_part->id) { - case 'A' : - handle_part_A(alp, ALP2_EVENT_PART_DATA); - break; - case 'B' : - handle_part_B(alp, ALP2_EVENT_PART_DATA); - break; - case 'F' : - handle_part_F(alp, ALP2_EVENT_PART_DATA); - break; - case 'H' : - handle_part_H(alp, ALP2_EVENT_PART_DATA); - break; - default : - /* Ignore unknown part. */ - break; - } -} - -/** - * This function handles callbacks from - * the lower-level (part) parser. - */ -static int alp2_callback(alp2_pp_t *pp, int event_type) -{ - alp2_t *alp = (alp2_t *)pp->user_data; - - /* Choose where to dispatch the event based - * on the event type. - */ - switch(event_type) { - case ALP2_EVENT_ENTRY_START : - handle_entry_start(alp); - break; - case ALP2_EVENT_ENTRY_END : - handle_entry_end(alp); - break; - case ALP2_EVENT_PART_START : - handle_part_start(alp); - break; - case ALP2_EVENT_PART_END : - handle_part_end(alp); - break; - case ALP2_EVENT_PART_DATA : - handle_part_data(alp); - break; - default : - /* Unexpected event type. */ - break; - } - - if (alp->done) { - /* Stop parsing. */ - return 0; - } - else { - /* Go on. */ - return 1; - } -} - -/** - * Initialise parser. - */ -// XXX Make callback a typedef -int alp2_create(alp2_t **_alp, apr_pool_t *mp, - void *user_data, int (*user_callback)(alp2_t *alp)) -{ - alp2_t *alp; - apr_pool_t *new_pool; - const char *errptr = NULL; - int erroffset; - - /* We require a callback. */ - if (user_callback == NULL) { - return -1; - } - - /* We will use our own memory pool. */ - apr_pool_create(&new_pool, mp); - alp = apr_pcalloc(mp, sizeof(alp2_t)); - *_alp = alp; - alp->mp = new_pool; - - alp->user_data = user_data; - alp->user_callback = user_callback; - - /* Initialise the part parser. */ - alp->pp = apr_pcalloc(mp, sizeof(alp2_pp_t)); - if (alp->pp == NULL) return -1; - if (alp2_pp_init(alp->pp, alp, alp2_callback, mp) < 0) { - return -2; - } - - /* Compile the patterns we use for parsing. */ - - /* part A pattern */ - if ((alp->part_a_pattern = pcre_compile( - "^\\[(.+)\\] (\\S+) ([.:0-9a-f]+) (\\d+) ([.:0-9a-f]+) (\\d+)$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -3; - } - - /* request line pattern */ - if ((alp->request_line_pattern = pcre_compile( - // TODO Needs improving (e.g. to support simplified HTTP/0.9 requests - "^(\\S+) (.*?) (HTTP/\\d\\.\\d)$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -4; - } - - /* header pattern */ - if ((alp->header_pattern = pcre_compile( - "^([^:]+):\\s*(.+)$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -5; - } - - /* response line pattern */ - if ((alp->response_line_pattern = pcre_compile( - "^(HTTP/\\d\\.\\d) (\\d{3})( (.+))?$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -6; - } - - /* Action trailer header pattern */ - if ((alp->trailer_action_pattern = pcre_compile( - "^Intercepted \\(phase (\\d)\\)$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -7; - } - - /* Stopwatch trailer header pattern */ - if ((alp->trailer_stopwatch_pattern = pcre_compile( - "^(\\d+) (\\d+)( \\((-|\\d+)\\*? (-|\\d+) (-|\\d+)\\))?$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -8; - } - - /* WebApp-Info trailer header pattern */ - if ((alp->trailer_webappinfo_pattern = pcre_compile( - "^\"(.*)\" \"(.*)\" \"(.*)\"$", - PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL) - { - return -9; - } - - return 1; -} - -/** - * Process a piece of a stream of audit log entries. - */ -int alp2_process(alp2_t *alp, const char *data, size_t len) -{ - alp2_pp_process(alp->pp, data, len); - - if (alp->done) { - return 0; - } - else { - return 1; - } -} - -/** - * Destroy the parser. - */ -void alp2_destroy(alp2_t *alp) -{ - apr_pool_destroy(alp->mp); -} - diff --git a/alp2/alp2.h b/alp2/alp2.h deleted file mode 100644 index 2a159d5627..0000000000 --- a/alp2/alp2.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _ALP2_H_ -#define _ALP2_H_ - -#include -#include - -#include "alp2_pp.h" -#include "pcre.h" - -/* -- Data structures -- */ - -typedef struct alp2_msg_t alp2_msg_t; - -struct alp2_msg_t { - const char *engine_message; - const char *target; - const char *id; - const char *rev; - const char *msg; - const char *data; - const char *file; - unsigned long file_line; - size_t offset; - int severity; - int warning; - apr_array_header_t *tags; -}; - -typedef struct auditlog2_t auditlog2_t; - -struct auditlog2_t { - apr_pool_t *mp; - - /* Transaction data */ - - const char *id; - - apr_time_t timestamp; - unsigned int duration; - - const char *src_ip; - unsigned int src_port; - const char *dst_ip; - unsigned int dst_port; - - /* Request */ - - unsigned int request_line_valid; - const char *request_line; - const char *request_method; - const char *request_uri; - apr_uri_t *parsed_uri; - const char *request_protocol; - apr_table_t *request_headers; - - /* Determine the hostname: The hostname from the URI is - * used where present, otherwise the value of the Host - * request header is used. - * - * If neither of these two is available we will use the - * combination of the destination IP and port as hostname. - * - * The resulting hostname may have the port attached. - */ - const char *hostname; - - /* Response */ - - const char *response_protocol; - unsigned int response_status; - const char *response_message; - apr_table_t *response_headers; - const char *response_tfn; - - /* Other */ - - apr_table_t *trailer_headers; - - unsigned int was_intercepted; - unsigned int intercept_phase; /* -1 if interception did not happen */ - - const char *producer; - const char *server; - const char *handler; - - const char *application_id; - const char *session_id; - const char *user_id; - - apr_array_header_t *messages; - - alp2_pp_entry_t *pp_entry; -}; - -typedef struct alp2_t alp2_t; - -struct alp2_t { - apr_pool_t *mp; - - void *user_data; - int (*user_callback)(alp2_t *alp); - - alp2_pp_t *pp; - - unsigned int previous_part_id; - unsigned int part_line_counter; - unsigned int part_data_done; - unsigned int seen_part_h; - - unsigned int done; - unsigned int parse_error; - apr_array_header_t *errors; - - /* Regular expression patterns. */ - // TODO All these need reviewing - pcre *part_a_pattern; - pcre *request_line_pattern; - pcre *header_pattern; - pcre *response_line_pattern; - - pcre *trailer_action_pattern; - pcre *trailer_stopwatch_pattern; - pcre *trailer_webappinfo_pattern; - - auditlog2_t *auditlog; -}; - -/* Higher-level (user) parser. */ - -/* NOTE Parser will create a subpool for its own use, but each - * entry will be created in a separate subpool directly - * under the main pool. This allows the created audit log - * entries to survive the death of the parser. - */ - - -/* -- Functions -- */ - -int alp2_create(alp2_t **_alp, apr_pool_t *mp, - void *user_data, int (*user_callback)(alp2_t *alp)); - -int alp2_process(alp2_t *alp, const char *data, size_t len); - -void alp2_destroy(alp2_t *alp); - -auditlog2_t *alp2_auditlog_create(apr_pool_t *mp); - -void alp2_auditlog_destroy(auditlog2_t *al); - -#endif diff --git a/alp2/alp2_pp.c b/alp2/alp2_pp.c deleted file mode 100755 index 5649dc1854..0000000000 --- a/alp2/alp2_pp.c +++ /dev/null @@ -1,362 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include -#include - -#include "alp2_pp.h" - -/** - * Take the line in the buffer and replace the new line - * at the end with a NUL byte. - */ -char *alp2_pp_line_chomp(alp2_pp_t *pp) { - if (pp->line_pos == 0) { - pp->line_buf[0] = '\0'; - } - else { - pp->line_buf[pp->line_pos - 1] = '\0'; - } - - return &(pp->line_buf[0]); -} - -/** - * Look into the line buffer to determine if it - * contains a boundary line. - */ -static int alp2_pp_is_boundary_line(alp2_pp_t *alp_pp) { - char *new_boundary = NULL; - unsigned int id; - size_t i; - - /* A boundary line cannot be less than 14 characters long. */ - if (alp_pp->line_pos < 15) { - return 0; - } - - /* The first two characters must both be dashes. */ - if ((alp_pp->line_buf[0] != '-')||(alp_pp->line_buf[1] != '-')) { - return 0; - } - - /* Extract the boundary. */ - i = 2; /* Start after the second dash. */ - while((isxdigit(alp_pp->line_buf[i]))&&(i < alp_pp->line_pos)) { - i++; - } - - /* The boundary cannot be shorter than 8 characters. */ - if (i - 2 < 8) { - return 0; - } - - // TODO Memory leak; use a single parser buffer to avoid per-entry - // allocation from the parser pool. - new_boundary = apr_pstrndup(alp_pp->mp, &(alp_pp->line_buf[2]), i - 2); - - /* Check if the rest of the line is valid. */ - if ( (i + 5 < alp_pp->line_pos) /* Need at lest 5 more bytes. */ - ||(alp_pp->line_buf[i + 0] != '-') - ||(alp_pp->line_buf[i + 1] < 'A') - ||(alp_pp->line_buf[i + 1] > 'Z') - ||(alp_pp->line_buf[i + 2] != '-') - ||(alp_pp->line_buf[i + 3] != '-') - ||(alp_pp->line_buf[i + 4] != '\n') ) - { - return 0; - } - - id = alp_pp->line_buf[i + 1]; - - /* Are we in a middle of an entry right now? */ - if (alp_pp->current_entry == NULL) { - /* We will accept a new boundary. */ - alp_pp->boundary = new_boundary; - - return id; - } - else { - /* The boundary must match the boundary of - * the entry we are currently working on. - */ - if (strcmp(alp_pp->current_entry->boundary, new_boundary) != 0) { - return 0; - } - else { - return id; - } - } - - return 0; -} - -/** - * Process data belonging to a single part. - */ -static void alp2_pp_process_part_data(alp2_pp_t *alp_pp) { - if (alp_pp->current_part == NULL) { - return; - } - - /* Invoke part processor. */ - if (alp_pp->callback != NULL) { - if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_DATA) == 0) { - alp_pp->done = 1; - } - } - - /* Keep track of part size. */ - alp_pp->current_part->size += alp_pp->line_pos; - - /* Update the MD5 hash calculation. */ - if ((alp_pp->current_entry != NULL)&&(alp_pp->line_pos > 0)) { - apr_md5_update(alp_pp->current_entry->md5_context, &alp_pp->line_buf[0], alp_pp->line_pos - 1); - } -} - -/** - * Initialise parser. - */ -int alp2_pp_init(alp2_pp_t *alp_pp, void *user_data, - int (*callback)(alp2_pp_t *alp_pp, int event_type), apr_pool_t *mp) -{ - memset(alp_pp, 0, sizeof(alp2_pp_t)); - - alp_pp->user_data = user_data; - alp_pp->callback = callback; - alp_pp->mp = mp; /* Use the parent pool directly. */ - - /* Set-up the line buffer. */ - alp_pp->line_buf = apr_pcalloc(mp, ALP2_MAX_LINE_SIZE); - alp_pp->line_size = ALP2_MAX_LINE_SIZE; - alp_pp->line_has_start = 1; - alp_pp->line_offset = 0; - - return 1; -} - -/** - * Process data the parser has stored in the input buffer. - */ -static apr_status_t alp2_pp_process_internal(alp2_pp_t *alp_pp) { - /* Do not proceed if we've previously - * encountered a fatal error. - */ - if (alp_pp->errored != 0) { - return ALP2_ERROR_FATAL; - } - - if (alp_pp->done) { - return ALP2_DONE; - } - - /* Go back straight away if we don't have anything to work with. */ - if (alp_pp->input_len == 0) { - return ALP2_NEED_DATA; - } - - while (alp_pp->input_pos < alp_pp->input_len) { - int c; - - if (alp_pp->done) { - return ALP2_DONE; - } - - if (alp_pp->line_pos >= alp_pp->line_size) { - /* Our line buffer is full with the - * line incomplete. - */ - alp2_pp_process_part_data(alp_pp); - - /* Reset line buffer . */ - alp_pp->line_pos = 0; - alp_pp->line_has_start = 0; - alp_pp->line_offset = alp_pp->current_offset; - } - - /* Consume one byte. */ - c = alp_pp->input_buf[alp_pp->input_pos]; - alp_pp->input_pos++; - alp_pp->current_offset++; - - /* Copy the byte to the line buffer. */ - alp_pp->line_buf[alp_pp->line_pos] = c; - alp_pp->line_pos++; - - /* Are we at the end of a line? */ - if (c == '\n') { - if (alp_pp->line_has_start) { - /* We have one complete line. */ - - int id = alp2_pp_is_boundary_line(alp_pp); - - if (id != 0) { - /* The line is a boundary. */ - - /* Finish with the previous part, if any. */ - if (alp_pp->current_part != NULL) { - /* Update the MD5 context. */ - apr_md5_update(alp_pp->current_entry->md5_context, - &alp_pp->line_buf[0], alp_pp->line_pos - 1); - - /* Event PART_END. */ - if (alp_pp->callback != NULL) { - if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_END) == 0) { - alp_pp->done = 1; - } - } - - /* Add part to the current entry. */ - *(alp2_pp_part_t **)apr_array_push(alp_pp->current_entry->parts) - = alp_pp->current_part; - - /* Delete part. */ - alp_pp->current_part = NULL; - - /* If the new part is part Z, then finish - * with the current entry. */ - if (id == 'Z') { - alp_pp->current_entry->size = alp_pp->current_offset - alp_pp->current_entry->offset; - - /* Create the MD5 digest. */ - apr_md5_final(alp_pp->current_entry->md5_digest, - alp_pp->current_entry->md5_context); - - /* Event ENTRY_END. */ - if (alp_pp->callback != NULL) { - if (alp_pp->callback(alp_pp, ALP2_EVENT_ENTRY_END) == 0) { - alp_pp->done = 1; - } - } - - /* We are about to destroy our only reference to the per-entry - * memory pool, but that is all right since we've passed all - * responsibility for the entry to the higher-level handler. - */ - alp_pp->current_entry = NULL; - } - } - - if (id != 'Z') { - /* Create new entry if necessary. */ - if (alp_pp->current_entry == NULL) { - apr_pool_t *new_pool = NULL; - - /* Create a per-entry pool directly from the main memory pool. */ - apr_pool_create(&new_pool, apr_pool_parent_get(alp_pp->mp)); - - alp_pp->current_entry = apr_pcalloc(new_pool, sizeof(alp2_pp_entry_t)); - alp_pp->current_entry->mp = new_pool; - alp_pp->current_entry->offset = alp_pp->line_offset; - alp_pp->current_entry->boundary = apr_pstrdup(new_pool, alp_pp->boundary); - alp_pp->boundary = NULL; - - alp_pp->current_entry->parts = apr_array_make(alp_pp->current_entry->mp, - 16, sizeof(alp2_pp_part_t *)); - - /* Initialise the MD5 context. */ - alp_pp->current_entry->md5_context = apr_pcalloc(alp_pp->current_entry->mp, - sizeof(apr_md5_ctx_t)); - apr_md5_init(alp_pp->current_entry->md5_context); - - /* Start calculating the has with the first line. */ - apr_md5_update(alp_pp->current_entry->md5_context, &alp_pp->line_buf[0], alp_pp->line_pos - 1); - - /* Event ENTRY_START. */ - if (alp_pp->callback != NULL) { - if (alp_pp->callback(alp_pp, ALP2_EVENT_ENTRY_START) == 0) { - alp_pp->done = 1; - } - } - } - - /* Create new part, but only if we are not - * dealing with an entry terminator. - */ - alp_pp->current_part = apr_pcalloc(alp_pp->current_entry->mp, sizeof(alp2_pp_part_t)); - alp_pp->current_part->id = id; - alp_pp->current_part->offset = alp_pp->current_offset; - - /* Event PART_START. */ - if (alp_pp->callback != NULL) { - if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_START) == 0) { - alp_pp->done = 1; - } - } - } - } - else { - /* The line does not contain a boundary, - * so process it as part data. - */ - alp2_pp_process_part_data(alp_pp); - } - } - else { - /* We have a chunk of data that is not a line, which - * probably means that our buffer was not big enough, either - * because the line (is a line and it) was too big, or because - * we are processing binary data. Ideally the latter. - */ - alp2_pp_process_part_data(alp_pp); - } - - /* Reset the line buffer. */ - alp_pp->line_pos = 0; - alp_pp->line_has_start = 1; - alp_pp->line_offset = alp_pp->current_offset; - } - } - - if (alp_pp->done) { - return ALP2_DONE; - } - else { - return ALP2_NEED_DATA; - } -} - -/** - * Process the provided data. - */ -int alp2_pp_process(alp2_pp_t *alp_pp, const char *data, size_t len) { - /* Do not proceed if we've previously - * encountered a fatal error. - */ - if (alp_pp->errored != 0) { - return ALP2_ERROR_FATAL; - } - - /* Check that we've used up the existing buffer. */ - if (alp_pp->input_pos < alp_pp->input_len) { - return ALP2_ERROR_INCORRECT_STATE; - } - - alp_pp->input_buf = data; - alp_pp->input_len = len; - alp_pp->input_pos = 0; - - return alp2_pp_process_internal(alp_pp); -} - -/** - * Clean-up the parser structures. - */ -void alp2_pp_terminate(alp2_pp_t *alp_pp) { - /* Nothing to do, but we may need - * to do something in the future. - */ -} diff --git a/alp2/alp2_pp.h b/alp2/alp2_pp.h deleted file mode 100644 index ba5c090946..0000000000 --- a/alp2/alp2_pp.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _ALP2_LL_H_ -#define _ALP2_LL_H_ - -#include -#include -#include -#include -#include - -/* -- Constants -- */ - -#define ALP2_MAX_LINE_SIZE 16384 - -#define ALP2_ERROR_INCORRECT_STATE -1001 -#define ALP2_ERROR_FATAL -1002 - -#define ALP2_DONE 0 -#define ALP2_NEED_DATA 1 - -#define ALP2_EVENT_ENTRY_START 1 -#define ALP2_EVENT_ENTRY_END 2 -#define ALP2_EVENT_PART_START 3 -#define ALP2_EVENT_PART_END 4 -#define ALP2_EVENT_PART_DATA 5 - - -/* -- Data structures -- */ - -typedef struct alp2_pp_part_t alp2_pp_part_t; - -struct alp2_pp_part_t { - int id; // XXX int here but unsigned int other places??? - - /* Relative to the beginning of the entry, not - * including the boundary lines. Just content. - */ - size_t offset; - - size_t size; -}; - -typedef struct alp2_pp_entry_t alp2_pp_entry_t; - -struct alp2_pp_entry_t { - apr_pool_t *mp; - apr_array_header_t *parts; - - /* Entry offset and size include - * the delimiting boundaries. - */ - size_t offset; - size_t size; - - const char *boundary; - - apr_md5_ctx_t *md5_context; - uint8_t md5_digest[APR_MD5_DIGESTSIZE]; -}; - -typedef struct alp2_pp_t alp2_pp_t; - -struct alp2_pp_t { - void *user_data; - int (*callback)(alp2_pp_t *alp, int event_type); - - /* The memory pool used during the parsing of - * individual audit log entries. Cleared between - * entries. - */ - apr_pool_t *mp; - - unsigned int errored; - unsigned int done; - - const char *boundary; - char *last_processed_part; - char *current_line; - - /* The number of bytes processed since - * the beginning or the last reset. - */ - size_t current_offset; - - const char *input_buf; - size_t input_len; - size_t input_pos; - - char *line_buf; - size_t line_pos; - size_t line_size; - unsigned int line_has_start; - size_t line_offset; - - alp2_pp_part_t *current_part; - alp2_pp_entry_t *current_entry; -}; - - -/* Functions. */ - -int alp2_pp_init(alp2_pp_t *alp_pp, void *user_data, - int (*callback)(alp2_pp_t *alp, int event_type), apr_pool_t *mp); - -int alp2_pp_process(alp2_pp_t *alp_pp, const char *data, size_t len); - -void alp2_pp_terminate(alp2_pp_t *alp_pp); - -char *alp2_pp_line_chomp(alp2_pp_t *alp_pp); - -#endif diff --git a/apache2/Makefile.am b/apache2/Makefile.am deleted file mode 100644 index 550be2edc5..0000000000 --- a/apache2/Makefile.am +++ /dev/null @@ -1,86 +0,0 @@ -pkglibdir = $(prefix)/lib -pkglib_LTLIBRARIES = mod_security2.la -#include_HEADERS = re.h modsecurity.h msc_logging.h msc_multipart.h \ -# msc_parsers.h msc_pcre.h msc_util.h msc_xml.h \ -# persist_dbm.h apache2.h msc_geo.h acmp.h utf8tables.h \ -# msc_lua.h msc_release.h - -mod_security2_la_SOURCES = mod_security2.c \ - apache2_config.c apache2_io.c apache2_util.c \ - re.c re_operators.c re_actions.c re_tfns.c \ - re_variables.c msc_logging.c msc_xml.c \ - msc_multipart.c modsecurity.c msc_parsers.c \ - msc_util.c msc_pcre.c persist_dbm.c msc_reqbody.c \ - msc_geo.c msc_gsb.c msc_crypt.c msc_tree.c msc_unicode.c acmp.c msc_lua.c msc_release.c \ - libinjection/libinjection_sqli.c - -mod_security2_la_CFLAGS = @APXS_CFLAGS@ @APR_CFLAGS@ @APU_CFLAGS@ \ - @PCRE_CFLAGS@ @LIBXML2_CFLAGS@ @LUA_CFLAGS@ @MODSEC_EXTRA_CFLAGS@ @CURL_CFLAGS@ -mod_security2_la_CPPFLAGS = @APR_CPPFLAGS@ @PCRE_CPPFLAGS@ @LIBXML2_CPPFLAGS@ -mod_security2_la_LIBADD = @APR_LDADD@ @APU_LDADD@ @PCRE_LDADD@ @LIBXML2_LDADD@ @LUA_LDADD@ - -if AIX -mod_security2_la_LDFLAGS = -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if HPUX -mod_security2_la_LDFLAGS = -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if MACOSX -mod_security2_la_LDFLAGS = -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if SOLARIS -mod_security2_la_LDFLAGS = -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if LINUX -mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version -R @PCRE_LD_PATH@ \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if FREEBSD -mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if OPENBSD -mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if NETBSD -mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \ - @APR_LDFLAGS@ @APU_LDFLAGS@ @APXS_LDFLAGS@ \ - @PCRE_LDFLAGS@ @LIBXML2_LDFLAGS@ @LUA_LDFLAGS@ -endif - -if LINUX -install-exec-hook: $(pkglib_LTLIBRARIES) - @echo "Removing unused static libraries..."; \ - for m in $(pkglib_LTLIBRARIES); do \ - base=`echo $$m | sed 's/\..*//'`; \ - rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \ - install -D -m444 $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES)/$$base.so; \ - done -else -install-exec-hook: $(pkglib_LTLIBRARIES) - @echo "Removing unused static libraries..."; \ - for m in $(pkglib_LTLIBRARIES); do \ - base=`echo $$m | sed 's/\..*//'`; \ - rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \ - cp -p $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES); \ - done -endif diff --git a/apache2/Makefile.win b/apache2/Makefile.win deleted file mode 100644 index 31a83a99cd..0000000000 --- a/apache2/Makefile.win +++ /dev/null @@ -1,69 +0,0 @@ -########################################################################### -# -# Usage: NMAKE -f Makefile.win APACHE={httpd installion dir} PCRE={pcre dir} LIBXML2={LibXML2 dir} [ LUA={Lua dir} ] -# -!IF "$(APACHE)" == "" || "$(PCRE)" == "" || "$(LIBXML2)" == "" -!ERROR NMAKE arguments: APACHE=dir PCRE=dir LIBXML2=dir are required to build mod_security2 for Windows -!ENDIF - -# Linking libraries -LIBS = $(APACHE)\lib\libhttpd.lib \ - $(APACHE)\lib\libapr-1.lib \ - $(APACHE)\lib\libaprutil-1.lib \ - $(PCRE)\pcre.lib \ - $(LIBXML2)\win32\bin.msvc\libxml2.lib \ - Ws2_32.lib - -########################################################################### -########################################################################### - -CC = CL - -MT = mt - -DEFS = /nologo /O2 /LD /W3 /wd4244 /wd4018 -DWIN32 -DWINNT -Dinline=APR_INLINE -D$(VERSION) - -DLL = mod_security2.so - -INCLUDES = -I. -I.. \ - -I$(PCRE)\include -I$(PCRE) \ - -I$(LIBXML2)\include \ - -I$(APACHE)\include - -# Lua is optional -!IF "$(LUA)" != "" -LIBS = $(LIBS) $(LUA)\lua5.1.lib -DEFS=$(DEFS) -DWITH_LUA -INCLUDES = $(INCLUDES) -I$(LUA)\include -I$(LUA) \ -!ENDIF - -CFLAGS= -MD $(INCLUDES) $(DEFS) - -LDFLAGS = - -OBJS = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \ - re.obj re_operators.obj re_actions.obj re_tfns.obj re_variables.obj \ - msc_logging.obj msc_xml.obj msc_multipart.obj modsecurity.obj \ - msc_parsers.obj msc_util.obj msc_pcre.obj persist_dbm.obj \ - msc_reqbody.obj msc_geo.obj msc_gsb.obj msc_crypt.obj msc_tree.obj msc_unicode.obj acmp.obj msc_lua.obj \ - msc_release.obj libinjection\libinjection_sqli.obj - -all: $(DLL) - -dll: $(DLL) - -.c.obj: - $(CC) $(CFLAGS) -c $< -Fo$@ - -.cpp.obj: - $(CC) $(CFLAGS) -c $< -Fo$@ - -$(DLL): $(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) -LD $(OBJS) -Fe$(DLL) $(LIBS) /link - IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);2 - -install: $(DLL) - copy /Y $(DLL) $(APACHE)\modules - -clean: - del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin *.manifest diff --git a/apache2/acmp.c b/apache2/acmp.c deleted file mode 100644 index 6e796b38db..0000000000 --- a/apache2/acmp.c +++ /dev/null @@ -1,597 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -/* Aho-Corasick Matching */ - -#include "acmp.h" - -#ifdef ACMP_USE_UTF8 -/* UTF support */ -#include "utf8tables.h" -#else -/* No UTF support */ -#define acmp_utf8_char_t long -#include -#define utf8_lcase(a) apr_tolower(a) -#endif - -#include -#include -#include - - -/* - ******************************************************************************* - ******************************************************************************* - * Data structures for acmp parser - */ - -/** - * One node in trie - */ -typedef struct acmp_node_t acmp_node_t; -typedef struct acmp_btree_node_t acmp_btree_node_t; -struct acmp_node_t { - acmp_utf8_char_t letter; - int is_last; - acmp_callback_t callback; - void *callback_data; - int depth; - - acmp_node_t *child; - acmp_node_t *sibling; - acmp_node_t *fail; - acmp_node_t *parent; - acmp_node_t *o_match; - - acmp_btree_node_t *btree; - - apr_size_t hit_count; - - char *text; - char *pattern; -}; - -struct acmp_btree_node_t { - acmp_utf8_char_t letter; - acmp_btree_node_t *left; - acmp_btree_node_t *right; - acmp_node_t *node; -}; - -/** - * Data related to parser, not to individual nodes - */ -struct ACMP { -#ifdef ACMP_USE_UTF8 - int is_utf8; -#endif - int is_case_sensitive; - apr_pool_t *parent_pool; - apr_pool_t *pool; - - int dict_count; - apr_size_t longest_entry; - - acmp_node_t *root_node; - - const char *data_start; - const char *data_end; - const char *data_pos; - apr_size_t data_len; - - apr_size_t *bp_buffer; - apr_size_t bp_buff_len; - - acmp_node_t *active_node; - char u8_buff[6]; - apr_size_t u8buff_len; - apr_size_t hit_count; - int is_failtree_done; - int is_active; - apr_size_t byte_pos; - apr_size_t char_pos; -}; - -/* - ******************************************************************************* - ******************************************************************************* - * Functions for UTF-8 support - */ - -#ifdef ACMP_USE_UTF8 -/** - * Returns length of utf-8 sequence based on its first byte - */ -static int utf8_seq_len(const char *first_byte) { - return utf8_seq_lengths[(unsigned int)(unsigned char)first_byte[0]]; -} - -/** - * Returns length of utf8-encoded text - */ -static size_t utf8_strlen(const char *str) { - int len = 0; - const char *c = str; - while (*c != 0) { - c += utf8_seq_len(c); - len++; - } - return len; -} - -/** - * Returns ucs code for given utf-8 sequence - */ -static acmp_utf8_char_t utf8_decodechar(const char *str) { - int len = utf8_seq_len(str); - acmp_utf8_char_t ch = 0; - switch (len) { - case 6: ch += (unsigned char)*str++; ch <<= 6; - case 5: ch += (unsigned char)*str++; ch <<= 6; - case 4: ch += (unsigned char)*str++; ch <<= 6; - case 3: ch += (unsigned char)*str++; ch <<= 6; - case 2: ch += (unsigned char)*str++; ch <<= 6; - case 1: ch += (unsigned char)*str++; - } - ch -= utf8_offsets[len - 1]; - return ch; -} - -/** - * Returns lowercase for given unicode character. Searches through - * utf8_lcase_map table, if it doesn't find the code assumes - * it doesn't have a lowercase variant and returns code itself. - */ -static long utf8_lcase(acmp_utf8_char_t ucs_code) { - long mid, left, right; - left = 1; - right = UTF8_LCASEMAP_LEN * 2 + 1; - - while (left <= right) { - mid = (left + right) >> 1; - mid -= (mid % 2); mid++; - if (ucs_code > utf8_lcase_map[mid]) - left = mid + 2; - else if (ucs_code < utf8_lcase_map[mid]) - right = mid - 2; - else if (ucs_code == utf8_lcase_map[mid]) - return utf8_lcase_map[mid - 1]; - } - return ucs_code; -} -#endif - -/* - ******************************************************************************* - ******************************************************************************* - * Code for local / static utility functions - */ - -/** - * Returns length of given string for parser's encoding - */ -static size_t acmp_strlen(ACMP *parser, const char *str) { -#ifdef ACMP_USE_UTF8 - return (parser->is_utf8 == 0) ? strlen(str) : utf8_strlen(str); -#else - return strlen(str); -#endif -} - -/** - * Turns string to array of ucs values, depending on parser's encoding - * str - string to convert, doesn't have to be NULL-terminated - * ucs_chars - where to write ucs values - * len - length of input string - */ -static void acmp_strtoucs(ACMP *parser, const char *str, acmp_utf8_char_t *ucs_chars, int len) { - int i; - const char *c = str; - -#ifdef ACMP_USE_UTF8 - if (parser->is_utf8) { - for (i = 0; i < len; i++) { - *(ucs_chars++) = utf8_decodechar(c); - c += utf8_seq_len(c); - } - } else -#endif - { - for (i = 0; i < len; i++) { - *(ucs_chars++) = *(c++); - } - } -} - -/** - * Returns node with given letter, or null if not found - */ -static acmp_node_t *acmp_child_for_code(acmp_node_t *parent_node, acmp_utf8_char_t ucs_code) { - acmp_node_t *node = parent_node->child; - if (node == NULL) return NULL; - for (;;) { - if (node->letter == ucs_code) return node; - node = node->sibling; - if (node == NULL) return NULL; - } -} - -/** - * Adds node to parent node, if it is not already there - */ -static void acmp_add_node_to_parent(acmp_node_t *parent, acmp_node_t *child) { - acmp_node_t *node = NULL; - - child->parent = parent; - if (parent->child == NULL) { - parent->child = child; - return; - } - - node = parent->child; - for (;;) { - if (node == child) return; - if (node->sibling == NULL) { - node->sibling = child; - return; - } - node = node->sibling; - } -} - -/** - * Copies values from one node to another, without child/sibling/fail pointers - * and without state variables. - */ -static void acmp_clone_node_no_state(acmp_node_t *from, acmp_node_t *to) { - memcpy(to, from, sizeof(acmp_node_t)); - to->child = NULL; - to->sibling = NULL; - to->fail = NULL; - to->hit_count = 0; -} - -static inline acmp_node_t *acmp_btree_find(acmp_node_t *node, acmp_utf8_char_t letter) { - acmp_btree_node_t *bnode = node->btree; - for (;;) { - if (bnode == NULL) return NULL; - if (bnode->letter == letter) return bnode->node; - if (bnode->letter > letter) { - bnode = bnode->left; - } else { - bnode = bnode->right; - } - } -} - -/** - * - */ -static inline acmp_node_t *acmp_goto(acmp_node_t *node, acmp_utf8_char_t letter) { - return acmp_btree_find(node, letter); -} - -/** - * Connects each node with its first fail node that is end of a phrase. - */ -static void acmp_connect_other_matches(ACMP *parser, acmp_node_t *node) { - acmp_node_t *child, *om; - - for (child = node->child; child != NULL; child = child->sibling) { - if (child->fail == NULL) continue; - for (om = child->fail; om != parser->root_node; om = om->fail) { - if (om->is_last) { - child->o_match = om; - break; - } - } - } - - /* Go recursively through children of this node that have a child node */ - for(child = node->child; child != NULL; child = child->sibling) { - if (child->child != NULL) acmp_connect_other_matches(parser, child); - } -} - -/** - * Adds leaves to binary tree, working from sorted array of keyword tree nodes - */ -static void acmp_add_btree_leaves(acmp_btree_node_t *node, acmp_node_t *nodes[], - int pos, int lb, int rb, apr_pool_t *pool) { - - int left = 0, right = 0; - if ((pos - lb) > 1) { - left = lb + (pos - lb) / 2; - node->left = apr_pcalloc(pool, sizeof(acmp_btree_node_t)); - /* ENH: Check alloc succeded */ - node->left->node = nodes[left]; - node->left->letter = nodes[left]->letter; -#ifdef DEBUG_ACMP - fprintf(stderr, "%lc ->left %lc\n", (wint_t)node->node->letter, (wint_t)node->left->node->letter); -#endif - } - if ((rb - pos) > 1) { - right = pos + (rb - pos) / 2; - node->right = apr_pcalloc(pool, sizeof(acmp_btree_node_t)); - /* ENH: Check alloc succeded */ - node->right->node = nodes[right]; - node->right->letter = nodes[right]->letter; -#ifdef DEBUG_ACMP - fprintf(stderr, "%lc ->right %lc\n", (wint_t)node->node->letter, (wint_t)node->right->node->letter); -#endif - } - if (node->right != NULL) { - acmp_add_btree_leaves(node->right, nodes, right, pos, rb, pool); - } - if (node->left != NULL) { - acmp_add_btree_leaves(node->left, nodes, left, lb, pos, pool); - } -} - -/** - * Builds balanced binary tree from children nodes of given node. - */ -static void acmp_build_binary_tree(ACMP *parser, acmp_node_t *node) { - apr_size_t count, i, j; - acmp_node_t *child = node->child; - acmp_node_t **nodes; - apr_size_t pos; - - /* Build an array big enough */ - for (count = 0; child != NULL; child = child->sibling) count++; - nodes = apr_pcalloc(parser->pool, count * sizeof(acmp_node_t *)); - /* ENH: Check alloc succeded */ - - /* ENH: Combine this in the loop below - we do not need two loops */ - child = node->child; - for (i = 0; i < count; i++) { - nodes[i] = child; - child = child->sibling; - }; - - /* We have array with all children of the node and number of those children - */ - for (i = 0; i < count - 1; i++) - for (j = i + 1; j < count; j++) { - acmp_node_t *tmp; - - if (nodes[i]->letter < nodes[j]->letter) continue; - - tmp = nodes[i]; - nodes[i] = nodes[j]; - nodes[j] = tmp; - } - node->btree = apr_pcalloc(parser->pool, sizeof(acmp_btree_node_t)); - /* ENH: Check alloc succeded */ - pos = count / 2; - node->btree->node = nodes[pos]; - node->btree->letter = nodes[pos]->letter; - acmp_add_btree_leaves(node->btree, nodes, pos, -1, count, parser->pool); - for (i = 0; i < count; i++) { - if (nodes[i]->child != NULL) acmp_build_binary_tree(parser, nodes[i]); - } -} - -/** - * Constructs fail paths on keyword trie - */ -static apr_status_t acmp_connect_fail_branches(ACMP *parser) { - /* Already connected ? */ - acmp_node_t *child, *node, *goto_node; - apr_array_header_t *arr, *arr2, *tmp; - - if (parser->is_failtree_done != 0) return APR_SUCCESS; - - parser->root_node->text = ""; - arr = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *)); - arr2 = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *)); - - parser->root_node->fail = parser->root_node; - - /* All first-level children will fail back to root node */ - for (child = parser->root_node->child; child != NULL; child = child->sibling) { - child->fail = parser->root_node; - *(acmp_node_t **)apr_array_push(arr) = child; -#ifdef DEBUG_ACMP - fprintf(stderr, "fail direction: *%s* => *%s*\n", child->text, child->fail->text); -#endif - } - - for (;;) { - while (apr_is_empty_array(arr) == 0) { - node = *(acmp_node_t **)apr_array_pop(arr); - node->fail = parser->root_node; - if (node->parent != parser->root_node) { - goto_node = acmp_child_for_code(node->parent->fail, node->letter); - node->fail = (goto_node != NULL) ? goto_node : parser->root_node; - } -#ifdef DEBUG_ACMP - fprintf(stderr, "fail direction: *%s* => *%s*\n", node->text, node->fail->text); -#endif - child = node->child; - while (child != NULL) { - *(acmp_node_t **)apr_array_push(arr2) = child; - child = child->sibling; - } - } - if (apr_is_empty_array(arr2) != 0) break; - - tmp = arr; - arr = arr2; - arr2 = tmp; - } - acmp_connect_other_matches(parser, parser->root_node); - if (parser->root_node->child != NULL) acmp_build_binary_tree(parser, parser->root_node); - parser->is_failtree_done = 1; - return APR_SUCCESS; -} - -/* - ******************************************************************************* - ******************************************************************************* - * Code for functions from header file - */ - - -/** - * flags - OR-ed values of ACMP_FLAG constants - * pool - apr_pool to use as parent pool, can be set to NULL - */ -ACMP *acmp_create(int flags, apr_pool_t *pool) { - apr_status_t rc; - apr_pool_t *p; - ACMP *parser; - - rc = apr_pool_create(&p, pool); - if (rc != APR_SUCCESS) return NULL; - - parser = apr_pcalloc(p, sizeof(ACMP)); - /* ENH: Check alloc succeded */ - parser->pool = p; - parser->parent_pool = pool; -#ifdef ACMP_USE_UTF8 - parser->is_utf8 = (flags & ACMP_FLAG_UTF8) == 0 ? 0 : 1; -#endif - parser->is_case_sensitive = (flags & ACMP_FLAG_CASE_SENSITIVE) == 0 ? 0 : 1; - parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t)); - /* ENH: Check alloc succeded */ - return parser; -} - -/** - * Creates fail tree and initializes buffer - */ -apr_status_t acmp_prepare(ACMP *parser) { - apr_status_t st; - - if (parser->bp_buff_len < parser->longest_entry) { - parser->bp_buff_len = parser->longest_entry * 2; - parser->bp_buffer = apr_pcalloc(parser->pool, sizeof(apr_size_t) * parser->bp_buff_len); - /* ENH: Check alloc succeded */ - } - - st = acmp_connect_fail_branches(parser); - parser->active_node = parser->root_node; - if (st != APR_SUCCESS) return st; - parser->is_active = 1; - return APR_SUCCESS; -} - -/** - * Adds pattern to parser - * parser - ACMP parser - * pattern - string with pattern to match - * callback - Optional, pointer to an acmp_callback_t function - * data - pointer to data that will be passed to callback function, only used if callback - * is supplied - * len - Length of pattern in characters, if zero string length is used. - */ -apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, - acmp_callback_t callback, void *data, apr_size_t len) -{ - size_t length, i, j; - acmp_utf8_char_t *ucs_chars; - acmp_node_t *parent, *child; - - if (parser->is_active != 0) return APR_EGENERAL; - - length = (len == 0) ? acmp_strlen(parser, pattern) : len; - ucs_chars = apr_pcalloc(parser->pool, length * sizeof(acmp_utf8_char_t)); - /* ENH: Check alloc succeded */ - - parent = parser->root_node; - acmp_strtoucs(parser, pattern, ucs_chars, length); - - for (i = 0; i < length; i++) { - acmp_utf8_char_t letter = ucs_chars[i]; - if (parser->is_case_sensitive == 0) { - letter = utf8_lcase(letter); - } - child = acmp_child_for_code(parent, letter); - if (child == NULL) { - child = apr_pcalloc(parser->pool, sizeof(acmp_node_t)); - /* ENH: Check alloc succeded */ - child->pattern = ""; - child->letter = letter; - child->depth = i; - child->text = apr_pcalloc(parser->pool, strlen(pattern) + 2); - /* ENH: Check alloc succeded */ - for (j = 0; j <= i; j++) child->text[j] = pattern[j]; - } - if (i == length - 1) { - if (child->is_last == 0) { - parser->dict_count++; - child->is_last = 1; - child->pattern = apr_pcalloc(parser->pool, strlen(pattern) + 2); - /* ENH: Check alloc succeded */ - strcpy(child->pattern, pattern); - } - child->callback = callback; - child->callback_data = data; - } - acmp_add_node_to_parent(parent, child); - parent = child; - } - if (length > parser->longest_entry) parser->longest_entry = length; - parser->is_failtree_done = 0; - - return APR_SUCCESS; -} - -/** - * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree - */ -apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len) { - ACMP *parser; - acmp_node_t *node, *go_to; - const char *end; - - if (acmpt->parser->is_failtree_done == 0) { - acmp_prepare(acmpt->parser); - }; - - parser = acmpt->parser; - if (acmpt->ptr == NULL) acmpt->ptr = parser->root_node; - node = acmpt->ptr; - end = data + len; - - while (data < end) { - acmp_utf8_char_t letter = (unsigned char)*data++; - - if (parser->is_case_sensitive == 0) letter = utf8_lcase(letter); - - go_to = NULL; - while (go_to == NULL) { - go_to = acmp_goto(node, letter); - if (go_to != NULL) { - if (go_to->is_last) { - *match = go_to->text; - return 1; - } - } - if (node == parser->root_node) break; - if (go_to == NULL) node = node->fail; - } - if (go_to != NULL) node = go_to; - - /* If node has o_match, then we found a pattern */ - if (node->o_match != NULL) { - *match = node->text; - return 1; - } - } - acmpt->ptr = node; - return 0; -} diff --git a/apache2/acmp.h b/apache2/acmp.h deleted file mode 100644 index 6566516770..0000000000 --- a/apache2/acmp.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef ACMP_H_ -#define ACMP_H_ - -#include -#include - -#define ACMP_FLAG_BYTE 0 -#define ACMP_FLAG_CASE_SENSITIVE 1 -#define ACMP_FLAG_CASE_INSENSITIVE 0 -#ifdef ACMP_USE_UTF8 -#define ACMP_FLAG_UTF8 0x100 -#endif - -/** - * Opaque struct with parser data - */ -typedef struct ACMP ACMP; - -/** - * Used to separate state from the trie for acmp_process_quick function - */ -typedef struct { - ACMP *parser; - void *ptr; -} ACMPT; - -/** - * Callback function. Arguments are: - * ACMP * - acmp parser that initiated callback - * void * - custom data you supplied when adding callback - * apr_size_t - position in bytes where pattern was found - * apr_size_t - position in chars where pattern was found, for multibyte strings - */ -typedef void (*acmp_callback_t)(ACMP *, void *, apr_size_t, apr_size_t); - -/** - * flags - OR-ed values of ACMP_FLAG constants - * pool - apr_pool to use as parent pool, can be set to NULL - */ -ACMP *acmp_create(int flags, apr_pool_t *pool); - -/** - * Destroys previously created parser - */ -void acmp_destroy(ACMP *parser); - -/** - * Creates parser with same options and same patterns - * parser - ACMP parser to duplicate - * pool - parent pool to use, if left as NULL original parser's parent pool is used - */ -ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool); - -/** - * Adds pattern to parser. Cannot be done after starting the search. - * parser - ACMP parser - * pattern - string with pattern to match - * callback - Optional, pointer to an acmp_callback_t function - * data - pointer to data that will be passed to callback function, only used if callback - * is supplied - * len - Length of pattern in characters, if zero string length is used. - */ -apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, - acmp_callback_t callback, void *data, apr_size_t len); - -/** - * Called to process incoming data stream. You must call acmp_done after sending - * last data packet - * - * data - ptr to incoming data - * len - size of data in bytes - */ -apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len); - -/** - * Returns number of matches on all patterns combined - */ -apr_size_t acmp_match_count_total(ACMP *parser); - -/** - * Returns number of matches for given pattern - */ -apr_size_t acmp_match_count(ACMP *parser, const char *pattern); - -/** - * Resets the state of parser so you can start using it with new set of data, - * or add new patterns. - */ -void acmp_reset(ACMP *parser); - -/** - * Creates an ACMPT struct that will use parser's tree, without duplicating its data - */ -ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool); - -/** - * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree - */ -apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len); - -/** - * Prepares parser for searching - */ -apr_status_t acmp_prepare(ACMP *parser); - - -#endif /*ACMP_H_*/ diff --git a/apache2/apache2.h b/apache2/apache2.h deleted file mode 100644 index 87a17ed2e9..0000000000 --- a/apache2/apache2.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _APACHE2_H_ -#define _APACHE2_H_ - -#include "http_core.h" -#include "http_request.h" -#include "httpd.h" -#include "ap_release.h" - -#include -#include - - -#if (!defined(NO_MODSEC_API)) -/* Optional functions. */ - -APR_DECLARE_OPTIONAL_FN(void, modsec_register_tfn, (const char *name, void *fn)); -APR_DECLARE_OPTIONAL_FN(void, modsec_register_operator, (const char *name, void *fn_init, void *fn_exec)); -APR_DECLARE_OPTIONAL_FN(void, modsec_register_variable, - (const char *name, unsigned int type, - unsigned int argc_min, unsigned int argc_max, - void *fn_validate, void *fn_generate, - unsigned int is_cacheable, unsigned int availability)); -APR_DECLARE_OPTIONAL_FN(void, modsec_register_reqbody_processor, (const char *name, void *fn_init, void *fn_process, void *fn_complete)); -#endif - -/* ap_get_server_version() is gone in 2.3.0. - * It was replaced by two calls in 2.2.4 and higher: - * ap_get_server_banner() - * ap_get_server_description() - */ -#if (AP_SERVER_MAJORVERSION_NUMBER > 2) \ - || ((AP_SERVER_MAJORVERSION_NUMBER == 2)&& (AP_SERVER_MINORVERSION_NUMBER > 2)) \ - || ((AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER == 2) && (AP_SERVER_PATCHLEVEL_NUMBER >= 4)) -#define apache_get_server_version() ap_get_server_banner() -#else -#define apache_get_server_version() ap_get_server_version() -#endif - - -/* Configuration functions. */ - -void DSOLOCAL *create_directory_config(apr_pool_t *mp, char *path); - -void DSOLOCAL *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child); - -void DSOLOCAL init_directory_config(directory_config *dcfg); - - -/* IO functions. */ - -apr_status_t DSOLOCAL input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, - ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes); - -apr_status_t DSOLOCAL output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in); - -apr_status_t DSOLOCAL read_request_body(modsec_rec *msr, char **error_msg); - - -/* Utility functions */ - -int DSOLOCAL perform_interception(modsec_rec *msr); - -apr_status_t DSOLOCAL send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status); - -int DSOLOCAL apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output); - -void DSOLOCAL record_time_checkpoint(modsec_rec *msr, int checkpoint_no); - -char DSOLOCAL *get_apr_error(apr_pool_t *p, apr_status_t rc); - -char DSOLOCAL *get_env_var(request_rec *r, char *name); - -void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...) PRINTF_ATTRIBUTE(3,4); - -void DSOLOCAL msr_log_error(modsec_rec *msr, const char *text, ...) PRINTF_ATTRIBUTE(2,3); - -void DSOLOCAL msr_log_warn(modsec_rec *msr, const char *text, ...) PRINTF_ATTRIBUTE(2,3); - -char DSOLOCAL *format_error_log_message(apr_pool_t *mp, error_message_t *em); - -const DSOLOCAL char *get_response_protocol(request_rec *r); - - -#endif - diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c deleted file mode 100644 index 84385f57c6..0000000000 --- a/apache2/apache2_config.c +++ /dev/null @@ -1,3532 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include - -#include "modsecurity.h" -#include "msc_logging.h" -#include "msc_util.h" -#include "http_log.h" -#include "apr_lib.h" -#include "acmp.h" -#include "msc_crypt.h" - -#if defined(WITH_LUA) -#include "msc_lua.h" -#endif - - -/* -- Directory context creation and initialisation -- */ - -/** - * Creates a fresh directory configuration. - */ -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; - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Created directory config %pp path %s", dcfg, path); - #endif - - dcfg->mp = mp; - dcfg->is_enabled = NOT_SET; - - dcfg->reqbody_access = NOT_SET; - dcfg->reqintercept_oe = NOT_SET; - dcfg->reqbody_buffering = NOT_SET; - dcfg->reqbody_inmemory_limit = NOT_SET; - dcfg->reqbody_limit = NOT_SET; - dcfg->reqbody_no_files_limit = NOT_SET; - dcfg->resbody_access = NOT_SET; - - dcfg->debuglog_name = NOT_SET_P; - dcfg->debuglog_level = NOT_SET; - dcfg->debuglog_fd = NOT_SET_P; - - dcfg->of_limit = NOT_SET; - dcfg->if_limit_action = NOT_SET; - dcfg->of_limit_action = NOT_SET; - dcfg->of_mime_types = NOT_SET_P; - dcfg->of_mime_types_cleared = NOT_SET; - - dcfg->cookie_format = NOT_SET; - dcfg->argument_separator = NOT_SET; - dcfg->cookiev0_separator = NOT_SET_P; - - dcfg->rule_inheritance = NOT_SET; - dcfg->rule_exceptions = apr_array_make(mp, 16, sizeof(rule_exception *)); - dcfg->hash_method = apr_array_make(mp, 16, sizeof(hash_method *)); - - /* audit log variables */ - dcfg->auditlog_flag = NOT_SET; - dcfg->auditlog_type = NOT_SET; - dcfg->max_rule_time = NOT_SET; - dcfg->auditlog_dirperms = NOT_SET; - dcfg->auditlog_fileperms = NOT_SET; - dcfg->auditlog_name = NOT_SET_P; - dcfg->auditlog2_name = NOT_SET_P; - dcfg->auditlog_fd = NOT_SET_P; - dcfg->auditlog2_fd = NOT_SET_P; - dcfg->auditlog_storage_dir = NOT_SET_P; - dcfg->auditlog_parts = NOT_SET_P; - dcfg->auditlog_relevant_regex = NOT_SET_P; - - dcfg->ruleset = NULL; - - /* Upload */ - dcfg->tmp_dir = NOT_SET_P; - dcfg->upload_dir = NOT_SET_P; - 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; - dcfg->tmp_default_actionset = NULL; - dcfg->tmp_rule_placeholders = NULL; - - /* Misc */ - dcfg->data_dir = NOT_SET_P; - dcfg->webappid = NOT_SET_P; - dcfg->sensor_id = NOT_SET_P; - dcfg->httpBlkey = NOT_SET_P; - - /* Content injection. */ - dcfg->content_injection_enabled = NOT_SET; - - /* Stream inspection */ - dcfg->stream_inbody_inspection = NOT_SET; - dcfg->stream_outbody_inspection = NOT_SET; - - /* Geo Lookups */ - dcfg->geo = NOT_SET_P; - - /* Gsb Lookups */ - dcfg->gsb = NOT_SET_P; - - /* Unicode Map */ - dcfg->u_map = NOT_SET_P; - - /* Cache */ - dcfg->cache_trans = NOT_SET; - dcfg->cache_trans_incremental = NOT_SET; - dcfg->cache_trans_min = NOT_SET; - dcfg->cache_trans_max = NOT_SET; - dcfg->cache_trans_maxitems = NOT_SET; - - /* Rule ids */ - dcfg->rule_id_htab = apr_hash_make(mp); - dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *)); - - dcfg->request_encoding = NOT_SET_P; - dcfg->disable_backend_compression = NOT_SET; - - /* Collection timeout */ - dcfg->col_timeout = NOT_SET; - - dcfg->crypto_key = NOT_SET_P; - dcfg->crypto_key_len = NOT_SET; - dcfg->crypto_key_add = NOT_SET; - dcfg->crypto_param_name = NOT_SET_P; - dcfg->hash_is_enabled = NOT_SET; - dcfg->hash_enforcement = NOT_SET; - dcfg->crypto_hash_href_rx = NOT_SET; - dcfg->crypto_hash_faction_rx = NOT_SET; - dcfg->crypto_hash_location_rx = NOT_SET; - dcfg->crypto_hash_iframesrc_rx = NOT_SET; - dcfg->crypto_hash_framesrc_rx = NOT_SET; - dcfg->crypto_hash_href_pm = NOT_SET; - dcfg->crypto_hash_faction_pm = NOT_SET; - dcfg->crypto_hash_location_pm = NOT_SET; - dcfg->crypto_hash_iframesrc_pm = NOT_SET; - dcfg->crypto_hash_framesrc_pm = NOT_SET; - - - /* xml external entity */ - dcfg->xml_external_entity = NOT_SET; - - return dcfg; -} - -/** - * 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) -{ - rule_exception **exceptions; - msre_rule **rules; - int i, j; - int mode = 0; - - rules = (msre_rule **)parent_phase_arr->elts; - for(i = 0; i < parent_phase_arr->nelts; i++) { - msre_rule *rule = (msre_rule *)rules[i]; - int copy = 1; - - if (mode == 0) { - /* First rule in the chain. */ - exceptions = (rule_exception **)exceptions_arr->elts; - for(j = 0; j < exceptions_arr->nelts; j++) { - - /* Process exceptions. */ - switch(exceptions[j]->type) { - case RULE_EXCEPTION_REMOVE_ID : - if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { - int ruleid = atoi(rule->actionset->id); - if (rule_id_in_range(ruleid, exceptions[j]->param)) copy--; - } - break; - case RULE_EXCEPTION_REMOVE_MSG : - if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { - char *my_error_msg = NULL; - - int rc = msc_regexec(exceptions[j]->param_data, - rule->actionset->msg, strlen(rule->actionset->msg), - &my_error_msg); - if (rc >= 0) copy--; - } - break; - case RULE_EXCEPTION_REMOVE_TAG : - if ((rule->actionset != NULL)&&(apr_is_empty_table(rule->actionset->actions) == 0)) { - char *my_error_msg = NULL; - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - int c; - - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - for (c = 0; c < tarr->nelts; c++) { - msre_action *action = (msre_action *)telts[c].val; - if(strcmp("tag", action->metadata->name) == 0) { - - int rc = msc_regexec(exceptions[j]->param_data, - action->param, strlen(action->param), - &my_error_msg); - if (rc >= 0) copy--; - } - } - } - break; - } - } - - if (copy > 0) { -#ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, rule->actionset->id); -#endif - - /* Copy the rule. */ - *(msre_rule **)apr_array_push(child_phase_arr) = rule; - if (rule->actionset->is_chained) mode = 2; - } else { - if (rule->actionset->is_chained) mode = 1; - } - } else { - if (mode == 2) { -#ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy chain %pp for rule %pp [id \"%s\"]", rule, rule->chain_starter, rule->chain_starter->actionset->id); -#endif - - /* Copy the rule (it belongs to the chain we want to include. */ - *(msre_rule **)apr_array_push(child_phase_arr) = rule; - } - - if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0; - } - } -} - -/** - * 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) -{ - copy_rules_phase(mp, parent_ruleset->phase_request_headers, - child_ruleset->phase_request_headers, exceptions_arr); - copy_rules_phase(mp, parent_ruleset->phase_request_body, - child_ruleset->phase_request_body, exceptions_arr); - copy_rules_phase(mp, parent_ruleset->phase_response_headers, - child_ruleset->phase_response_headers, exceptions_arr); - copy_rules_phase(mp, parent_ruleset->phase_response_body, - child_ruleset->phase_response_body, exceptions_arr); - copy_rules_phase(mp, parent_ruleset->phase_logging, - child_ruleset->phase_logging, exceptions_arr); - - return 1; -} - -/** - * Merges two directory configurations. - */ -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); - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Merge parent %pp child %pp RESULT %pp", _parent, _child, merged); - #endif - - if (merged == NULL) return NULL; - - /* Use values from the child configuration where possible, - * otherwise use the parent's. - */ - - merged->is_enabled = (child->is_enabled == NOT_SET - ? parent->is_enabled : child->is_enabled); - - /* IO parameters */ - merged->reqbody_access = (child->reqbody_access == NOT_SET - ? parent->reqbody_access : child->reqbody_access); - merged->reqbody_buffering = (child->reqbody_buffering == NOT_SET - ? parent->reqbody_buffering : child->reqbody_buffering); - merged->reqbody_inmemory_limit = (child->reqbody_inmemory_limit == NOT_SET - ? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit); - merged->reqbody_limit = (child->reqbody_limit == NOT_SET - ? parent->reqbody_limit : child->reqbody_limit); - merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET - ? parent->reqbody_no_files_limit : child->reqbody_no_files_limit); - merged->resbody_access = (child->resbody_access == NOT_SET - ? parent->resbody_access : child->resbody_access); - - merged->of_limit = (child->of_limit == NOT_SET - ? parent->of_limit : child->of_limit); - merged->if_limit_action = (child->if_limit_action == NOT_SET - ? parent->if_limit_action : child->if_limit_action); - merged->of_limit_action = (child->of_limit_action == NOT_SET - ? parent->of_limit_action : child->of_limit_action); - merged->reqintercept_oe = (child->reqintercept_oe == NOT_SET - ? parent->reqintercept_oe : child->reqintercept_oe); - - if (child->of_mime_types != NOT_SET_P) { - /* Child added to the table */ - - if (child->of_mime_types_cleared == 1) { - /* The list of MIME types was cleared in the child, - * which means the parent's MIME types went away and - * we should not take them into consideration here. - */ - merged->of_mime_types = child->of_mime_types; - merged->of_mime_types_cleared = 1; - } else { - /* Add MIME types defined in the child to those - * defined in the parent context. - */ - if (parent->of_mime_types == NOT_SET_P) { - merged->of_mime_types = child->of_mime_types; - merged->of_mime_types_cleared = NOT_SET; - } else { - merged->of_mime_types = apr_table_overlay(mp, parent->of_mime_types, - child->of_mime_types); - if (merged->of_mime_types == NULL) return NULL; - } - } - } else { - /* Child did not add to the table */ - - if (child->of_mime_types_cleared == 1) { - merged->of_mime_types_cleared = 1; - } else { - merged->of_mime_types = parent->of_mime_types; - merged->of_mime_types_cleared = parent->of_mime_types_cleared; - } - } - - /* debug log */ - if (child->debuglog_fd == NOT_SET_P) { - merged->debuglog_name = parent->debuglog_name; - merged->debuglog_fd = parent->debuglog_fd; - } else { - merged->debuglog_name = child->debuglog_name; - merged->debuglog_fd = child->debuglog_fd; - } - - merged->debuglog_level = (child->debuglog_level == NOT_SET - ? parent->debuglog_level : child->debuglog_level); - - merged->cookie_format = (child->cookie_format == NOT_SET - ? parent->cookie_format : child->cookie_format); - merged->argument_separator = (child->argument_separator == NOT_SET - ? parent->argument_separator : child->argument_separator); - merged->cookiev0_separator = (child->cookiev0_separator == NOT_SET_P - ? parent->cookiev0_separator : child->cookiev0_separator); - - - /* rule inheritance */ - if ((child->rule_inheritance == NOT_SET)||(child->rule_inheritance == 1)) { - merged->rule_inheritance = parent->rule_inheritance; - if ((child->ruleset == NULL)&&(parent->ruleset == NULL)) { - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "No rules in this context."); - #endif - - /* Do nothing, there are no rules in either context. */ - } else - if (child->ruleset == NULL) { - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent rules in this context."); - #endif - - /* Copy the rules from the parent context. */ - merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); - copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); - } else - if (parent->ruleset == NULL) { - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using child rules in this context."); - #endif - - /* Copy child rules. */ - merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp); - merged->ruleset->phase_request_headers = apr_array_copy(mp, - child->ruleset->phase_request_headers); - merged->ruleset->phase_request_body = apr_array_copy(mp, - child->ruleset->phase_request_body); - merged->ruleset->phase_response_headers = apr_array_copy(mp, - child->ruleset->phase_response_headers); - merged->ruleset->phase_response_body = apr_array_copy(mp, - child->ruleset->phase_response_body); - merged->ruleset->phase_logging = apr_array_copy(mp, - child->ruleset->phase_logging); - } else { - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent then child rules in this context."); - #endif - - /* Copy parent rules, then add child rules to it. */ - merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); - copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); - - apr_array_cat(merged->ruleset->phase_request_headers, - child->ruleset->phase_request_headers); - apr_array_cat(merged->ruleset->phase_request_body, - child->ruleset->phase_request_body); - apr_array_cat(merged->ruleset->phase_response_headers, - child->ruleset->phase_response_headers); - apr_array_cat(merged->ruleset->phase_response_body, - child->ruleset->phase_response_body); - apr_array_cat(merged->ruleset->phase_logging, - child->ruleset->phase_logging); - } - } else { - merged->rule_inheritance = 0; - if (child->ruleset != NULL) { - /* Copy child rules. */ - merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp); - merged->ruleset->phase_request_headers = apr_array_copy(mp, - child->ruleset->phase_request_headers); - merged->ruleset->phase_request_body = apr_array_copy(mp, - child->ruleset->phase_request_body); - merged->ruleset->phase_response_headers = apr_array_copy(mp, - child->ruleset->phase_response_headers); - merged->ruleset->phase_response_body = apr_array_copy(mp, - child->ruleset->phase_response_body); - merged->ruleset->phase_logging = apr_array_copy(mp, - child->ruleset->phase_logging); - } - } - - /* Merge rule exceptions. */ - merged->rule_exceptions = apr_array_append(mp, parent->rule_exceptions, - child->rule_exceptions); - - merged->hash_method = apr_array_append(mp, parent->hash_method, - child->hash_method); - - /* audit log variables */ - merged->auditlog_flag = (child->auditlog_flag == NOT_SET - ? parent->auditlog_flag : child->auditlog_flag); - merged->auditlog_type = (child->auditlog_type == NOT_SET - ? parent->auditlog_type : child->auditlog_type); - merged->max_rule_time = (child->max_rule_time == NOT_SET - ? parent->max_rule_time : child->max_rule_time); - merged->auditlog_dirperms = (child->auditlog_dirperms == NOT_SET - ? parent->auditlog_dirperms : child->auditlog_dirperms); - merged->auditlog_fileperms = (child->auditlog_fileperms == NOT_SET - ? parent->auditlog_fileperms : child->auditlog_fileperms); - if (child->auditlog_fd != NOT_SET_P) { - merged->auditlog_fd = child->auditlog_fd; - merged->auditlog_name = child->auditlog_name; - } else { - merged->auditlog_fd = parent->auditlog_fd; - merged->auditlog_name = parent->auditlog_name; - } - if (child->auditlog2_fd != NOT_SET_P) { - merged->auditlog2_fd = child->auditlog2_fd; - merged->auditlog2_name = child->auditlog2_name; - } else { - merged->auditlog2_fd = parent->auditlog2_fd; - merged->auditlog2_name = parent->auditlog2_name; - } - merged->auditlog_storage_dir = (child->auditlog_storage_dir == NOT_SET_P - ? parent->auditlog_storage_dir : child->auditlog_storage_dir); - merged->auditlog_parts = (child->auditlog_parts == NOT_SET_P - ? parent->auditlog_parts : child->auditlog_parts); - merged->auditlog_relevant_regex = (child->auditlog_relevant_regex == NOT_SET_P - ? parent->auditlog_relevant_regex : child->auditlog_relevant_regex); - - /* Upload */ - merged->tmp_dir = (child->tmp_dir == NOT_SET_P - ? parent->tmp_dir : child->tmp_dir); - merged->upload_dir = (child->upload_dir == NOT_SET_P - ? parent->upload_dir : child->upload_dir); - merged->upload_keep_files = (child->upload_keep_files == NOT_SET - ? parent->upload_keep_files : child->upload_keep_files); - merged->upload_validates_files = (child->upload_validates_files == NOT_SET - ? 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 - ? parent->data_dir : child->data_dir); - merged->webappid = (child->webappid == NOT_SET_P - ? parent->webappid : child->webappid); - merged->sensor_id = (child->sensor_id == NOT_SET_P - ? parent->sensor_id : child->sensor_id); - merged->httpBlkey = (child->httpBlkey == NOT_SET_P - ? parent->httpBlkey : child->httpBlkey); - - /* Content injection. */ - merged->content_injection_enabled = (child->content_injection_enabled == NOT_SET - ? parent->content_injection_enabled : child->content_injection_enabled); - - /* Stream inspection */ - merged->stream_inbody_inspection = (child->stream_inbody_inspection == NOT_SET - ? parent->stream_inbody_inspection : child->stream_inbody_inspection); - merged->stream_outbody_inspection = (child->stream_outbody_inspection == NOT_SET - ? parent->stream_outbody_inspection : child->stream_outbody_inspection); - - /* Geo Lookup */ - merged->geo = (child->geo == NOT_SET_P - ? parent->geo : child->geo); - - /* Gsb Lookup */ - merged->gsb = (child->gsb == NOT_SET_P - ? parent->gsb : child->gsb); - - /* Unicode Map */ - merged->u_map = (child->u_map == NOT_SET_P - ? parent->u_map : child->u_map); - - /* Cache */ - merged->cache_trans = (child->cache_trans == NOT_SET - ? parent->cache_trans : child->cache_trans); - merged->cache_trans_incremental = (child->cache_trans_incremental == NOT_SET - ? parent->cache_trans_incremental : child->cache_trans_incremental); - merged->cache_trans_min = (child->cache_trans_min == (apr_size_t)NOT_SET - ? parent->cache_trans_min : child->cache_trans_min); - merged->cache_trans_max = (child->cache_trans_max == (apr_size_t)NOT_SET - ? parent->cache_trans_max : child->cache_trans_max); - merged->cache_trans_maxitems = (child->cache_trans_maxitems == (apr_size_t)NOT_SET - ? parent->cache_trans_maxitems : child->cache_trans_maxitems); - - /* Merge component signatures. */ - merged->component_signatures = apr_array_append(mp, parent->component_signatures, - child->component_signatures); - - merged->request_encoding = (child->request_encoding == NOT_SET_P - ? parent->request_encoding : child->request_encoding); - - merged->disable_backend_compression = (child->disable_backend_compression == NOT_SET - ? parent->disable_backend_compression : child->disable_backend_compression); - - merged->col_timeout = (child->col_timeout == NOT_SET - ? parent->col_timeout : child->col_timeout); - - /* Hash */ - merged->crypto_key = (child->crypto_key == NOT_SET_P - ? parent->crypto_key : child->crypto_key); - merged->crypto_key_len = (child->crypto_key_len == NOT_SET - ? parent->crypto_key_len : child->crypto_key_len); - merged->crypto_key_add = (child->crypto_key_add == NOT_SET - ? parent->crypto_key_add : child->crypto_key_add); - merged->crypto_param_name = (child->crypto_param_name == NOT_SET_P - ? parent->crypto_param_name : child->crypto_param_name); - merged->hash_is_enabled = (child->hash_is_enabled == NOT_SET - ? parent->hash_is_enabled : child->hash_is_enabled); - merged->hash_enforcement = (child->hash_enforcement == NOT_SET - ? parent->hash_enforcement : child->hash_enforcement); - merged->crypto_hash_href_rx = (child->crypto_hash_href_rx == NOT_SET - ? parent->crypto_hash_href_rx : child->crypto_hash_href_rx); - merged->crypto_hash_faction_rx = (child->crypto_hash_faction_rx == NOT_SET - ? parent->crypto_hash_faction_rx : child->crypto_hash_faction_rx); - merged->crypto_hash_location_rx = (child->crypto_hash_location_rx == NOT_SET - ? parent->crypto_hash_location_rx : child->crypto_hash_location_rx); - merged->crypto_hash_iframesrc_rx = (child->crypto_hash_iframesrc_rx == NOT_SET - ? parent->crypto_hash_iframesrc_rx : child->crypto_hash_iframesrc_rx); - merged->crypto_hash_framesrc_rx = (child->crypto_hash_framesrc_rx == NOT_SET - ? parent->crypto_hash_framesrc_rx : child->crypto_hash_framesrc_rx); - merged->crypto_hash_href_pm = (child->crypto_hash_href_pm == NOT_SET - ? parent->crypto_hash_href_pm : child->crypto_hash_href_pm); - merged->crypto_hash_faction_pm = (child->crypto_hash_faction_pm == NOT_SET - ? parent->crypto_hash_faction_pm : child->crypto_hash_faction_pm); - merged->crypto_hash_location_pm = (child->crypto_hash_location_pm == NOT_SET - ? parent->crypto_hash_location_pm : child->crypto_hash_location_pm); - merged->crypto_hash_iframesrc_pm = (child->crypto_hash_iframesrc_pm == NOT_SET - ? parent->crypto_hash_iframesrc_pm : child->crypto_hash_iframesrc_pm); - merged->crypto_hash_framesrc_pm = (child->crypto_hash_framesrc_pm == NOT_SET - ? parent->crypto_hash_framesrc_pm : child->crypto_hash_framesrc_pm); - - /* xml external entity */ - merged->xml_external_entity = (child->xml_external_entity == NOT_SET - ? parent->xml_external_entity : child->xml_external_entity); - - return merged; -} - -/** - * Initialise directory configuration. This function is *not* meant - * to be called for directory configuration instances created during - * the configuration phase. It can only be called on copies of those - * (created fresh for every transaction). - */ -void init_directory_config(directory_config *dcfg) -{ - if (dcfg == NULL) return; - - if (dcfg->is_enabled == NOT_SET) dcfg->is_enabled = 0; - - if (dcfg->reqbody_access == NOT_SET) dcfg->reqbody_access = 0; - if (dcfg->reqintercept_oe == NOT_SET) dcfg->reqintercept_oe = 0; - if (dcfg->reqbody_buffering == NOT_SET) dcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF; - if (dcfg->reqbody_inmemory_limit == NOT_SET) - dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; - if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; - if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; - if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0; - if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; - if (dcfg->if_limit_action == NOT_SET) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT; - if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; - - if (dcfg->of_mime_types == NOT_SET_P) { - dcfg->of_mime_types = apr_table_make(dcfg->mp, 3); - if (dcfg->of_mime_types_cleared != 1) { - apr_table_setn(dcfg->of_mime_types, "text/plain", "1"); - apr_table_setn(dcfg->of_mime_types, "text/html", "1"); - } - } - - if (dcfg->debuglog_fd == NOT_SET_P) dcfg->debuglog_fd = NULL; - if (dcfg->debuglog_name == NOT_SET_P) dcfg->debuglog_name = NULL; - if (dcfg->debuglog_level == NOT_SET) dcfg->debuglog_level = 0; - - if (dcfg->cookie_format == NOT_SET) dcfg->cookie_format = 0; - if (dcfg->argument_separator == NOT_SET) dcfg->argument_separator = '&'; - if (dcfg->cookiev0_separator == NOT_SET_P) dcfg->cookiev0_separator = NULL; - - if (dcfg->rule_inheritance == NOT_SET) dcfg->rule_inheritance = 1; - - /* audit log variables */ - if (dcfg->auditlog_flag == NOT_SET) dcfg->auditlog_flag = 0; - if (dcfg->auditlog_type == NOT_SET) dcfg->auditlog_type = AUDITLOG_SERIAL; - if (dcfg->max_rule_time == NOT_SET) dcfg->max_rule_time = 0; - if (dcfg->auditlog_dirperms == NOT_SET) dcfg->auditlog_dirperms = CREATEMODE_DIR; - if (dcfg->auditlog_fileperms == NOT_SET) dcfg->auditlog_fileperms = CREATEMODE; - if (dcfg->auditlog_fd == NOT_SET_P) dcfg->auditlog_fd = NULL; - if (dcfg->auditlog2_fd == NOT_SET_P) dcfg->auditlog2_fd = NULL; - if (dcfg->auditlog_name == NOT_SET_P) dcfg->auditlog_name = NULL; - if (dcfg->auditlog2_name == NOT_SET_P) dcfg->auditlog2_name = NULL; - if (dcfg->auditlog_storage_dir == NOT_SET_P) dcfg->auditlog_storage_dir = NULL; - if (dcfg->auditlog_parts == NOT_SET_P) dcfg->auditlog_parts = "ABCFHZ"; - if (dcfg->auditlog_relevant_regex == NOT_SET_P) dcfg->auditlog_relevant_regex = NULL; - - /* Upload */ - if (dcfg->tmp_dir == NOT_SET_P) dcfg->tmp_dir = guess_tmp_dir(dcfg->mp); - 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 = 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; - if (dcfg->webappid == NOT_SET_P) dcfg->webappid = "default"; - if (dcfg->sensor_id == NOT_SET_P) dcfg->sensor_id = "default"; - if (dcfg->httpBlkey == NOT_SET_P) dcfg->httpBlkey = NULL; - - /* Content injection. */ - if (dcfg->content_injection_enabled == NOT_SET) dcfg->content_injection_enabled = 0; - - /* Stream inspection */ - if (dcfg->stream_inbody_inspection == NOT_SET) dcfg->stream_inbody_inspection = 0; - if (dcfg->stream_outbody_inspection == NOT_SET) dcfg->stream_outbody_inspection = 0; - - /* Geo Lookup */ - if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; - - /* Gsb Lookup */ - if (dcfg->gsb == NOT_SET_P) dcfg->gsb = NULL; - - /* Unicode Map */ - if (dcfg->u_map == NOT_SET_P) dcfg->u_map = NULL; - - /* Cache */ - if (dcfg->cache_trans == NOT_SET) dcfg->cache_trans = MODSEC_CACHE_DISABLED; - if (dcfg->cache_trans_incremental == NOT_SET) dcfg->cache_trans_incremental = 0; - if (dcfg->cache_trans_min == (apr_size_t)NOT_SET) dcfg->cache_trans_min = 32; - if (dcfg->cache_trans_max == (apr_size_t)NOT_SET) dcfg->cache_trans_max = 1024; - 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; - - if (dcfg->disable_backend_compression == NOT_SET) dcfg->disable_backend_compression = 0; - - if (dcfg->col_timeout == NOT_SET) dcfg->col_timeout = 3600; - - /* Hash */ - if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = getkey(dcfg->mp); - if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = strlen(dcfg->crypto_key); - if (dcfg->crypto_key_add == NOT_SET) dcfg->crypto_key_add = HASH_KEYONLY; - if (dcfg->crypto_param_name == NOT_SET_P) dcfg->crypto_param_name = "crypt"; - if (dcfg->hash_is_enabled == NOT_SET) dcfg->hash_is_enabled = HASH_DISABLED; - if (dcfg->hash_enforcement == NOT_SET) dcfg->hash_enforcement = HASH_DISABLED; - if (dcfg->crypto_hash_href_rx == NOT_SET) dcfg->crypto_hash_href_rx = 0; - if (dcfg->crypto_hash_faction_rx == NOT_SET) dcfg->crypto_hash_faction_rx = 0; - if (dcfg->crypto_hash_location_rx == NOT_SET) dcfg->crypto_hash_location_rx = 0; - if (dcfg->crypto_hash_iframesrc_rx == NOT_SET) dcfg->crypto_hash_iframesrc_rx = 0; - if (dcfg->crypto_hash_framesrc_rx == NOT_SET) dcfg->crypto_hash_framesrc_rx = 0; - if (dcfg->crypto_hash_href_pm == NOT_SET) dcfg->crypto_hash_href_pm = 0; - if (dcfg->crypto_hash_faction_pm == NOT_SET) dcfg->crypto_hash_faction_pm = 0; - if (dcfg->crypto_hash_location_pm == NOT_SET) dcfg->crypto_hash_location_pm = 0; - if (dcfg->crypto_hash_iframesrc_pm == NOT_SET) dcfg->crypto_hash_iframesrc_pm = 0; - if (dcfg->crypto_hash_framesrc_pm == NOT_SET) dcfg->crypto_hash_framesrc_pm = 0; - - /* xml external entity */ - if (dcfg->xml_external_entity == NOT_SET) dcfg->xml_external_entity = 0; - -} - -/** - * - */ -static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, - const char *p1, const char *p2, const char *p3) -{ - char *my_error_msg = NULL; - //msre_rule *rule = NULL, *tmp_rule = NULL; - char *rid = NULL; - msre_rule *rule = NULL; - extern msc_engine *modsecurity; - int offset = 0; - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Rule: type=%d p1='%s' p2='%s' p3='%s'", type, p1, p2, p3); - #endif - - /* Create a ruleset if one does not exist. */ - if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) { - dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool); - if (dcfg->ruleset == NULL) return FATAL_ERROR; - } - - /* Create the rule now. */ - switch(type) { - #if defined(WITH_LUA) - case RULE_TYPE_LUA : - rule = msre_rule_lua_create(dcfg->ruleset, cmd->directive->filename, - cmd->directive->line_num, p1, p2, &my_error_msg); - break; - #endif - default : - rule = msre_rule_create(dcfg->ruleset, type, cmd->directive->filename, - cmd->directive->line_num, p1, p2, p3, &my_error_msg); - break; - } - - if (rule == NULL) { - return my_error_msg; - } - - /* Rules must have uniq ID */ - if ( -#if defined(WITH_LUA) - type != RULE_TYPE_LUA && -#endif - (dcfg->tmp_chain_starter == NULL)) - if(rule->actionset == NULL) - return "ModSecurity: Rules must have at least id action"; - - if(rule->actionset != NULL && (dcfg->tmp_chain_starter == NULL)) { - if(rule->actionset->id == NOT_SET_P -#if defined(WITH_LUA) - && (type != RULE_TYPE_LUA) -#endif - ) - return "ModSecurity: No action id present within the rule"; -#if defined(WITH_LUA) - if(type != RULE_TYPE_LUA) -#endif - { - rid = apr_hash_get(dcfg->rule_id_htab, rule->actionset->id, APR_HASH_KEY_STRING); - if(rid != NULL) { - return "ModSecurity: Found another rule with the same id"; - } else { - apr_hash_set(dcfg->rule_id_htab, apr_pstrdup(dcfg->mp, rule->actionset->id), APR_HASH_KEY_STRING, apr_pstrdup(dcfg->mp, "1")); - } - - //tmp_rule = msre_ruleset_fetch_rule(dcfg->ruleset, rule->actionset->id, offset); - //if(tmp_rule != NULL) - // return "ModSecurity: Found another rule with the same id"; - } - } - - /* Create default actionset if one does not already exist. */ - if (dcfg->tmp_default_actionset == NULL) { - dcfg->tmp_default_actionset = msre_actionset_create_default(modsecurity->msre); - if (dcfg->tmp_default_actionset == NULL) return FATAL_ERROR; - } - - /* Check some cases prior to merging so we know where it came from */ - - /* Check syntax for chained rules */ - if ((rule->actionset != NULL) && (dcfg->tmp_chain_starter != NULL)) { - /* Must NOT specify a disruptive action. */ - if (rule->actionset->intercept_action != NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions can only " - "be specified by chain starter rules."); - } - - /* Must NOT specify a skipafter action. */ - if (rule->actionset->skip_after != NOT_SET_P) { - return apr_psprintf(cmd->pool, "ModSecurity: SkipAfter actions can only " - "be specified by chain starter rules."); - } - - /* Must NOT specify a phase. */ - if (rule->actionset->phase != NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: Execution phases can only be " - "specified by chain starter rules."); - } - - /* Must NOT use metadata actions. */ - /* ENH: loop through to check for tags */ - if ((rule->actionset->id != NOT_SET_P) - ||(rule->actionset->rev != NOT_SET_P) - ||(rule->actionset->msg != NOT_SET_P) - ||(rule->actionset->severity != NOT_SET) - ||(rule->actionset->version != NOT_SET_P) - ||(rule->actionset->accuracy != NOT_SET) - ||(rule->actionset->maturity != NOT_SET) - ||(rule->actionset->logdata != NOT_SET_P)) - { - return apr_psprintf(cmd->pool, "ModSecurity: Metadata actions (id, rev, msg, tag, severity, ver, accuracy, maturity, logdata) " - " can only be specified by chain starter rules."); - } - - /* Must NOT use skip. */ - if (rule->actionset->skip_count != NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: The skip action can only be used " - " by chain starter rules. "); - } - } - - /* Merge actions with the parent. - * - * ENH Probably do not want this done fully for chained rules. - */ - rule->actionset = msre_actionset_merge(modsecurity->msre, dcfg->tmp_default_actionset, - rule->actionset, 1); - - /* Keep track of the parent action for "block" */ - rule->actionset->parent_intercept_action_rec = dcfg->tmp_default_actionset->intercept_action_rec; - rule->actionset->parent_intercept_action = dcfg->tmp_default_actionset->intercept_action; - - /* Must NOT specify a disruptive action in logging phase. */ - if ((rule->actionset != NULL) - && (rule->actionset->phase == PHASE_LOGGING) - && (rule->actionset->intercept_action != ACTION_ALLOW) - && (rule->actionset->intercept_action != ACTION_ALLOW_REQUEST) - && (rule->actionset->intercept_action != ACTION_NONE) - ) { - return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions " - "cannot be specified in the logging phase."); - } - - if (dcfg->tmp_chain_starter != NULL) { - rule->chain_starter = dcfg->tmp_chain_starter; - rule->actionset->phase = rule->chain_starter->actionset->phase; - } - - if (rule->actionset->is_chained != 1) { - /* If this rule is part of the chain but does - * not want more rules to follow in the chain - * then cut it (the chain). - */ - dcfg->tmp_chain_starter = NULL; - } else { - /* On the other hand, if this rule wants other - * rules to follow it, then start a new chain - * if there isn't one already. - */ - if (dcfg->tmp_chain_starter == NULL) { - dcfg->tmp_chain_starter = rule; - } - } - - /* Optimisation */ - if ((rule->op_name != NULL)&&(strcasecmp(rule->op_name, "inspectFile") == 0)) { - dcfg->upload_validates_files = 1; - } - - /* Create skip table if one does not already exist. */ - if (dcfg->tmp_rule_placeholders == NULL) { - dcfg->tmp_rule_placeholders = apr_table_make(cmd->pool, 10); - if (dcfg->tmp_rule_placeholders == NULL) return FATAL_ERROR; - } - - /* Keep track of any rule IDs we need to skip after */ - if (rule->actionset->skip_after != NOT_SET_P) { - char *tmp_id = apr_pstrdup(cmd->pool, rule->actionset->skip_after); - apr_table_setn(dcfg->tmp_rule_placeholders, tmp_id, tmp_id); - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Watching for skipafter target rule id=\"%s\".", tmp_id); - #endif - - } - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Adding rule %pp phase=%d id=\"%s\".", rule, rule->actionset->phase, (rule->actionset->id == NOT_SET_P - ? "(none)" : rule->actionset->id)); - #endif - - /* Add rule to the recipe. */ - if (msre_ruleset_rule_add(dcfg->ruleset, rule, rule->actionset->phase) < 0) { - return "Internal Error: Failed to add rule to the ruleset."; - } - - /* Add an additional placeholder if this rule ID is on the list */ - if ((rule->actionset->id != NULL) && apr_table_get(dcfg->tmp_rule_placeholders, rule->actionset->id)) { - msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule)); - if (phrule == NULL) { - return FATAL_ERROR; - } - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id); - #endif - - /* shallow copy of original rule with placeholder marked as target */ - memcpy(phrule, rule, sizeof(msre_rule)); - phrule->placeholder = RULE_PH_SKIPAFTER; - - /* Add placeholder. */ - if (msre_ruleset_rule_add(dcfg->ruleset, phrule, phrule->actionset->phase) < 0) { - return "Internal Error: Failed to add placeholder to the ruleset."; - } - - /* No longer need to search for the ID */ - apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); - } - - /* Update the unparsed rule */ - rule->unparsed = msre_rule_generate_unparsed(dcfg->ruleset->mp, rule, NULL, NULL, NULL); - - return NULL; -} - -/** - * - */ -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; - extern msc_engine *modsecurity; - int p; - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Rule: type=%d p1='%s' p2='%s' p3='%s'", RULE_TYPE_MARKER, p1, p2, p3); - #endif - - /* Create a ruleset if one does not exist. */ - if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) { - dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool); - if (dcfg->ruleset == NULL) return FATAL_ERROR; - } - - /* Create the rule now. */ - rule = msre_rule_create(dcfg->ruleset, RULE_TYPE_MARKER, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg); - if (rule == NULL) { - return my_error_msg; - } - - /* This is a marker */ - rule->placeholder = RULE_PH_MARKER; - - /* Add placeholder to each phase */ - for (p = PHASE_FIRST; p <= PHASE_LAST; p++) { - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Adding marker %pp phase=%d id=\"%s\".", rule, p, (rule->actionset->id == NOT_SET_P - ? "(none)" : rule->actionset->id)); - #endif - - if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) { - return "Internal Error: Failed to add marker to the ruleset."; - } - } - - /* No longer need to search for the ID */ - if (dcfg->tmp_rule_placeholders != NULL) { - apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); - } - - return NULL; -} - -/** - * - */ -static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, - const char *p1, const char *p2, int offset) -{ - char *my_error_msg = NULL; - msre_rule *rule = NULL; - msre_actionset *new_actionset = NULL; - msre_ruleset *ruleset = dcfg->ruleset; - extern msc_engine *modsecurity; - - /* Get the ruleset if one exists */ - if ((ruleset == NULL)||(ruleset == NOT_SET_P)) { - return NULL; - } - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Update rule id=\"%s\" with action \"%s\".", p1, p2); - #endif - - /* Fetch the rule */ - rule = msre_ruleset_fetch_rule(ruleset, p1, offset); - if (rule == NULL) { - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Update rule id=\"%s\" with action \"%s\" failed: Rule not found.", p1, p2); - #endif - return NULL; - } - - /* Check the rule actionset */ - /* ENH: Can this happen? */ - if (rule->actionset == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Attempt to update action for rule \"%s\" failed: Rule does not have an actionset.", p1); - } - - /* Create a new actionset */ - new_actionset = msre_actionset_create(modsecurity->msre, p2, &my_error_msg); - if (new_actionset == NULL) return FATAL_ERROR; - if (my_error_msg != NULL) return my_error_msg; - - /* Must NOT change an id */ - if ((new_actionset->id != NOT_SET_P) && (rule->actionset->id != NULL) && (strcmp(rule->actionset->id, new_actionset->id) != 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Rule IDs cannot be updated via SecRuleUpdateActionById."); - } - - /* Must NOT alter the phase */ - if ((new_actionset->phase != NOT_SET) && (rule->actionset->phase != new_actionset->phase)) { - return apr_psprintf(cmd->pool, "ModSecurity: Rule phases cannot be updated via SecRuleUpdateActionById."); - } - - #ifdef DEBUG_CONF - { - char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset); - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Update rule %pp id=\"%s\" old action: \"%s\"", - rule, - (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id), - actions); - } - #endif - - /* Merge new actions with the rule */ - /* ENH: Will this leak the old actionset? */ - rule->actionset = msre_actionset_merge(modsecurity->msre, rule->actionset, - new_actionset, 1); - msre_actionset_set_defaults(rule->actionset); - - /* Update the unparsed rule */ - rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, NULL, NULL); - - #ifdef DEBUG_CONF - { - char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset); - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Update rule %pp id=\"%s\" new action: \"%s\"", - rule, - (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id), - actions); - } - #endif - - return NULL; -} - -/* -- Configuration directives -- */ - -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) -{ - 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_cookiev0_separator(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - if (strlen(p1) != 1) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie v0 separator: %s", p1); - } - - dcfg->cookiev0_separator = p1; - - return NULL; -} - -static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - if (strlen(p1) != 1) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid argument separator: %s", p1); - } - - dcfg->argument_separator = p1[0]; - - return NULL; -} - -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; - else - if (strcasecmp(p1, "Off") == 0) dcfg->auditlog_flag = AUDITLOG_OFF; - else - if (strcasecmp(p1, "RelevantOnly") == 0) dcfg->auditlog_flag = AUDITLOG_RELEVANT; - else - return (const char *)apr_psprintf(cmd->pool, - "ModSecurity: Unrecognised parameter value for SecAuditEngine: %s", p1); - - return NULL; -} - -static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = _dcfg; - - dcfg->auditlog_name = (char *)p1; - - if (dcfg->auditlog_name[0] == '|') { - const char *pipe_name = dcfg->auditlog_name + 1; - piped_log *pipe_log; - - pipe_log = ap_open_piped_log(cmd->pool, pipe_name); - if (pipe_log == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log pipe: %s", - pipe_name); - } - dcfg->auditlog_fd = ap_piped_log_write_fd(pipe_log); - } - else { - const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog_name); - apr_status_t rc; - - rc = apr_file_open(&dcfg->auditlog_fd, file_name, - APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, - CREATEMODE, cmd->pool); - - if (rc != APR_SUCCESS) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log file: %s", - file_name); - } - } - - return NULL; -} - -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) { - return apr_psprintf(cmd->pool, "ModSecurity: Cannot configure a secondary audit log without a primary defined: %s", p1); - } - - dcfg->auditlog2_name = (char *)p1; - - if (dcfg->auditlog2_name[0] == '|') { - const char *pipe_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name + 1); - piped_log *pipe_log; - - pipe_log = ap_open_piped_log(cmd->pool, pipe_name); - if (pipe_log == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log pipe: %s", - pipe_name); - } - dcfg->auditlog2_fd = ap_piped_log_write_fd(pipe_log); - } - else { - const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name); - apr_status_t rc; - - rc = apr_file_open(&dcfg->auditlog2_fd, file_name, - APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, - CREATEMODE, cmd->pool); - - if (rc != APR_SUCCESS) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log file: %s", - file_name); - } - } - - return NULL; -} - -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) { - return apr_psprintf(cmd->pool, "Invalid parts specification for SecAuditLogParts: %s", p1); - } - - dcfg->auditlog_parts = (char *)p1; - return NULL; -} - -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); - if (dcfg->auditlog_relevant_regex == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); - } - - return NULL; -} - -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; - else - if (strcasecmp(p1, "Concurrent") == 0) dcfg->auditlog_type = AUDITLOG_CONCURRENT; - else - return (const char *)apr_psprintf(cmd->pool, - "ModSecurity: Unrecognised parameter value for SecAuditLogType: %s", p1); - - return NULL; -} - -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; - - if (strcasecmp(p1, "default") == 0) { - dcfg->auditlog_dirperms = NOT_SET; - } - else { - long int mode = strtol(p1, NULL, 8); /* expects octal mode */ - if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogDirMode: %s", p1); - } - - dcfg->auditlog_dirperms = mode2fileperms(mode); - } - - return NULL; -} - -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; - - if (strcasecmp(p1, "default") == 0) { - dcfg->auditlog_fileperms = NOT_SET; - } - else { - long int mode = strtol(p1, NULL, 8); /* expects octal mode */ - if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogFileMode: %s", p1); - } - - dcfg->auditlog_fileperms = mode2fileperms(mode); - } - - return NULL; -} - -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); - - return NULL; -} - -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; - else - if (strcmp(p1, "1") == 0) dcfg->cookie_format = COOKIES_V1; - else { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie format: %s", p1); - } - - return NULL; -} - -static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - char cwd[1025] = ""; - - if (cmd->server->is_virtual) { - return "ModSecurity: SecChrootDir not allowed in VirtualHost"; - } - - chroot_dir = (char *)p1; - - if (getcwd(cwd, 1024) == NULL) { - return "ModSecurity: Failed to get the current working directory"; - } - - if (chdir(chroot_dir) < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)", - chroot_dir, errno, strerror(errno)); - } - - if (chdir(cwd) < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)", - cwd, errno, strerror(errno)); - } - - return NULL; -} - -/** - * 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) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - /* ENH Enforce "Name/VersionX.Y.Z (comment)" format. */ - *(char **)apr_array_push(dcfg->component_signatures) = (char *)p1; - - return NULL; -} - -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) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - if (cmd->server->is_virtual) { - return "ModSecurity: SecDataDir not allowed in VirtualHost."; - } - - dcfg->data_dir = ap_server_root_relative(cmd->pool, p1); - - return NULL; -} - -static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - apr_status_t rc; - - dcfg->debuglog_name = ap_server_root_relative(cmd->pool, p1); - - rc = apr_file_open(&dcfg->debuglog_fd, dcfg->debuglog_name, - APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, - CREATEMODE, cmd->pool); - - if (rc != APR_SUCCESS) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open debug log file: %s", - dcfg->debuglog_name); - } - - return NULL; -} - -/** -* \brief Add SecCollectionTimeout configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_collection_timeout(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - dcfg->col_timeout = atoi(p1); - /* max 30 days */ - if ((dcfg->col_timeout >= 0)&&(dcfg->col_timeout <= 2592000)) return NULL; - - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCollectionTimeout: %s", 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); - if ((dcfg->debuglog_level >= 0)&&(dcfg->debuglog_level <= 9)) return NULL; - - 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) -{ - directory_config *dcfg = (directory_config *)_dcfg; - extern msc_engine *modsecurity; - char *my_error_msg = NULL; - - dcfg->tmp_default_actionset = msre_actionset_create(modsecurity->msre, p1, &my_error_msg); - if (dcfg->tmp_default_actionset == NULL) { - if (my_error_msg != NULL) return my_error_msg; - else return FATAL_ERROR; - } - - /* Must specify a disruptive action. */ - /* ENH: Remove this requirement? */ - if (dcfg->tmp_default_actionset->intercept_action == NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a disruptive action."); - } - - /* Must specify a phase. */ - /* ENH: Remove this requirement? */ - if (dcfg->tmp_default_actionset->phase == NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a phase."); - } - - /* Must not use metadata actions. */ - /* ENH: loop through to check for tags */ - if ((dcfg->tmp_default_actionset->id != NOT_SET_P) - ||(dcfg->tmp_default_actionset->rev != NOT_SET_P) - ||(dcfg->tmp_default_actionset->version != NOT_SET_P) - ||(dcfg->tmp_default_actionset->maturity != NOT_SET) - ||(dcfg->tmp_default_actionset->accuracy != NOT_SET) - ||(dcfg->tmp_default_actionset->msg != NOT_SET_P)) - { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " - "contain any metadata actions (id, rev, msg, tag, severity, ver, accuracy, maturity, logdata)."); - } - /* These are just a warning for now. */ - if ((dcfg->tmp_default_actionset->severity != NOT_SET) - ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P)) - { - ap_log_perror(APLOG_MARK, - APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool, - "ModSecurity: WARNING Using \"severity\" or \"logdata\" in " - "SecDefaultAction is deprecated (%s:%d).", - cmd->directive->filename, cmd->directive->line_num); - } - - if (apr_table_get(dcfg->tmp_default_actionset->actions, "t")) { - ap_log_perror(APLOG_MARK, - APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool, - "ModSecurity: WARNING Using transformations in " - "SecDefaultAction is deprecated (%s:%d).", - cmd->directive->filename, cmd->directive->line_num); - } - - /* Must not use chain. */ - if (dcfg->tmp_default_actionset->is_chained != NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " - "contain a chain action."); - } - - /* Must not use skip. */ - if (dcfg->tmp_default_actionset->skip_count != NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " - "contain a skip action."); - } - - /* Must not use skipAfter. */ - if (dcfg->tmp_default_actionset->skip_after != NOT_SET_P) { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " - "contain a skipAfter action."); - } - - return NULL; -} - -static const char *cmd_disable_backend_compression(cmd_parms *cmd, void *_dcfg, int flag) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - dcfg->disable_backend_compression = flag; - return NULL; -} - -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; - - if (cmd->server->is_virtual) { - return "ModSecurity: SecGuardianLog not allowed in VirtualHost"; - } - - if (p2 != NULL) { - if (strncmp(p2, "env=", 4) != 0) { - return "ModSecurity: Error in condition clause"; - } - if ( (p2[4] == '\0') || ((p2[4] == '!')&&(p2[5] == '\0')) ) { - return "ModSecurity: Missing variable name"; - } - guardianlog_condition = apr_pstrdup(cmd->pool, p2 + 4); - } - - guardianlog_name = (char *)p1; - - if (guardianlog_name[0] == '|') { - const char *pipe_name = ap_server_root_relative(cmd->pool, guardianlog_name + 1); - piped_log *pipe_log; - - pipe_log = ap_open_piped_log(cmd->pool, pipe_name); - if (pipe_log == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log pipe: %s", - pipe_name); - } - guardianlog_fd = ap_piped_log_write_fd(pipe_log); - } - else { - const char *file_name = ap_server_root_relative(cmd->pool, guardianlog_name); - apr_status_t rc; - - rc = apr_file_open(&guardianlog_fd, file_name, - APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, - CREATEMODE, cmd->pool); - - if (rc != APR_SUCCESS) { - return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log file: %s", - file_name); - } - } - - return NULL; -} - -/** -* \brief Add SecStreamInBodyInspection configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_stream_inbody_inspection(cmd_parms *cmd, void *_dcfg, int flag) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - dcfg->stream_inbody_inspection = flag; - return NULL; -} - - -/** -* \brief Add SecStreamOutBodyInspection configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, int flag) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - dcfg->stream_outbody_inspection = flag; - return NULL; -} -/** -* \brief Add SecRulePerfTime configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_rule_perf_time(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - long int limit; - - if (dcfg == NULL) return NULL; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRulePerfTime: %s", p1); - } - - dcfg->max_rule_time = limit; - - return NULL; -} - -/** -* \brief Add SecReadStateLimit configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - long int limit; - - if (dcfg == NULL) return NULL; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecReadStateLimit: %s", p1); - } - - conn_read_state_limit = limit; - - return NULL; -} - -/** -* \brief Add SecWriteStateLimit configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_conn_write_state_limit(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - long int limit; - - if (dcfg == NULL) return NULL; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecWriteStateLimit: %s", p1); - } - - conn_write_state_limit = limit; - - return NULL; -} - - -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; - - if (dcfg == NULL) return NULL; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyInMemoryLimit: %s", p1); - } - - dcfg->reqbody_inmemory_limit = limit; - - return NULL; -} - -static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - long int limit; - - if (dcfg == NULL) return NULL; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimit: %s", p1); - } - - dcfg->reqbody_limit = limit; - - return NULL; -} - -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; - - if (dcfg == NULL) return NULL; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyNoFilesLimit: %s", p1); - } - - dcfg->reqbody_no_files_limit = limit; - - return NULL; -} - -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; - - if (strcasecmp(p1, "on") == 0) dcfg->reqbody_access = 1; - else - if (strcasecmp(p1, "off") == 0) dcfg->reqbody_access = 0; - else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyAccess: %s", p1); - - return NULL; -} - -/** -* \brief Add SecInterceptOnError configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On success -*/ -static const char *cmd_request_intercept_on_error(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (strcasecmp(p1, "on") == 0) dcfg->reqintercept_oe = 1; - else - if (strcasecmp(p1, "off") == 0) dcfg->reqintercept_oe = 0; - else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecInterceptOnError: %s", p1); - - return NULL; -} - - -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; - - /* ENH Validate encoding */ - - dcfg->request_encoding = p1; - - return NULL; -} - -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; - - if (strcasecmp(p1, "on") == 0) dcfg->resbody_access = 1; - else - if (strcasecmp(p1, "off") == 0) dcfg->resbody_access = 0; - else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyAccess: %s", p1); - - return NULL; -} - -static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - long int limit; - - limit = strtol(p1, NULL, 10); - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimit: %s", p1); - } - - if (limit > RESPONSE_BODY_HARD_LIMIT) { - return apr_psprintf(cmd->pool, "ModSecurity: Response size limit can not exceed the hard limit: %li", RESPONSE_BODY_HARD_LIMIT); - } - - dcfg->of_limit = limit; - - return NULL; -} - -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; - - if (dcfg->is_enabled == MODSEC_DETECTION_ONLY) { - dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL; - return NULL; - } - - if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL; - else - if (strcasecmp(p1, "Reject") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; - else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimitAction: %s", p1); - - return NULL; -} - -/** -* \brief Add SecRequestBodyLimitAction configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On success -*/ -static const char *cmd_resquest_body_limit_action(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (dcfg->is_enabled == MODSEC_DETECTION_ONLY) { - dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - return NULL; - } - - if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - else - if (strcasecmp(p1, "Reject") == 0) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT; - else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimitAction: %s", p1); - - return NULL; -} - -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); - - /* TODO check whether the parameter is a valid MIME type of "???" */ - - if ((dcfg->of_mime_types == NULL)||(dcfg->of_mime_types == NOT_SET_P)) { - dcfg->of_mime_types = apr_table_make(cmd->pool, 10); - } - - strtolower_inplace((unsigned char *)p1); - apr_table_setn(dcfg->of_mime_types, p1, "1"); - - return NULL; -} - -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; - - dcfg->of_mime_types_cleared = 1; - - if ((dcfg->of_mime_types != NULL)&&(dcfg->of_mime_types != NOT_SET_P)) { - apr_table_clear(dcfg->of_mime_types); - } - - return NULL; -} - -/** - * \brief Add SecRuleUpdateTargetById - * - * \param cmd Pointer to configuration data - * \param _dcfg Pointer to directory configuration - * \param p1 Pointer to configuration option - * \param p2 Pointer to configuration option - * \param p3 Pointer to configuration option - * - * \retval NULL On failure|Success - */ -static const char *cmd_rule_update_target_by_id(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2, const char *p3) -{ - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - if(p1 == NULL) { - return apr_psprintf(cmd->pool, "Updating target by ID with no ID"); - } - - re->type = RULE_EXCEPTION_REMOVE_ID; - /* TODO: Validate the range here, while we can still tell the user if it's invalid */ - re->param = p1; - - if(dcfg->ruleset == NULL) { - return apr_psprintf(cmd->pool, "Updating target by ID with no ruleset in this context"); - } - - return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3); -} - -/** - * \brief Add SecRuleUpdateTargetByTag configuration option - * - * \param cmd Pointer to configuration data - * \param _dcfg Pointer to directory configuration - * \param p1 Pointer to configuration option RULETAG - * \param p2 Pointer to configuration option TARGET - * \param p3 Pointer to configuration option REPLACED_TARGET - * \todo Finish documenting - * - * \retval NULL On success - * \retval apr_psprintf On failure - * - * \todo Figure out error checking - */ -static const char *cmd_rule_update_target_by_tag(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2, const char *p3) -{ - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - if(p1 == NULL) { - return apr_psprintf(cmd->pool, "Updating target by tag with no tag"); - } - - re->type = RULE_EXCEPTION_REMOVE_TAG; - re->param = p1; - re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); - } - - return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3); -} -/** - * \brief Add SecRuleUpdateTargetByMsg configuration option - * - * \param cmd Pointer to configuration data - * \param _dcfg Pointer to directory configuration - * \param p1 Pointer to configuration option RULEMSG - * \param p2 Pointer to configuration option TARGET - * \param p3 Pointer to configuration option REPLACED_TARGET - * \todo Finish documenting - * - * \retval NULL On success - * \retval apr_psprintf On failure - * - * \todo Figure out error checking - */ -static const char *cmd_rule_update_target_by_msg(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2, const char *p3) -{ - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - if(p1 == NULL) { - return apr_psprintf(cmd->pool, "Updating target by message with no message"); - } - - re->type = RULE_EXCEPTION_REMOVE_MSG; - re->param = p1; - re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); - } - - return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, 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) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (strcasecmp(p1, "on") == 0) dcfg->is_enabled = MODSEC_ENABLED; - else - if (strcasecmp(p1, "off") == 0) dcfg->is_enabled = MODSEC_DISABLED; - else - if (strcasecmp(p1, "detectiononly") == 0) { - dcfg->is_enabled = MODSEC_DETECTION_ONLY; - dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL; - dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - } else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRuleEngine: %s", p1); - - return NULL; -} - -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) -{ - #if defined(WITH_LUA) - const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); - return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL); - #else - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Ignoring SecRuleScript \"%s\" directive (%s:%d): No Lua scripting support.", p1, cmd->directive->filename, cmd->directive->line_num); - return NULL; - #endif -} - -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; - - re->type = RULE_EXCEPTION_REMOVE_ID; - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - /* Remove the corresponding rules from the context straight away. */ - msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); - - return NULL; -} - -/** -* \brief Add SecRuleRemoveByTag configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On success -*/ -static const char *cmd_rule_remove_by_tag(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_REMOVE_TAG; - re->param = p1; - re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); - } - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - /* Remove the corresponding rules from the context straight away. */ - msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg); - #endif - - return NULL; -} - -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; - - re->type = RULE_EXCEPTION_REMOVE_MSG; - re->param = p1; - re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); - } - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - /* Remove the corresponding rules from the context straight away. */ - msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); - - #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg); - #endif - - return NULL; -} - -static const char *cmd_rule_update_action_by_id(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2) -{ - int offset = 0, rule_id = atoi(p1); - char *opt = strchr(p1,':'); - char *savedptr = NULL; - char *param = apr_pstrdup(cmd->pool, p1); - - if ((rule_id == LONG_MAX)||(rule_id == LONG_MIN)||(rule_id <= 0)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for ID for update action: %s", p1); - } - - if(opt != NULL) { - opt++; - offset = atoi(opt); - opt = apr_strtok(param,":", &savedptr); - return update_rule_action(cmd, (directory_config *)_dcfg, (const char *)opt, p2, offset); - } - - return update_rule_action(cmd, (directory_config *)_dcfg, p1, p2, offset); -} - -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"; - } - new_server_signature = (char *)p1; - return NULL; -} - -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; - - if (strcasecmp(p1, "none") == 0) dcfg->tmp_dir = NULL; - else dcfg->tmp_dir = ap_server_root_relative(cmd->pool, p1); - - return NULL; -} - -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; - - if (strcasecmp(p1, "none") == 0) dcfg->upload_dir = NULL; - else dcfg->upload_dir = ap_server_root_relative(cmd->pool, p1); - - return NULL; -} - -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; - - if (strcasecmp(p1, "default") == 0) { - dcfg->upload_filemode = NOT_SET; - } - else { - long int mode = strtol(p1, NULL, 8); /* expects octal mode */ - if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecUploadFileMode: %s", p1); - } - - dcfg->upload_filemode = (int)mode; - } - - return NULL; -} - -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; - - if (strcasecmp(p1, "on") == 0) { - dcfg->upload_keep_files = KEEP_FILES_ON; - } else - if (strcasecmp(p1, "off") == 0) { - dcfg->upload_keep_files = KEEP_FILES_OFF; - } else - if (strcasecmp(p1, "relevantonly") == 0) { - dcfg->upload_keep_files = KEEP_FILES_RELEVANT_ONLY; - } else { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for SecUploadKeepFiles: %s", - p1); - } - return NULL; -} - -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, ., _, -) */ - dcfg->webappid = p1; - - return NULL; -} - -static const char *cmd_sensor_id(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - /* ENH enforce format (letters, digits, ., _, -) */ - dcfg->sensor_id = p1; - - return NULL; -} - -/** -* \brief Add SecXmlExternalEntity configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_xml_external_entity(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (strcasecmp(p1, "on") == 0) { - dcfg->xml_external_entity = 1; - } - else if (strcasecmp(p1, "off") == 0) { - dcfg->xml_external_entity = 0; - } - else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecXmlExternalEntity: %s", p1); - - return NULL; -} - - -/** -* \brief Add SecHashEngine configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_hash_engine(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (strcasecmp(p1, "on") == 0) { - dcfg->hash_is_enabled = HASH_ENABLED; - dcfg->hash_enforcement = HASH_ENABLED; - } - else if (strcasecmp(p1, "off") == 0) { - dcfg->hash_is_enabled = HASH_DISABLED; - dcfg->hash_enforcement = HASH_DISABLED; - } - else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecHashEngine: %s", p1); - - return NULL; -} - -/** -* \brief Add SecHashPram configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On success -*/ -static const char *cmd_hash_param(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - if (dcfg == NULL) return NULL; - - if (p1 == NULL) return NULL; - dcfg->crypto_param_name = p1; - - return NULL; -} - -/** -* \brief Add SecHashKey configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param _p1 Pointer to configuration option -* \param _p2 Pointer to configuration option -* -* \retval NULL On success -*/ -static const char *cmd_hash_key(cmd_parms *cmd, void *_dcfg, const char *_p1, const char *_p2) -{ - directory_config *dcfg = (directory_config *)_dcfg; - char *p1 = NULL; - - if (dcfg == NULL) return NULL; - if (_p1 == NULL) return NULL; - - if (strcasecmp(_p1, "Rand") == 0) { - p1 = apr_pstrdup(cmd->pool, getkey(cmd->pool)); - dcfg->crypto_key = p1; - dcfg->crypto_key_len = strlen(dcfg->crypto_key); - } else { - p1 = apr_pstrdup(cmd->pool, _p1); - dcfg->crypto_key = p1; - dcfg->crypto_key_len = strlen(p1); - } - - if(_p2 == NULL) { - return NULL; - } else { - if (strcasecmp(_p2, "KeyOnly") == 0) - dcfg->crypto_key_add = HASH_KEYONLY; - else if (strcasecmp(_p2, "SessionID") == 0) - dcfg->crypto_key_add = HASH_SESSIONID; - else if (strcasecmp(_p2, "RemoteIP") == 0) - dcfg->crypto_key_add = HASH_REMOTEIP; - } - return NULL; -} - -/** -* \brief Add SecHashMethodPm configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* \param p2 Pointer to configuration option -* -* \retval NULL On failure -* \retval apr_psprintf On Success -*/ -static const char *cmd_hash_method_pm(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2) -{ - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(hash_method)); - const char *_p2 = apr_pstrdup(cmd->pool, p2); - ACMP *p = NULL; - const char *phrase = NULL; - const char *next = NULL; - - if (dcfg == NULL) return NULL; - - p = acmp_create(0, cmd->pool); - if (p == NULL) return NULL; - - if(phrase == NULL) - phrase = apr_pstrdup(cmd->pool, _p2); - - for (;;) { - while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++; - if (*phrase == '\0') break; - next = phrase; - while((apr_isspace(*next) == 0) && (*next != 0)) next++; - acmp_add_pattern(p, phrase, NULL, NULL, next - phrase); - phrase = next; - } - - acmp_prepare(p); - - if (strcasecmp(p1, "HashHref") == 0) { - re->type = HASH_URL_HREF_HASH_PM; - re->param = _p2; - re->param_data = (void *)p; - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2); - } - dcfg->crypto_hash_href_pm = 1; - } - else if (strcasecmp(p1, "HashFormAction") == 0) { - re->type = HASH_URL_FACTION_HASH_PM; - re->param = _p2; - re->param_data = (void *)p; - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2); - } - dcfg->crypto_hash_faction_pm = 1; - } - else if (strcasecmp(p1, "HashLocation") == 0) { - re->type = HASH_URL_LOCATION_HASH_PM; - re->param = _p2; - re->param_data = (void *)p; - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2); - } - dcfg->crypto_hash_location_pm = 1; - } - else if (strcasecmp(p1, "HashIframeSrc") == 0) { - re->type = HASH_URL_IFRAMESRC_HASH_PM; - re->param = _p2; - re->param_data = (void *)p; - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2); - } - dcfg->crypto_hash_iframesrc_pm = 1; - } - else if (strcasecmp(p1, "HashFrameSrc") == 0) { - re->type = HASH_URL_FRAMESRC_HASH_PM; - re->param = _p2; - re->param_data = (void *)p; - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2); - } - dcfg->crypto_hash_framesrc_pm = 1; - } - - *(hash_method **)apr_array_push(dcfg->hash_method) = re; - - return NULL; -} - -/** - * \brief Add SecHashMethodRx configuration option - * - * \param cmd Pointer to configuration data - * \param _dcfg Pointer to directory configuration - * \param p1 Pointer to configuration option - * \param p2 Pointer to configuration option - * - * \retval NULL On failure - * \retval apr_psprintf On Success - */ -static const char *cmd_hash_method_rx(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2) -{ - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(hash_method)); - const char *_p2 = apr_pstrdup(cmd->pool, p2); - if (dcfg == NULL) return NULL; - - if (strcasecmp(p1, "HashHref") == 0) { - re->type = HASH_URL_HREF_HASH_RX; - re->param = _p2; - re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2); - } - dcfg->crypto_hash_href_rx = 1; - } - else if (strcasecmp(p1, "HashFormAction") == 0) { - re->type = HASH_URL_FACTION_HASH_RX; - re->param = _p2; - re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2); - } - dcfg->crypto_hash_faction_rx = 1; - } - else if (strcasecmp(p1, "HashLocation") == 0) { - re->type = HASH_URL_LOCATION_HASH_RX; - re->param = _p2; - re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2); - } - dcfg->crypto_hash_location_rx = 1; - } - else if (strcasecmp(p1, "HashIframeSrc") == 0) { - re->type = HASH_URL_IFRAMESRC_HASH_RX; - re->param = _p2; - re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2); - } - dcfg->crypto_hash_iframesrc_rx = 1; - } - else if (strcasecmp(p1, "HashFrameSrc") == 0) { - re->type = HASH_URL_FRAMESRC_HASH_RX; - re->param = _p2; - re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL); - if (re->param_data == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2); - } - dcfg->crypto_hash_framesrc_rx = 1; - } - - *(hash_method **)apr_array_push(dcfg->hash_method) = re; - - return NULL; -} - -/** -* \brief Add SecHttpBlKey configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On success -*/ -static const char *cmd_httpBl_key(cmd_parms *cmd, void *_dcfg, const char *p1) -{ - directory_config *dcfg = (directory_config *)_dcfg; - - if (dcfg == NULL) return NULL; - - if (p1 == NULL) return NULL; - dcfg->httpBlkey = 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 *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); - char *error_msg; - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (geo_init(dcfg, filename, &error_msg) <= 0) { - return error_msg; - } - - return NULL; -} - -/** -* \brief Add SecUnicodeCodePage configuration option -* -* Depcrecated -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On success -*/ -static const char *cmd_unicode_codepage(cmd_parms *cmd, - void *_dcfg, const char *p1) -{ - long val; - - val = atol(p1); - if (val <= 0) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for " - "SecUnicodeCodePage: %s", p1); - } - - unicode_codepage = (unsigned long int)val; - - return NULL; -} - -/** -* \brief Add SecUnicodeMapFile configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On success -*/ -static const char *cmd_unicode_map(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2) -{ - const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); - char *error_msg; - long val = 0; - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if(p2 != NULL) { - val = atol(p2); - if (val <= 0) { - return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for " - "SecUnicodeMapFile: %s", p2); - } - - unicode_codepage = (unsigned long int)val; - } - - if (unicode_map_init(dcfg, filename, &error_msg) <= 0) { - return error_msg; - } - - return NULL; -} - -/** -* \brief Add SecGsbLookupDb configuration option -* -* \param cmd Pointer to configuration data -* \param _dcfg Pointer to directory configuration -* \param p1 Pointer to configuration option -* -* \retval NULL On success -*/ -static const char *cmd_gsb_lookup_db(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); - char *error_msg; - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (gsb_db_init(dcfg, filename, &error_msg) <= 0) { - return error_msg; - } - - return NULL; -} - -/* -- Cache -- */ - -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; - - if (strcasecmp(p1, "on") == 0) - dcfg->cache_trans = MODSEC_CACHE_ENABLED; - else if (strcasecmp(p1, "off") == 0) - dcfg->cache_trans = MODSEC_CACHE_DISABLED; - else - return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCacheTransformations: %s", p1); - - /* Process options */ - if (p2 != NULL) { - apr_table_t *vartable = apr_table_make(cmd->pool, 4); - apr_status_t rc; - char *error_msg = NULL; - const char *charval = NULL; - apr_int64_t intval = 0; - - if (vartable == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Unable to process options for SecCacheTransformations"); - } - rc = msre_parse_generic(cmd->pool, p2, vartable, &error_msg); - if (rc < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: Unable to parse options for SecCacheTransformations: %s", error_msg); - } - - /* incremental */ - charval = apr_table_get(vartable, "incremental"); - if (charval != NULL) { - if (strcasecmp(charval, "on") == 0) - dcfg->cache_trans_incremental = 1; - else if (strcasecmp(charval, "off") == 0) - dcfg->cache_trans_incremental = 0; - else - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations invalid incremental value: %s", charval); - } - - /* minlen */ - charval = apr_table_get(vartable, "minlen"); - if (charval != NULL) { - intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be positive: %s", charval); - } - - /* The NOT_SET indicator is -1, a signed long, and therfore - * we cannot be >= the unsigned value of NOT_SET. - */ - if ((unsigned long)intval >= (unsigned long)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET); - } - dcfg->cache_trans_min = (apr_size_t)intval; - } - - /* maxlen */ - charval = apr_table_get(vartable, "maxlen"); - if (charval != NULL) { - intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be positive: %s", charval); - } - - /* The NOT_SET indicator is -1, a signed long, and therfore - * we cannot be >= the unsigned value of NOT_SET. - */ - if ((unsigned long)intval >= (unsigned long)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET); - } - if ((intval != 0) && ((apr_size_t)intval < dcfg->cache_trans_min)) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %lu < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min); - } - dcfg->cache_trans_max = (apr_size_t)intval; - - } - - /* maxitems */ - charval = apr_table_get(vartable, "maxitems"); - if (charval != NULL) { - intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems must be positive: %s", charval); - } - dcfg->cache_trans_maxitems = (apr_size_t)intval; - } - } - - return NULL; -} - - -/* -- Configuration directives definitions -- */ - -#define CMD_SCOPE_MAIN (RSRC_CONF) -#define CMD_SCOPE_ANY (RSRC_CONF | ACCESS_CONF) - -#if defined(HTACCESS_CONFIG) -#define CMD_SCOPE_HTACCESS (OR_OPTIONS) -#endif - -const command_rec module_directives[] = { - -#ifdef HTACCESS_CONFIG - AP_INIT_TAKE1 ( - "SecAction", - cmd_action, - NULL, - CMD_SCOPE_HTACCESS, - "an action list" - ), -#else - AP_INIT_TAKE1 ( - "SecAction", - cmd_action, - NULL, - CMD_SCOPE_ANY, - "an action list" - ), -#endif - - AP_INIT_TAKE1 ( - "SecArgumentSeparator", - cmd_argument_separator, - NULL, - CMD_SCOPE_ANY, - "character that will be used as separator when parsing application/x-www-form-urlencoded content." - ), - - AP_INIT_TAKE1 ( - "SecCookiev0Separator", - cmd_cookiev0_separator, - NULL, - CMD_SCOPE_ANY, - "character that will be used as separator when parsing cookie v0 content." - ), - - AP_INIT_TAKE1 ( - "SecAuditEngine", - cmd_audit_engine, - NULL, - CMD_SCOPE_ANY, - "On, Off or RelevantOnly to determine the level of audit logging" - ), - - AP_INIT_TAKE1 ( - "SecAuditLog", - cmd_audit_log, - NULL, - CMD_SCOPE_ANY, - "filename of the primary audit log file" - ), - - AP_INIT_TAKE1 ( - "SecAuditLog2", - cmd_audit_log2, - NULL, - CMD_SCOPE_ANY, - "filename of the secondary audit log file" - ), - - AP_INIT_TAKE1 ( - "SecAuditLogParts", - cmd_audit_log_parts, - NULL, - CMD_SCOPE_ANY, - "list of audit log parts that go into the log." - ), - - AP_INIT_TAKE1 ( - "SecAuditLogRelevantStatus", - cmd_audit_log_relevant_status, - NULL, - CMD_SCOPE_ANY, - "regular expression that will be used to determine if the response status is relevant for audit logging" - ), - - AP_INIT_TAKE1 ( - "SecAuditLogType", - cmd_audit_log_type, - NULL, - CMD_SCOPE_ANY, - "whether to use the old audit log format (Serial) or new (Concurrent)" - ), - - AP_INIT_TAKE1 ( - "SecAuditLogStorageDir", - cmd_audit_log_storage_dir, - NULL, - CMD_SCOPE_ANY, - "path to the audit log storage area; absolute, or relative to the root of the server" - ), - - AP_INIT_TAKE1 ( - "SecAuditLogDirMode", - cmd_audit_log_dirmode, - NULL, - CMD_SCOPE_ANY, - "octal permissions mode for concurrent audit log directories" - ), - - AP_INIT_TAKE1 ( - "SecAuditLogFileMode", - cmd_audit_log_filemode, - NULL, - CMD_SCOPE_ANY, - "octal permissions mode for concurrent audit log files" - ), - - AP_INIT_TAKE12 ( - "SecCacheTransformations", - cmd_cache_transformations, - NULL, - CMD_SCOPE_ANY, - "whether or not to cache transformations. Defaults to true." - ), - - AP_INIT_TAKE1 ( - "SecChrootDir", - cmd_chroot_dir, - NULL, - CMD_SCOPE_MAIN, - "path of the directory to which server will be chrooted" - ), - - AP_INIT_TAKE1 ( - "SecComponentSignature", - cmd_component_signature, - NULL, - CMD_SCOPE_MAIN, - "component signature to add to ModSecurity signature." - ), - - AP_INIT_FLAG ( - "SecContentInjection", - cmd_content_injection, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_FLAG ( - "SecStreamOutBodyInspection", - cmd_stream_outbody_inspection, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_FLAG ( - "SecStreamInBodyInspection", - cmd_stream_inbody_inspection, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE1 ( - "SecCookieFormat", - cmd_cookie_format, - NULL, - CMD_SCOPE_ANY, - "version of the Cookie specification to use for parsing. Possible values are 0 and 1." - ), - - AP_INIT_TAKE1 ( - "SecDataDir", - cmd_data_dir, - NULL, - CMD_SCOPE_MAIN, - "path to the persistent data storage area" // TODO - ), - - AP_INIT_TAKE1 ( - "SecDebugLog", - cmd_debug_log, - NULL, - CMD_SCOPE_ANY, - "path to the debug log file" - ), - - AP_INIT_TAKE1 ( - "SecDebugLogLevel", - cmd_debug_log_level, - NULL, - CMD_SCOPE_ANY, - "debug log level, which controls the verbosity of logging." - " Use values from 0 (no logging) to 9 (a *lot* of logging)." - ), - - AP_INIT_TAKE1 ( - "SecCollectionTimeout", - cmd_collection_timeout, - NULL, - CMD_SCOPE_ANY, - "set default collections timeout. default it 3600" - ), - - AP_INIT_TAKE1 ( - "SecDefaultAction", - cmd_default_action, - NULL, - CMD_SCOPE_ANY, - "default action list" - ), - - AP_INIT_FLAG ( - "SecDisableBackendCompression", - cmd_disable_backend_compression, - NULL, - CMD_SCOPE_ANY, - "When set to On, removes the compression headers from the backend requests." - ), - - AP_INIT_TAKE1 ( - "SecGsbLookupDB", - cmd_gsb_lookup_db, - NULL, - RSRC_CONF, - "database google safe browsing" - ), - - AP_INIT_TAKE1 ( - "SecUnicodeCodePage", - cmd_unicode_codepage, - NULL, - CMD_SCOPE_MAIN, - "Unicode CodePage" - ), - - AP_INIT_TAKE12 ( - "SecUnicodeMapFile", - cmd_unicode_map, - NULL, - CMD_SCOPE_MAIN, - "Unicode Map file" - ), - - AP_INIT_TAKE1 ( - "SecGeoLookupDB", - cmd_geo_lookup_db, - NULL, - RSRC_CONF, - "database for geographical lookups module." - ), - - AP_INIT_TAKE12 ( - "SecGuardianLog", - cmd_guardian_log, - NULL, - CMD_SCOPE_MAIN, - "The filename of the filter debugging log file" - ), - - AP_INIT_TAKE1 ( - "SecMarker", - cmd_marker, - NULL, - CMD_SCOPE_ANY, - "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, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE1 ( - "SecInterceptOnError", - cmd_request_intercept_on_error, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE1 ( - "SecRulePerfTime", - cmd_rule_perf_time, - NULL, - CMD_SCOPE_ANY, - "Threshold to log slow rules in usecs." - ), - - AP_INIT_TAKE1 ( - "SecReadStateLimit", - cmd_conn_read_state_limit, - NULL, - CMD_SCOPE_ANY, - "maximum number of threads in READ_BUSY state per ip address" - ), - - AP_INIT_TAKE1 ( - "SecWriteStateLimit", - cmd_conn_write_state_limit, - NULL, - CMD_SCOPE_ANY, - "maximum number of threads in WRITE_BUSY state per ip address" - ), - - AP_INIT_TAKE1 ( - "SecRequestBodyInMemoryLimit", - cmd_request_body_inmemory_limit, - NULL, - CMD_SCOPE_ANY, - "maximum request body size that will be placed in memory (except for POST urlencoded requests)." - ), - - AP_INIT_TAKE1 ( - "SecRequestBodyLimit", - cmd_request_body_limit, - NULL, - CMD_SCOPE_ANY, - "maximum request body size ModSecurity will accept." - ), - - AP_INIT_TAKE1 ( - "SecRequestBodyNoFilesLimit", - cmd_request_body_no_files_limit, - NULL, - CMD_SCOPE_ANY, - "maximum request body size ModSecurity will accept, but excluding the size of uploaded files." - ), - - AP_INIT_TAKE1 ( - "SecRequestEncoding", - cmd_request_encoding, - NULL, - CMD_SCOPE_ANY, - "character encoding used in request." - ), - - AP_INIT_TAKE1 ( - "SecResponseBodyAccess", - cmd_response_body_access, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE1 ( - "SecResponseBodyLimit", - cmd_response_body_limit, - NULL, - CMD_SCOPE_ANY, - "byte limit for response body" - ), - - AP_INIT_TAKE1 ( - "SecResponseBodyLimitAction", - cmd_response_body_limit_action, - NULL, - CMD_SCOPE_ANY, - "what happens when the response body limit is reached" - ), - - AP_INIT_TAKE1 ( - "SecRequestBodyLimitAction", - cmd_resquest_body_limit_action, - NULL, - CMD_SCOPE_ANY, - "what happens when the request body limit is reached" - ), - - AP_INIT_ITERATE ( - "SecResponseBodyMimeType", - cmd_response_body_mime_type, - NULL, - CMD_SCOPE_ANY, - "adds given MIME types to the list of types that will be buffered on output" - ), - - AP_INIT_NO_ARGS ( - "SecResponseBodyMimeTypesClear", - cmd_response_body_mime_types_clear, - NULL, - CMD_SCOPE_ANY, - "clears the list of MIME types that will be buffered on output" - ), - -#ifdef HTACCESS_CONFIG - AP_INIT_TAKE23 ( - "SecRule", - cmd_rule, - NULL, - CMD_SCOPE_HTACCESS, - "rule target, operator and optional action list" - ), -#else - AP_INIT_TAKE23 ( - "SecRule", - cmd_rule, - NULL, - CMD_SCOPE_ANY, - "rule target, operator and optional action list" - ), -#endif - - AP_INIT_TAKE1 ( - "SecRuleEngine", - cmd_rule_engine, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE1 ( - "SecXmlExternalEntity", - cmd_xml_external_entity, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_FLAG ( - "SecRuleInheritance", - cmd_rule_inheritance, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE12 ( - "SecRuleScript", - cmd_rule_script, - NULL, - CMD_SCOPE_ANY, - "rule script and optional actionlist" - ), - -#ifdef HTACCESS_CONFIG - AP_INIT_ITERATE ( - "SecRuleRemoveById", - cmd_rule_remove_by_id, - NULL, - CMD_SCOPE_HTACCESS, - "rule ID for removal" - ), - - AP_INIT_ITERATE ( - "SecRuleRemoveByTag", - cmd_rule_remove_by_tag, - NULL, - CMD_SCOPE_HTACCESS, - "rule tag for removal" - ), - - AP_INIT_ITERATE ( - "SecRuleRemoveByMsg", - cmd_rule_remove_by_msg, - NULL, - CMD_SCOPE_HTACCESS, - "rule message for removal" - ), -#else - AP_INIT_ITERATE ( - "SecRuleRemoveById", - cmd_rule_remove_by_id, - NULL, - CMD_SCOPE_ANY, - "rule ID for removal" - ), - - AP_INIT_ITERATE ( - "SecRuleRemoveByTag", - cmd_rule_remove_by_tag, - NULL, - CMD_SCOPE_ANY, - "rule tag for removal" - ), - - AP_INIT_ITERATE ( - "SecRuleRemoveByMsg", - cmd_rule_remove_by_msg, - NULL, - CMD_SCOPE_ANY, - "rule message for removal" - ), -#endif - - AP_INIT_TAKE2 ( - "SecHashMethodPm", - cmd_hash_method_pm, - NULL, - CMD_SCOPE_ANY, - "Hash method and pattern" - ), - - AP_INIT_TAKE2 ( - "SecHashMethodRx", - cmd_hash_method_rx, - NULL, - CMD_SCOPE_ANY, - "Hash method and regex" - ), - -#ifdef HTACCESS_CONFIG - AP_INIT_TAKE2 ( - "SecRuleUpdateActionById", - cmd_rule_update_action_by_id, - NULL, - CMD_SCOPE_HTACCESS, - "updated action list" - ), - - AP_INIT_TAKE23 ( - "SecRuleUpdateTargetById", - cmd_rule_update_target_by_id, - NULL, - CMD_SCOPE_HTACCESS, - "updated target list" - ), - - AP_INIT_TAKE23 ( - "SecRuleUpdateTargetByTag", - cmd_rule_update_target_by_tag, - NULL, - CMD_SCOPE_HTACCESS, - "rule tag pattern and updated target list" - ), - - AP_INIT_TAKE23 ( - "SecRuleUpdateTargetByMsg", - cmd_rule_update_target_by_msg, - NULL, - CMD_SCOPE_HTACCESS, - "rule message pattern and updated target list" - ), -#else - AP_INIT_TAKE2 ( - "SecRuleUpdateActionById", - cmd_rule_update_action_by_id, - NULL, - CMD_SCOPE_ANY, - "updated action list" - ), - - AP_INIT_TAKE23 ( - "SecRuleUpdateTargetById", - cmd_rule_update_target_by_id, - NULL, - CMD_SCOPE_ANY, - "updated target list" - ), - - AP_INIT_TAKE23 ( - "SecRuleUpdateTargetByTag", - cmd_rule_update_target_by_tag, - NULL, - CMD_SCOPE_ANY, - "rule tag pattern and updated target list" - ), - - AP_INIT_TAKE23 ( - "SecRuleUpdateTargetByMsg", - cmd_rule_update_target_by_msg, - NULL, - CMD_SCOPE_ANY, - "rule message pattern and updated target list" - ), -#endif - - AP_INIT_TAKE1 ( - "SecServerSignature", - cmd_server_signature, - NULL, - CMD_SCOPE_MAIN, - "the new signature of the server" - ), - - AP_INIT_TAKE1 ( - "SecTmpDir", - cmd_tmp_dir, - NULL, - CMD_SCOPE_ANY, - "path to the temporary storage area" - ), - - AP_INIT_TAKE1 ( - "SecUploadDir", - cmd_upload_dir, - NULL, - CMD_SCOPE_ANY, - "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, - NULL, - CMD_SCOPE_ANY, - "octal permissions mode for uploaded files" - ), - - AP_INIT_TAKE1 ( - "SecUploadKeepFiles", - cmd_upload_keep_files, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE1 ( - "SecWebAppId", - cmd_web_app_id, - NULL, - CMD_SCOPE_ANY, - "id" - ), - - AP_INIT_TAKE1 ( - "SecSensorId", - cmd_sensor_id, - NULL, - CMD_SCOPE_MAIN, - "sensor id" - ), - - AP_INIT_TAKE1 ( - "SecHttpBlKey", - cmd_httpBl_key, - NULL, - CMD_SCOPE_ANY, - "httpBl access key" - ), - - AP_INIT_TAKE1 ( - "SecHashEngine", - cmd_hash_engine, - NULL, - CMD_SCOPE_ANY, - "On or Off" - ), - - AP_INIT_TAKE2 ( - "SecHashKey", - cmd_hash_key, - NULL, - CMD_SCOPE_ANY, - "Set Hash key" - ), - - AP_INIT_TAKE1 ( - "SecHashParam", - cmd_hash_param, - NULL, - CMD_SCOPE_ANY, - "Set Hash parameter" - ), - - { NULL } -}; diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c deleted file mode 100644 index 88f1903183..0000000000 --- a/apache2/apache2_io.c +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) - * - * You may not use this file except in compliance with - * the License.  You may obtain a copy of the License at - * - *     http://www.apache.org/licenses/LICENSE-2.0 - * - * If any of the files related to licensing are missing or if you have any - * other questions related to licensing please contact Trustwave Holdings, Inc. - * directly using the email address security@modsecurity.org. - */ - -#include - -#include "modsecurity.h" -#include "apache2.h" -#include "msc_crypt.h" - -/* -- Input filter -- */ - -#if 0 -static void dummy_free_func(void *data) {} -#endif - -/** - * This request filter will forward the previously stored - * request body further down the chain (most likely to the - * processing module). - */ -apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, - ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes) -{ - modsec_rec *msr = (modsec_rec *)f->ctx; - msc_data_chunk *chunk = NULL; - apr_bucket *bucket; - apr_status_t rc; - char *my_error_msg = NULL; - - if (msr == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, - "ModSecurity: Internal error in input filter: msr is null."); - ap_remove_input_filter(f); - return APR_EGENERAL; - } - - /* Make sure we are using the current request */ - msr->r = f->r; - - if (msr->phase < PHASE_REQUEST_BODY) { - msr_log(msr, 1, "Internal error: REQUEST_BODY phase incomplete for input filter in phase %d", msr->phase); - return APR_EGENERAL; - } - - if ((msr->if_status == IF_STATUS_COMPLETE)||(msr->if_status == IF_STATUS_NONE)) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %pp, r %pp).", f, f->r); - } - ap_remove_input_filter(f); - return ap_get_brigade(f->next, bb_out, mode, block, nbytes); - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Forwarding input: mode=%d, block=%d, nbytes=%" APR_OFF_T_FMT - " (f %pp, r %pp).", mode, block, nbytes, f, f->r); - } - - if (msr->if_started_forwarding == 0) { - msr->if_started_forwarding = 1; - rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg); - if (rc == -1) { - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - return APR_EGENERAL; - } - } - - rc = modsecurity_request_body_retrieve(msr, &chunk, (unsigned int)nbytes, &my_error_msg); - if (rc == -1) { - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - return APR_EGENERAL; - } - - if (chunk && (!msr->txcfg->stream_inbody_inspection || (msr->txcfg->stream_inbody_inspection && msr->if_stream_changed == 0))) { - /* Copy the data we received in the chunk */ - bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL, - f->r->connection->bucket_alloc); - -#if 0 - - It would seem that we cannot prevent other filters in the chain - from modifying data in-place. Hence we copy. - - if (chunk->is_permanent) { - /* Do not make a copy of the data we received in the chunk. */ - bucket = apr_bucket_heap_create(chunk->data, chunk->length, dummy_free_func, - f->r->connection->bucket_alloc); - } else { - /* Copy the data we received in the chunk. */ - bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL, - f->r->connection->bucket_alloc); - } - -#endif - - if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(bb_out, bucket); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Forwarded %" APR_SIZE_T_FMT " bytes.", chunk->length); - } - } else if (msr->stream_input_data != NULL) { - - msr->if_stream_changed = 0; - - bucket = apr_bucket_heap_create(msr->stream_input_data, msr->stream_input_length, NULL, - f->r->connection->bucket_alloc); - - if (msr->txcfg->stream_inbody_inspection) { - if(msr->stream_input_data != NULL) { - free(msr->stream_input_data); - msr->stream_input_data = NULL; - } - } - - if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(bb_out, bucket); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input stream filter: Forwarded %" APR_SIZE_T_FMT " bytes.", msr->stream_input_length); - } - - } - - if (rc == 0) { - modsecurity_request_body_retrieve_end(msr); - - bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(bb_out, bucket); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Sent EOS."); - } - - /* We're done */ - msr->if_status = IF_STATUS_COMPLETE; - ap_remove_input_filter(f); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Input forwarding complete."); - } - } - - return APR_SUCCESS; -} - -/** - * Reads request body from a client. - */ -apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { - request_rec *r = msr->r; - unsigned int seen_eos; - apr_bucket_brigade *bb_in; - apr_bucket *bucket; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (msr->reqbody_should_exist != 1) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: This request does not have a body."); - } - return 0; - } - - if (msr->txcfg->reqbody_access != 1) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Request body access not enabled."); - } - return 0; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Reading request body."); - } - - if (modsecurity_request_body_start(msr, error_msg) < 0) { - return -1; - } - - seen_eos = 0; - bb_in = apr_brigade_create(msr->mp, r->connection->bucket_alloc); - if (bb_in == NULL) return -1; - do { - apr_status_t rc; - - rc = ap_get_brigade(r->input_filters, bb_in, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); - if (rc != APR_SUCCESS) { - /* NOTE Apache returns AP_FILTER_ERROR here when the request is - * too large and APR_EGENERAL when the client disconnects. - */ - switch(rc) { - case APR_EOF : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -6; - case APR_TIMEUP : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -4; - case AP_FILTER_ERROR : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: HTTP Error 413 - Request entity too large. (Most likely.)"); - return -3; - case APR_EGENERAL : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: Client went away."); - return -2; - default : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -1; - } - } - - /* Loop through the buckets in the brigade in order - * to extract the size of the data available. - */ - for(bucket = APR_BRIGADE_FIRST(bb_in); - bucket != APR_BRIGADE_SENTINEL(bb_in); - bucket = APR_BUCKET_NEXT(bucket)) - { - const char *buf; - apr_size_t buflen; - - rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ); - if (rc != APR_SUCCESS) { - *error_msg = apr_psprintf(msr->mp, "Failed reading input / bucket (%d): %s", rc, get_apr_error(msr->mp, rc)); - return -1; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Input filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.", - bucket->type->name, buflen); - } - - /* Check request body limit (should only trigger on chunked requests). */ - if (msr->reqbody_length + buflen > (apr_size_t)msr->txcfg->reqbody_limit) { - if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { - *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - return -5; - } else if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) { - - *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - - } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){ - - *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - - } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)){ - - *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - - } else { - - *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - - return -5; - } - } - - if (msr->txcfg->stream_inbody_inspection == 1) { - msr->stream_input_length+=buflen; - modsecurity_request_body_to_stream(msr, buf, buflen, error_msg); - } - - msr->reqbody_length += buflen; - - if (buflen != 0) { - int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg); - if (rcbs < 0) { - if (rcbs == -5) { - if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { - *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - return -5; - } else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) { - *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) { - *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - } else { - *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - return -5; - } - } - - if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) - return -1; - } - - } - - if (APR_BUCKET_IS_EOS(bucket)) { - seen_eos = 1; - } - } - - apr_brigade_cleanup(bb_in); - } while(!seen_eos); - - // TODO: Why ignore the return code here? - modsecurity_request_body_end(msr, error_msg); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Completed receiving request body (length %" APR_SIZE_T_FMT ").", - msr->reqbody_length); - } - - msr->if_status = IF_STATUS_WANTS_TO_RUN; - - return 1; -} - - -/* -- Output filter -- */ - -/** - * Examines the configuration and the response MIME type - * in order to determine whether output buffering should - * run or not. - */ -static int output_filter_should_run(modsec_rec *msr, request_rec *r) { - char *content_type = NULL; - - /* Check configuration. */ - if (msr->txcfg->resbody_access != 1) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Response body buffering is not enabled."); - } - - return 0; - } - - /* Check MIME type. */ - - if ((msr->txcfg->of_mime_types == NULL)||(msr->txcfg->of_mime_types == NOT_SET_P)) { - msr_log(msr, 1, "Output filter: MIME type structures corrupted (internal error)."); - return -1; - } - - if (r->content_type != NULL) { - char *p = NULL; - - content_type = apr_pstrdup(msr->mp, r->content_type); - if (content_type == NULL) { - msr_log(msr, 1, "Output filter: Failed to allocate memory for content type."); - return -1; - } - - /* Hide the character encoding information - * if present. Sometimes the content type header - * looks like this "text/html; charset=xyz" ... - */ - p = strstr(content_type, ";"); - if (p != NULL) { - *p = '\0'; - } - - strtolower_inplace((unsigned char *)content_type); - - if (strcmp(content_type, "text/html") == 0) { - /* Useful information to have should we later - * decide to do something with the HTML output. - */ - msr->resbody_contains_html = 1; - } - } else { - content_type = "null"; - } - - if (apr_table_get(msr->txcfg->of_mime_types, content_type) != NULL) return 1; - - msr_log(msr, 4, "Output filter: Not buffering response body for unconfigured MIME type \"%s\".", content_type); - - return 0; -} - -/** - * Initialises the output filter. - */ -static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, - apr_bucket_brigade *bb_in) -{ - request_rec *r = f->r; - const char *s_content_length = NULL; - apr_status_t rc; - - msr->of_brigade = apr_brigade_create(msr->mp, f->c->bucket_alloc); - if (msr->of_brigade == NULL) { - msr_log(msr, 1, "Output filter: Failed to create brigade."); - return -1; - } - msr->of_status = OF_STATUS_IN_PROGRESS; - - rc = output_filter_should_run(msr, r); - if (rc < 0) return -1; /* output_filter_should_run() generates error msg */ - if (rc == 0) return 0; - - /* Do not check the output limit if we are willing to - * process partial response bodies. - */ - - if (msr->txcfg->of_limit_action == RESPONSE_BODY_LIMIT_ACTION_PARTIAL) { - return 1; - } - - /* Look up the Content-Length header to see if we know - * the amount of data coming our way. If we do and if - * it's too much we might want to stop processing right here. - */ - s_content_length = apr_table_get(r->headers_out, "Content-Length"); - if (s_content_length == NULL) { - /* Try this too, mod_cgi seems to put headers there. */ - s_content_length = apr_table_get(r->err_headers_out, "Content-Length"); - } - - if (s_content_length != NULL) { - long int len; - - len = strtol(s_content_length, NULL, 10); - if ((len == LONG_MIN)||(len == LONG_MAX)||(len < 0)||(len >= 1073741824)) { - msr_log(msr, 1, "Output filter: Invalid Content-Length: %s", log_escape_nq(r->pool, - (char *)s_content_length)); - return -1; /* Invalid. */ - } - - if (len == 0) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Skipping response since Content-Length is zero."); - } - - return 0; - } - - if (len > msr->txcfg->of_limit) { - msr_log(msr, 1, "Output filter: Content-Length (%s) over the limit (%ld).", - log_escape_nq(r->pool, (char *)s_content_length), msr->txcfg->of_limit); - msr->outbound_error = 1; - return -2; /* Over the limit. */ - } - } - - return 1; -} - -/** - * Send the accumulated content down the filter stream - * and to the client. - */ -static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) { - apr_status_t rc; - - rc = ap_pass_brigade(f->next, msr->of_brigade); - if (rc != APR_SUCCESS) { - /* TODO: These need to move to flags in 2.6. For now log them - * at level 4 so that they are not confusing users. - */ - int log_level = 4; - - if (msr->txcfg->debuglog_level >= log_level) { - switch(rc) { - case AP_NOBODY_WROTE : - msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): No data", rc); - break; - case AP_FILTER_ERROR : - /* Look like this is caused by the error - * already being handled, so we should ignore it - * - msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): Filter error", rc); - */ - break; - default : - msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): %s", - rc, get_apr_error(msr->mp, rc)); - break; - } - } - - return rc; - } - - return APR_SUCCESS; -} - -/** \brief Inject data into brigade - * - * \param msr ModSecurity transation resource - * \param ap_filter_t Apache filter - * - */ -static void inject_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) { - apr_bucket *b; - - if (msr->txcfg->content_injection_enabled && msr->stream_output_data != NULL) { - apr_bucket *bucket_ci = NULL; - - bucket_ci = apr_bucket_heap_create(msr->stream_output_data, - msr->stream_output_length, NULL, f->r->connection->bucket_alloc); - - for (b = APR_BRIGADE_FIRST(msr->of_brigade); b != APR_BRIGADE_SENTINEL(msr->of_brigade); b = APR_BUCKET_NEXT(b)) { - if(!APR_BUCKET_IS_METADATA(b)) - apr_bucket_delete(b); - } - - APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection: Data reinjected bytes [%"APR_SIZE_T_FMT"]",msr->stream_output_length); - } - - } -} - -/** - * - */ -static void prepend_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) { - if ((msr->txcfg->content_injection_enabled) && (msr->content_prepend) && (!msr->of_skipping)) { - apr_bucket *bucket_ci = NULL; - - bucket_ci = apr_bucket_heap_create(msr->content_prepend, - msr->content_prepend_len, NULL, f->r->connection->bucket_alloc); - APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection (b): Added content to top: %s", - log_escape_nq_ex(msr->mp, msr->content_prepend, msr->content_prepend_len)); - } - } -} - -/** - * - */ -static int flatten_response_body(modsec_rec *msr) { - apr_status_t rc; - - msr->resbody_status = RESBODY_STATUS_READ_BRIGADE; - - if (msr->resbody_length + 1 <= 0) { - msr_log(msr, 1, "Output filter: Invalid response length: %" APR_SIZE_T_FMT, msr->resbody_length); - return -1; - } - - msr->resbody_data = apr_palloc(msr->mp, msr->resbody_length + 1); - if (msr->resbody_data == NULL) { - msr_log(msr, 1, "Output filter: Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT, - msr->resbody_length + 1); - return -1; - } - - rc = apr_brigade_flatten(msr->of_brigade, msr->resbody_data, &msr->resbody_length); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, - get_apr_error(msr->mp, rc)); - return -1; - } - - msr->resbody_data[msr->resbody_length] = '\0'; - msr->resbody_status = RESBODY_STATUS_READ; - - if (msr->txcfg->stream_outbody_inspection && msr->txcfg->hash_is_enabled == HASH_DISABLED) { - - msr->stream_output_length = msr->resbody_length; - - if (msr->stream_output_data == NULL) { - msr_log(msr, 1, "Output filter: Stream Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT, - msr->stream_output_length + 1); - return -1; - } - - memset(msr->stream_output_data, 0, msr->stream_output_length+1); - memcpy(msr->stream_output_data, msr->resbody_data, msr->stream_output_length); - msr->stream_output_data[msr->stream_output_length] = '\0'; - } else if (msr->txcfg->stream_outbody_inspection && msr->txcfg->hash_is_enabled == HASH_ENABLED) { - int retval = 0; - apr_time_t time1 = apr_time_now(); - - retval = init_response_body_html_parser(msr); - - if(retval == 1) { - retval = hash_response_body_links(msr); - if(retval > 0) { - retval = inject_hashed_response_body(msr, retval); - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hash completed in %" APR_TIME_T_FMT " usec.", (apr_time_now() - time1)); - } - - } - } - - if(msr->of_stream_changed == 0) { - msr->stream_output_length = msr->resbody_length; - - if (msr->stream_output_data == NULL) { - msr_log(msr, 1, "Output filter: Stream Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT, - msr->stream_output_length + 1); - return -1; - } - - memset(msr->stream_output_data, 0, msr->stream_output_length+1); - memcpy(msr->stream_output_data, msr->resbody_data, msr->stream_output_length); - msr->stream_output_data[msr->stream_output_length] = '\0'; - } - } - - return 1; -} - -/** - * Output filter. - */ -apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { - request_rec *r = f->r; - modsec_rec *msr = (modsec_rec *)f->ctx; - apr_bucket *bucket = NULL, *eos_bucket = NULL; - apr_status_t rc; - int start_skipping = 0; - - /* Do we have the context? */ - if (msr == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, - "ModSecurity: Internal Error: msr is null in output filter."); - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - - msr->r = r; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Output filter: Receiving output (f %pp, r %pp).", f, f->r); - } - - /* Put back the Accept-Encoding and TE request headers - * if they were removed from the request. - */ - if (msr->txcfg->disable_backend_compression) { - char *ae = (char *)apr_table_get(msr->request_headers, "Accept-Encoding"); - char *te = (char *)apr_table_get(msr->request_headers, "TE"); - - if ((ae != NULL)&&(apr_table_get(f->r->headers_in, "Accept-Encoding") == NULL)) { - apr_table_add(f->r->headers_in, "Accept-Encoding", ae); - } - - if ((te != NULL)&&(apr_table_get(f->r->headers_in, "TE") == NULL)) { - apr_table_add(f->r->headers_in, "TE", te); - } - } - - /* Initialise on first invocation */ - if (msr->of_status == OF_STATUS_NOT_STARTED) { - /* Update our context from the request structure. */ - msr->r = r; - msr->response_status = r->status; - msr->status_line = ((r->status_line != NULL) - ? r->status_line : ap_get_status_line(r->status)); - msr->response_protocol = get_response_protocol(r); - - if(msr->txcfg->crypto_hash_location_rx == 1 || msr->txcfg->crypto_hash_location_pm == 1) - rc = modify_response_header(msr); - - msr->response_headers = apr_table_overlay(msr->mp, r->err_headers_out, r->headers_out); - - /* Process phase RESPONSE_HEADERS */ - rc = modsecurity_process_phase(msr, PHASE_RESPONSE_HEADERS); - if (rc < 0) { /* error */ - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - - if (rc > 0) { /* transaction needs to be interrupted */ - int status = perform_interception(msr); - if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ - ap_remove_output_filter(f); - msr->of_status = OF_STATUS_COMPLETE; - msr->resbody_status = RESBODY_STATUS_ERROR; - return send_error_bucket(msr, f, status); - } - } - - msr->outbound_error = 0; - /* Decide whether to observe the response body. */ - rc = output_filter_init(msr, f, bb_in); - switch(rc) { - case -2 : /* response too large */ - case -1 : /* error */ - /* there's something wrong with this response */ - ap_remove_output_filter(f); - msr->of_status = OF_STATUS_COMPLETE; - msr->resbody_status = RESBODY_STATUS_ERROR; - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - case 0 : - /* We do not want to observe this response body - * but we need to remain attached to observe - * when it is completed so that we can run - * the RESPONSE_BODY phase. - */ - msr->of_skipping = 1; - msr->resbody_status = RESBODY_STATUS_NOT_READ; - break; - default : - /* Continue (observe the response body). */ - break; - } - - /* If injecting content unset headers now. */ - if (msr->txcfg->content_injection_enabled == 0) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection: Not enabled."); - } - } else { - if ((msr->content_prepend) || (msr->content_append)) { - apr_table_unset(msr->r->headers_out, "Content-Length"); - apr_table_unset(msr->r->headers_out, "Last-Modified"); - apr_table_unset(msr->r->headers_out, "ETag"); - apr_table_unset(msr->r->headers_out, "Expires"); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection: Removing headers (C-L, L-M, Etag, Expires)."); - } - } else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection: Nothing to inject."); - } - } - } - - /* Content injection (prepend & non-buffering). */ - if ((msr->txcfg->content_injection_enabled) && (msr->content_prepend) && (msr->of_skipping)) { - apr_bucket *bucket_ci = apr_bucket_heap_create(msr->content_prepend, - msr->content_prepend_len, NULL, f->r->connection->bucket_alloc); - APR_BRIGADE_INSERT_HEAD(bb_in, bucket_ci); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection (nb): Added content to top: %s", - log_escape_nq_ex(msr->mp, msr->content_prepend, msr->content_prepend_len)); - } - } - } else - if (msr->of_status == OF_STATUS_COMPLETE) { - msr_log(msr, 1, "Output filter: Internal error: output filtering complete yet filter was invoked."); - ap_remove_output_filter(f); - return APR_EGENERAL; - } - - - /* Loop through the buckets in the brigade in order - * to extract the size of the data available. - */ - for(bucket = APR_BRIGADE_FIRST(bb_in); - bucket != APR_BRIGADE_SENTINEL(bb_in); - bucket = APR_BUCKET_NEXT(bucket)) { - const char *buf; - apr_size_t buflen; - - /* Look into response data if configured to do so, - * unless we've already processed a partial response. - */ - if ((msr->of_skipping == 0)&&(!msr->of_partial)) { /* Observe the response data. */ - /* Retrieve data from the bucket. */ - rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ); - if (rc != APR_SUCCESS) { - msr->of_status = OF_STATUS_COMPLETE; - msr->resbody_status = RESBODY_STATUS_ERROR; - - msr_log(msr, 1, "Output filter: Failed to read bucket (rc %d): %s", - rc, get_apr_error(r->pool, rc)); - - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Output filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.", - bucket->type->name, buflen); - } - - /* Check the response size. */ - if (msr->resbody_length > (apr_size_t)msr->txcfg->of_limit) { - /* The size of the response is larger than what we're - * ready to accept. We need to decide what we want to do - * about it. - */ - msr->outbound_error = 1; - if (msr->txcfg->of_limit_action == RESPONSE_BODY_LIMIT_ACTION_REJECT) { - /* Reject response. */ - msr_log(msr, 1, "Output filter: Response body too large (over limit of %ld, " - "total not specified).", msr->txcfg->of_limit); - - msr->of_status = OF_STATUS_COMPLETE; - msr->resbody_status = RESBODY_STATUS_PARTIAL; - - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } else { - /* Process partial response. */ - start_skipping = 1; - msr->resbody_length = msr->txcfg->of_limit; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Processing partial response body (limit %ld)", - msr->txcfg->of_limit); - } - } - } else { - msr->resbody_length += buflen; - } - } - - /* Have we reached the end of the response? */ - if (APR_BUCKET_IS_EOS(bucket)) { - eos_bucket = bucket; - - /* Inject content (append & non-buffering). */ - if ((msr->txcfg->content_injection_enabled) && (msr->content_append) - && (msr->of_skipping || msr->of_partial || start_skipping)) - { - apr_bucket *bucket_ci = NULL; - - bucket_ci = apr_bucket_heap_create(msr->content_append, - msr->content_append_len, NULL, f->r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(bucket, bucket_ci); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content-Injection (nb): Added content to bottom: %s", - log_escape_nq_ex(msr->mp, msr->content_append, msr->content_append_len)); - } - } - - msr->of_done_reading = 1; - } - } - - /* Add buckets in this brigade to the brigade - * we have in the context, but only if we actually - * want to keep the response body. - */ - if ((msr->of_skipping == 0)&&(msr->of_partial == 0)) { - ap_save_brigade(f, &msr->of_brigade, &bb_in, msr->mp); - - /* Do we need to process a partial response? */ - if (start_skipping) { - - if (msr->txcfg->stream_outbody_inspection) { - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - - msr->stream_output_data = (char *)malloc(msr->resbody_length+1); - } - - if (flatten_response_body(msr) < 0) { - if (msr->txcfg->stream_outbody_inspection) { - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - } - - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - - /* Process phase RESPONSE_BODY */ - rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); - if (rc < 0) { - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - if (rc > 0) { - int status = perform_interception(msr); - if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ - ap_remove_output_filter(f); - return send_error_bucket(msr, f, status); - } - } - - /* Prepend content as necessary. */ - prepend_content_to_of_brigade(msr, f); - - if ((rc = send_of_brigade(msr, f)) != APR_SUCCESS) { - return rc; - } - - msr->of_partial = 1; - } - - if (msr->of_done_reading == 0) { - /* We are done for now. We will be called again with more data. */ - return APR_SUCCESS; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Completed receiving response body (buffered %s - %" APR_SIZE_T_FMT " bytes).", - (msr->of_partial ? "partial" : "full"), msr->resbody_length); - } - } else { /* Not looking at response data. */ - if (msr->of_done_reading == 0) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Output filter: Sending input brigade directly."); - } - - return ap_pass_brigade(f->next, bb_in); - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Completed receiving response body (non-buffering)."); - } - } - - /* We've done our thing; remove us from the filter list. */ - msr->of_status = OF_STATUS_COMPLETE; - ap_remove_output_filter(f); - - /* Process phase RESPONSE_BODY, but - * only if it hasn't been processed already. - */ - if (msr->phase < PHASE_RESPONSE_BODY) { - - if (msr->txcfg->stream_outbody_inspection) { - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - - msr->stream_output_data = (char *)malloc(msr->resbody_length+1); - } - - if (flatten_response_body(msr) < 0) { - if (msr->txcfg->stream_outbody_inspection) { - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - } - - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - - rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); - if (rc < 0) { - ap_remove_output_filter(f); - return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); - } - if (rc > 0) { - int status = perform_interception(msr); - if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ - ap_remove_output_filter(f); - return send_error_bucket(msr, f, status); - } - } - } - - /* Now send data down the filter stream - * (full-buffering only). - */ - if ((msr->of_skipping == 0)&&(!msr->of_partial)) { - if(msr->of_stream_changed == 1) { - inject_content_to_of_brigade(msr,f); - msr->of_stream_changed = 0; - } - - if (msr->txcfg->stream_outbody_inspection) { - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - } - - prepend_content_to_of_brigade(msr, f); - - /* Inject content into response (append & buffering). */ - if ((msr->txcfg->content_injection_enabled) && (msr->content_append)) { - apr_bucket *bucket_ci = NULL; - - bucket_ci = apr_bucket_heap_create(msr->content_append, - msr->content_append_len, NULL, f->r->connection->bucket_alloc); - APR_BUCKET_INSERT_BEFORE(eos_bucket, bucket_ci); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content-Injection (b): Added content to bottom: %s", - log_escape_nq_ex(msr->mp, msr->content_append, msr->content_append_len)); - } - } - - /* Send data down the filter stream. */ - if ((rc = send_of_brigade(msr, f)) != APR_SUCCESS) { - return rc; - } - } - - /* Another job well done! */ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Output forwarding complete."); - } - - if ((msr->of_skipping == 0)&&(msr->of_partial == 0)) { - return APR_SUCCESS; - } else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Output filter: Sending input brigade directly."); - } - - return ap_pass_brigade(f->next, bb_in); - } -} diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c deleted file mode 100644 index 0960dc8e63..0000000000 --- a/apache2/apache2_util.c +++ /dev/null @@ -1,396 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "modsecurity.h" -#include "apache2.h" -#include "http_core.h" -#include "util_script.h" - -/** - * Sends a brigade with an error bucket down the filter chain. - */ -apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { - apr_bucket_brigade *brigade = NULL; - apr_bucket *bucket = NULL; - - /* Set the status line explicitly for the error document */ - f->r->status_line = ap_get_status_line(status); - - brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); - if (brigade == NULL) return APR_EGENERAL; - - bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; - - APR_BRIGADE_INSERT_TAIL(brigade, bucket); - - bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; - - APR_BRIGADE_INSERT_TAIL(brigade, bucket); - - ap_pass_brigade(f->next, brigade); - - /* NOTE: - * It may not matter what we do from the filter as it may be too - * late to even generate an error (already sent to client). Nick Kew - * recommends to return APR_EGENERAL in hopes that the handler in control - * will notice and do The Right Thing. So, that is what we do now. - */ - - return APR_EGENERAL; -} - -/** - * Execute system command. First line of the output will be returned in - * the "output" parameter. - */ -int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) { - apr_procattr_t *procattr = NULL; - apr_proc_t *procnew = NULL; - apr_status_t rc = APR_SUCCESS; - const char *const *env = NULL; - apr_file_t *script_out = NULL; - request_rec *r = msr->r; - - if (argv == NULL) { - argv = apr_pcalloc(r->pool, 3 * sizeof(char *)); - argv[0] = command; - argv[1] = NULL; - } - - ap_add_cgi_vars(r); - ap_add_common_vars(r); - - /* PHP hack, getting around its silly security checks. */ - apr_table_add(r->subprocess_env, "PATH_TRANSLATED", command); - apr_table_add(r->subprocess_env, "REDIRECT_STATUS", "302"); - - env = (const char * const *)ap_create_environment(r->pool, r->subprocess_env); - if (env == NULL) { - msr_log(msr, 1, "Exec: Unable to create environment."); - return -1; - } - - procnew = apr_pcalloc(r->pool, sizeof(*procnew)); - if (procnew == NULL) { - msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew)); - return -1; - } - - apr_procattr_create(&procattr, r->pool); - if (procattr == NULL) { - msr_log(msr, 1, "Exec: Unable to create procattr."); - return -1; - } - - apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); - apr_procattr_cmdtype_set(procattr, APR_SHELLCMD); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command)); - } - - rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command), - get_apr_error(r->pool, rc)); - return -1; - } - - apr_pool_note_subprocess(r->pool, procnew, APR_KILL_AFTER_TIMEOUT); - - script_out = procnew->out; - if (!script_out) { - msr_log(msr, 1, "Exec: Failed to get script output pipe."); - return -1; - } - - apr_file_pipe_timeout_set(script_out, r->server->timeout); - - /* Now read from the pipe. */ - { - char buf[260] = ""; - char *p = buf; - apr_size_t nbytes = 255; - apr_status_t rc2; - - rc2 = apr_file_read(script_out, buf, &nbytes); - if (rc2 == APR_SUCCESS) { - buf[nbytes] = 0; - - /* if there is more than one line ignore them */ - while(*p != 0) { - if (*p == 0x0a) *p = 0; - p++; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Exec: First line from script output: \"%s\"", - log_escape(r->pool, buf)); - } - - if (output != NULL) *output = apr_pstrdup(r->pool, buf); - - /* Soak up the remaining data. */ - nbytes = 255; - while(apr_file_read(script_out, buf, &nbytes) == APR_SUCCESS) nbytes = 255; - } else { - msr_log(msr, 1, "Exec: Execution failed while reading output: %s (%s)", - log_escape_nq(r->pool, command), - get_apr_error(r->pool, rc2)); - return -1; - } - } - - apr_proc_wait(procnew, NULL, NULL, APR_WAIT); - - return 1; -} - -/** - * Returns a new string that contains the error - * message for the given return code. - */ -char *get_apr_error(apr_pool_t *p, apr_status_t rc) { - char *text = apr_pcalloc(p, 201); - if (text == NULL) return NULL; - apr_strerror(rc, text, 200); - return text; -} - -/** - * Retrieve named environment variable. - */ -char *get_env_var(request_rec *r, char *name) { - char *result = (char *)apr_table_get(r->notes, name); - - if (result == NULL) { - result = (char *)apr_table_get(r->subprocess_env, name); - } - - if (result == NULL) { - result = getenv(name); - } - - return result; -} - -/** - * Extended internal log helper function. Use msr_log instead. If fixup is - * true, the message will be stripped of any trailing newline and any - * required bytes will be escaped. - */ -static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec *msr, - int level, int fixup, const char *text, va_list ap) -{ - apr_size_t nbytes, nbytes_written; - apr_file_t *debuglog_fd = NULL; - int filter_debug_level = 0; - char *remote = NULL; - char *parse_remote = NULL; - char *saved = NULL; - char *str = NULL; - char str1[1024] = ""; - char str2[1256] = ""; - - /* Find the logging FD and determine the logging level from configuration. */ - if (dcfg != NULL) { - if ((dcfg->debuglog_fd != NULL)&&(dcfg->debuglog_fd != NOT_SET_P)) { - debuglog_fd = dcfg->debuglog_fd; - } - - if (dcfg->debuglog_level != NOT_SET) { - filter_debug_level = dcfg->debuglog_level; - } - } - - /* Return immediately if we don't have where to write - * or if the log level of the message is higher than - * wanted in the log. - */ - if ((level > 3)&&( (debuglog_fd == NULL) || (level > filter_debug_level) )) return; - - /* Construct the message. */ - apr_vsnprintf(str1, sizeof(str1), text, ap); - if (fixup) { - int len = strlen(str1); - - /* Strip line ending. */ - if (len && str1[len - 1] == '\n') { - str1[len - 1] = '\0'; - } - if (len > 1 && str1[len - 2] == '\r') { - str1[len - 2] = '\0'; - } - } - - /* Construct the log entry. */ - apr_snprintf(str2, sizeof(str2), - "[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s\n", - current_logtime(msr->mp), ap_get_server_name(r), (r->server), - r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), - level, (fixup ? log_escape_nq(msr->mp, str1) : str1)); - - /* Write to the debug log. */ - if ((debuglog_fd != NULL)&&(level <= filter_debug_level)) { - nbytes = strlen(str2); - apr_file_write_full(debuglog_fd, str2, nbytes, &nbytes_written); - } - - /* Send message levels 1-3 to the Apache error log and - * add it to the message list in the audit log. */ - if (level <= 3) { - char *unique_id = (char *)get_env_var(r, "UNIQUE_ID"); - char *hostname = (char *)msr->hostname; - - if (unique_id != NULL) { - unique_id = apr_psprintf(msr->mp, " [unique_id \"%s\"]", - log_escape(msr->mp, unique_id)); - } - else unique_id = ""; - - if (hostname != NULL) { - hostname = apr_psprintf(msr->mp, " [hostname \"%s\"]", - log_escape(msr->mp, hostname)); - } - else hostname = ""; - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, - "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", r->useragent_ip ? r->useragent_ip : r->connection->client_ip, str1, - hostname, log_escape(msr->mp, r->uri), unique_id); -#else - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, - "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", msr->remote_addr ? msr->remote_addr : r->connection->remote_ip, str1, - hostname, log_escape(msr->mp, r->uri), unique_id); -#endif - - /* Add this message to the list. */ - if (msr != NULL) { - /* Force relevency if this is an alert */ - msr->is_relevant++; - - *(const char **)apr_array_push(msr->alerts) = apr_pstrdup(msr->mp, str1); - } - } - - return; -} - -/** - * Logs one message at the given level to the debug log (and to the - * Apache error log if the message is important enough. - */ -void msr_log(modsec_rec *msr, int level, const char *text, ...) { - va_list ap; - - va_start(ap, text); - internal_log_ex(msr->r, msr->txcfg, msr, level, 0, text, ap); - va_end(ap); -} - - -/** - * Logs one message at level 3 to the debug log and to the - * Apache error log. This is intended for error callbacks. - */ -void msr_log_error(modsec_rec *msr, const char *text, ...) { - va_list ap; - - va_start(ap, text); - internal_log_ex(msr->r, msr->txcfg, msr, 3, 1, text, ap); - va_end(ap); -} - -/** - * Logs one message at level 4 to the debug log and to the - * Apache error log. This is intended for warning callbacks. - * - * The 'text' will first be escaped. - */ -void msr_log_warn(modsec_rec *msr, const char *text, ...) { - va_list ap; - - va_start(ap, text); - internal_log_ex(msr->r, msr->txcfg, msr, 4, 1, text, ap); - va_end(ap); -} - - -/** - * Converts an Apache error log message into one line of text. - */ -char *format_error_log_message(apr_pool_t *mp, error_message_t *em) { - char *s_file = "", *s_line = "", *s_level = ""; - char *s_status = "", *s_message = ""; - char *msg = NULL; - - if (em == NULL) return NULL; - - if (em->file != NULL) { - s_file = apr_psprintf(mp, "[file \"%s\"] ", - log_escape(mp, (char *)em->file)); - if (s_file == NULL) return NULL; - } - - if (em->line > 0) { - s_line = apr_psprintf(mp, "[line %d] ", em->line); - if (s_line == NULL) return NULL; - } - - s_level = apr_psprintf(mp, "[level %d] ", em->level); - if (s_level == NULL) return NULL; - - if (em->status != 0) { - s_status = apr_psprintf(mp, "[status %d] ", em->status); - if (s_status == NULL) return NULL; - } - - if (em->message != NULL) { - s_message = log_escape_nq(mp, em->message); - if (s_message == NULL) return NULL; - } - - msg = apr_psprintf(mp, "%s%s%s%s%s", s_file, s_line, s_level, s_status, s_message); - if (msg == NULL) return NULL; - - return msg; -} - -/** - * Determines the reponse protocol Apache will use (or has used) - * to respond to the given request. - */ -const char *get_response_protocol(request_rec *r) { - int proto_num = r->proto_num; - - if (r->assbackwards) { - return NULL; - } - - if (proto_num > HTTP_VERSION(1,0) - && apr_table_get(r->subprocess_env, "downgrade-1.0")) - { - proto_num = HTTP_VERSION(1,0); - } - - if (proto_num == HTTP_VERSION(1,0) - && apr_table_get(r->subprocess_env, "force-response-1.0")) - { - return "HTTP/1.0"; - } - - return AP_SERVER_PROTOCOL; -} diff --git a/apache2/libinjection/COPYING.txt b/apache2/libinjection/COPYING.txt deleted file mode 100644 index ef2f78d77e..0000000000 --- a/apache2/libinjection/COPYING.txt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012, 2013 - * Nick Galbreath -- nickg [at] client9 [dot] com - * http://www.client9.com/projects/libinjection/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of libinjection nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This is the standard "new" BSD license: - * http://www.opensource.org/licenses/bsd-license.php - */ diff --git a/apache2/libinjection/libinjection.h b/apache2/libinjection/libinjection.h deleted file mode 100644 index 210596044c..0000000000 --- a/apache2/libinjection/libinjection.h +++ /dev/null @@ -1,294 +0,0 @@ -/** - * Copyright 2012, 2013 Nick Galbreath - * nickg@client9.com - * BSD License -- see COPYING.txt for details - * - * https://libinjection.client9.com/ - * - */ - -#ifndef _LIBINJECTION_H -#define _LIBINJECTION_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Version info. - * See python's normalized version - * http://www.python.org/dev/peps/pep-0386/#normalizedversion - */ -#define LIBINJECTION_VERSION "3.5.3" - -/** - * Libinjection's sqli module makes a "normalized" - * value of the token. This is the maximum size - * Token with values larger than this will be truncated - */ -#ifndef LIBINJECTION_SQLI_TOKEN_SIZE -#define LIBINJECTION_SQLI_TOKEN_SIZE 32 -#endif - -/** - * Number of tokens used to create a fingerprint - */ -#ifndef LIBINJECTION_SQLI_MAX_TOKENS -#define LIBINJECTION_SQLI_MAX_TOKENS 5 -#endif - -#if LIBINJECTION_SQLI_MAX_TOKENS >= 8 -#define LIBINJECTION_SQLI_BUFFER_SZ (LIBINJECTION_SQLI_MAX_TOKENS + 1) -#else -#define LIBINJECTION_SQLI_BUFFER_SZ 8 -#endif - - -enum lookup_type { - FLAG_NONE = 0, - FLAG_QUOTE_NONE = 1 << 1, - FLAG_QUOTE_SINGLE = 1 << 2, - FLAG_QUOTE_DOUBLE = 1 << 3, - - FLAG_SQL_ANSI = 1 << 4, - FLAG_SQL_MYSQL = 1 << 5, - - LOOKUP_WORD, - LOOKUP_TYPE, - LOOKUP_OPERATOR, - LOOKUP_FINGERPRINT -}; - -typedef struct { -#ifdef SWIG -%immutable; -#endif - char type; - char str_open; - char str_close; - - /* - * position and length of token - * in original string - */ - size_t pos; - size_t len; - - /* count: - * in type 'v', used for number of opening '@' - * but maybe unsed in other contexts - */ - int count; - - char val[LIBINJECTION_SQLI_TOKEN_SIZE]; -} stoken_t; - - -/** - * Pointer to function, takes cstr input, - * returns '\0' for no match, else a char - */ -struct libinjection_sqli_state; -typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len); - -typedef struct libinjection_sqli_state { -#ifdef SWIG -%immutable; -#endif - - /* - * input, does not need to be null terminated. - * it is also not modified. - */ - const char *s; - - /* - * input length - */ - size_t slen; - - /* - * How to lookup a word or fingerprint - */ - ptr_lookup_fn lookup; - void* userdata; - - /* - * - */ - int flags; - - /* - * pos is index in string we are at when tokenizing - */ - size_t pos; - - /* MAX TOKENS + 1 since we use one extra token - * to determine the type of the previous token - */ - stoken_t tokenvec[LIBINJECTION_SQLI_BUFFER_SZ]; - - /* - * Pointer to token position in tokenvec, above - */ - stoken_t *current; - - /* - * fingerprint pattern c-string - * +1 for ending null - * Mimimum of 8 bytes to add gcc's -fstack-protector to work - */ - char fingerprint[LIBINJECTION_SQLI_BUFFER_SZ]; - - /* - * Line number of code that said decided if the input was SQLi or - * not. Most of the time it's line that said "it's not a matching - * fingerprint" but there is other logic that sometimes approves - * an input. This is only useful for debugging. - * - */ - int reason; - - /* Number of ddw (dash-dash-white) comments - * These comments are in the form of - * '--[whitespace]' or '--[EOF]' - * - * All databases treat this as a comment. - */ - int stats_comment_ddw; - - /* Number of ddx (dash-dash-[notwhite]) comments - * - * ANSI SQL treats these are comments, MySQL treats this as - * two unary operators '-' '-' - * - * If you are parsing result returns FALSE and - * stats_comment_dd > 0, you should reparse with - * COMMENT_MYSQL - * - */ - int stats_comment_ddx; - - /* - * c-style comments found /x .. x/ - */ - int stats_comment_c; - - /* '#' operators or mysql EOL comments found - * - */ - int stats_comment_hash; - - /* - * number of tokens folded away - */ - int stats_folds; - - /* - * total tokens processed - */ - int stats_tokens; - -} sfilter; - -/** - * - */ -void libinjection_sqli_init(sfilter* sql_state, - const char* s, size_t slen, - int flags); - -/** - * Main API: tests for SQLi in three possible contexts, no quotes, - * single quote and double quote - * - * \param sql_state - * \param s - * \param slen - * \param fn a pointer to a function that determines if a fingerprint - * is a match or not. If NULL, then a hardwired list is - * used. Useful for loading fingerprints data from custom - * sources. - * - * \return 1 (true) if SQLi, 0 (false) if benign - */ -int libinjection_is_sqli(sfilter * sql_state); - -/* FOR H@CKERS ONLY - * - */ -void libinjection_sqli_callback(sfilter* sql_state, ptr_lookup_fn fn, void* userdata); - - -/* - * Resets state, but keeps initial string and callbacks - */ -void libinjection_sqli_reset(sfilter* sql_state, int flags); - -/** - * - */ - -/** - * This detects SQLi in a single context, mostly useful for custom - * logic and debugging. - * - * \param sql_state - * - * \returns a pointer to sfilter.fingerprint as convenience - * do not free! - * - */ -const char* libinjection_sqli_fingerprint(sfilter * sql_state, int flags); - -/** - * The default "word" to token-type or fingerprint function. This - * uses a ASCII case-insensitive binary tree. - */ -char libinjection_sqli_lookup_word(sfilter *sql_state, int lookup_type, - const char* s, size_t slen); - -/* Streaming tokenization interface. - * - * sql_state->current is updated with the current token. - * - * \returns 1, has a token, keep going, or 0 no tokens - * - */ -int libinjection_sqli_tokenize(sfilter * sql_state); - -/** - * parses and folds input, up to 5 tokens - * - */ -int libinjection_sqli_fold(sfilter * sql_state); - -/** The built-in default function to match fingerprints - * and do false negative/positive analysis. This calls the following - * two functions. With this, you over-ride one part or the other. - * - * return libinjection_sqli_blacklist(sql_state) && - * libinject_sqli_not_whitelist(sql_state); - * - * \param sql_state should be filled out after libinjection_sqli_fingerprint is called - */ -int libinjection_sqli_check_fingerprint(sfilter *sql_state); - -/* Given a pattern determine if it's a SQLi pattern. - * - * \return TRUE if sqli, false otherwise - */ -int libinjection_sqli_blacklist(sfilter* sql_state); - -/* Given a positive match for a pattern (i.e. pattern is SQLi), this function - * does additional analysis to reduce false positives. - * - * \return TRUE if sqli, false otherwise - */ -int libinjection_sqli_not_whitelist(sfilter* sql_state); - -#ifdef __cplusplus -} -#endif - -#endif /* _LIBINJECTION_H */ diff --git a/apache2/libinjection/libinjection_sqli.c b/apache2/libinjection/libinjection_sqli.c deleted file mode 100644 index 91e34a6558..0000000000 --- a/apache2/libinjection/libinjection_sqli.c +++ /dev/null @@ -1,2191 +0,0 @@ -/** - * Copyright 2012,2013 Nick Galbreath - * nickg@client9.com - * BSD License -- see COPYING.txt for details - * - * https://libinjection.client9.com/ - * - */ - -#include -#include -#include -#include -#include -#include - -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef FALSE -#define FALSE 0 -#endif - -#define CHAR_NULL '\0' -#define CHAR_SINGLE '\'' -#define CHAR_DOUBLE '"' -#define CHAR_TICK '`' - -/* faster than calling out to libc isdigit */ -#define ISDIGIT(a) ((unsigned)((a) - '0') <= 9) - -#if 0 -#define FOLD_DEBUG printf("%d \t more=%d pos=%d left=%d\n", __LINE__, more, (int)pos, (int)left); -#else -#define FOLD_DEBUG -#endif - -#include "libinjection_sqli_data.h" - -/* - * not making public just yet - */ -typedef enum { - TYPE_NONE = 0, - TYPE_KEYWORD = (int)'k', - TYPE_UNION = (int)'U', - TYPE_GROUP = (int)'B', - TYPE_EXPRESSION = (int)'E', - TYPE_SQLTYPE = (int)'t', - TYPE_FUNCTION = (int)'f', - TYPE_BAREWORD = (int)'n', - TYPE_NUMBER = (int)'1', - TYPE_VARIABLE = (int)'v', - TYPE_STRING = (int)'s', - TYPE_OPERATOR = (int)'o', - TYPE_LOGIC_OPERATOR = (int)'&', - TYPE_COMMENT = (int)'c', - TYPE_COLLATE = (int)'A', - TYPE_LEFTPARENS = (int)'(', - TYPE_RIGHTPARENS = (int)')', /* not used? */ - TYPE_LEFTBRACE = (int)'{', - TYPE_RIGHTBRACE = (int)'}', - TYPE_DOT = (int)'.', - TYPE_COMMA = (int)',', - TYPE_COLON = (int)':', - TYPE_SEMICOLON = (int)';', - TYPE_TSQL = (int)'T', /* TSQL start */ - TYPE_UNKNOWN = (int)'?', - TYPE_EVIL = (int)'X', /* unparsable, abort */ - TYPE_FINGERPRINT = (int)'F', /* not really a token */ - TYPE_BACKSLASH = (int)'\\' -} sqli_token_types; - -/** - * Initializes parsing state - * - */ -static char flag2delim(int flag) -{ - if (flag & FLAG_QUOTE_SINGLE) { - return CHAR_SINGLE; - } else if (flag & FLAG_QUOTE_DOUBLE) { - return CHAR_DOUBLE; - } else { - return CHAR_NULL; - } -} - -/* memchr2 finds a string of 2 characters inside another string - * This a specialized version of "memmem" or "memchr". - * 'memmem' doesn't exist on all platforms - * - * Porting notes: this is just a special version of - * astring.find("AB") - * - */ -static const char * -memchr2(const char *haystack, size_t haystack_len, char c0, char c1) -{ - const char *cur = haystack; - const char *last = haystack + haystack_len - 1; - - if (haystack_len < 2) { - return NULL; - } - - while (cur < last) { - if (cur[0] == c0) { - if (cur[1] == c1) { - return cur; - } else { - cur += 2; /* (c0 == c1) ? 1 : 2; */ - } - } else { - cur += 1; - } - } - - return NULL; -} - -/** - */ -static const char * -my_memmem(const char* haystack, size_t hlen, const char* needle, size_t nlen) -{ - assert(haystack); - assert(needle); - assert(nlen > 1); - const char* cur; - const char* last = haystack + hlen - nlen; - for (cur = haystack; cur <= last; ++cur) { - if (cur[0] == needle[0] && memcmp(cur, needle, nlen) == 0) { - return cur; - } - } - return NULL; -} - -/** Find largest string containing certain characters. - * - * C Standard library 'strspn' only works for 'c-strings' (null terminated) - * This works on arbitrary length. - * - * Performance notes: - * not critical - * - * Porting notes: - * if accept is 'ABC', then this function would be similar to - * a_regexp.match(a_str, '[ABC]*'), - */ -static size_t -strlenspn(const char *s, size_t len, const char *accept) -{ - size_t i; - for (i = 0; i < len; ++i) { - /* likely we can do better by inlining this function - * but this works for now - */ - if (strchr(accept, s[i]) == NULL) { - return i; - } - } - return len; -} - -static size_t -strlencspn(const char *s, size_t len, const char *accept) -{ - size_t i; - for (i = 0; i < len; ++i) { - /* likely we can do better by inlining this function - * but this works for now - */ - if (strchr(accept, s[i]) != NULL) { - return i; - } - } - return len; -} -static int char_is_white(char ch) { - /* ' ' space is 0x32 - '\t 0x09 \011 horizontal tab - '\n' 0x0a \012 new line - '\v' 0x0b \013 verical tab - '\f' 0x0c \014 new page - '\r' 0x0d \015 carriage return - 0x00 \000 null (oracle) - 0xa0 \240 is latin1 - */ - return strchr(" \t\n\v\f\r\240\000", ch) != NULL; -} - -/* DANGER DANGER - * This is -very specialized function- - * - * this compares a ALL_UPPER CASE C STRING - * with a *arbitrary memory* + length - * - * Sane people would just make a copy, up-case - * and use a hash table. - * - * Required since libc version uses the current locale - * and is much slower. - */ -static int cstrcasecmp(const char *a, const char *b, size_t n) -{ - char cb; - - for (; n > 0; a++, b++, n--) { - cb = *b; - if (cb >= 'a' && cb <= 'z') { - cb -= 0x20; - } - if (*a != cb) { - return *a - cb; - } else if (*a == '\0') { - return -1; - } - } - - return (*a == 0) ? 0 : 1; -} - -/** - * Case sensitive string compare. - * Here only to make code more readable - */ -static int streq(const char *a, const char *b) -{ - return strcmp(a, b) == 0; -} - -/** - * - * - * - * Porting Notes: - * given a mapping/hash of string to char - * this is just - * typecode = mapping[key.upper()] - */ - -static char bsearch_keyword_type(const char *key, size_t len, - const keyword_t * keywords, size_t numb) -{ - size_t pos; - size_t left = 0; - size_t right = numb - 1; - - while (left < right) { - pos = (left + right) >> 1; - - /* arg0 = upper case only, arg1 = mixed case */ - if (cstrcasecmp(keywords[pos].word, key, len) < 0) { - left = pos + 1; - } else { - right = pos; - } - } - if ((left == right) && cstrcasecmp(keywords[left].word, key, len) == 0) { - return keywords[left].type; - } else { - return CHAR_NULL; - } -} - -static char is_keyword(const char* key, size_t len) -{ - return bsearch_keyword_type(key, len, sql_keywords, sql_keywords_sz); -} - -/* st_token methods - * - * The following functions manipulates the stoken_t type - * - * - */ - -static void st_clear(stoken_t * st) -{ - memset(st, 0, sizeof(stoken_t)); -} - -static void st_assign_char(stoken_t * st, const char stype, size_t pos, size_t len, - const char value) -{ - st->type = (char) stype; - st->pos = pos; - st->len = len; - st->val[0] = value; - st->val[1] = CHAR_NULL; -} - -static void st_assign(stoken_t * st, const char stype, - size_t pos, size_t len, const char* value) -{ - const size_t MSIZE = LIBINJECTION_SQLI_TOKEN_SIZE; - size_t last = len < MSIZE ? len : (MSIZE - 1); - st->type = (char) stype; - st->pos = pos; - st->len = len; - memcpy(st->val, value, last); - st->val[last] = CHAR_NULL; -} - -static void st_copy(stoken_t * dest, const stoken_t * src) -{ - memcpy(dest, src, sizeof(stoken_t)); -} - -static int st_is_arithmetic_op(const stoken_t* st) -{ - const char ch = st->val[0]; - return (st->type == TYPE_OPERATOR && st->len == 1 && - (ch == '*' || ch == '/' || ch == '-' || ch == '+' || ch == '%')); -} - -static int st_is_unary_op(const stoken_t * st) -{ - const char* str = st->val; - const size_t len = st->len; - - if (st->type != TYPE_OPERATOR) { - return FALSE; - } - - switch (len) { - case 1: - return *str == '+' || *str == '-' || *str == '!' || *str == '~'; - case 2: - return str[0] == '!' && str[1] == '!'; - case 3: - return cstrcasecmp("NOT", str, 3) == 0; - default: - return FALSE; - } -} - -/* Parsers - * - * - */ - -static size_t parse_white(sfilter * sf) -{ - return sf->pos + 1; -} - -static size_t parse_operator1(sfilter * sf) -{ - const char *cs = sf->s; - size_t pos = sf->pos; - - st_assign_char(sf->current, TYPE_OPERATOR, pos, 1, cs[pos]); - return pos + 1; -} - -static size_t parse_other(sfilter * sf) -{ - const char *cs = sf->s; - size_t pos = sf->pos; - - st_assign_char(sf->current, TYPE_UNKNOWN, pos, 1, cs[pos]); - return pos + 1; -} - -static size_t parse_char(sfilter * sf) -{ - const char *cs = sf->s; - size_t pos = sf->pos; - - st_assign_char(sf->current, cs[pos], pos, 1, cs[pos]); - return pos + 1; -} - -static size_t parse_eol_comment(sfilter * sf) -{ - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - const char *endpos = - (const char *) memchr((const void *) (cs + pos), '\n', slen - pos); - if (endpos == NULL) { - st_assign(sf->current, TYPE_COMMENT, pos, slen - pos, cs + pos); - return slen; - } else { - st_assign(sf->current, TYPE_COMMENT, pos, endpos - cs - pos, cs + pos); - return (endpos - cs) + 1; - } -} - -/** In Ansi mode, hash is an operator - * In MYSQL mode, it's a EOL comment like '--' - */ -static size_t parse_hash(sfilter * sf) -{ - sf->stats_comment_hash += 1; - if (sf->flags & FLAG_SQL_MYSQL) { - sf->stats_comment_hash += 1; - return parse_eol_comment(sf); - } else { - st_assign_char(sf->current, TYPE_OPERATOR, sf->pos, 1, '#'); - return sf->pos + 1; - } -} - -static size_t parse_dash(sfilter * sf) -{ - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - /* - * five cases - * 1) --[white] this is always a SQL comment - * 2) --[EOF] this is a comment - * 3) --[notwhite] in MySQL this is NOT a comment but two unary operators - * 4) --[notwhite] everyone else thinks this is a comment - * 5) -[not dash] '-' is a unary operator - */ - - if (pos + 2 < slen && cs[pos + 1] == '-' && char_is_white(cs[pos+2]) ) { - return parse_eol_comment(sf); - } else if (pos +2 == slen && cs[pos + 1] == '-') { - return parse_eol_comment(sf); - } else if (pos + 1 < slen && cs[pos + 1] == '-' && (sf->flags & FLAG_SQL_ANSI)) { - /* --[not-white] not-white case: - * - */ - sf->stats_comment_ddx += 1; - return parse_eol_comment(sf); - } else { - st_assign_char(sf->current, TYPE_OPERATOR, pos, 1, '-'); - return pos + 1; - } -} - - -/** This detects MySQL comments, comments that - * start with /x! We just ban these now but - * previously we attempted to parse the inside - * - * For reference: - * the form of /x![anything]x/ or /x!12345[anything] x/ - * - * Mysql 3 (maybe 4), allowed this: - * /x!0selectx/ 1; - * where 0 could be any number. - * - * The last version of MySQL 3 was in 2003. - - * It is unclear if the MySQL 3 syntax was allowed - * in MySQL 4. The last version of MySQL 4 was in 2008 - * - */ -static size_t is_mysql_comment(const char *cs, const size_t len, size_t pos) -{ - /* so far... - * cs[pos] == '/' && cs[pos+1] == '*' - */ - - if (pos + 2 >= len) { - /* not a mysql comment */ - return 0; - } - - if (cs[pos + 2] != '!') { - /* not a mysql comment */ - return 0; - } - - /* - * this is a mysql comment - * got "/x!" - */ - return 1; -} - -static size_t parse_slash(sfilter * sf) -{ - size_t clen; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - const char* cur = cs + pos; - char ctype = TYPE_COMMENT; - size_t pos1 = pos + 1; - if (pos1 == slen || cs[pos1] != '*') { - return parse_operator1(sf); - } - - /* - * skip over initial '/x' - */ - const char* ptr = memchr2(cur + 2, slen - (pos + 2), '*', '/'); - - /* - * (ptr == NULL) causes false positive in cppcheck 1.61 - * casting to type seems to fix it - */ - if (ptr == (const char*) NULL) { - /* till end of line */ - clen = slen - pos; - } else { - clen = (ptr + 2) - cur; - } - - /* - * postgresql allows nested comments which makes - * this is incompatible with parsing so - * if we find a '/x' inside the coment, then - * make a new token. - * - * Also, Mysql's "conditional" comments for version - * are an automatic black ban! - */ - - if (memchr2(cur + 2, ptr - (cur + 1), '/', '*') != NULL) { - ctype = TYPE_EVIL; - } else if (is_mysql_comment(cs, slen, pos)) { - ctype = TYPE_EVIL; - } - - st_assign(sf->current, ctype, pos, clen, cs + pos); - return pos + clen; -} - - -static size_t parse_backslash(sfilter * sf) -{ - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - /* - * Weird MySQL alias for NULL, "\N" (capital N only) - */ - if (pos + 1 < slen && cs[pos +1] == 'N') { - st_assign(sf->current, TYPE_NUMBER, pos, 2, cs + pos); - return pos + 2; - } else { - st_assign_char(sf->current, TYPE_BACKSLASH, pos, 1, cs[pos]); - return pos + 1; - } -} - -static size_t parse_operator2(sfilter * sf) -{ - char ch; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - if (pos + 1 >= slen) { - return parse_operator1(sf); - } - - if (pos + 2 < slen && - cs[pos] == '<' && - cs[pos + 1] == '=' && - cs[pos + 2] == '>') { - /* - * special 3-char operator - */ - st_assign(sf->current, TYPE_OPERATOR, pos, 3, cs + pos); - return pos + 3; - } - - ch = sf->lookup(sf, LOOKUP_OPERATOR, cs + pos, 2); - if (ch != CHAR_NULL) { - st_assign(sf->current, ch, pos, 2, cs+pos); - return pos + 2; - } - - /* - * not an operator.. what to do with the two - * characters we got? - */ - - if (cs[pos] == ':') { - /* ':' is not an operator */ - st_assign(sf->current, TYPE_COLON, pos, 1, cs+pos); - return pos + 1; - } else { - /* - * must be a single char operator - */ - return parse_operator1(sf); - } -} - -/* - * Ok! " \" " one backslash = escaped! - * " \\" " two backslash = not escaped! - * "\\\" " three backslash = escaped! - */ -static int is_backslash_escaped(const char* end, const char* start) -{ - const char* ptr; - for (ptr = end; ptr >= start; ptr--) { - if (*ptr != '\\') { - break; - } - } - /* if number of backslashes is odd, it is escaped */ - - return (end - ptr) & 1; -} - -static size_t is_double_delim_escaped(const char* cur, const char* end) -{ - return ((cur + 1) < end) && *(cur+1) == *cur; -} - -/* Look forward for doubling of deliminter - * - * case 'foo''bar' --> foo''bar - * - * ending quote isn't duplicated (i.e. escaped) - * since it's the wrong char or EOL - * - */ -static size_t parse_string_core(const char *cs, const size_t len, size_t pos, - stoken_t * st, char delim, size_t offset) -{ - /* - * offset is to skip the perhaps first quote char - */ - const char *qpos = - (const char *) memchr((const void *) (cs + pos + offset), delim, - len - pos - offset); - - /* - * then keep string open/close info - */ - if (offset > 0) { - /* - * this is real quote - */ - st->str_open = delim; - } else { - /* - * this was a simulated quote - */ - st->str_open = CHAR_NULL; - } - - while (TRUE) { - if (qpos == NULL) { - /* - * string ended with no trailing quote - * assign what we have - */ - st_assign(st, TYPE_STRING, pos + offset, len - pos - offset, cs + pos + offset); - st->str_close = CHAR_NULL; - return len; - } else if ( is_backslash_escaped(qpos - 1, cs + pos + offset)) { - /* keep going, move ahead one character */ - qpos = - (const char *) memchr((const void *) (qpos + 1), delim, - (cs + len) - (qpos + 1)); - continue; - } else if (is_double_delim_escaped(qpos, cs + len)) { - /* keep going, move ahead two characters */ - qpos = - (const char *) memchr((const void *) (qpos + 2), delim, - (cs + len) - (qpos + 2)); - continue; - } else { - /* hey it's a normal string */ - st_assign(st, TYPE_STRING, pos + offset, - qpos - (cs + pos + offset), cs + pos + offset); - st->str_close = delim; - return qpos - cs + 1; - } - } -} - -/** - * Used when first char is a ' or " - */ -static size_t parse_string(sfilter * sf) -{ - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - /* - * assert cs[pos] == single or double quote - */ - return parse_string_core(cs, slen, pos, sf->current, cs[pos], 1); -} - -/** - * Used when first char is: - * N or n: mysql "National Character set" - * E : psql "Escaped String" - */ -static size_t parse_estring(sfilter * sf) -{ - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - if (pos + 2 >= slen || cs[pos+1] != CHAR_SINGLE) { - return parse_word(sf); - } - return parse_string_core(cs, slen, pos, sf->current, CHAR_SINGLE, 2); -} - -static size_t parse_ustring(sfilter * sf) -{ - const char *cs = sf->s; - size_t slen = sf->slen; - size_t pos = sf->pos; - - if (pos + 2 < slen && cs[pos+1] == '&' && cs[pos+2] == '\'') { - sf->pos += 2; - pos = parse_string(sf); - sf->current->str_open = 'u'; - if (sf->current->str_close == '\'') { - sf->current->str_close = 'u'; - } - return pos; - } else { - return parse_word(sf); - } -} - -static size_t parse_qstring_core(sfilter * sf, int offset) -{ - char ch; - const char *strend; - const char *cs = sf->s; - size_t slen = sf->slen; - size_t pos = sf->pos + offset; - - /* if we are already at end of string.. - if current char is not q or Q - if we don't have 2 more chars - if char2 != a single quote - then, just treat as word - */ - if (pos >= slen || - (cs[pos] != 'q' && cs[pos] != 'Q') || - pos + 2 >= slen || - cs[pos + 1] != '\'') { - return parse_word(sf); - } - - ch = cs[pos + 2]; - - /* the ch > 127 is un-needed since - * we assume char is signed - */ - if (ch < 33 /* || ch > 127 */) { - return parse_word(sf); - } - switch (ch) { - case '(' : ch = ')'; break; - case '[' : ch = ']'; break; - case '{' : ch = '}'; break; - case '<' : ch = '>'; break; - } - - strend = memchr2(cs + pos + 3, slen - pos - 3, ch, '\''); - if (strend == NULL) { - st_assign(sf->current, TYPE_STRING, pos + 3, slen - pos - 3, cs + pos + 3); - sf->current->str_open = 'q'; - sf->current->str_close = CHAR_NULL; - return slen; - } else { - st_assign(sf->current, TYPE_STRING, pos + 3, strend - cs - pos - 3, cs + pos + 3); - sf->current->str_open = 'q'; - sf->current->str_close = 'q'; - return (strend - cs) + 2; - } -} - -/* - * Oracle's q string - */ -static size_t parse_qstring(sfilter * sf) -{ - return parse_qstring_core(sf, 0); -} - -/* - * mysql's N'STRING' or - * ... Oracle's nq string - */ -static size_t parse_nqstring(sfilter * sf) -{ - size_t slen = sf->slen; - size_t pos = sf->pos; - if (pos + 2 < slen && sf->s[pos+1] == CHAR_SINGLE) { - return parse_estring(sf); - } - return parse_qstring_core(sf, 1); -} - -/* - * binary literal string - * re: [bB]'[01]*' - */ -static size_t parse_bstring(sfilter *sf) -{ - size_t wlen; - const char *cs = sf->s; - size_t pos = sf->pos; - size_t slen = sf->slen; - - /* need at least 2 more characters - * if next char isn't a single quote, then - * continue as normal word - */ - if (pos + 2 >= slen || cs[pos+1] != '\'') { - return parse_word(sf); - } - - wlen = strlenspn(cs + pos + 2, sf->slen - pos - 2, "01"); - if (pos + 2 + wlen >= slen || cs[pos + 2 + wlen] != '\'') { - return parse_word(sf); - } - st_assign(sf->current, TYPE_NUMBER, pos, wlen + 3, cs + pos); - return pos + 2 + wlen + 1; -} - -/* - * hex literal string - * re: [XX]'[0123456789abcdefABCDEF]*' - * mysql has requirement of having EVEN number of chars, - * but pgsql does not - */ -static size_t parse_xstring(sfilter *sf) -{ - size_t wlen; - const char *cs = sf->s; - size_t pos = sf->pos; - size_t slen = sf->slen; - - /* need at least 2 more characters - * if next char isn't a single quote, then - * continue as normal word - */ - if (pos + 2 >= slen || cs[pos+1] != '\'') { - return parse_word(sf); - } - - wlen = strlenspn(cs + pos + 2, sf->slen - pos - 2, "0123456789ABCDEFabcdef"); - if (pos + 2 + wlen >= slen || cs[pos + 2 + wlen] != '\'') { - return parse_word(sf); - } - st_assign(sf->current, TYPE_NUMBER, pos, wlen + 3, cs + pos); - return pos + 2 + wlen + 1; -} - -static size_t parse_word(sfilter * sf) -{ - char ch; - char delim; - size_t i; - const char *cs = sf->s; - size_t pos = sf->pos; - size_t wlen = strlencspn(cs + pos, sf->slen - pos, - " {}<>:\\?=@!#~+-*/&|^%(),';\t\n\v\f\r\"\000"); - - st_assign(sf->current, TYPE_BAREWORD, pos, wlen, cs + pos); - - /* now we need to look inside what we good for "." and "`" - * and see if what is before is a keyword or not - */ - for (i =0; i < sf->current->len; ++i) { - delim = sf->current->val[i]; - if (delim == '.' || delim == '`') { - ch = sf->lookup(sf, LOOKUP_WORD, sf->current->val, i); - if (ch != TYPE_NONE && ch != TYPE_BAREWORD) { - /* needed for swig */ - st_clear(sf->current); - /* - * we got something like "SELECT.1" - * or SELECT`column` - */ - st_assign(sf->current, ch, pos, i, cs + pos); - return pos + i; - } - } - } - - /* - * do normal lookup with word including '.' - */ - if (wlen < LIBINJECTION_SQLI_TOKEN_SIZE) { - - ch = sf->lookup(sf, LOOKUP_WORD, sf->current->val, wlen); - if (ch == CHAR_NULL) { - ch = TYPE_BAREWORD; - } - sf->current->type = ch; - } - return pos + wlen; -} - -/* MySQL backticks are a cross between string and - * and a bare word. - * - */ -static size_t parse_tick(sfilter* sf) -{ - size_t pos = parse_string_core(sf->s, sf->slen, sf->pos, sf->current, CHAR_TICK, 1); - - /* we could check to see if start and end of - * of string are both "`", i.e. make sure we have - * matching set. `foo` vs. `foo - * but I don't think it matters much - */ - - /* check value of string to see if it's a keyword, - * function, operator, etc - */ - char ch = sf->lookup(sf, LOOKUP_WORD, sf->current->val, sf->current->len); - if (ch == TYPE_FUNCTION) { - /* if it's a function, then convert token */ - sf->current->type = TYPE_FUNCTION; - } else { - /* otherwise it's a 'n' type -- mysql treats - * everything as a bare word - */ - sf->current->type = TYPE_BAREWORD; - } - return pos; -} - -static size_t parse_var(sfilter * sf) -{ - size_t xlen; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos + 1; - - /* - * var_count is only used to reconstruct - * the input. It counts the number of '@' - * seen 0 in the case of NULL, 1 or 2 - */ - - /* - * move past optional other '@' - */ - if (pos < slen && cs[pos] == '@') { - pos += 1; - sf->current->count = 2; - } else { - sf->current->count = 1; - } - - /* - * MySQL allows @@`version` - */ - if (pos < slen) { - if (cs[pos] == '`') { - sf->pos = pos; - pos = parse_tick(sf); - sf->current->type = TYPE_VARIABLE; - return pos; - } else if (cs[pos] == CHAR_SINGLE || cs[pos] == CHAR_DOUBLE) { - sf->pos = pos; - pos = parse_string(sf); - sf->current->type = TYPE_VARIABLE; - return pos; - } - } - - - xlen = strlencspn(cs + pos, slen - pos, - " <>:\\?=@!#~+-*/&|^%(),';\t\n\v\f\r'`\""); - if (xlen == 0) { - st_assign(sf->current, TYPE_VARIABLE, pos, 0, cs + pos); - return pos; - } else { - st_assign(sf->current, TYPE_VARIABLE, pos, xlen, cs + pos); - return pos + xlen; - } -} - -static size_t parse_money(sfilter *sf) -{ - size_t xlen; - const char* strend; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - if (pos + 1 == slen) { - /* end of line */ - st_assign_char(sf->current, TYPE_BAREWORD, pos, 1, '$'); - return slen; - } - - /* - * $1,000.00 or $1.000,00 ok! - * This also parses $....,,,111 but that's ok - */ - - xlen = strlenspn(cs + pos + 1, slen - pos - 1, "0123456789.,"); - if (xlen == 0) { - if (cs[pos + 1] == '$') { - /* we have $$ .. find ending $$ and make string */ - strend = memchr2(cs + pos + 2, slen - pos -2, '$', '$'); - if (strend == NULL) { - /* fell off edge */ - st_assign(sf->current, TYPE_STRING, pos + 2, slen - (pos + 2), cs + pos + 2); - sf->current->str_open = '$'; - sf->current->str_close = CHAR_NULL; - return slen; - } else { - st_assign(sf->current, TYPE_STRING, pos + 2, strend - (cs + pos + 2), cs + pos + 2); - sf->current->str_open = '$'; - sf->current->str_close = '$'; - return strend - cs + 2; - } - } else { - /* ok it's not a number or '$$', but maybe it's pgsql "$ quoted strings" */ - xlen = strlenspn(cs + pos + 1, slen - pos - 1, "abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - if (xlen == 0) { - /* hmm it's "$" _something_ .. just add $ and keep going*/ - st_assign_char(sf->current, TYPE_BAREWORD, pos, 1, '$'); - return pos + 1; - } - /* we have $foobar????? */ - /* is it $foobar$ */ - if (pos + xlen + 1 == slen || cs[pos+xlen+1] != '$') { - /* not $foobar$, or fell off edge */ - st_assign_char(sf->current, TYPE_BAREWORD, pos, 1, '$'); - return pos + 1; - } - - /* we have $foobar$ ... find it again */ - strend = my_memmem(cs+xlen+2, slen - (pos+xlen+2), cs + pos, xlen+2); - - if (strend == NULL) { - /* fell off edge */ - st_assign(sf->current, TYPE_STRING, pos+xlen+2, slen - pos - xlen - 2, cs+pos+xlen+2); - sf->current->str_open = '$'; - sf->current->str_close = CHAR_NULL; - return slen; - } else { - /* got one */ - st_assign(sf->current, TYPE_STRING, pos+xlen+2, strend - (cs + pos + xlen + 2), cs+pos+xlen+2); - sf->current->str_open = '$'; - sf->current->str_close = '$'; - return (strend + xlen + 2) - cs; - } - } - } else if (xlen == 1 && cs[pos + 1] == '.') { - /* $. should parsed as a word */ - return parse_word(sf); - } else { - st_assign(sf->current, TYPE_NUMBER, pos, 1 + xlen, cs + pos); - return pos + 1 + xlen; - } -} - -static size_t parse_number(sfilter * sf) -{ - size_t xlen; - size_t start; - const char* digits = NULL; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - int have_dot = 0; - int have_e = 0; - int have_exp = 0; - - /* cs[pos] == '0' has 1/10 chance of being true, - * while pos+1< slen is almost always true - */ - if (cs[pos] == '0' && pos + 1 < slen) { - if (cs[pos + 1] == 'X' || cs[pos + 1] == 'x') { - digits = "0123456789ABCDEFabcdef"; - } else if (cs[pos + 1] == 'B' || cs[pos + 1] == 'b') { - digits = "01"; - } - - if (digits) { - xlen = strlenspn(cs + pos + 2, slen - pos - 2, digits); - if (xlen == 0) { - st_assign(sf->current, TYPE_BAREWORD, pos, 2, cs + pos); - return pos + 2; - } else { - st_assign(sf->current, TYPE_NUMBER, pos, 2 + xlen, cs + pos); - return pos + 2 + xlen; - } - } - } - - start = pos; - while (pos < slen && ISDIGIT(cs[pos])) { - pos += 1; - } - - if (pos < slen && cs[pos] == '.') { - have_dot = 1; - pos += 1; - while (pos < slen && ISDIGIT(cs[pos])) { - pos += 1; - } - if (pos - start == 1) { - /* only one character read so far */ - st_assign_char(sf->current, TYPE_DOT, start, 1, '.'); - return pos; - } - } - - if (pos < slen) { - if (cs[pos] == 'E' || cs[pos] == 'e') { - have_e = 1; - pos += 1; - if (pos < slen && (cs[pos] == '+' || cs[pos] == '-')) { - pos += 1; - } - while (pos < slen && ISDIGIT(cs[pos])) { - have_exp = 1; - pos += 1; - } - } - } - - /* oracle's ending float or double suffix - * http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements003.htm#i139891 - */ - if (pos < slen && (cs[pos] == 'd' || cs[pos] == 'D' || cs[pos] == 'f' || cs[pos] == 'F')) { - if (pos + 1 == slen) { - /* line ends evaluate "... 1.2f$" as '1.2f' */ - pos += 1; - } else if ((char_is_white(cs[pos+1]) || cs[pos+1] == ';')) { - /* - * easy case, evaluate "... 1.2f ... as '1.2f' - */ - pos += 1; - } else if (cs[pos+1] == 'u' || cs[pos+1] == 'U') { - /* - * a bit of a hack but makes '1fUNION' parse as '1f UNION' - */ - pos += 1; - } else { - /* it's like "123FROM" */ - /* parse as "123" only */ - } - } - - if (have_dot == 1 && have_e == 1 && have_exp == 0) { - /* very special form of - * "1234.e" - * "10.10E" - * ".E" - * this is a WORD not a number!! */ - st_assign(sf->current, TYPE_BAREWORD, start, pos - start, cs + start); - } else { - st_assign(sf->current, TYPE_NUMBER, start, pos - start, cs + start); - } - return pos; -} - -int libinjection_sqli_tokenize(sfilter * sf) -{ - pt2Function fnptr; - size_t *pos = &sf->pos; - stoken_t *current = sf->current; - const char *s = sf->s; - const size_t slen = sf->slen; - - if (slen == 0) { - return FALSE; - } - - st_clear(current); - sf->current = current; - - /* - * if we are at beginning of string - * and in single-quote or double quote mode - * then pretend the input starts with a quote - */ - if (*pos == 0 && (sf->flags & (FLAG_QUOTE_SINGLE | FLAG_QUOTE_DOUBLE))) { - *pos = parse_string_core(s, slen, 0, current, flag2delim(sf->flags), 0); - sf->stats_tokens += 1; - return TRUE; - } - - while (*pos < slen) { - - /* - * get current character - */ - const unsigned ch = (unsigned int) (s[*pos]); - - /* - * if not ascii, then continue... - * actually probably need to just assuming - * it's a string - */ - if (ch > 127) { - fnptr = parse_word; - } else { - - /* - * look up the parser, and call it - * - * Porting Note: this is mapping of char to function - * charparsers[ch]() - */ - fnptr = char_parse_map[ch]; - } - *pos = (*fnptr) (sf); - - /* - * - */ - if (current->type != CHAR_NULL) { - sf->stats_tokens += 1; - return TRUE; - } - } - return FALSE; -} - -void libinjection_sqli_init(sfilter * sf, const char *s, size_t len, int flags) -{ - if (flags == 0) { - flags = FLAG_QUOTE_NONE | FLAG_SQL_ANSI; - } - - memset(sf, 0, sizeof(sfilter)); - sf->s = s; - sf->slen = len; - sf->lookup = libinjection_sqli_lookup_word; - sf->userdata = 0; - sf->flags = flags; - sf->current = &(sf->tokenvec[0]); -} - -void libinjection_sqli_reset(sfilter * sf, int flags) -{ - ptr_lookup_fn lookup = sf->lookup;; - void *userdata = sf->userdata; - - if (flags == 0) { - flags = FLAG_QUOTE_NONE | FLAG_SQL_ANSI; - } - libinjection_sqli_init(sf, sf->s, sf->slen, flags); - sf->lookup = lookup; - sf->userdata = userdata; -} - -void libinjection_sqli_callback(sfilter * sf, ptr_lookup_fn fn, void* userdata) -{ - if (fn == NULL) { - sf->lookup = libinjection_sqli_lookup_word; - sf->userdata = (void*)(NULL); - } else { - sf->lookup = fn; - sf->userdata = userdata; - } -} - -/** See if two tokens can be merged since they are compound SQL phrases. - * - * This takes two tokens, and, if they are the right type, - * merges their values together. Then checks to see if the - * new value is special using the PHRASES mapping. - * - * Example: "UNION" + "ALL" ==> "UNION ALL" - * - * C Security Notes: this is safe to use C-strings (null-terminated) - * since the types involved by definition do not have embedded nulls - * (e.g. there is no keyword with embedded null) - * - * Porting Notes: since this is C, it's oddly complicated. - * This is just: multikeywords[token.value + ' ' + token2.value] - * - */ -static int syntax_merge_words(sfilter * sf,stoken_t * a, stoken_t * b) -{ - size_t sz1; - size_t sz2; - size_t sz3; - char tmp[LIBINJECTION_SQLI_TOKEN_SIZE]; - char ch; - - /* first token is of right type? */ - if (! - (a->type == TYPE_KEYWORD || - a->type == TYPE_BAREWORD || - a->type == TYPE_OPERATOR || - a->type == TYPE_UNION || - a->type == TYPE_FUNCTION || - a->type == TYPE_EXPRESSION || - a->type == TYPE_SQLTYPE)) { - return CHAR_NULL; - } - - if (b->type != TYPE_KEYWORD && b->type != TYPE_BAREWORD && - b->type != TYPE_OPERATOR && b->type != TYPE_SQLTYPE && - b->type != TYPE_LOGIC_OPERATOR && - b->type != TYPE_FUNCTION && - b->type != TYPE_UNION && b->type != TYPE_EXPRESSION) { - return CHAR_NULL; - } - - sz1 = a->len; - sz2 = b->len; - sz3 = sz1 + sz2 + 1; /* +1 for space in the middle */ - if (sz3 >= LIBINJECTION_SQLI_TOKEN_SIZE) { /* make sure there is room for ending null */ - return FALSE; - } - /* - * oddly annoying last.val + ' ' + current.val - */ - memcpy(tmp, a->val, sz1); - tmp[sz1] = ' '; - memcpy(tmp + sz1 + 1, b->val, sz2); - tmp[sz3] = CHAR_NULL; - - ch = sf->lookup(sf, LOOKUP_WORD, tmp, sz3); - - if (ch != CHAR_NULL) { - st_assign(a, ch, a->pos, sz3, tmp); - return TRUE; - } else { - return FALSE; - } -} - -int libinjection_sqli_fold(sfilter * sf) -{ - stoken_t last_comment; - - /* POS is the position of where the NEXT token goes */ - size_t pos = 0; - - /* LEFT is a count of how many tokens that are already - folded or processed (i.e. part of the fingerprint) */ - size_t left = 0; - - int more = 1; - - st_clear(&last_comment); - - /* Skip all initial comments, right-parens ( and unary operators - * - */ - sf->current = &(sf->tokenvec[0]); - while (more) { - more = libinjection_sqli_tokenize(sf); - if ( ! (sf->current->type == TYPE_COMMENT || sf->current->type == TYPE_LEFTPARENS || st_is_unary_op(sf->current))) { - break; - } - } - - if (! more) { - /* If input was only comments, unary or (, then exit */ - return 0; - } else { - /* it's some other token */ - pos += 1; - } - - while (1) { - FOLD_DEBUG; - - /* do we have all the max number of tokens? if so do - * some special cases for 5 tokens - */ - if (pos >= LIBINJECTION_SQLI_MAX_TOKENS) { - if ( - ( - sf->tokenvec[0].type == TYPE_NUMBER && - (sf->tokenvec[1].type == TYPE_OPERATOR || sf->tokenvec[1].type == TYPE_COMMA) && - sf->tokenvec[2].type == TYPE_LEFTPARENS && - sf->tokenvec[3].type == TYPE_NUMBER && - sf->tokenvec[4].type == TYPE_RIGHTPARENS - ) || - ( - sf->tokenvec[0].type == TYPE_BAREWORD && - sf->tokenvec[1].type == TYPE_OPERATOR && - sf->tokenvec[2].type == TYPE_LEFTPARENS && - (sf->tokenvec[3].type == TYPE_BAREWORD || sf->tokenvec[3].type == TYPE_NUMBER) && - sf->tokenvec[4].type == TYPE_RIGHTPARENS - ) || - ( - sf->tokenvec[0].type == TYPE_NUMBER && - sf->tokenvec[1].type == TYPE_RIGHTPARENS && - sf->tokenvec[2].type == TYPE_COMMA && - sf->tokenvec[3].type == TYPE_LEFTPARENS && - sf->tokenvec[4].type == TYPE_NUMBER - ) - ) - { - if (pos > LIBINJECTION_SQLI_MAX_TOKENS) { - //printf("TRUNCATE pos = %lu vs. %lu\n", pos, LIBINJECTION_SQLI_MAX_TOKENS); - st_copy(&(sf->tokenvec[1]), &(sf->tokenvec[LIBINJECTION_SQLI_MAX_TOKENS])); - pos = 2; - left = 0; - } else { - //printf("HEREIAM\n"); - pos = 1; - left = 0; - } - } - } - - if (! more || left >= LIBINJECTION_SQLI_MAX_TOKENS) { - left = pos; - break; - } - - /* get up to two tokens */ - while (more && pos <= LIBINJECTION_SQLI_MAX_TOKENS && (pos - left) < 2) { - sf->current = &(sf->tokenvec[pos]); - more = libinjection_sqli_tokenize(sf); - if (more) { - if (sf->current->type == TYPE_COMMENT) { - st_copy(&last_comment, sf->current); - } else { - last_comment.type = CHAR_NULL; - pos += 1; - } - } - } - FOLD_DEBUG; - /* did we get 2 tokens? if not then we are done */ - if (pos - left < 2) { - left = pos; - continue; - } - - /* FOLD: "ss" -> "s" - * "foo" "bar" is valid SQL - * just ignore second string - */ - if (sf->tokenvec[left].type == TYPE_STRING && sf->tokenvec[left+1].type == TYPE_STRING) { - pos -= 1; - sf->stats_folds += 1; - continue; - } else if (sf->tokenvec[left].type == TYPE_SEMICOLON && sf->tokenvec[left+1].type == TYPE_SEMICOLON) { - /* not sure how various engines handle - * 'select 1;;drop table foo' or - * 'select 1; /x foo x/; drop table foo' - * to prevent surprises, just fold away repeated semicolons - */ - pos -= 1; - sf->stats_folds += 1; - continue; - } else if (sf->tokenvec[left].type == TYPE_SEMICOLON && - sf->tokenvec[left+1].type == TYPE_FUNCTION && - cstrcasecmp("IF", sf->tokenvec[left+1].val, sf->tokenvec[left+1].len) == 0) { - /* IF is normally a function, except in Transact-SQL where it can be used as a - * standalone control flow operator, e.g. ; IF 1=1 ... - * if found after a semicolon, convert from 'f' type to 'T' type - */ - sf->tokenvec[left+1].type = TYPE_TSQL; - left += 2; - continue; /* reparse everything, but we probably can advance left, and pos */ - } else if ((sf->tokenvec[left].type ==TYPE_OPERATOR || - sf->tokenvec[left].type ==TYPE_LOGIC_OPERATOR) && - st_is_unary_op(&sf->tokenvec[left+1])) { - pos -= 1; - sf->stats_folds += 1; - left = 0; - continue; - } else if (sf->tokenvec[left].type ==TYPE_LEFTPARENS && - st_is_unary_op(&sf->tokenvec[left+1])) { - pos -= 1; - sf->stats_folds += 1; - if (left > 0) { - left -= 1; - } - continue; - } else if (syntax_merge_words(sf, &sf->tokenvec[left], &sf->tokenvec[left+1])) { - pos -= 1; - sf->stats_folds += 1; - if (left > 0) { - left -= 1; - } - continue; - } else if ((sf->tokenvec[left].type == TYPE_BAREWORD || sf->tokenvec[left].type == TYPE_VARIABLE) && - sf->tokenvec[left+1].type == TYPE_LEFTPARENS && ( - /* TSQL functions but common enough to be collumn names */ - cstrcasecmp("USER_ID", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("USER_NAME", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - - /* Function in MYSQL */ - cstrcasecmp("DATABASE", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("PASSWORD", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("USER", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - - /* Mysql words that act as a variable and are a function */ - - /* TSQL current_users is fake-variable */ - /* http://msdn.microsoft.com/en-us/library/ms176050.aspx */ - cstrcasecmp("CURRENT_USER", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("CURRENT_DATE", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("CURRENT_TIME", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("CURRENT_TIMESTAMP", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("LOCALTIME", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("LOCALTIMESTAMP", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 - )) { - - /* pos is the same - * other conversions need to go here... for instance - * password CAN be a function, coalese CAN be a function - */ - sf->tokenvec[left].type = TYPE_FUNCTION; - continue; - } else if (sf->tokenvec[left].type == TYPE_KEYWORD && ( - cstrcasecmp("IN", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 || - cstrcasecmp("NOT IN", sf->tokenvec[left].val, sf->tokenvec[left].len) == 0 - )) { - - if (sf->tokenvec[left+1].type == TYPE_LEFTPARENS) { - /* got .... IN ( ... (or 'NOT IN') - * it's an operator - */ - sf->tokenvec[left].type = TYPE_OPERATOR; - } else { - /* - * it's a nothing - */ - sf->tokenvec[left].type = TYPE_BAREWORD; - } - - /* "IN" can be used as "IN BOOLEAN MODE" for mysql - * in which case merging of words can be done later - * other wise it acts as an equality operator __ IN (values..) - * - * here we got "IN" "(" so it's an operator. - * also back track to handle "NOT IN" - * might need to do the same with like - * two use cases "foo" LIKE "BAR" (normal operator) - * "foo" = LIKE(1,2) - */ - continue; - } else if (sf->tokenvec[left].type == TYPE_SQLTYPE && - (sf->tokenvec[left+1].type == TYPE_BAREWORD || - sf->tokenvec[left+1].type == TYPE_NUMBER || - sf->tokenvec[left+1].type == TYPE_VARIABLE || - sf->tokenvec[left+1].type == TYPE_STRING)) { - st_copy(&sf->tokenvec[left], &sf->tokenvec[left+1]); - pos -= 1; - sf->stats_folds += 1; - left = 0; - continue; - } else if (sf->tokenvec[left].type == TYPE_COLLATE && - sf->tokenvec[left+1].type == TYPE_BAREWORD) { - /* - * there are too many collation types.. so if the bareword has a "_" - * then it's TYPE_SQLTYPE - */ - if (strchr(sf->tokenvec[left+1].val, '_') != NULL) { - sf->tokenvec[left+1].type = TYPE_SQLTYPE; - } - } else if (sf->tokenvec[left].type == TYPE_BACKSLASH) { - if (st_is_arithmetic_op(&(sf->tokenvec[left+1]))) { - /* very weird case in TSQL where '\%1' is parsed as '0 % 1', etc */ - sf->tokenvec[left].type = TYPE_NUMBER; - } else { - /* just ignore it.. Again T-SQL seems to parse \1 as "1" */ - st_copy(&sf->tokenvec[left], &sf->tokenvec[left+1]); - pos -= 1; - sf->stats_folds += 1; - } - left = 0; - continue; - } else if (sf->tokenvec[left].type == TYPE_LEFTPARENS && - sf->tokenvec[left+1].type == TYPE_LEFTPARENS) { - pos -= 1; - sf->stats_folds += 1; - continue; - } else if (sf->tokenvec[left].type == TYPE_RIGHTPARENS && - sf->tokenvec[left+1].type == TYPE_RIGHTPARENS) { - pos -= 1; - sf->stats_folds += 1; - continue; - } else if (sf->tokenvec[left].type == TYPE_LEFTBRACE && - sf->tokenvec[left+1].type == TYPE_BAREWORD) { - /* weird ODBC / MYSQL {foo expr} --> expr - * but for this rule we just strip away the "{ foo" part - */ - if (left > 0) { - left -= 1; - } - pos -= 2; - sf->stats_folds += 2; - continue; - } else if (sf->tokenvec[left+1].type == TYPE_RIGHTBRACE) { - pos -= 1; - sf->stats_folds += 1; - continue; - } - - /* all cases of handing 2 tokens is done - and nothing matched. Get one more token - */ - FOLD_DEBUG; - while (more && pos <= LIBINJECTION_SQLI_MAX_TOKENS && pos - left < 3) { - sf->current = &(sf->tokenvec[pos]); - more = libinjection_sqli_tokenize(sf); - if (more) { - if (sf->current->type == TYPE_COMMENT) { - st_copy(&last_comment, sf->current); - } else { - last_comment.type = CHAR_NULL; - pos += 1; - } - } - } - - /* do we have three tokens? If not then we are done */ - if (pos -left < 3) { - left = pos; - continue; - } - - /* - * now look for three token folding - */ - if (sf->tokenvec[left].type == TYPE_NUMBER && - sf->tokenvec[left+1].type == TYPE_OPERATOR && - sf->tokenvec[left+2].type == TYPE_NUMBER) { - pos -= 2; - continue; - } else if (sf->tokenvec[left].type == TYPE_OPERATOR && - sf->tokenvec[left+1].type != TYPE_LEFTPARENS && - sf->tokenvec[left+2].type == TYPE_OPERATOR) { - if (left > 0) { - left -= 1; - } - pos -= 2; - continue; - } else if (sf->tokenvec[left].type == TYPE_LOGIC_OPERATOR && - sf->tokenvec[left+2].type == TYPE_LOGIC_OPERATOR) { - pos -= 2; - continue; - } else if (sf->tokenvec[left].type == TYPE_VARIABLE && - sf->tokenvec[left+1].type == TYPE_OPERATOR && - (sf->tokenvec[left+2].type == TYPE_VARIABLE || - sf->tokenvec[left+2].type == TYPE_NUMBER || - sf->tokenvec[left+2].type == TYPE_BAREWORD)) { - pos -= 2; - continue; - } else if ((sf->tokenvec[left].type == TYPE_BAREWORD || - sf->tokenvec[left].type == TYPE_NUMBER ) && - sf->tokenvec[left+1].type == TYPE_OPERATOR && - (sf->tokenvec[left+2].type == TYPE_NUMBER || - sf->tokenvec[left+2].type == TYPE_BAREWORD)) { - pos -= 2; - continue; - } else if ((sf->tokenvec[left].type == TYPE_BAREWORD || - sf->tokenvec[left].type == TYPE_NUMBER || - sf->tokenvec[left].type == TYPE_VARIABLE || - sf->tokenvec[left].type == TYPE_STRING) && - sf->tokenvec[left+1].type == TYPE_OPERATOR && - streq(sf->tokenvec[left+1].val, "::") && - sf->tokenvec[left+2].type == TYPE_SQLTYPE) { - pos -= 2; - sf->stats_folds += 2; - continue; - } else if ((sf->tokenvec[left].type == TYPE_BAREWORD || - sf->tokenvec[left].type == TYPE_NUMBER || - sf->tokenvec[left].type == TYPE_STRING || - sf->tokenvec[left].type == TYPE_VARIABLE) && - sf->tokenvec[left+1].type == TYPE_COMMA && - (sf->tokenvec[left+2].type == TYPE_NUMBER || - sf->tokenvec[left+2].type == TYPE_BAREWORD || - sf->tokenvec[left+2].type == TYPE_STRING || - sf->tokenvec[left+2].type == TYPE_VARIABLE)) { - pos -= 2; - if (left > 0) { - left -= 1; - } - continue; - } else if ((sf->tokenvec[left].type == TYPE_EXPRESSION || - sf->tokenvec[left].type == TYPE_GROUP || - sf->tokenvec[left].type == TYPE_COMMA) && - st_is_unary_op(&sf->tokenvec[left+1]) && - sf->tokenvec[left+2].type == TYPE_LEFTPARENS) { - /* got something like SELECT + (, LIMIT + ( - * remove unary operator - */ - st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]); - pos -= 1; - continue; - } else if ((sf->tokenvec[left].type == TYPE_KEYWORD || - sf->tokenvec[left].type == TYPE_EXPRESSION || - sf->tokenvec[left].type == TYPE_GROUP ) && - st_is_unary_op(&sf->tokenvec[left+1]) && - (sf->tokenvec[left+2].type == TYPE_NUMBER || - sf->tokenvec[left+2].type == TYPE_BAREWORD || - sf->tokenvec[left+2].type == TYPE_VARIABLE || - sf->tokenvec[left+2].type == TYPE_STRING || - sf->tokenvec[left+2].type == TYPE_FUNCTION )) { - /* remove unary operators - * select - 1 - */ - st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]); - pos -= 1; - continue; - } else if (sf->tokenvec[left].type == TYPE_COMMA && - st_is_unary_op(&sf->tokenvec[left+1]) && - (sf->tokenvec[left+2].type == TYPE_NUMBER || - sf->tokenvec[left+2].type == TYPE_BAREWORD || - sf->tokenvec[left+2].type == TYPE_VARIABLE || - sf->tokenvec[left+2].type == TYPE_STRING || - sf->tokenvec[left+2].type == TYPE_FUNCTION )) { - /* - * interesting case turn ", -1" ->> ",1" PLUS we need to back up - * one token if possible to see if more folding can be done - * "1,-1" --> "1" - */ - st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]); - if (left > 0) { - left -= 1; - } - /* pos is >= 3 so this is safe */ - assert(pos >= 3); - pos -= 3; - continue; - } else if ((sf->tokenvec[left].type == TYPE_BAREWORD) && - (sf->tokenvec[left+1].type == TYPE_DOT) && - (sf->tokenvec[left+2].type == TYPE_BAREWORD)) { - /* ignore the '.n' - * typically is this databasename.table - */ - assert(pos >= 3); - pos -= 2; - continue; - } else if ((sf->tokenvec[left].type == TYPE_EXPRESSION) && - (sf->tokenvec[left+1].type == TYPE_DOT) && - (sf->tokenvec[left+2].type == TYPE_BAREWORD)) { - /* select . `foo` --> select `foo` */ - st_copy(&sf->tokenvec[left+1], &sf->tokenvec[left+2]); - pos -= 1; - continue; - } - - - /* no folding -- assume left-most token is - is good, now use the existing 2 tokens -- - do not get another - */ - - left += 1; - - } /* while(1) */ - - /* if we have 4 or less tokens, and we had a comment token - * at the end, add it back - */ - - if (left < LIBINJECTION_SQLI_MAX_TOKENS && last_comment.type == TYPE_COMMENT) { - st_copy(&sf->tokenvec[left], &last_comment); - left += 1; - } - - /* sometimes we grab a 6th token to help - determine the type of token 5. - */ - if (left > LIBINJECTION_SQLI_MAX_TOKENS) { - left = LIBINJECTION_SQLI_MAX_TOKENS; - } - - return (int)left; -} - -/* secondary api: detects SQLi in a string, GIVEN a context. - * - * A context can be: - * * CHAR_NULL (\0), process as is - * * CHAR_SINGLE ('), process pretending input started with a - * single quote. - * * CHAR_DOUBLE ("), process pretending input started with a - * double quote. - * - */ -const char* libinjection_sqli_fingerprint(sfilter * sql_state, int flags) -{ - int i; - int tlen = 0; - - libinjection_sqli_reset(sql_state, flags); - - tlen = libinjection_sqli_fold(sql_state); - - /* Check for magic PHP backquote comment - * If: - * * last token is of type "bareword" - * * And is quoted in a backtick - * * And isn't closed - * * And it's empty? - * Then convert it to comment - */ - if (tlen > 2 && - sql_state->tokenvec[tlen-1].type == TYPE_BAREWORD && - sql_state->tokenvec[tlen-1].str_open == CHAR_TICK && - sql_state->tokenvec[tlen-1].len == 0 && - sql_state->tokenvec[tlen-1].str_close == CHAR_NULL) { - sql_state->tokenvec[tlen-1].type = TYPE_COMMENT; - } - - for (i = 0; i < tlen; ++i) { - sql_state->fingerprint[i] = sql_state->tokenvec[i].type; - } - - /* - * make the fingerprint pattern a c-string (null delimited) - */ - sql_state->fingerprint[tlen] = CHAR_NULL; - - /* - * check for 'X' in pattern, and then - * clear out all tokens - * - * this means parsing could not be done - * accurately due to pgsql's double comments - * or other syntax that isn't consistent. - * Should be very rare false positive - */ - if (strchr(sql_state->fingerprint, TYPE_EVIL)) { - /* needed for SWIG */ - memset((void*)sql_state->fingerprint, 0, LIBINJECTION_SQLI_MAX_TOKENS + 1); - memset((void*)sql_state->tokenvec[0].val, 0, LIBINJECTION_SQLI_TOKEN_SIZE); - - sql_state->fingerprint[0] = TYPE_EVIL; - - sql_state->tokenvec[0].type = TYPE_EVIL; - sql_state->tokenvec[0].val[0] = TYPE_EVIL; - sql_state->tokenvec[1].type = CHAR_NULL; - } - - - return sql_state->fingerprint; -} - - -/** - * - */ -#define UNUSED(x) (void)(x) - -int libinjection_sqli_check_fingerprint(sfilter* sql_state) -{ - return libinjection_sqli_blacklist(sql_state) && - libinjection_sqli_not_whitelist(sql_state); -} - -char libinjection_sqli_lookup_word(sfilter *sql_state, int lookup_type, - const char* str, size_t len) -{ - if (lookup_type == LOOKUP_FINGERPRINT) { - return libinjection_sqli_check_fingerprint(sql_state) ? 'X' : '\0'; - } else { - return bsearch_keyword_type(str, len, sql_keywords, sql_keywords_sz); - } -} - -int libinjection_sqli_blacklist(sfilter* sql_state) -{ - /* - * use minimum of 8 bytes to make sure gcc -fstack-protector - * works correctly - */ - char fp2[LIBINJECTION_SQLI_MAX_TOKENS + 2 < 8 ? 8 : LIBINJECTION_SQLI_MAX_TOKENS + 2]; - char ch; - size_t i; - size_t len = strlen(sql_state->fingerprint); - - if (len < 1) { - sql_state->reason = __LINE__; - return FALSE; - } - - /* - to keep everything compatible, convert the - v0 fingerprint pattern to v1 - v0: up to 5 chars, mixed case - v1: 1 char is '0', up to 5 more chars, upper case - */ - - fp2[0] = '0'; - for (i = 0; i < len; ++i) { - ch = sql_state->fingerprint[i]; - if (ch >= 'a' && ch <= 'z') { - ch -= 0x20; - } - fp2[i+1] = ch; - } - fp2[i+1] = '\0'; - - int patmatch = is_keyword(fp2, len + 1) == TYPE_FINGERPRINT; - - /* - * No match. - * - * Set sql_state->reason to current line number - * only for debugging purposes. - */ - if (!patmatch) { - sql_state->reason = __LINE__; - return FALSE; - } - - return TRUE; -} - -/* - * return TRUE if sqli, false is benign - */ -int libinjection_sqli_not_whitelist(sfilter* sql_state) -{ - /* - * We assume we got a SQLi match - * This next part just helps reduce false positives. - * - */ - char ch; - size_t tlen = strlen(sql_state->fingerprint); - - if (tlen > 1 && sql_state->fingerprint[tlen-1] == TYPE_COMMENT) { - /* - * if ending comment is contains 'sp_password' then it's sqli! - * MS Audit log appearantly ignores anything with - * 'sp_password' in it. Unable to find primary refernece to - * this "feature" of SQL Server but seems to be known sqli - * technique - */ - if (my_memmem(sql_state->s, sql_state->slen, - "sp_password", strlen("sp_password"))) { - sql_state->reason = __LINE__; - return TRUE; - } - } - - switch (tlen) { - case 2:{ - /* - * case 2 are "very small SQLi" which make them - * hard to tell from normal input... - */ - - if (sql_state->fingerprint[1] == TYPE_UNION) { - if (sql_state->stats_tokens == 2) { - /* not sure why but 1U comes up in Sqli attack - * likely part of parameter splitting/etc. - * lots of reasons why "1 union" might be normal - * input, so beep only if other SQLi things are present - */ - /* it really is a number and 'union' - * other wise it has folding or comments - */ - sql_state->reason = __LINE__; - return FALSE; - } else { - sql_state->reason = __LINE__; - return TRUE; - } - } - /* - * if 'comment' is '#' ignore.. too many FP - */ - if (sql_state->tokenvec[1].val[0] == '#') { - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * for fingerprint like 'nc', only comments of /x are treated - * as SQL... ending comments of "--" and "#" are not sqli - */ - if (sql_state->tokenvec[0].type == TYPE_BAREWORD && - sql_state->tokenvec[1].type == TYPE_COMMENT && - sql_state->tokenvec[1].val[0] != '/') { - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * if '1c' ends with '/x' then it's sqli - */ - if (sql_state->tokenvec[0].type == TYPE_NUMBER && - sql_state->tokenvec[1].type == TYPE_COMMENT && - sql_state->tokenvec[1].val[0] == '/') { - return TRUE; - } - - /** - * there are some odd base64-looking query string values - * 1234-ABCDEFEhfhihwuefi-- - * which evaluate to "1c"... these are not SQLi - * but 1234-- probably is. - * Make sure the "1" in "1c" is actually a true decimal number - * - * Need to check -original- string since the folding step - * may have merged tokens, e.g. "1+FOO" is folded into "1" - * - * Note: evasion: 1*1-- - */ - if (sql_state->tokenvec[0].type == TYPE_NUMBER && - sql_state->tokenvec[1].type == TYPE_COMMENT) { - if (sql_state->stats_tokens > 2) { - /* we have some folding going on, highly likely sqli */ - sql_state->reason = __LINE__; - return TRUE; - } - /* - * we check that next character after the number is either whitespace, - * or '/' or a '-' ==> sqli. - */ - ch = sql_state->s[sql_state->tokenvec[0].len]; - if ( ch <= 32 ) { - /* next char was whitespace,e.g. "1234 --" - * this isn't exactly correct.. ideally we should skip over all whitespace - * but this seems to be ok for now - */ - return TRUE; - } - if (ch == '/' && sql_state->s[sql_state->tokenvec[0].len + 1] == '*') { - return TRUE; - } - if (ch == '-' && sql_state->s[sql_state->tokenvec[0].len + 1] == '-') { - return TRUE; - } - - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * detect obvious sqli scans.. many people put '--' in plain text - * so only detect if input ends with '--', e.g. 1-- but not 1-- foo - */ - if ((sql_state->tokenvec[1].len > 2) - && sql_state->tokenvec[1].val[0] == '-') { - sql_state->reason = __LINE__; - return FALSE; - } - - break; - } /* case 2 */ - case 3:{ - /* - * ...foo' + 'bar... - * no opening quote, no closing quote - * and each string has data - */ - - if (streq(sql_state->fingerprint, "sos") - || streq(sql_state->fingerprint, "s&s")) { - - if ((sql_state->tokenvec[0].str_open == CHAR_NULL) - && (sql_state->tokenvec[2].str_close == CHAR_NULL) - && (sql_state->tokenvec[0].str_close == sql_state->tokenvec[2].str_open)) { - /* - * if ....foo" + "bar.... - */ - sql_state->reason = __LINE__; - return TRUE; - } - if (sql_state->stats_tokens == 3) { - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * not sqli - */ - sql_state->reason = __LINE__; - return FALSE; - } else if (streq(sql_state->fingerprint, "s&n") || - streq(sql_state->fingerprint, "n&1") || - streq(sql_state->fingerprint, "1&1") || - streq(sql_state->fingerprint, "1&v") || - streq(sql_state->fingerprint, "1&s")) { - /* 'sexy and 17' not sqli - * 'sexy and 17<18' sqli - */ - if (sql_state->stats_tokens == 3) { - sql_state->reason = __LINE__; - return FALSE; - } - } else if (sql_state->tokenvec[1].type == TYPE_KEYWORD) { - if ((sql_state->tokenvec[1].len < 5) || - cstrcasecmp("INTO", sql_state->tokenvec[1].val, 4)) { - /* if it's not "INTO OUTFILE", or "INTO DUMPFILE" (MySQL) - * then treat as safe - */ - sql_state->reason = __LINE__; - return FALSE; - } - } - break; - } /* case 3 */ - case 4: - case 5: { - /* nothing right now */ - break; - } /* case 5 */ - } /* end switch */ - - return TRUE; -} - -/** Main API, detects SQLi in an input. - * - * - */ -static int reparse_as_mysql(sfilter * sql_state) -{ - return sql_state->stats_comment_ddx || - sql_state->stats_comment_hash; -} - -int libinjection_is_sqli(sfilter * sql_state) -{ - const char *s = sql_state->s; - size_t slen = sql_state->slen; - - /* - * no input? not sqli - */ - if (slen == 0) { - return FALSE; - } - - /* - * test input "as-is" - */ - libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_ANSI); - if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT, - sql_state->fingerprint, strlen(sql_state->fingerprint))) { - return TRUE; - } else if (reparse_as_mysql(sql_state)) { - libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL); - if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT, - sql_state->fingerprint, strlen(sql_state->fingerprint))) { - return TRUE; - } - } - - /* - * if input has a single_quote, then - * test as if input was actually ' - * example: if input if "1' = 1", then pretend it's - * "'1' = 1" - * Porting Notes: example the same as doing - * is_string_sqli(sql_state, "'" + s, slen+1, NULL, fn, arg) - * - */ - if (memchr(s, CHAR_SINGLE, slen)) { - libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_ANSI); - if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT, - sql_state->fingerprint, strlen(sql_state->fingerprint))) { - return TRUE; - } else if (reparse_as_mysql(sql_state)) { - libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_MYSQL); - if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT, - sql_state->fingerprint, strlen(sql_state->fingerprint))) { - return TRUE; - } - } - } - - /* - * same as above but with a double-quote " - */ - if (memchr(s, CHAR_DOUBLE, slen)) { - libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_DOUBLE | FLAG_SQL_MYSQL); - if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT, - sql_state->fingerprint, strlen(sql_state->fingerprint))) { - return TRUE; - } - } - - /* - * Hurray, input is not SQLi - */ - return FALSE; -} diff --git a/apache2/libinjection/libinjection_sqli_data.h b/apache2/libinjection/libinjection_sqli_data.h deleted file mode 100644 index 20e1a5f6a5..0000000000 --- a/apache2/libinjection/libinjection_sqli_data.h +++ /dev/null @@ -1,10133 +0,0 @@ - -#ifndef _LIBINJECTION_SQLI_DATA_H -#define _LIBINJECTION_SQLI_DATA_H - -#include "libinjection.h" - -typedef struct { - const char *word; - char type; -} keyword_t; - -static size_t parse_money(sfilter * sf); -static size_t parse_other(sfilter * sf); -static size_t parse_white(sfilter * sf); -static size_t parse_operator1(sfilter *sf); -static size_t parse_char(sfilter *sf); -static size_t parse_hash(sfilter *sf); -static size_t parse_dash(sfilter *sf); -static size_t parse_slash(sfilter *sf); -static size_t parse_backslash(sfilter * sf); -static size_t parse_operator2(sfilter *sf); -static size_t parse_string(sfilter *sf); -static size_t parse_word(sfilter * sf); -static size_t parse_var(sfilter * sf); -static size_t parse_number(sfilter * sf); -static size_t parse_tick(sfilter * sf); -static size_t parse_ustring(sfilter * sf); -static size_t parse_qstring(sfilter * sf); -static size_t parse_nqstring(sfilter * sf); -static size_t parse_xstring(sfilter * sf); -static size_t parse_bstring(sfilter * sf); -static size_t parse_estring(sfilter * sf); - - -typedef size_t (*pt2Function)(sfilter *sf); -static const pt2Function char_parse_map[] = { - &parse_white, /* 0 */ - &parse_white, /* 1 */ - &parse_white, /* 2 */ - &parse_white, /* 3 */ - &parse_white, /* 4 */ - &parse_white, /* 5 */ - &parse_white, /* 6 */ - &parse_white, /* 7 */ - &parse_white, /* 8 */ - &parse_white, /* 9 */ - &parse_white, /* 10 */ - &parse_white, /* 11 */ - &parse_white, /* 12 */ - &parse_white, /* 13 */ - &parse_white, /* 14 */ - &parse_white, /* 15 */ - &parse_white, /* 16 */ - &parse_white, /* 17 */ - &parse_white, /* 18 */ - &parse_white, /* 19 */ - &parse_white, /* 20 */ - &parse_white, /* 21 */ - &parse_white, /* 22 */ - &parse_white, /* 23 */ - &parse_white, /* 24 */ - &parse_white, /* 25 */ - &parse_white, /* 26 */ - &parse_white, /* 27 */ - &parse_white, /* 28 */ - &parse_white, /* 29 */ - &parse_white, /* 30 */ - &parse_white, /* 31 */ - &parse_white, /* 32 */ - &parse_operator2, /* 33 */ - &parse_string, /* 34 */ - &parse_hash, /* 35 */ - &parse_money, /* 36 */ - &parse_operator1, /* 37 */ - &parse_operator2, /* 38 */ - &parse_string, /* 39 */ - &parse_char, /* 40 */ - &parse_char, /* 41 */ - &parse_operator2, /* 42 */ - &parse_operator1, /* 43 */ - &parse_char, /* 44 */ - &parse_dash, /* 45 */ - &parse_number, /* 46 */ - &parse_slash, /* 47 */ - &parse_number, /* 48 */ - &parse_number, /* 49 */ - &parse_number, /* 50 */ - &parse_number, /* 51 */ - &parse_number, /* 52 */ - &parse_number, /* 53 */ - &parse_number, /* 54 */ - &parse_number, /* 55 */ - &parse_number, /* 56 */ - &parse_number, /* 57 */ - &parse_operator2, /* 58 */ - &parse_char, /* 59 */ - &parse_operator2, /* 60 */ - &parse_operator2, /* 61 */ - &parse_operator2, /* 62 */ - &parse_other, /* 63 */ - &parse_var, /* 64 */ - &parse_word, /* 65 */ - &parse_bstring, /* 66 */ - &parse_word, /* 67 */ - &parse_word, /* 68 */ - &parse_estring, /* 69 */ - &parse_word, /* 70 */ - &parse_word, /* 71 */ - &parse_word, /* 72 */ - &parse_word, /* 73 */ - &parse_word, /* 74 */ - &parse_word, /* 75 */ - &parse_word, /* 76 */ - &parse_word, /* 77 */ - &parse_nqstring, /* 78 */ - &parse_word, /* 79 */ - &parse_word, /* 80 */ - &parse_qstring, /* 81 */ - &parse_word, /* 82 */ - &parse_word, /* 83 */ - &parse_word, /* 84 */ - &parse_ustring, /* 85 */ - &parse_word, /* 86 */ - &parse_word, /* 87 */ - &parse_xstring, /* 88 */ - &parse_word, /* 89 */ - &parse_word, /* 90 */ - &parse_other, /* 91 */ - &parse_backslash, /* 92 */ - &parse_other, /* 93 */ - &parse_operator1, /* 94 */ - &parse_word, /* 95 */ - &parse_tick, /* 96 */ - &parse_word, /* 97 */ - &parse_bstring, /* 98 */ - &parse_word, /* 99 */ - &parse_word, /* 100 */ - &parse_estring, /* 101 */ - &parse_word, /* 102 */ - &parse_word, /* 103 */ - &parse_word, /* 104 */ - &parse_word, /* 105 */ - &parse_word, /* 106 */ - &parse_word, /* 107 */ - &parse_word, /* 108 */ - &parse_word, /* 109 */ - &parse_nqstring, /* 110 */ - &parse_word, /* 111 */ - &parse_word, /* 112 */ - &parse_qstring, /* 113 */ - &parse_word, /* 114 */ - &parse_word, /* 115 */ - &parse_word, /* 116 */ - &parse_ustring, /* 117 */ - &parse_word, /* 118 */ - &parse_word, /* 119 */ - &parse_xstring, /* 120 */ - &parse_word, /* 121 */ - &parse_word, /* 122 */ - &parse_char, /* 123 */ - &parse_operator2, /* 124 */ - &parse_char, /* 125 */ - &parse_operator1, /* 126 */ - &parse_white, /* 127 */ -}; - -static const keyword_t sql_keywords[] = { - {"!!", 'o'}, - {"!<", 'o'}, - {"!=", 'o'}, - {"!>", 'o'}, - {"%=", 'o'}, - {"&&", '&'}, - {"&=", 'o'}, - {"*=", 'o'}, - {"+=", 'o'}, - {"-=", 'o'}, - {"/=", 'o'}, - {"0&((((", 'F'}, - {"0&(((1", 'F'}, - {"0&(((F", 'F'}, - {"0&(((N", 'F'}, - {"0&(((S", 'F'}, - {"0&(((V", 'F'}, - {"0&((1)", 'F'}, - {"0&((1O", 'F'}, - {"0&((F(", 'F'}, - {"0&((N)", 'F'}, - {"0&((NO", 'F'}, - {"0&((S)", 'F'}, - {"0&((SO", 'F'}, - {"0&((V)", 'F'}, - {"0&((VO", 'F'}, - {"0&(1)O", 'F'}, - {"0&(1)U", 'F'}, - {"0&(1O(", 'F'}, - {"0&(1OF", 'F'}, - {"0&(1OS", 'F'}, - {"0&(1OV", 'F'}, - {"0&(F((", 'F'}, - {"0&(F()", 'F'}, - {"0&(F(1", 'F'}, - {"0&(F(F", 'F'}, - {"0&(F(N", 'F'}, - {"0&(F(S", 'F'}, - {"0&(F(V", 'F'}, - {"0&(N)O", 'F'}, - {"0&(N)U", 'F'}, - {"0&(NO(", 'F'}, - {"0&(NOF", 'F'}, - {"0&(NOS", 'F'}, - {"0&(NOV", 'F'}, - {"0&(S)O", 'F'}, - {"0&(S)U", 'F'}, - {"0&(SO(", 'F'}, - {"0&(SO1", 'F'}, - {"0&(SOF", 'F'}, - {"0&(SON", 'F'}, - {"0&(SOS", 'F'}, - {"0&(SOV", 'F'}, - {"0&(V)O", 'F'}, - {"0&(V)U", 'F'}, - {"0&(VO(", 'F'}, - {"0&(VOF", 'F'}, - {"0&(VOS", 'F'}, - {"0&1O((", 'F'}, - {"0&1O(1", 'F'}, - {"0&1O(F", 'F'}, - {"0&1O(N", 'F'}, - {"0&1O(S", 'F'}, - {"0&1O(V", 'F'}, - {"0&1OF(", 'F'}, - {"0&1OS(", 'F'}, - {"0&1OS1", 'F'}, - {"0&1OSF", 'F'}, - {"0&1OSO", 'F'}, - {"0&1OSU", 'F'}, - {"0&1OSV", 'F'}, - {"0&1OV(", 'F'}, - {"0&1OVF", 'F'}, - {"0&1OVO", 'F'}, - {"0&1OVS", 'F'}, - {"0&1OVU", 'F'}, - {"0&1UE(", 'F'}, - {"0&1UE1", 'F'}, - {"0&1UEF", 'F'}, - {"0&1UEK", 'F'}, - {"0&1UEN", 'F'}, - {"0&1UES", 'F'}, - {"0&1UEV", 'F'}, - {"0&F(((", 'F'}, - {"0&F(()", 'F'}, - {"0&F((1", 'F'}, - {"0&F((F", 'F'}, - {"0&F((N", 'F'}, - {"0&F((S", 'F'}, - {"0&F((V", 'F'}, - {"0&F()O", 'F'}, - {"0&F()U", 'F'}, - {"0&F(1)", 'F'}, - {"0&F(1O", 'F'}, - {"0&F(F(", 'F'}, - {"0&F(N)", 'F'}, - {"0&F(NO", 'F'}, - {"0&F(S)", 'F'}, - {"0&F(SO", 'F'}, - {"0&F(V)", 'F'}, - {"0&F(VO", 'F'}, - {"0&NO((", 'F'}, - {"0&NO(1", 'F'}, - {"0&NO(F", 'F'}, - {"0&NO(N", 'F'}, - {"0&NO(S", 'F'}, - {"0&NO(V", 'F'}, - {"0&NOF(", 'F'}, - {"0&NOS(", 'F'}, - {"0&NOS1", 'F'}, - {"0&NOSF", 'F'}, - {"0&NOSO", 'F'}, - {"0&NOSU", 'F'}, - {"0&NOSV", 'F'}, - {"0&NOV(", 'F'}, - {"0&NOVF", 'F'}, - {"0&NOVO", 'F'}, - {"0&NOVS", 'F'}, - {"0&NOVU", 'F'}, - {"0&NUE(", 'F'}, - {"0&NUE1", 'F'}, - {"0&NUEF", 'F'}, - {"0&NUEK", 'F'}, - {"0&NUEN", 'F'}, - {"0&NUES", 'F'}, - {"0&NUEV", 'F'}, - {"0&SO((", 'F'}, - {"0&SO(1", 'F'}, - {"0&SO(F", 'F'}, - {"0&SO(N", 'F'}, - {"0&SO(S", 'F'}, - {"0&SO(V", 'F'}, - {"0&SO1(", 'F'}, - {"0&SO1F", 'F'}, - {"0&SO1N", 'F'}, - {"0&SO1O", 'F'}, - {"0&SO1S", 'F'}, - {"0&SO1U", 'F'}, - {"0&SO1V", 'F'}, - {"0&SOF(", 'F'}, - {"0&SON(", 'F'}, - {"0&SON1", 'F'}, - {"0&SONF", 'F'}, - {"0&SONO", 'F'}, - {"0&SONU", 'F'}, - {"0&SOS(", 'F'}, - {"0&SOS1", 'F'}, - {"0&SOSF", 'F'}, - {"0&SOSO", 'F'}, - {"0&SOSU", 'F'}, - {"0&SOSV", 'F'}, - {"0&SOV(", 'F'}, - {"0&SOVF", 'F'}, - {"0&SOVO", 'F'}, - {"0&SOVS", 'F'}, - {"0&SOVU", 'F'}, - {"0&SUE(", 'F'}, - {"0&SUE1", 'F'}, - {"0&SUEF", 'F'}, - {"0&SUEK", 'F'}, - {"0&SUEN", 'F'}, - {"0&SUES", 'F'}, - {"0&SUEV", 'F'}, - {"0&VO((", 'F'}, - {"0&VO(1", 'F'}, - {"0&VO(F", 'F'}, - {"0&VO(N", 'F'}, - {"0&VO(S", 'F'}, - {"0&VO(V", 'F'}, - {"0&VOF(", 'F'}, - {"0&VOS(", 'F'}, - {"0&VOS1", 'F'}, - {"0&VOSF", 'F'}, - {"0&VOSO", 'F'}, - {"0&VOSU", 'F'}, - {"0&VOSV", 'F'}, - {"0&VUE(", 'F'}, - {"0&VUE1", 'F'}, - {"0&VUEF", 'F'}, - {"0&VUEK", 'F'}, - {"0&VUEN", 'F'}, - {"0&VUES", 'F'}, - {"0&VUEV", 'F'}, - {"0)UE((", 'F'}, - {"0)UE(1", 'F'}, - {"0)UE(F", 'F'}, - {"0)UE(N", 'F'}, - {"0)UE(S", 'F'}, - {"0)UE(V", 'F'}, - {"0)UE1K", 'F'}, - {"0)UE1O", 'F'}, - {"0)UEF(", 'F'}, - {"0)UEK(", 'F'}, - {"0)UEK1", 'F'}, - {"0)UEKF", 'F'}, - {"0)UEKN", 'F'}, - {"0)UEKS", 'F'}, - {"0)UEKV", 'F'}, - {"0)UENK", 'F'}, - {"0)UENO", 'F'}, - {"0)UESK", 'F'}, - {"0)UESO", 'F'}, - {"0)UEVK", 'F'}, - {"0)UEVO", 'F'}, - {"01&(((", 'F'}, - {"01&((1", 'F'}, - {"01&((E", 'F'}, - {"01&((F", 'F'}, - {"01&((N", 'F'}, - {"01&((S", 'F'}, - {"01&((V", 'F'}, - {"01&(1&", 'F'}, - {"01&(1)", 'F'}, - {"01&(1,", 'F'}, - {"01&(1O", 'F'}, - {"01&(E(", 'F'}, - {"01&(E1", 'F'}, - {"01&(EF", 'F'}, - {"01&(EK", 'F'}, - {"01&(EN", 'F'}, - {"01&(EO", 'F'}, - {"01&(ES", 'F'}, - {"01&(EV", 'F'}, - {"01&(F(", 'F'}, - {"01&(N&", 'F'}, - {"01&(N)", 'F'}, - {"01&(N,", 'F'}, - {"01&(NO", 'F'}, - {"01&(S&", 'F'}, - {"01&(S)", 'F'}, - {"01&(S,", 'F'}, - {"01&(SO", 'F'}, - {"01&(V&", 'F'}, - {"01&(V)", 'F'}, - {"01&(V,", 'F'}, - {"01&(VO", 'F'}, - {"01&1", 'F'}, - {"01&1&(", 'F'}, - {"01&1&1", 'F'}, - {"01&1&F", 'F'}, - {"01&1&N", 'F'}, - {"01&1&S", 'F'}, - {"01&1&V", 'F'}, - {"01&1)&", 'F'}, - {"01&1)O", 'F'}, - {"01&1)U", 'F'}, - {"01&1;", 'F'}, - {"01&1;C", 'F'}, - {"01&1;E", 'F'}, - {"01&1;T", 'F'}, - {"01&1B(", 'F'}, - {"01&1B1", 'F'}, - {"01&1BF", 'F'}, - {"01&1BN", 'F'}, - {"01&1BS", 'F'}, - {"01&1BV", 'F'}, - {"01&1C", 'F'}, - {"01&1EK", 'F'}, - {"01&1EN", 'F'}, - {"01&1F(", 'F'}, - {"01&1K(", 'F'}, - {"01&1K1", 'F'}, - {"01&1KF", 'F'}, - {"01&1KN", 'F'}, - {"01&1KS", 'F'}, - {"01&1KV", 'F'}, - {"01&1O(", 'F'}, - {"01&1OF", 'F'}, - {"01&1OO", 'F'}, - {"01&1OS", 'F'}, - {"01&1OV", 'F'}, - {"01&1TN", 'F'}, - {"01&1U", 'F'}, - {"01&1U(", 'F'}, - {"01&1U;", 'F'}, - {"01&1UC", 'F'}, - {"01&1UE", 'F'}, - {"01&E((", 'F'}, - {"01&E(1", 'F'}, - {"01&E(F", 'F'}, - {"01&E(N", 'F'}, - {"01&E(O", 'F'}, - {"01&E(S", 'F'}, - {"01&E(V", 'F'}, - {"01&E1", 'F'}, - {"01&E1;", 'F'}, - {"01&E1C", 'F'}, - {"01&E1K", 'F'}, - {"01&E1O", 'F'}, - {"01&EF(", 'F'}, - {"01&EK(", 'F'}, - {"01&EK1", 'F'}, - {"01&EKF", 'F'}, - {"01&EKN", 'F'}, - {"01&EKS", 'F'}, - {"01&EKU", 'F'}, - {"01&EKV", 'F'}, - {"01&EN", 'F'}, - {"01&EN;", 'F'}, - {"01&ENC", 'F'}, - {"01&ENK", 'F'}, - {"01&ENO", 'F'}, - {"01&ES", 'F'}, - {"01&ES;", 'F'}, - {"01&ESC", 'F'}, - {"01&ESK", 'F'}, - {"01&ESO", 'F'}, - {"01&EUE", 'F'}, - {"01&EV", 'F'}, - {"01&EV;", 'F'}, - {"01&EVC", 'F'}, - {"01&EVK", 'F'}, - {"01&EVO", 'F'}, - {"01&F((", 'F'}, - {"01&F()", 'F'}, - {"01&F(1", 'F'}, - {"01&F(E", 'F'}, - {"01&F(F", 'F'}, - {"01&F(N", 'F'}, - {"01&F(S", 'F'}, - {"01&F(V", 'F'}, - {"01&K&(", 'F'}, - {"01&K&1", 'F'}, - {"01&K&F", 'F'}, - {"01&K&N", 'F'}, - {"01&K&S", 'F'}, - {"01&K&V", 'F'}, - {"01&K((", 'F'}, - {"01&K(1", 'F'}, - {"01&K(F", 'F'}, - {"01&K(N", 'F'}, - {"01&K(S", 'F'}, - {"01&K(V", 'F'}, - {"01&K1O", 'F'}, - {"01&KF(", 'F'}, - {"01&KNK", 'F'}, - {"01&KO(", 'F'}, - {"01&KO1", 'F'}, - {"01&KOF", 'F'}, - {"01&KOK", 'F'}, - {"01&KON", 'F'}, - {"01&KOS", 'F'}, - {"01&KOV", 'F'}, - {"01&KSO", 'F'}, - {"01&KVO", 'F'}, - {"01&N&(", 'F'}, - {"01&N&1", 'F'}, - {"01&N&F", 'F'}, - {"01&N&N", 'F'}, - {"01&N&S", 'F'}, - {"01&N&V", 'F'}, - {"01&N)&", 'F'}, - {"01&N)O", 'F'}, - {"01&N)U", 'F'}, - {"01&N;", 'F'}, - {"01&N;C", 'F'}, - {"01&N;E", 'F'}, - {"01&N;T", 'F'}, - {"01&NB(", 'F'}, - {"01&NB1", 'F'}, - {"01&NBF", 'F'}, - {"01&NBN", 'F'}, - {"01&NBS", 'F'}, - {"01&NBV", 'F'}, - {"01&NC", 'F'}, - {"01&NEN", 'F'}, - {"01&NF(", 'F'}, - {"01&NK(", 'F'}, - {"01&NK1", 'F'}, - {"01&NKF", 'F'}, - {"01&NKN", 'F'}, - {"01&NKS", 'F'}, - {"01&NKV", 'F'}, - {"01&NO(", 'F'}, - {"01&NOF", 'F'}, - {"01&NOS", 'F'}, - {"01&NOV", 'F'}, - {"01&NTN", 'F'}, - {"01&NU", 'F'}, - {"01&NU(", 'F'}, - {"01&NU;", 'F'}, - {"01&NUC", 'F'}, - {"01&NUE", 'F'}, - {"01&S", 'F'}, - {"01&S&(", 'F'}, - {"01&S&1", 'F'}, - {"01&S&F", 'F'}, - {"01&S&N", 'F'}, - {"01&S&S", 'F'}, - {"01&S&V", 'F'}, - {"01&S)&", 'F'}, - {"01&S)O", 'F'}, - {"01&S)U", 'F'}, - {"01&S1", 'F'}, - {"01&S1;", 'F'}, - {"01&S1C", 'F'}, - {"01&S1O", 'F'}, - {"01&S;", 'F'}, - {"01&S;C", 'F'}, - {"01&S;E", 'F'}, - {"01&S;T", 'F'}, - {"01&SB(", 'F'}, - {"01&SB1", 'F'}, - {"01&SBF", 'F'}, - {"01&SBN", 'F'}, - {"01&SBS", 'F'}, - {"01&SBV", 'F'}, - {"01&SC", 'F'}, - {"01&SEK", 'F'}, - {"01&SEN", 'F'}, - {"01&SF(", 'F'}, - {"01&SK(", 'F'}, - {"01&SK1", 'F'}, - {"01&SKF", 'F'}, - {"01&SKN", 'F'}, - {"01&SKS", 'F'}, - {"01&SKV", 'F'}, - {"01&SO(", 'F'}, - {"01&SO1", 'F'}, - {"01&SOF", 'F'}, - {"01&SON", 'F'}, - {"01&SOO", 'F'}, - {"01&SOS", 'F'}, - {"01&SOV", 'F'}, - {"01&STN", 'F'}, - {"01&SU", 'F'}, - {"01&SU(", 'F'}, - {"01&SU;", 'F'}, - {"01&SUC", 'F'}, - {"01&SUE", 'F'}, - {"01&SV", 'F'}, - {"01&SV;", 'F'}, - {"01&SVC", 'F'}, - {"01&SVO", 'F'}, - {"01&T((", 'F'}, - {"01&T(1", 'F'}, - {"01&T(F", 'F'}, - {"01&T(N", 'F'}, - {"01&T(S", 'F'}, - {"01&T(V", 'F'}, - {"01&V", 'F'}, - {"01&V&(", 'F'}, - {"01&V&1", 'F'}, - {"01&V&F", 'F'}, - {"01&V&N", 'F'}, - {"01&V&S", 'F'}, - {"01&V&V", 'F'}, - {"01&V)&", 'F'}, - {"01&V)O", 'F'}, - {"01&V)U", 'F'}, - {"01&V;", 'F'}, - {"01&V;C", 'F'}, - {"01&V;E", 'F'}, - {"01&V;T", 'F'}, - {"01&VB(", 'F'}, - {"01&VB1", 'F'}, - {"01&VBF", 'F'}, - {"01&VBN", 'F'}, - {"01&VBS", 'F'}, - {"01&VBV", 'F'}, - {"01&VC", 'F'}, - {"01&VEK", 'F'}, - {"01&VEN", 'F'}, - {"01&VF(", 'F'}, - {"01&VK(", 'F'}, - {"01&VK1", 'F'}, - {"01&VKF", 'F'}, - {"01&VKN", 'F'}, - {"01&VKS", 'F'}, - {"01&VKV", 'F'}, - {"01&VO(", 'F'}, - {"01&VOF", 'F'}, - {"01&VOO", 'F'}, - {"01&VOS", 'F'}, - {"01&VS", 'F'}, - {"01&VS;", 'F'}, - {"01&VSC", 'F'}, - {"01&VSO", 'F'}, - {"01&VTN", 'F'}, - {"01&VU", 'F'}, - {"01&VU(", 'F'}, - {"01&VU;", 'F'}, - {"01&VUC", 'F'}, - {"01&VUE", 'F'}, - {"01((((", 'F'}, - {"01(((E", 'F'}, - {"01(((U", 'F'}, - {"01((EK", 'F'}, - {"01((EN", 'F'}, - {"01((U(", 'F'}, - {"01(EKN", 'F'}, - {"01(ENK", 'F'}, - {"01(U((", 'F'}, - {"01(U(E", 'F'}, - {"01)&((", 'F'}, - {"01)&(1", 'F'}, - {"01)&(E", 'F'}, - {"01)&(F", 'F'}, - {"01)&(N", 'F'}, - {"01)&(S", 'F'}, - {"01)&(V", 'F'}, - {"01)&1", 'F'}, - {"01)&1&", 'F'}, - {"01)&1)", 'F'}, - {"01)&1;", 'F'}, - {"01)&1B", 'F'}, - {"01)&1C", 'F'}, - {"01)&1F", 'F'}, - {"01)&1O", 'F'}, - {"01)&1U", 'F'}, - {"01)&F(", 'F'}, - {"01)&N", 'F'}, - {"01)&N&", 'F'}, - {"01)&N)", 'F'}, - {"01)&N;", 'F'}, - {"01)&NB", 'F'}, - {"01)&NC", 'F'}, - {"01)&NF", 'F'}, - {"01)&NO", 'F'}, - {"01)&NU", 'F'}, - {"01)&S", 'F'}, - {"01)&S&", 'F'}, - {"01)&S)", 'F'}, - {"01)&S;", 'F'}, - {"01)&SB", 'F'}, - {"01)&SC", 'F'}, - {"01)&SF", 'F'}, - {"01)&SO", 'F'}, - {"01)&SU", 'F'}, - {"01)&V", 'F'}, - {"01)&V&", 'F'}, - {"01)&V)", 'F'}, - {"01)&V;", 'F'}, - {"01)&VB", 'F'}, - {"01)&VC", 'F'}, - {"01)&VF", 'F'}, - {"01)&VO", 'F'}, - {"01)&VU", 'F'}, - {"01),((", 'F'}, - {"01),(1", 'F'}, - {"01),(F", 'F'}, - {"01),(N", 'F'}, - {"01),(S", 'F'}, - {"01),(V", 'F'}, - {"01);E(", 'F'}, - {"01);E1", 'F'}, - {"01);EF", 'F'}, - {"01);EK", 'F'}, - {"01);EN", 'F'}, - {"01);EO", 'F'}, - {"01);ES", 'F'}, - {"01);EV", 'F'}, - {"01);T(", 'F'}, - {"01);T1", 'F'}, - {"01);TF", 'F'}, - {"01);TK", 'F'}, - {"01);TN", 'F'}, - {"01);TO", 'F'}, - {"01);TS", 'F'}, - {"01);TV", 'F'}, - {"01)B((", 'F'}, - {"01)B(1", 'F'}, - {"01)B(F", 'F'}, - {"01)B(N", 'F'}, - {"01)B(S", 'F'}, - {"01)B(V", 'F'}, - {"01)B1", 'F'}, - {"01)B1&", 'F'}, - {"01)B1;", 'F'}, - {"01)B1C", 'F'}, - {"01)B1K", 'F'}, - {"01)B1N", 'F'}, - {"01)B1O", 'F'}, - {"01)B1U", 'F'}, - {"01)BF(", 'F'}, - {"01)BN", 'F'}, - {"01)BN&", 'F'}, - {"01)BN;", 'F'}, - {"01)BNC", 'F'}, - {"01)BNK", 'F'}, - {"01)BNO", 'F'}, - {"01)BNU", 'F'}, - {"01)BS", 'F'}, - {"01)BS&", 'F'}, - {"01)BS;", 'F'}, - {"01)BSC", 'F'}, - {"01)BSK", 'F'}, - {"01)BSO", 'F'}, - {"01)BSU", 'F'}, - {"01)BV", 'F'}, - {"01)BV&", 'F'}, - {"01)BV;", 'F'}, - {"01)BVC", 'F'}, - {"01)BVK", 'F'}, - {"01)BVO", 'F'}, - {"01)BVU", 'F'}, - {"01)E((", 'F'}, - {"01)E(1", 'F'}, - {"01)E(F", 'F'}, - {"01)E(N", 'F'}, - {"01)E(S", 'F'}, - {"01)E(V", 'F'}, - {"01)E1C", 'F'}, - {"01)E1O", 'F'}, - {"01)EF(", 'F'}, - {"01)EK(", 'F'}, - {"01)EK1", 'F'}, - {"01)EKF", 'F'}, - {"01)EKN", 'F'}, - {"01)EKS", 'F'}, - {"01)EKV", 'F'}, - {"01)ENC", 'F'}, - {"01)ENO", 'F'}, - {"01)ESC", 'F'}, - {"01)ESO", 'F'}, - {"01)EVC", 'F'}, - {"01)EVO", 'F'}, - {"01)K((", 'F'}, - {"01)K(1", 'F'}, - {"01)K(F", 'F'}, - {"01)K(N", 'F'}, - {"01)K(S", 'F'}, - {"01)K(V", 'F'}, - {"01)K1&", 'F'}, - {"01)K1;", 'F'}, - {"01)K1B", 'F'}, - {"01)K1E", 'F'}, - {"01)K1O", 'F'}, - {"01)K1U", 'F'}, - {"01)KB(", 'F'}, - {"01)KB1", 'F'}, - {"01)KBF", 'F'}, - {"01)KBN", 'F'}, - {"01)KBS", 'F'}, - {"01)KBV", 'F'}, - {"01)KF(", 'F'}, - {"01)KN&", 'F'}, - {"01)KN;", 'F'}, - {"01)KNB", 'F'}, - {"01)KNE", 'F'}, - {"01)KNK", 'F'}, - {"01)KNU", 'F'}, - {"01)KS&", 'F'}, - {"01)KS;", 'F'}, - {"01)KSB", 'F'}, - {"01)KSE", 'F'}, - {"01)KSO", 'F'}, - {"01)KSU", 'F'}, - {"01)KUE", 'F'}, - {"01)KV&", 'F'}, - {"01)KV;", 'F'}, - {"01)KVB", 'F'}, - {"01)KVE", 'F'}, - {"01)KVO", 'F'}, - {"01)KVU", 'F'}, - {"01)O((", 'F'}, - {"01)O(1", 'F'}, - {"01)O(E", 'F'}, - {"01)O(F", 'F'}, - {"01)O(N", 'F'}, - {"01)O(S", 'F'}, - {"01)O(V", 'F'}, - {"01)O1", 'F'}, - {"01)O1&", 'F'}, - {"01)O1)", 'F'}, - {"01)O1;", 'F'}, - {"01)O1B", 'F'}, - {"01)O1C", 'F'}, - {"01)O1K", 'F'}, - {"01)O1O", 'F'}, - {"01)O1U", 'F'}, - {"01)OF(", 'F'}, - {"01)ON&", 'F'}, - {"01)ON)", 'F'}, - {"01)ON;", 'F'}, - {"01)ONB", 'F'}, - {"01)ONC", 'F'}, - {"01)ONK", 'F'}, - {"01)ONO", 'F'}, - {"01)ONU", 'F'}, - {"01)OS", 'F'}, - {"01)OS&", 'F'}, - {"01)OS)", 'F'}, - {"01)OS;", 'F'}, - {"01)OSB", 'F'}, - {"01)OSC", 'F'}, - {"01)OSK", 'F'}, - {"01)OSO", 'F'}, - {"01)OSU", 'F'}, - {"01)OV", 'F'}, - {"01)OV&", 'F'}, - {"01)OV)", 'F'}, - {"01)OV;", 'F'}, - {"01)OVB", 'F'}, - {"01)OVC", 'F'}, - {"01)OVK", 'F'}, - {"01)OVO", 'F'}, - {"01)OVU", 'F'}, - {"01)U((", 'F'}, - {"01)U(E", 'F'}, - {"01)UE(", 'F'}, - {"01)UE1", 'F'}, - {"01)UEF", 'F'}, - {"01)UEK", 'F'}, - {"01)UEN", 'F'}, - {"01)UES", 'F'}, - {"01)UEV", 'F'}, - {"01,(((", 'F'}, - {"01,((1", 'F'}, - {"01,((E", 'F'}, - {"01,((F", 'F'}, - {"01,((N", 'F'}, - {"01,((S", 'F'}, - {"01,((V", 'F'}, - {"01,(1)", 'F'}, - {"01,(1O", 'F'}, - {"01,(E(", 'F'}, - {"01,(E1", 'F'}, - {"01,(EF", 'F'}, - {"01,(EK", 'F'}, - {"01,(EN", 'F'}, - {"01,(ES", 'F'}, - {"01,(EV", 'F'}, - {"01,(F(", 'F'}, - {"01,(N)", 'F'}, - {"01,(NO", 'F'}, - {"01,(S)", 'F'}, - {"01,(SO", 'F'}, - {"01,(V)", 'F'}, - {"01,(VO", 'F'}, - {"01,F((", 'F'}, - {"01,F()", 'F'}, - {"01,F(1", 'F'}, - {"01,F(F", 'F'}, - {"01,F(N", 'F'}, - {"01,F(S", 'F'}, - {"01,F(T", 'F'}, - {"01,F(V", 'F'}, - {"01,V),", 'F'}, - {"01,V)O", 'F'}, - {"01,VB(", 'F'}, - {"01,VB1", 'F'}, - {"01,VBF", 'F'}, - {"01,VBN", 'F'}, - {"01,VBS", 'F'}, - {"01,VBV", 'F'}, - {"01,VO(", 'F'}, - {"01,VOF", 'F'}, - {"01,VOS", 'F'}, - {"01,VUE", 'F'}, - {"01;E((", 'F'}, - {"01;E(1", 'F'}, - {"01;E(E", 'F'}, - {"01;E(F", 'F'}, - {"01;E(N", 'F'}, - {"01;E(S", 'F'}, - {"01;E(V", 'F'}, - {"01;E1,", 'F'}, - {"01;E1;", 'F'}, - {"01;E1C", 'F'}, - {"01;E1K", 'F'}, - {"01;E1O", 'F'}, - {"01;E1T", 'F'}, - {"01;EF(", 'F'}, - {"01;EK(", 'F'}, - {"01;EK1", 'F'}, - {"01;EKF", 'F'}, - {"01;EKN", 'F'}, - {"01;EKO", 'F'}, - {"01;EKS", 'F'}, - {"01;EKV", 'F'}, - {"01;EN,", 'F'}, - {"01;EN;", 'F'}, - {"01;ENC", 'F'}, - {"01;ENK", 'F'}, - {"01;ENO", 'F'}, - {"01;ENT", 'F'}, - {"01;EO(", 'F'}, - {"01;ES,", 'F'}, - {"01;ES;", 'F'}, - {"01;ESC", 'F'}, - {"01;ESK", 'F'}, - {"01;ESO", 'F'}, - {"01;EST", 'F'}, - {"01;EV,", 'F'}, - {"01;EV;", 'F'}, - {"01;EVC", 'F'}, - {"01;EVK", 'F'}, - {"01;EVO", 'F'}, - {"01;EVT", 'F'}, - {"01;N:T", 'F'}, - {"01;T((", 'F'}, - {"01;T(1", 'F'}, - {"01;T(E", 'F'}, - {"01;T(F", 'F'}, - {"01;T(N", 'F'}, - {"01;T(S", 'F'}, - {"01;T(V", 'F'}, - {"01;T1,", 'F'}, - {"01;T1;", 'F'}, - {"01;T1C", 'F'}, - {"01;T1F", 'F'}, - {"01;T1K", 'F'}, - {"01;T1O", 'F'}, - {"01;T1T", 'F'}, - {"01;T;", 'F'}, - {"01;T;C", 'F'}, - {"01;TF(", 'F'}, - {"01;TK(", 'F'}, - {"01;TK1", 'F'}, - {"01;TKF", 'F'}, - {"01;TKK", 'F'}, - {"01;TKN", 'F'}, - {"01;TKO", 'F'}, - {"01;TKS", 'F'}, - {"01;TKV", 'F'}, - {"01;TN(", 'F'}, - {"01;TN,", 'F'}, - {"01;TN1", 'F'}, - {"01;TN;", 'F'}, - {"01;TNC", 'F'}, - {"01;TNF", 'F'}, - {"01;TNK", 'F'}, - {"01;TNN", 'F'}, - {"01;TNO", 'F'}, - {"01;TNS", 'F'}, - {"01;TNT", 'F'}, - {"01;TNV", 'F'}, - {"01;TO(", 'F'}, - {"01;TS,", 'F'}, - {"01;TS;", 'F'}, - {"01;TSC", 'F'}, - {"01;TSF", 'F'}, - {"01;TSK", 'F'}, - {"01;TSO", 'F'}, - {"01;TST", 'F'}, - {"01;TT(", 'F'}, - {"01;TT1", 'F'}, - {"01;TTF", 'F'}, - {"01;TTN", 'F'}, - {"01;TTS", 'F'}, - {"01;TTV", 'F'}, - {"01;TV,", 'F'}, - {"01;TV;", 'F'}, - {"01;TVC", 'F'}, - {"01;TVF", 'F'}, - {"01;TVK", 'F'}, - {"01;TVO", 'F'}, - {"01;TVT", 'F'}, - {"01A(((", 'F'}, - {"01A((1", 'F'}, - {"01A((F", 'F'}, - {"01A((N", 'F'}, - {"01A((S", 'F'}, - {"01A((V", 'F'}, - {"01A(F(", 'F'}, - {"01A(N)", 'F'}, - {"01A(NO", 'F'}, - {"01A(S)", 'F'}, - {"01A(SO", 'F'}, - {"01A(V)", 'F'}, - {"01A(VO", 'F'}, - {"01AF((", 'F'}, - {"01AF()", 'F'}, - {"01AF(1", 'F'}, - {"01AF(F", 'F'}, - {"01AF(N", 'F'}, - {"01AF(S", 'F'}, - {"01AF(V", 'F'}, - {"01ASO(", 'F'}, - {"01ASO1", 'F'}, - {"01ASOF", 'F'}, - {"01ASON", 'F'}, - {"01ASOS", 'F'}, - {"01ASOV", 'F'}, - {"01ASUE", 'F'}, - {"01ATO(", 'F'}, - {"01ATO1", 'F'}, - {"01ATOF", 'F'}, - {"01ATON", 'F'}, - {"01ATOS", 'F'}, - {"01ATOV", 'F'}, - {"01ATUE", 'F'}, - {"01AVO(", 'F'}, - {"01AVOF", 'F'}, - {"01AVOS", 'F'}, - {"01AVUE", 'F'}, - {"01B(((", 'F'}, - {"01B((1", 'F'}, - {"01B((F", 'F'}, - {"01B((N", 'F'}, - {"01B((S", 'F'}, - {"01B((V", 'F'}, - {"01B(1)", 'F'}, - {"01B(1O", 'F'}, - {"01B(F(", 'F'}, - {"01B(N)", 'F'}, - {"01B(NO", 'F'}, - {"01B(S)", 'F'}, - {"01B(SO", 'F'}, - {"01B(V)", 'F'}, - {"01B(VO", 'F'}, - {"01B1", 'F'}, - {"01B1&(", 'F'}, - {"01B1&1", 'F'}, - {"01B1&F", 'F'}, - {"01B1&N", 'F'}, - {"01B1&S", 'F'}, - {"01B1&V", 'F'}, - {"01B1,(", 'F'}, - {"01B1,F", 'F'}, - {"01B1,V", 'F'}, - {"01B1;", 'F'}, - {"01B1;C", 'F'}, - {"01B1B(", 'F'}, - {"01B1B1", 'F'}, - {"01B1BF", 'F'}, - {"01B1BN", 'F'}, - {"01B1BS", 'F'}, - {"01B1BV", 'F'}, - {"01B1C", 'F'}, - {"01B1K(", 'F'}, - {"01B1K1", 'F'}, - {"01B1KF", 'F'}, - {"01B1KN", 'F'}, - {"01B1KS", 'F'}, - {"01B1KV", 'F'}, - {"01B1O(", 'F'}, - {"01B1OF", 'F'}, - {"01B1OS", 'F'}, - {"01B1OV", 'F'}, - {"01B1U(", 'F'}, - {"01B1UE", 'F'}, - {"01BE((", 'F'}, - {"01BE(1", 'F'}, - {"01BE(F", 'F'}, - {"01BE(N", 'F'}, - {"01BE(S", 'F'}, - {"01BE(V", 'F'}, - {"01BEK(", 'F'}, - {"01BF((", 'F'}, - {"01BF()", 'F'}, - {"01BF(1", 'F'}, - {"01BF(F", 'F'}, - {"01BF(N", 'F'}, - {"01BF(S", 'F'}, - {"01BF(V", 'F'}, - {"01BN", 'F'}, - {"01BN&(", 'F'}, - {"01BN&1", 'F'}, - {"01BN&F", 'F'}, - {"01BN&N", 'F'}, - {"01BN&S", 'F'}, - {"01BN&V", 'F'}, - {"01BN,(", 'F'}, - {"01BN,F", 'F'}, - {"01BN,V", 'F'}, - {"01BN;", 'F'}, - {"01BN;C", 'F'}, - {"01BNB(", 'F'}, - {"01BNB1", 'F'}, - {"01BNBF", 'F'}, - {"01BNBN", 'F'}, - {"01BNBS", 'F'}, - {"01BNBV", 'F'}, - {"01BNC", 'F'}, - {"01BNK(", 'F'}, - {"01BNK1", 'F'}, - {"01BNKF", 'F'}, - {"01BNKN", 'F'}, - {"01BNKS", 'F'}, - {"01BNKV", 'F'}, - {"01BNO(", 'F'}, - {"01BNOF", 'F'}, - {"01BNOS", 'F'}, - {"01BNOV", 'F'}, - {"01BNU(", 'F'}, - {"01BNUE", 'F'}, - {"01BS", 'F'}, - {"01BS&(", 'F'}, - {"01BS&1", 'F'}, - {"01BS&F", 'F'}, - {"01BS&N", 'F'}, - {"01BS&S", 'F'}, - {"01BS&V", 'F'}, - {"01BS,(", 'F'}, - {"01BS,F", 'F'}, - {"01BS,V", 'F'}, - {"01BS;", 'F'}, - {"01BS;C", 'F'}, - {"01BSB(", 'F'}, - {"01BSB1", 'F'}, - {"01BSBF", 'F'}, - {"01BSBN", 'F'}, - {"01BSBS", 'F'}, - {"01BSBV", 'F'}, - {"01BSC", 'F'}, - {"01BSK(", 'F'}, - {"01BSK1", 'F'}, - {"01BSKF", 'F'}, - {"01BSKN", 'F'}, - {"01BSKS", 'F'}, - {"01BSKV", 'F'}, - {"01BSO(", 'F'}, - {"01BSO1", 'F'}, - {"01BSOF", 'F'}, - {"01BSON", 'F'}, - {"01BSOS", 'F'}, - {"01BSOV", 'F'}, - {"01BSU(", 'F'}, - {"01BSUE", 'F'}, - {"01BV", 'F'}, - {"01BV&(", 'F'}, - {"01BV&1", 'F'}, - {"01BV&F", 'F'}, - {"01BV&N", 'F'}, - {"01BV&S", 'F'}, - {"01BV&V", 'F'}, - {"01BV,(", 'F'}, - {"01BV,1", 'F'}, - {"01BV,F", 'F'}, - {"01BV,N", 'F'}, - {"01BV,S", 'F'}, - {"01BV,V", 'F'}, - {"01BV;", 'F'}, - {"01BV;C", 'F'}, - {"01BVB(", 'F'}, - {"01BVB1", 'F'}, - {"01BVBF", 'F'}, - {"01BVBN", 'F'}, - {"01BVBS", 'F'}, - {"01BVBV", 'F'}, - {"01BVC", 'F'}, - {"01BVK(", 'F'}, - {"01BVK1", 'F'}, - {"01BVKF", 'F'}, - {"01BVKN", 'F'}, - {"01BVKS", 'F'}, - {"01BVKV", 'F'}, - {"01BVO(", 'F'}, - {"01BVOF", 'F'}, - {"01BVOS", 'F'}, - {"01BVU(", 'F'}, - {"01BVUE", 'F'}, - {"01C", 'F'}, - {"01E(((", 'F'}, - {"01E((1", 'F'}, - {"01E((F", 'F'}, - {"01E((N", 'F'}, - {"01E((S", 'F'}, - {"01E((V", 'F'}, - {"01E(1)", 'F'}, - {"01E(1O", 'F'}, - {"01E(F(", 'F'}, - {"01E(N)", 'F'}, - {"01E(NO", 'F'}, - {"01E(S)", 'F'}, - {"01E(SO", 'F'}, - {"01E(V)", 'F'}, - {"01E(VO", 'F'}, - {"01E1C", 'F'}, - {"01E1O(", 'F'}, - {"01E1OF", 'F'}, - {"01E1OS", 'F'}, - {"01E1OV", 'F'}, - {"01E1UE", 'F'}, - {"01EF((", 'F'}, - {"01EF()", 'F'}, - {"01EF(1", 'F'}, - {"01EF(F", 'F'}, - {"01EF(N", 'F'}, - {"01EF(S", 'F'}, - {"01EF(V", 'F'}, - {"01EK((", 'F'}, - {"01EK(1", 'F'}, - {"01EK(E", 'F'}, - {"01EK(F", 'F'}, - {"01EK(N", 'F'}, - {"01EK(S", 'F'}, - {"01EK(V", 'F'}, - {"01EK1C", 'F'}, - {"01EK1O", 'F'}, - {"01EK1U", 'F'}, - {"01EKF(", 'F'}, - {"01EKNC", 'F'}, - {"01EKNE", 'F'}, - {"01EKNU", 'F'}, - {"01EKOK", 'F'}, - {"01EKSC", 'F'}, - {"01EKSO", 'F'}, - {"01EKSU", 'F'}, - {"01EKU(", 'F'}, - {"01EKU1", 'F'}, - {"01EKUE", 'F'}, - {"01EKUF", 'F'}, - {"01EKUN", 'F'}, - {"01EKUS", 'F'}, - {"01EKUV", 'F'}, - {"01EKVC", 'F'}, - {"01EKVO", 'F'}, - {"01EKVU", 'F'}, - {"01ENC", 'F'}, - {"01ENEN", 'F'}, - {"01ENO(", 'F'}, - {"01ENOF", 'F'}, - {"01ENOS", 'F'}, - {"01ENOV", 'F'}, - {"01ENUE", 'F'}, - {"01EOKN", 'F'}, - {"01ESC", 'F'}, - {"01ESO(", 'F'}, - {"01ESO1", 'F'}, - {"01ESOF", 'F'}, - {"01ESON", 'F'}, - {"01ESOS", 'F'}, - {"01ESOV", 'F'}, - {"01ESUE", 'F'}, - {"01EU((", 'F'}, - {"01EU(1", 'F'}, - {"01EU(F", 'F'}, - {"01EU(N", 'F'}, - {"01EU(S", 'F'}, - {"01EU(V", 'F'}, - {"01EU1,", 'F'}, - {"01EU1C", 'F'}, - {"01EU1O", 'F'}, - {"01EUEF", 'F'}, - {"01EUEK", 'F'}, - {"01EUF(", 'F'}, - {"01EUN,", 'F'}, - {"01EUNC", 'F'}, - {"01EUNO", 'F'}, - {"01EUS,", 'F'}, - {"01EUSC", 'F'}, - {"01EUSO", 'F'}, - {"01EUV,", 'F'}, - {"01EUVC", 'F'}, - {"01EUVO", 'F'}, - {"01EVC", 'F'}, - {"01EVO(", 'F'}, - {"01EVOF", 'F'}, - {"01EVOS", 'F'}, - {"01EVUE", 'F'}, - {"01F(((", 'F'}, - {"01F(()", 'F'}, - {"01F((1", 'F'}, - {"01F((E", 'F'}, - {"01F((F", 'F'}, - {"01F((N", 'F'}, - {"01F((S", 'F'}, - {"01F((V", 'F'}, - {"01F()1", 'F'}, - {"01F()F", 'F'}, - {"01F()K", 'F'}, - {"01F()N", 'F'}, - {"01F()O", 'F'}, - {"01F()S", 'F'}, - {"01F()U", 'F'}, - {"01F()V", 'F'}, - {"01F(1)", 'F'}, - {"01F(1N", 'F'}, - {"01F(1O", 'F'}, - {"01F(E(", 'F'}, - {"01F(E1", 'F'}, - {"01F(EF", 'F'}, - {"01F(EK", 'F'}, - {"01F(EN", 'F'}, - {"01F(ES", 'F'}, - {"01F(EV", 'F'}, - {"01F(F(", 'F'}, - {"01F(N)", 'F'}, - {"01F(N,", 'F'}, - {"01F(NO", 'F'}, - {"01F(S)", 'F'}, - {"01F(SO", 'F'}, - {"01F(V)", 'F'}, - {"01F(VO", 'F'}, - {"01K(((", 'F'}, - {"01K((1", 'F'}, - {"01K((F", 'F'}, - {"01K((N", 'F'}, - {"01K((S", 'F'}, - {"01K((V", 'F'}, - {"01K(1O", 'F'}, - {"01K(F(", 'F'}, - {"01K(N)", 'F'}, - {"01K(NO", 'F'}, - {"01K(S)", 'F'}, - {"01K(SO", 'F'}, - {"01K(V)", 'F'}, - {"01K(VO", 'F'}, - {"01K)&(", 'F'}, - {"01K)&1", 'F'}, - {"01K)&F", 'F'}, - {"01K)&N", 'F'}, - {"01K)&S", 'F'}, - {"01K)&V", 'F'}, - {"01K);E", 'F'}, - {"01K);T", 'F'}, - {"01K)B(", 'F'}, - {"01K)B1", 'F'}, - {"01K)BF", 'F'}, - {"01K)BN", 'F'}, - {"01K)BS", 'F'}, - {"01K)BV", 'F'}, - {"01K)E(", 'F'}, - {"01K)E1", 'F'}, - {"01K)EF", 'F'}, - {"01K)EK", 'F'}, - {"01K)EN", 'F'}, - {"01K)ES", 'F'}, - {"01K)EV", 'F'}, - {"01K)OF", 'F'}, - {"01K)UE", 'F'}, - {"01K1", 'F'}, - {"01K1&(", 'F'}, - {"01K1&1", 'F'}, - {"01K1&F", 'F'}, - {"01K1&N", 'F'}, - {"01K1&S", 'F'}, - {"01K1&V", 'F'}, - {"01K1;", 'F'}, - {"01K1;C", 'F'}, - {"01K1;E", 'F'}, - {"01K1;T", 'F'}, - {"01K1B(", 'F'}, - {"01K1B1", 'F'}, - {"01K1BF", 'F'}, - {"01K1BN", 'F'}, - {"01K1BS", 'F'}, - {"01K1BV", 'F'}, - {"01K1C", 'F'}, - {"01K1E(", 'F'}, - {"01K1E1", 'F'}, - {"01K1EF", 'F'}, - {"01K1EK", 'F'}, - {"01K1EN", 'F'}, - {"01K1ES", 'F'}, - {"01K1EV", 'F'}, - {"01K1O(", 'F'}, - {"01K1OF", 'F'}, - {"01K1OS", 'F'}, - {"01K1OV", 'F'}, - {"01K1U(", 'F'}, - {"01K1UE", 'F'}, - {"01KF((", 'F'}, - {"01KF()", 'F'}, - {"01KF(1", 'F'}, - {"01KF(F", 'F'}, - {"01KF(N", 'F'}, - {"01KF(S", 'F'}, - {"01KF(V", 'F'}, - {"01KN", 'F'}, - {"01KN&(", 'F'}, - {"01KN&1", 'F'}, - {"01KN&F", 'F'}, - {"01KN&N", 'F'}, - {"01KN&S", 'F'}, - {"01KN&V", 'F'}, - {"01KN;", 'F'}, - {"01KN;C", 'F'}, - {"01KN;E", 'F'}, - {"01KN;T", 'F'}, - {"01KNB(", 'F'}, - {"01KNB1", 'F'}, - {"01KNBF", 'F'}, - {"01KNBN", 'F'}, - {"01KNBS", 'F'}, - {"01KNBV", 'F'}, - {"01KNC", 'F'}, - {"01KNE(", 'F'}, - {"01KNE1", 'F'}, - {"01KNEF", 'F'}, - {"01KNEN", 'F'}, - {"01KNES", 'F'}, - {"01KNEV", 'F'}, - {"01KNU(", 'F'}, - {"01KNUE", 'F'}, - {"01KS", 'F'}, - {"01KS&(", 'F'}, - {"01KS&1", 'F'}, - {"01KS&F", 'F'}, - {"01KS&N", 'F'}, - {"01KS&S", 'F'}, - {"01KS&V", 'F'}, - {"01KS;", 'F'}, - {"01KS;C", 'F'}, - {"01KS;E", 'F'}, - {"01KS;T", 'F'}, - {"01KSB(", 'F'}, - {"01KSB1", 'F'}, - {"01KSBF", 'F'}, - {"01KSBN", 'F'}, - {"01KSBS", 'F'}, - {"01KSBV", 'F'}, - {"01KSC", 'F'}, - {"01KSE(", 'F'}, - {"01KSE1", 'F'}, - {"01KSEF", 'F'}, - {"01KSEK", 'F'}, - {"01KSEN", 'F'}, - {"01KSES", 'F'}, - {"01KSEV", 'F'}, - {"01KSO(", 'F'}, - {"01KSO1", 'F'}, - {"01KSOF", 'F'}, - {"01KSON", 'F'}, - {"01KSOS", 'F'}, - {"01KSOV", 'F'}, - {"01KSU(", 'F'}, - {"01KSUE", 'F'}, - {"01KUE(", 'F'}, - {"01KUE1", 'F'}, - {"01KUEF", 'F'}, - {"01KUEK", 'F'}, - {"01KUEN", 'F'}, - {"01KUES", 'F'}, - {"01KUEV", 'F'}, - {"01KV", 'F'}, - {"01KV&(", 'F'}, - {"01KV&1", 'F'}, - {"01KV&F", 'F'}, - {"01KV&N", 'F'}, - {"01KV&S", 'F'}, - {"01KV&V", 'F'}, - {"01KV;", 'F'}, - {"01KV;C", 'F'}, - {"01KV;E", 'F'}, - {"01KV;T", 'F'}, - {"01KVB(", 'F'}, - {"01KVB1", 'F'}, - {"01KVBF", 'F'}, - {"01KVBN", 'F'}, - {"01KVBS", 'F'}, - {"01KVBV", 'F'}, - {"01KVC", 'F'}, - {"01KVE(", 'F'}, - {"01KVE1", 'F'}, - {"01KVEF", 'F'}, - {"01KVEK", 'F'}, - {"01KVEN", 'F'}, - {"01KVES", 'F'}, - {"01KVEV", 'F'}, - {"01KVO(", 'F'}, - {"01KVOF", 'F'}, - {"01KVOS", 'F'}, - {"01KVU(", 'F'}, - {"01KVUE", 'F'}, - {"01N&F(", 'F'}, - {"01N(((", 'F'}, - {"01N((1", 'F'}, - {"01N((F", 'F'}, - {"01N((N", 'F'}, - {"01N((S", 'F'}, - {"01N((V", 'F'}, - {"01N(1)", 'F'}, - {"01N(1O", 'F'}, - {"01N(F(", 'F'}, - {"01N(S)", 'F'}, - {"01N(SO", 'F'}, - {"01N(V)", 'F'}, - {"01N(VO", 'F'}, - {"01N)UE", 'F'}, - {"01N,F(", 'F'}, - {"01NE((", 'F'}, - {"01NE(1", 'F'}, - {"01NE(F", 'F'}, - {"01NE(N", 'F'}, - {"01NE(S", 'F'}, - {"01NE(V", 'F'}, - {"01NE1C", 'F'}, - {"01NE1O", 'F'}, - {"01NEF(", 'F'}, - {"01NENC", 'F'}, - {"01NENO", 'F'}, - {"01NESC", 'F'}, - {"01NESO", 'F'}, - {"01NEVC", 'F'}, - {"01NEVO", 'F'}, - {"01NF((", 'F'}, - {"01NF()", 'F'}, - {"01NF(1", 'F'}, - {"01NF(F", 'F'}, - {"01NF(N", 'F'}, - {"01NF(S", 'F'}, - {"01NF(V", 'F'}, - {"01NU((", 'F'}, - {"01NU(E", 'F'}, - {"01NUE", 'F'}, - {"01NUE(", 'F'}, - {"01NUE1", 'F'}, - {"01NUE;", 'F'}, - {"01NUEC", 'F'}, - {"01NUEF", 'F'}, - {"01NUEK", 'F'}, - {"01NUEN", 'F'}, - {"01NUES", 'F'}, - {"01NUEV", 'F'}, - {"01O(((", 'F'}, - {"01O((1", 'F'}, - {"01O((E", 'F'}, - {"01O((F", 'F'}, - {"01O((N", 'F'}, - {"01O((O", 'F'}, - {"01O((S", 'F'}, - {"01O((T", 'F'}, - {"01O((V", 'F'}, - {"01O(1&", 'F'}, - {"01O(1)", 'F'}, - {"01O(1,", 'F'}, - {"01O(1O", 'F'}, - {"01O(E(", 'F'}, - {"01O(E1", 'F'}, - {"01O(EE", 'F'}, - {"01O(EF", 'F'}, - {"01O(EK", 'F'}, - {"01O(EN", 'F'}, - {"01O(ES", 'F'}, - {"01O(EV", 'F'}, - {"01O(F(", 'F'}, - {"01O(N&", 'F'}, - {"01O(N)", 'F'}, - {"01O(N,", 'F'}, - {"01O(NO", 'F'}, - {"01O(O(", 'F'}, - {"01O(O1", 'F'}, - {"01O(OF", 'F'}, - {"01O(ON", 'F'}, - {"01O(OS", 'F'}, - {"01O(OV", 'F'}, - {"01O(S&", 'F'}, - {"01O(S)", 'F'}, - {"01O(S,", 'F'}, - {"01O(SO", 'F'}, - {"01O(T(", 'F'}, - {"01O(V&", 'F'}, - {"01O(V)", 'F'}, - {"01O(V,", 'F'}, - {"01O(VO", 'F'}, - {"01OF((", 'F'}, - {"01OF()", 'F'}, - {"01OF(1", 'F'}, - {"01OF(E", 'F'}, - {"01OF(F", 'F'}, - {"01OF(N", 'F'}, - {"01OF(S", 'F'}, - {"01OF(V", 'F'}, - {"01OK&(", 'F'}, - {"01OK&1", 'F'}, - {"01OK&F", 'F'}, - {"01OK&N", 'F'}, - {"01OK&S", 'F'}, - {"01OK&V", 'F'}, - {"01OK((", 'F'}, - {"01OK(1", 'F'}, - {"01OK(F", 'F'}, - {"01OK(N", 'F'}, - {"01OK(S", 'F'}, - {"01OK(V", 'F'}, - {"01OK1C", 'F'}, - {"01OK1O", 'F'}, - {"01OKF(", 'F'}, - {"01OKNC", 'F'}, - {"01OKO(", 'F'}, - {"01OKO1", 'F'}, - {"01OKOF", 'F'}, - {"01OKON", 'F'}, - {"01OKOS", 'F'}, - {"01OKOV", 'F'}, - {"01OKSC", 'F'}, - {"01OKSO", 'F'}, - {"01OKVC", 'F'}, - {"01OKVO", 'F'}, - {"01ONSU", 'F'}, - {"01OS&(", 'F'}, - {"01OS&1", 'F'}, - {"01OS&E", 'F'}, - {"01OS&F", 'F'}, - {"01OS&K", 'F'}, - {"01OS&N", 'F'}, - {"01OS&S", 'F'}, - {"01OS&T", 'F'}, - {"01OS&U", 'F'}, - {"01OS&V", 'F'}, - {"01OS((", 'F'}, - {"01OS(E", 'F'}, - {"01OS(U", 'F'}, - {"01OS)&", 'F'}, - {"01OS),", 'F'}, - {"01OS);", 'F'}, - {"01OS)B", 'F'}, - {"01OS)E", 'F'}, - {"01OS)K", 'F'}, - {"01OS)O", 'F'}, - {"01OS)U", 'F'}, - {"01OS,(", 'F'}, - {"01OS,F", 'F'}, - {"01OS,V", 'F'}, - {"01OS1(", 'F'}, - {"01OS1F", 'F'}, - {"01OS1N", 'F'}, - {"01OS1S", 'F'}, - {"01OS1U", 'F'}, - {"01OS1V", 'F'}, - {"01OS;", 'F'}, - {"01OS;C", 'F'}, - {"01OS;E", 'F'}, - {"01OS;N", 'F'}, - {"01OS;T", 'F'}, - {"01OSA(", 'F'}, - {"01OSAF", 'F'}, - {"01OSAS", 'F'}, - {"01OSAT", 'F'}, - {"01OSAV", 'F'}, - {"01OSB(", 'F'}, - {"01OSB1", 'F'}, - {"01OSBE", 'F'}, - {"01OSBF", 'F'}, - {"01OSBN", 'F'}, - {"01OSBS", 'F'}, - {"01OSBV", 'F'}, - {"01OSC", 'F'}, - {"01OSE(", 'F'}, - {"01OSE1", 'F'}, - {"01OSEF", 'F'}, - {"01OSEK", 'F'}, - {"01OSEN", 'F'}, - {"01OSEO", 'F'}, - {"01OSES", 'F'}, - {"01OSEU", 'F'}, - {"01OSEV", 'F'}, - {"01OSF(", 'F'}, - {"01OSK(", 'F'}, - {"01OSK)", 'F'}, - {"01OSK1", 'F'}, - {"01OSKB", 'F'}, - {"01OSKF", 'F'}, - {"01OSKN", 'F'}, - {"01OSKS", 'F'}, - {"01OSKU", 'F'}, - {"01OSKV", 'F'}, - {"01OSO(", 'F'}, - {"01OSO1", 'F'}, - {"01OSOF", 'F'}, - {"01OSOK", 'F'}, - {"01OSON", 'F'}, - {"01OSOS", 'F'}, - {"01OSOT", 'F'}, - {"01OSOU", 'F'}, - {"01OSOV", 'F'}, - {"01OSU", 'F'}, - {"01OSU(", 'F'}, - {"01OSU1", 'F'}, - {"01OSU;", 'F'}, - {"01OSUC", 'F'}, - {"01OSUE", 'F'}, - {"01OSUF", 'F'}, - {"01OSUK", 'F'}, - {"01OSUN", 'F'}, - {"01OSUO", 'F'}, - {"01OSUS", 'F'}, - {"01OSUT", 'F'}, - {"01OSUV", 'F'}, - {"01OSV(", 'F'}, - {"01OSVF", 'F'}, - {"01OSVO", 'F'}, - {"01OSVS", 'F'}, - {"01OSVU", 'F'}, - {"01OT((", 'F'}, - {"01OT(1", 'F'}, - {"01OT(F", 'F'}, - {"01OT(N", 'F'}, - {"01OT(S", 'F'}, - {"01OT(T", 'F'}, - {"01OT(V", 'F'}, - {"01OU((", 'F'}, - {"01OU(E", 'F'}, - {"01OUEK", 'F'}, - {"01OUEN", 'F'}, - {"01OV", 'F'}, - {"01OV&(", 'F'}, - {"01OV&1", 'F'}, - {"01OV&E", 'F'}, - {"01OV&F", 'F'}, - {"01OV&K", 'F'}, - {"01OV&N", 'F'}, - {"01OV&S", 'F'}, - {"01OV&T", 'F'}, - {"01OV&U", 'F'}, - {"01OV&V", 'F'}, - {"01OV((", 'F'}, - {"01OV(E", 'F'}, - {"01OV(U", 'F'}, - {"01OV)&", 'F'}, - {"01OV),", 'F'}, - {"01OV);", 'F'}, - {"01OV)B", 'F'}, - {"01OV)E", 'F'}, - {"01OV)K", 'F'}, - {"01OV)O", 'F'}, - {"01OV)U", 'F'}, - {"01OV,(", 'F'}, - {"01OV,1", 'F'}, - {"01OV,F", 'F'}, - {"01OV,N", 'F'}, - {"01OV,S", 'F'}, - {"01OV,V", 'F'}, - {"01OV;", 'F'}, - {"01OV;C", 'F'}, - {"01OV;E", 'F'}, - {"01OV;N", 'F'}, - {"01OV;T", 'F'}, - {"01OVA(", 'F'}, - {"01OVAF", 'F'}, - {"01OVAS", 'F'}, - {"01OVAT", 'F'}, - {"01OVAV", 'F'}, - {"01OVB(", 'F'}, - {"01OVB1", 'F'}, - {"01OVBE", 'F'}, - {"01OVBF", 'F'}, - {"01OVBN", 'F'}, - {"01OVBS", 'F'}, - {"01OVBV", 'F'}, - {"01OVC", 'F'}, - {"01OVE(", 'F'}, - {"01OVE1", 'F'}, - {"01OVEF", 'F'}, - {"01OVEK", 'F'}, - {"01OVEN", 'F'}, - {"01OVEO", 'F'}, - {"01OVES", 'F'}, - {"01OVEU", 'F'}, - {"01OVEV", 'F'}, - {"01OVF(", 'F'}, - {"01OVK(", 'F'}, - {"01OVK)", 'F'}, - {"01OVK1", 'F'}, - {"01OVKB", 'F'}, - {"01OVKF", 'F'}, - {"01OVKN", 'F'}, - {"01OVKS", 'F'}, - {"01OVKU", 'F'}, - {"01OVKV", 'F'}, - {"01OVO(", 'F'}, - {"01OVOF", 'F'}, - {"01OVOK", 'F'}, - {"01OVOS", 'F'}, - {"01OVOT", 'F'}, - {"01OVOU", 'F'}, - {"01OVS(", 'F'}, - {"01OVS1", 'F'}, - {"01OVSF", 'F'}, - {"01OVSO", 'F'}, - {"01OVSU", 'F'}, - {"01OVSV", 'F'}, - {"01OVU", 'F'}, - {"01OVU(", 'F'}, - {"01OVU1", 'F'}, - {"01OVU;", 'F'}, - {"01OVUC", 'F'}, - {"01OVUE", 'F'}, - {"01OVUF", 'F'}, - {"01OVUK", 'F'}, - {"01OVUN", 'F'}, - {"01OVUO", 'F'}, - {"01OVUS", 'F'}, - {"01OVUT", 'F'}, - {"01OVUV", 'F'}, - {"01SF((", 'F'}, - {"01SF()", 'F'}, - {"01SF(1", 'F'}, - {"01SF(F", 'F'}, - {"01SF(N", 'F'}, - {"01SF(S", 'F'}, - {"01SF(V", 'F'}, - {"01SUE", 'F'}, - {"01SUE;", 'F'}, - {"01SUEC", 'F'}, - {"01SUEK", 'F'}, - {"01SV", 'F'}, - {"01SV;", 'F'}, - {"01SV;C", 'F'}, - {"01SVC", 'F'}, - {"01SVO(", 'F'}, - {"01SVOF", 'F'}, - {"01SVOS", 'F'}, - {"01U", 'F'}, - {"01U(((", 'F'}, - {"01U((1", 'F'}, - {"01U((E", 'F'}, - {"01U((F", 'F'}, - {"01U((N", 'F'}, - {"01U((S", 'F'}, - {"01U((V", 'F'}, - {"01U(1)", 'F'}, - {"01U(1O", 'F'}, - {"01U(E(", 'F'}, - {"01U(E1", 'F'}, - {"01U(EF", 'F'}, - {"01U(EK", 'F'}, - {"01U(EN", 'F'}, - {"01U(ES", 'F'}, - {"01U(EV", 'F'}, - {"01U(F(", 'F'}, - {"01U(N)", 'F'}, - {"01U(NO", 'F'}, - {"01U(S)", 'F'}, - {"01U(SO", 'F'}, - {"01U(V)", 'F'}, - {"01U(VO", 'F'}, - {"01U1,(", 'F'}, - {"01U1,F", 'F'}, - {"01U1,V", 'F'}, - {"01U1C", 'F'}, - {"01U1O(", 'F'}, - {"01U1OF", 'F'}, - {"01U1OS", 'F'}, - {"01U1OV", 'F'}, - {"01U;", 'F'}, - {"01U;C", 'F'}, - {"01UC", 'F'}, - {"01UE", 'F'}, - {"01UE((", 'F'}, - {"01UE(1", 'F'}, - {"01UE(E", 'F'}, - {"01UE(F", 'F'}, - {"01UE(N", 'F'}, - {"01UE(O", 'F'}, - {"01UE(S", 'F'}, - {"01UE(V", 'F'}, - {"01UE1", 'F'}, - {"01UE1&", 'F'}, - {"01UE1(", 'F'}, - {"01UE1)", 'F'}, - {"01UE1,", 'F'}, - {"01UE1;", 'F'}, - {"01UE1B", 'F'}, - {"01UE1C", 'F'}, - {"01UE1F", 'F'}, - {"01UE1K", 'F'}, - {"01UE1N", 'F'}, - {"01UE1O", 'F'}, - {"01UE1S", 'F'}, - {"01UE1U", 'F'}, - {"01UE1V", 'F'}, - {"01UE;", 'F'}, - {"01UE;C", 'F'}, - {"01UEC", 'F'}, - {"01UEF", 'F'}, - {"01UEF(", 'F'}, - {"01UEF,", 'F'}, - {"01UEF;", 'F'}, - {"01UEFC", 'F'}, - {"01UEK", 'F'}, - {"01UEK(", 'F'}, - {"01UEK1", 'F'}, - {"01UEK;", 'F'}, - {"01UEKC", 'F'}, - {"01UEKF", 'F'}, - {"01UEKN", 'F'}, - {"01UEKO", 'F'}, - {"01UEKS", 'F'}, - {"01UEKV", 'F'}, - {"01UEN", 'F'}, - {"01UEN&", 'F'}, - {"01UEN(", 'F'}, - {"01UEN)", 'F'}, - {"01UEN,", 'F'}, - {"01UEN1", 'F'}, - {"01UEN;", 'F'}, - {"01UENB", 'F'}, - {"01UENC", 'F'}, - {"01UENF", 'F'}, - {"01UENK", 'F'}, - {"01UENN", 'F'}, - {"01UENO", 'F'}, - {"01UENS", 'F'}, - {"01UENU", 'F'}, - {"01UEOK", 'F'}, - {"01UEON", 'F'}, - {"01UEOO", 'F'}, - {"01UES", 'F'}, - {"01UES&", 'F'}, - {"01UES(", 'F'}, - {"01UES)", 'F'}, - {"01UES,", 'F'}, - {"01UES1", 'F'}, - {"01UES;", 'F'}, - {"01UESB", 'F'}, - {"01UESC", 'F'}, - {"01UESF", 'F'}, - {"01UESK", 'F'}, - {"01UESO", 'F'}, - {"01UESU", 'F'}, - {"01UESV", 'F'}, - {"01UEV", 'F'}, - {"01UEV&", 'F'}, - {"01UEV(", 'F'}, - {"01UEV)", 'F'}, - {"01UEV,", 'F'}, - {"01UEV;", 'F'}, - {"01UEVB", 'F'}, - {"01UEVC", 'F'}, - {"01UEVF", 'F'}, - {"01UEVK", 'F'}, - {"01UEVN", 'F'}, - {"01UEVO", 'F'}, - {"01UEVS", 'F'}, - {"01UEVU", 'F'}, - {"01UF((", 'F'}, - {"01UF()", 'F'}, - {"01UF(1", 'F'}, - {"01UF(F", 'F'}, - {"01UF(N", 'F'}, - {"01UF(S", 'F'}, - {"01UF(V", 'F'}, - {"01UK((", 'F'}, - {"01UK(E", 'F'}, - {"01UN((", 'F'}, - {"01UN(1", 'F'}, - {"01UN(F", 'F'}, - {"01UN(S", 'F'}, - {"01UN(V", 'F'}, - {"01UN,(", 'F'}, - {"01UN,F", 'F'}, - {"01UN,V", 'F'}, - {"01UN1(", 'F'}, - {"01UN1,", 'F'}, - {"01UN1O", 'F'}, - {"01UNC", 'F'}, - {"01UNE(", 'F'}, - {"01UNE1", 'F'}, - {"01UNEF", 'F'}, - {"01UNEN", 'F'}, - {"01UNES", 'F'}, - {"01UNEV", 'F'}, - {"01UNF(", 'F'}, - {"01UNO(", 'F'}, - {"01UNOF", 'F'}, - {"01UNOS", 'F'}, - {"01UNOV", 'F'}, - {"01UNS(", 'F'}, - {"01UNS,", 'F'}, - {"01UNSO", 'F'}, - {"01UO((", 'F'}, - {"01UO(E", 'F'}, - {"01UON(", 'F'}, - {"01UON1", 'F'}, - {"01UONF", 'F'}, - {"01UONS", 'F'}, - {"01US,(", 'F'}, - {"01US,F", 'F'}, - {"01US,V", 'F'}, - {"01USC", 'F'}, - {"01USO(", 'F'}, - {"01USO1", 'F'}, - {"01USOF", 'F'}, - {"01USON", 'F'}, - {"01USOS", 'F'}, - {"01USOV", 'F'}, - {"01UTN(", 'F'}, - {"01UTN1", 'F'}, - {"01UTNF", 'F'}, - {"01UTNS", 'F'}, - {"01UV,(", 'F'}, - {"01UV,1", 'F'}, - {"01UV,F", 'F'}, - {"01UV,N", 'F'}, - {"01UV,S", 'F'}, - {"01UV,V", 'F'}, - {"01UVC", 'F'}, - {"01UVO(", 'F'}, - {"01UVOF", 'F'}, - {"01UVOS", 'F'}, - {"01VF((", 'F'}, - {"01VF()", 'F'}, - {"01VF(1", 'F'}, - {"01VF(F", 'F'}, - {"01VF(N", 'F'}, - {"01VF(S", 'F'}, - {"01VF(V", 'F'}, - {"01VO((", 'F'}, - {"01VO(1", 'F'}, - {"01VO(F", 'F'}, - {"01VO(N", 'F'}, - {"01VO(S", 'F'}, - {"01VO(V", 'F'}, - {"01VOF(", 'F'}, - {"01VOS(", 'F'}, - {"01VOS1", 'F'}, - {"01VOSF", 'F'}, - {"01VOSO", 'F'}, - {"01VOSU", 'F'}, - {"01VOSV", 'F'}, - {"01VS", 'F'}, - {"01VS;", 'F'}, - {"01VS;C", 'F'}, - {"01VSC", 'F'}, - {"01VSO(", 'F'}, - {"01VSO1", 'F'}, - {"01VSOF", 'F'}, - {"01VSON", 'F'}, - {"01VSOS", 'F'}, - {"01VSOV", 'F'}, - {"01VUE", 'F'}, - {"01VUE;", 'F'}, - {"01VUEC", 'F'}, - {"01VUEK", 'F'}, - {"0;TKNC", 'F'}, - {"0E((((", 'F'}, - {"0E(((1", 'F'}, - {"0E(((E", 'F'}, - {"0E(((F", 'F'}, - {"0E(((N", 'F'}, - {"0E(((S", 'F'}, - {"0E(((V", 'F'}, - {"0E((1(", 'F'}, - {"0E((1)", 'F'}, - {"0E((1,", 'F'}, - {"0E((1F", 'F'}, - {"0E((1N", 'F'}, - {"0E((1O", 'F'}, - {"0E((1S", 'F'}, - {"0E((1V", 'F'}, - {"0E((E(", 'F'}, - {"0E((E1", 'F'}, - {"0E((EF", 'F'}, - {"0E((EK", 'F'}, - {"0E((EN", 'F'}, - {"0E((ES", 'F'}, - {"0E((EV", 'F'}, - {"0E((F(", 'F'}, - {"0E((N(", 'F'}, - {"0E((N)", 'F'}, - {"0E((N,", 'F'}, - {"0E((N1", 'F'}, - {"0E((NF", 'F'}, - {"0E((NO", 'F'}, - {"0E((S(", 'F'}, - {"0E((S)", 'F'}, - {"0E((S,", 'F'}, - {"0E((S1", 'F'}, - {"0E((SF", 'F'}, - {"0E((SO", 'F'}, - {"0E((SV", 'F'}, - {"0E((V(", 'F'}, - {"0E((V)", 'F'}, - {"0E((V,", 'F'}, - {"0E((VF", 'F'}, - {"0E((VO", 'F'}, - {"0E((VS", 'F'}, - {"0E(1)&", 'F'}, - {"0E(1),", 'F'}, - {"0E(1)1", 'F'}, - {"0E(1)F", 'F'}, - {"0E(1)K", 'F'}, - {"0E(1)N", 'F'}, - {"0E(1)O", 'F'}, - {"0E(1)S", 'F'}, - {"0E(1)U", 'F'}, - {"0E(1)V", 'F'}, - {"0E(1,F", 'F'}, - {"0E(1F(", 'F'}, - {"0E(1N)", 'F'}, - {"0E(1O(", 'F'}, - {"0E(1OF", 'F'}, - {"0E(1OS", 'F'}, - {"0E(1OV", 'F'}, - {"0E(1S)", 'F'}, - {"0E(1V)", 'F'}, - {"0E(1VO", 'F'}, - {"0E(E((", 'F'}, - {"0E(E(1", 'F'}, - {"0E(E(E", 'F'}, - {"0E(E(F", 'F'}, - {"0E(E(N", 'F'}, - {"0E(E(S", 'F'}, - {"0E(E(V", 'F'}, - {"0E(E1O", 'F'}, - {"0E(EF(", 'F'}, - {"0E(EK(", 'F'}, - {"0E(EK1", 'F'}, - {"0E(EKF", 'F'}, - {"0E(EKN", 'F'}, - {"0E(EKS", 'F'}, - {"0E(EKV", 'F'}, - {"0E(ENO", 'F'}, - {"0E(ESO", 'F'}, - {"0E(EVO", 'F'}, - {"0E(F((", 'F'}, - {"0E(F()", 'F'}, - {"0E(F(1", 'F'}, - {"0E(F(F", 'F'}, - {"0E(F(N", 'F'}, - {"0E(F(S", 'F'}, - {"0E(F(V", 'F'}, - {"0E(N((", 'F'}, - {"0E(N(1", 'F'}, - {"0E(N(F", 'F'}, - {"0E(N(S", 'F'}, - {"0E(N(V", 'F'}, - {"0E(N)&", 'F'}, - {"0E(N),", 'F'}, - {"0E(N)1", 'F'}, - {"0E(N)F", 'F'}, - {"0E(N)K", 'F'}, - {"0E(N)N", 'F'}, - {"0E(N)O", 'F'}, - {"0E(N)S", 'F'}, - {"0E(N)U", 'F'}, - {"0E(N)V", 'F'}, - {"0E(N,F", 'F'}, - {"0E(N1)", 'F'}, - {"0E(N1O", 'F'}, - {"0E(NF(", 'F'}, - {"0E(NO(", 'F'}, - {"0E(NOF", 'F'}, - {"0E(NOS", 'F'}, - {"0E(NOV", 'F'}, - {"0E(S)&", 'F'}, - {"0E(S),", 'F'}, - {"0E(S)1", 'F'}, - {"0E(S)F", 'F'}, - {"0E(S)K", 'F'}, - {"0E(S)N", 'F'}, - {"0E(S)O", 'F'}, - {"0E(S)S", 'F'}, - {"0E(S)U", 'F'}, - {"0E(S)V", 'F'}, - {"0E(S,F", 'F'}, - {"0E(S1)", 'F'}, - {"0E(S1O", 'F'}, - {"0E(SF(", 'F'}, - {"0E(SO(", 'F'}, - {"0E(SO1", 'F'}, - {"0E(SOF", 'F'}, - {"0E(SON", 'F'}, - {"0E(SOS", 'F'}, - {"0E(SOV", 'F'}, - {"0E(SV)", 'F'}, - {"0E(SVO", 'F'}, - {"0E(V)&", 'F'}, - {"0E(V),", 'F'}, - {"0E(V)1", 'F'}, - {"0E(V)F", 'F'}, - {"0E(V)K", 'F'}, - {"0E(V)N", 'F'}, - {"0E(V)O", 'F'}, - {"0E(V)S", 'F'}, - {"0E(V)U", 'F'}, - {"0E(V)V", 'F'}, - {"0E(V,F", 'F'}, - {"0E(VF(", 'F'}, - {"0E(VO(", 'F'}, - {"0E(VOF", 'F'}, - {"0E(VOS", 'F'}, - {"0E(VS)", 'F'}, - {"0E(VSO", 'F'}, - {"0E1&((", 'F'}, - {"0E1&(E", 'F'}, - {"0E1)", 'F'}, - {"0E1);", 'F'}, - {"0E1);C", 'F'}, - {"0E1)C", 'F'}, - {"0E1)KN", 'F'}, - {"0E1)O(", 'F'}, - {"0E1)O1", 'F'}, - {"0E1)OF", 'F'}, - {"0E1)ON", 'F'}, - {"0E1)OS", 'F'}, - {"0E1)OV", 'F'}, - {"0E1)UE", 'F'}, - {"0E1,((", 'F'}, - {"0E1,(1", 'F'}, - {"0E1,(F", 'F'}, - {"0E1,(N", 'F'}, - {"0E1,(S", 'F'}, - {"0E1,(V", 'F'}, - {"0E1,F(", 'F'}, - {"0E1,V,", 'F'}, - {"0E1,VK", 'F'}, - {"0E1,VO", 'F'}, - {"0E1F((", 'F'}, - {"0E1F()", 'F'}, - {"0E1F(1", 'F'}, - {"0E1F(F", 'F'}, - {"0E1F(N", 'F'}, - {"0E1F(S", 'F'}, - {"0E1F(V", 'F'}, - {"0E1K((", 'F'}, - {"0E1K(1", 'F'}, - {"0E1K(E", 'F'}, - {"0E1K(F", 'F'}, - {"0E1K(N", 'F'}, - {"0E1K(S", 'F'}, - {"0E1K(V", 'F'}, - {"0E1K1)", 'F'}, - {"0E1K1K", 'F'}, - {"0E1K1O", 'F'}, - {"0E1KF(", 'F'}, - {"0E1KN", 'F'}, - {"0E1KN)", 'F'}, - {"0E1KN;", 'F'}, - {"0E1KNC", 'F'}, - {"0E1KNK", 'F'}, - {"0E1KS)", 'F'}, - {"0E1KSK", 'F'}, - {"0E1KSO", 'F'}, - {"0E1KV)", 'F'}, - {"0E1KVK", 'F'}, - {"0E1KVO", 'F'}, - {"0E1N)U", 'F'}, - {"0E1N;", 'F'}, - {"0E1N;C", 'F'}, - {"0E1NC", 'F'}, - {"0E1NKN", 'F'}, - {"0E1O((", 'F'}, - {"0E1O(1", 'F'}, - {"0E1O(E", 'F'}, - {"0E1O(F", 'F'}, - {"0E1O(N", 'F'}, - {"0E1O(S", 'F'}, - {"0E1O(V", 'F'}, - {"0E1OF(", 'F'}, - {"0E1OS&", 'F'}, - {"0E1OS(", 'F'}, - {"0E1OS)", 'F'}, - {"0E1OS,", 'F'}, - {"0E1OS1", 'F'}, - {"0E1OSF", 'F'}, - {"0E1OSK", 'F'}, - {"0E1OSO", 'F'}, - {"0E1OSU", 'F'}, - {"0E1OSV", 'F'}, - {"0E1OV&", 'F'}, - {"0E1OV(", 'F'}, - {"0E1OV)", 'F'}, - {"0E1OV,", 'F'}, - {"0E1OVF", 'F'}, - {"0E1OVK", 'F'}, - {"0E1OVO", 'F'}, - {"0E1OVS", 'F'}, - {"0E1OVU", 'F'}, - {"0E1S;", 'F'}, - {"0E1S;C", 'F'}, - {"0E1SC", 'F'}, - {"0E1UE(", 'F'}, - {"0E1UE1", 'F'}, - {"0E1UEF", 'F'}, - {"0E1UEK", 'F'}, - {"0E1UEN", 'F'}, - {"0E1UES", 'F'}, - {"0E1UEV", 'F'}, - {"0E1V", 'F'}, - {"0E1V;", 'F'}, - {"0E1V;C", 'F'}, - {"0E1VC", 'F'}, - {"0E1VO(", 'F'}, - {"0E1VOF", 'F'}, - {"0E1VOS", 'F'}, - {"0ECHO", 'F'}, - {"0ECHO;", 'F'}, - {"0ECHOC", 'F'}, - {"0EE(((", 'F'}, - {"0EE((F", 'F'}, - {"0EE(F(", 'F'}, - {"0EEK((", 'F'}, - {"0EEK(F", 'F'}, - {"0EF(((", 'F'}, - {"0EF(()", 'F'}, - {"0EF((1", 'F'}, - {"0EF((F", 'F'}, - {"0EF((N", 'F'}, - {"0EF((O", 'F'}, - {"0EF((S", 'F'}, - {"0EF((V", 'F'}, - {"0EF()&", 'F'}, - {"0EF(),", 'F'}, - {"0EF()1", 'F'}, - {"0EF()F", 'F'}, - {"0EF()K", 'F'}, - {"0EF()N", 'F'}, - {"0EF()O", 'F'}, - {"0EF()S", 'F'}, - {"0EF()U", 'F'}, - {"0EF()V", 'F'}, - {"0EF(1)", 'F'}, - {"0EF(1,", 'F'}, - {"0EF(1O", 'F'}, - {"0EF(F(", 'F'}, - {"0EF(N)", 'F'}, - {"0EF(N,", 'F'}, - {"0EF(NO", 'F'}, - {"0EF(O)", 'F'}, - {"0EF(S)", 'F'}, - {"0EF(S,", 'F'}, - {"0EF(SO", 'F'}, - {"0EF(V)", 'F'}, - {"0EF(V,", 'F'}, - {"0EF(VO", 'F'}, - {"0EK(((", 'F'}, - {"0EK((1", 'F'}, - {"0EK((E", 'F'}, - {"0EK((F", 'F'}, - {"0EK((N", 'F'}, - {"0EK((S", 'F'}, - {"0EK((V", 'F'}, - {"0EK(1(", 'F'}, - {"0EK(1)", 'F'}, - {"0EK(1,", 'F'}, - {"0EK(1F", 'F'}, - {"0EK(1N", 'F'}, - {"0EK(1O", 'F'}, - {"0EK(1S", 'F'}, - {"0EK(1V", 'F'}, - {"0EK(E(", 'F'}, - {"0EK(E1", 'F'}, - {"0EK(EF", 'F'}, - {"0EK(EK", 'F'}, - {"0EK(EN", 'F'}, - {"0EK(ES", 'F'}, - {"0EK(EV", 'F'}, - {"0EK(F(", 'F'}, - {"0EK(N(", 'F'}, - {"0EK(N)", 'F'}, - {"0EK(N,", 'F'}, - {"0EK(N1", 'F'}, - {"0EK(NF", 'F'}, - {"0EK(NO", 'F'}, - {"0EK(S(", 'F'}, - {"0EK(S)", 'F'}, - {"0EK(S,", 'F'}, - {"0EK(S1", 'F'}, - {"0EK(SF", 'F'}, - {"0EK(SO", 'F'}, - {"0EK(SV", 'F'}, - {"0EK(V(", 'F'}, - {"0EK(V)", 'F'}, - {"0EK(V,", 'F'}, - {"0EK(VF", 'F'}, - {"0EK(VO", 'F'}, - {"0EK(VS", 'F'}, - {"0EK1&(", 'F'}, - {"0EK1)", 'F'}, - {"0EK1);", 'F'}, - {"0EK1)C", 'F'}, - {"0EK1)K", 'F'}, - {"0EK1)O", 'F'}, - {"0EK1)U", 'F'}, - {"0EK1,(", 'F'}, - {"0EK1,F", 'F'}, - {"0EK1,V", 'F'}, - {"0EK1F(", 'F'}, - {"0EK1K(", 'F'}, - {"0EK1K1", 'F'}, - {"0EK1KF", 'F'}, - {"0EK1KN", 'F'}, - {"0EK1KS", 'F'}, - {"0EK1KV", 'F'}, - {"0EK1N", 'F'}, - {"0EK1N)", 'F'}, - {"0EK1N;", 'F'}, - {"0EK1NC", 'F'}, - {"0EK1NF", 'F'}, - {"0EK1NK", 'F'}, - {"0EK1O(", 'F'}, - {"0EK1OF", 'F'}, - {"0EK1OS", 'F'}, - {"0EK1OV", 'F'}, - {"0EK1S", 'F'}, - {"0EK1S;", 'F'}, - {"0EK1SC", 'F'}, - {"0EK1SF", 'F'}, - {"0EK1SK", 'F'}, - {"0EK1UE", 'F'}, - {"0EK1V", 'F'}, - {"0EK1V;", 'F'}, - {"0EK1VC", 'F'}, - {"0EK1VF", 'F'}, - {"0EK1VK", 'F'}, - {"0EK1VO", 'F'}, - {"0EKE((", 'F'}, - {"0EKE(F", 'F'}, - {"0EKEK(", 'F'}, - {"0EKF((", 'F'}, - {"0EKF()", 'F'}, - {"0EKF(1", 'F'}, - {"0EKF(F", 'F'}, - {"0EKF(N", 'F'}, - {"0EKF(O", 'F'}, - {"0EKF(S", 'F'}, - {"0EKF(V", 'F'}, - {"0EKN&(", 'F'}, - {"0EKN((", 'F'}, - {"0EKN(1", 'F'}, - {"0EKN(F", 'F'}, - {"0EKN(S", 'F'}, - {"0EKN(V", 'F'}, - {"0EKN)", 'F'}, - {"0EKN);", 'F'}, - {"0EKN)C", 'F'}, - {"0EKN)K", 'F'}, - {"0EKN)O", 'F'}, - {"0EKN)U", 'F'}, - {"0EKN,(", 'F'}, - {"0EKN,F", 'F'}, - {"0EKN,V", 'F'}, - {"0EKN1", 'F'}, - {"0EKN1;", 'F'}, - {"0EKN1C", 'F'}, - {"0EKN1F", 'F'}, - {"0EKN1K", 'F'}, - {"0EKN1O", 'F'}, - {"0EKNF(", 'F'}, - {"0EKNK(", 'F'}, - {"0EKNK1", 'F'}, - {"0EKNKF", 'F'}, - {"0EKNKN", 'F'}, - {"0EKNKS", 'F'}, - {"0EKNKV", 'F'}, - {"0EKNUE", 'F'}, - {"0EKO((", 'F'}, - {"0EKO(1", 'F'}, - {"0EKO(F", 'F'}, - {"0EKO(N", 'F'}, - {"0EKO(S", 'F'}, - {"0EKO(V", 'F'}, - {"0EKOK(", 'F'}, - {"0EKS&(", 'F'}, - {"0EKS)", 'F'}, - {"0EKS);", 'F'}, - {"0EKS)C", 'F'}, - {"0EKS)K", 'F'}, - {"0EKS)O", 'F'}, - {"0EKS)U", 'F'}, - {"0EKS,(", 'F'}, - {"0EKS,F", 'F'}, - {"0EKS,V", 'F'}, - {"0EKS1", 'F'}, - {"0EKS1;", 'F'}, - {"0EKS1C", 'F'}, - {"0EKS1F", 'F'}, - {"0EKS1K", 'F'}, - {"0EKS1O", 'F'}, - {"0EKSF(", 'F'}, - {"0EKSK(", 'F'}, - {"0EKSK1", 'F'}, - {"0EKSKF", 'F'}, - {"0EKSKN", 'F'}, - {"0EKSKS", 'F'}, - {"0EKSKV", 'F'}, - {"0EKSO(", 'F'}, - {"0EKSO1", 'F'}, - {"0EKSOF", 'F'}, - {"0EKSON", 'F'}, - {"0EKSOS", 'F'}, - {"0EKSOV", 'F'}, - {"0EKSUE", 'F'}, - {"0EKSV", 'F'}, - {"0EKSV;", 'F'}, - {"0EKSVC", 'F'}, - {"0EKSVF", 'F'}, - {"0EKSVK", 'F'}, - {"0EKSVO", 'F'}, - {"0EKV&(", 'F'}, - {"0EKV)", 'F'}, - {"0EKV);", 'F'}, - {"0EKV)C", 'F'}, - {"0EKV)K", 'F'}, - {"0EKV)O", 'F'}, - {"0EKV)U", 'F'}, - {"0EKV,(", 'F'}, - {"0EKV,1", 'F'}, - {"0EKV,F", 'F'}, - {"0EKV,N", 'F'}, - {"0EKV,S", 'F'}, - {"0EKV,V", 'F'}, - {"0EKVF(", 'F'}, - {"0EKVK(", 'F'}, - {"0EKVK1", 'F'}, - {"0EKVKF", 'F'}, - {"0EKVKN", 'F'}, - {"0EKVKS", 'F'}, - {"0EKVKV", 'F'}, - {"0EKVO(", 'F'}, - {"0EKVOF", 'F'}, - {"0EKVOS", 'F'}, - {"0EKVS", 'F'}, - {"0EKVS;", 'F'}, - {"0EKVSC", 'F'}, - {"0EKVSF", 'F'}, - {"0EKVSK", 'F'}, - {"0EKVSO", 'F'}, - {"0EKVUE", 'F'}, - {"0EN&((", 'F'}, - {"0EN&(E", 'F'}, - {"0EN(((", 'F'}, - {"0EN((1", 'F'}, - {"0EN((F", 'F'}, - {"0EN((N", 'F'}, - {"0EN((S", 'F'}, - {"0EN((V", 'F'}, - {"0EN(1O", 'F'}, - {"0EN(F(", 'F'}, - {"0EN(S)", 'F'}, - {"0EN(SO", 'F'}, - {"0EN(V)", 'F'}, - {"0EN(VO", 'F'}, - {"0EN)", 'F'}, - {"0EN);", 'F'}, - {"0EN);C", 'F'}, - {"0EN)C", 'F'}, - {"0EN)KN", 'F'}, - {"0EN)O(", 'F'}, - {"0EN)O1", 'F'}, - {"0EN)OF", 'F'}, - {"0EN)ON", 'F'}, - {"0EN)OS", 'F'}, - {"0EN)OV", 'F'}, - {"0EN)UE", 'F'}, - {"0EN,((", 'F'}, - {"0EN,(1", 'F'}, - {"0EN,(F", 'F'}, - {"0EN,(N", 'F'}, - {"0EN,(S", 'F'}, - {"0EN,(V", 'F'}, - {"0EN,F(", 'F'}, - {"0EN,V,", 'F'}, - {"0EN,VK", 'F'}, - {"0EN,VO", 'F'}, - {"0EN1;", 'F'}, - {"0EN1;C", 'F'}, - {"0EN1C", 'F'}, - {"0EN1O(", 'F'}, - {"0EN1OF", 'F'}, - {"0EN1OS", 'F'}, - {"0EN1OV", 'F'}, - {"0ENF((", 'F'}, - {"0ENF()", 'F'}, - {"0ENF(1", 'F'}, - {"0ENF(F", 'F'}, - {"0ENF(N", 'F'}, - {"0ENF(S", 'F'}, - {"0ENF(V", 'F'}, - {"0ENK((", 'F'}, - {"0ENK(1", 'F'}, - {"0ENK(E", 'F'}, - {"0ENK(F", 'F'}, - {"0ENK(N", 'F'}, - {"0ENK(S", 'F'}, - {"0ENK(V", 'F'}, - {"0ENK1)", 'F'}, - {"0ENK1K", 'F'}, - {"0ENK1O", 'F'}, - {"0ENKF(", 'F'}, - {"0ENKN)", 'F'}, - {"0ENKN,", 'F'}, - {"0ENKN;", 'F'}, - {"0ENKNB", 'F'}, - {"0ENKNC", 'F'}, - {"0ENKNK", 'F'}, - {"0ENKS)", 'F'}, - {"0ENKSK", 'F'}, - {"0ENKSO", 'F'}, - {"0ENKV)", 'F'}, - {"0ENKVK", 'F'}, - {"0ENKVO", 'F'}, - {"0ENO((", 'F'}, - {"0ENO(1", 'F'}, - {"0ENO(E", 'F'}, - {"0ENO(F", 'F'}, - {"0ENO(N", 'F'}, - {"0ENO(S", 'F'}, - {"0ENO(V", 'F'}, - {"0ENOF(", 'F'}, - {"0ENOS&", 'F'}, - {"0ENOS(", 'F'}, - {"0ENOS)", 'F'}, - {"0ENOS,", 'F'}, - {"0ENOS1", 'F'}, - {"0ENOSF", 'F'}, - {"0ENOSK", 'F'}, - {"0ENOSO", 'F'}, - {"0ENOSU", 'F'}, - {"0ENOSV", 'F'}, - {"0ENOV&", 'F'}, - {"0ENOV(", 'F'}, - {"0ENOV)", 'F'}, - {"0ENOV,", 'F'}, - {"0ENOVF", 'F'}, - {"0ENOVK", 'F'}, - {"0ENOVO", 'F'}, - {"0ENOVS", 'F'}, - {"0ENOVU", 'F'}, - {"0ENUE(", 'F'}, - {"0ENUE1", 'F'}, - {"0ENUEF", 'F'}, - {"0ENUEK", 'F'}, - {"0ENUEN", 'F'}, - {"0ENUES", 'F'}, - {"0ENUEV", 'F'}, - {"0EO(((", 'F'}, - {"0EO((1", 'F'}, - {"0EO((F", 'F'}, - {"0EO((N", 'F'}, - {"0EO((S", 'F'}, - {"0EO((V", 'F'}, - {"0EO(1,", 'F'}, - {"0EO(1O", 'F'}, - {"0EO(F(", 'F'}, - {"0EO(N,", 'F'}, - {"0EO(NO", 'F'}, - {"0EO(S,", 'F'}, - {"0EO(SO", 'F'}, - {"0EO(V,", 'F'}, - {"0EO(VO", 'F'}, - {"0EOK((", 'F'}, - {"0EOK(E", 'F'}, - {"0ES&((", 'F'}, - {"0ES&(E", 'F'}, - {"0ES)", 'F'}, - {"0ES);", 'F'}, - {"0ES);C", 'F'}, - {"0ES)C", 'F'}, - {"0ES)KN", 'F'}, - {"0ES)O(", 'F'}, - {"0ES)O1", 'F'}, - {"0ES)OF", 'F'}, - {"0ES)ON", 'F'}, - {"0ES)OS", 'F'}, - {"0ES)OV", 'F'}, - {"0ES)UE", 'F'}, - {"0ES,((", 'F'}, - {"0ES,(1", 'F'}, - {"0ES,(F", 'F'}, - {"0ES,(N", 'F'}, - {"0ES,(S", 'F'}, - {"0ES,(V", 'F'}, - {"0ES,F(", 'F'}, - {"0ES,V,", 'F'}, - {"0ES,VK", 'F'}, - {"0ES,VO", 'F'}, - {"0ES1", 'F'}, - {"0ES1;", 'F'}, - {"0ES1;C", 'F'}, - {"0ES1C", 'F'}, - {"0ES1O(", 'F'}, - {"0ES1OF", 'F'}, - {"0ES1OS", 'F'}, - {"0ES1OV", 'F'}, - {"0ESF((", 'F'}, - {"0ESF()", 'F'}, - {"0ESF(1", 'F'}, - {"0ESF(F", 'F'}, - {"0ESF(N", 'F'}, - {"0ESF(S", 'F'}, - {"0ESF(V", 'F'}, - {"0ESK((", 'F'}, - {"0ESK(1", 'F'}, - {"0ESK(E", 'F'}, - {"0ESK(F", 'F'}, - {"0ESK(N", 'F'}, - {"0ESK(S", 'F'}, - {"0ESK(V", 'F'}, - {"0ESK1)", 'F'}, - {"0ESK1K", 'F'}, - {"0ESK1O", 'F'}, - {"0ESKF(", 'F'}, - {"0ESKN", 'F'}, - {"0ESKN)", 'F'}, - {"0ESKN;", 'F'}, - {"0ESKNC", 'F'}, - {"0ESKNK", 'F'}, - {"0ESKS)", 'F'}, - {"0ESKSK", 'F'}, - {"0ESKSO", 'F'}, - {"0ESKV)", 'F'}, - {"0ESKVK", 'F'}, - {"0ESKVO", 'F'}, - {"0ESO((", 'F'}, - {"0ESO(1", 'F'}, - {"0ESO(E", 'F'}, - {"0ESO(F", 'F'}, - {"0ESO(N", 'F'}, - {"0ESO(S", 'F'}, - {"0ESO(V", 'F'}, - {"0ESO1&", 'F'}, - {"0ESO1(", 'F'}, - {"0ESO1)", 'F'}, - {"0ESO1,", 'F'}, - {"0ESO1F", 'F'}, - {"0ESO1K", 'F'}, - {"0ESO1N", 'F'}, - {"0ESO1O", 'F'}, - {"0ESO1S", 'F'}, - {"0ESO1U", 'F'}, - {"0ESO1V", 'F'}, - {"0ESOF(", 'F'}, - {"0ESON&", 'F'}, - {"0ESON(", 'F'}, - {"0ESON)", 'F'}, - {"0ESON,", 'F'}, - {"0ESON1", 'F'}, - {"0ESONF", 'F'}, - {"0ESONK", 'F'}, - {"0ESONO", 'F'}, - {"0ESONU", 'F'}, - {"0ESOS&", 'F'}, - {"0ESOS(", 'F'}, - {"0ESOS)", 'F'}, - {"0ESOS,", 'F'}, - {"0ESOS1", 'F'}, - {"0ESOSF", 'F'}, - {"0ESOSK", 'F'}, - {"0ESOSO", 'F'}, - {"0ESOSU", 'F'}, - {"0ESOSV", 'F'}, - {"0ESOV&", 'F'}, - {"0ESOV(", 'F'}, - {"0ESOV)", 'F'}, - {"0ESOV,", 'F'}, - {"0ESOVF", 'F'}, - {"0ESOVK", 'F'}, - {"0ESOVO", 'F'}, - {"0ESOVS", 'F'}, - {"0ESOVU", 'F'}, - {"0ESUE(", 'F'}, - {"0ESUE1", 'F'}, - {"0ESUEF", 'F'}, - {"0ESUEK", 'F'}, - {"0ESUEN", 'F'}, - {"0ESUES", 'F'}, - {"0ESUEV", 'F'}, - {"0ESV", 'F'}, - {"0ESV;", 'F'}, - {"0ESV;C", 'F'}, - {"0ESVC", 'F'}, - {"0ESVO(", 'F'}, - {"0ESVOF", 'F'}, - {"0ESVOS", 'F'}, - {"0EV&((", 'F'}, - {"0EV&(E", 'F'}, - {"0EV)", 'F'}, - {"0EV);", 'F'}, - {"0EV);C", 'F'}, - {"0EV)C", 'F'}, - {"0EV)KN", 'F'}, - {"0EV)O(", 'F'}, - {"0EV)O1", 'F'}, - {"0EV)OF", 'F'}, - {"0EV)ON", 'F'}, - {"0EV)OS", 'F'}, - {"0EV)OV", 'F'}, - {"0EV)UE", 'F'}, - {"0EV,((", 'F'}, - {"0EV,(1", 'F'}, - {"0EV,(F", 'F'}, - {"0EV,(N", 'F'}, - {"0EV,(S", 'F'}, - {"0EV,(V", 'F'}, - {"0EV,1,", 'F'}, - {"0EV,1K", 'F'}, - {"0EV,1O", 'F'}, - {"0EV,F(", 'F'}, - {"0EV,N,", 'F'}, - {"0EV,NK", 'F'}, - {"0EV,NO", 'F'}, - {"0EV,S,", 'F'}, - {"0EV,SK", 'F'}, - {"0EV,SO", 'F'}, - {"0EV,V,", 'F'}, - {"0EV,VK", 'F'}, - {"0EV,VO", 'F'}, - {"0EVF((", 'F'}, - {"0EVF()", 'F'}, - {"0EVF(1", 'F'}, - {"0EVF(F", 'F'}, - {"0EVF(N", 'F'}, - {"0EVF(S", 'F'}, - {"0EVF(V", 'F'}, - {"0EVK((", 'F'}, - {"0EVK(1", 'F'}, - {"0EVK(E", 'F'}, - {"0EVK(F", 'F'}, - {"0EVK(N", 'F'}, - {"0EVK(S", 'F'}, - {"0EVK(V", 'F'}, - {"0EVK1)", 'F'}, - {"0EVK1K", 'F'}, - {"0EVK1O", 'F'}, - {"0EVKF(", 'F'}, - {"0EVKN", 'F'}, - {"0EVKN)", 'F'}, - {"0EVKN;", 'F'}, - {"0EVKNC", 'F'}, - {"0EVKNK", 'F'}, - {"0EVKS)", 'F'}, - {"0EVKSK", 'F'}, - {"0EVKSO", 'F'}, - {"0EVKV)", 'F'}, - {"0EVKVK", 'F'}, - {"0EVKVO", 'F'}, - {"0EVN", 'F'}, - {"0EVN)U", 'F'}, - {"0EVN;", 'F'}, - {"0EVN;C", 'F'}, - {"0EVNC", 'F'}, - {"0EVNKN", 'F'}, - {"0EVNO(", 'F'}, - {"0EVNOF", 'F'}, - {"0EVNOS", 'F'}, - {"0EVNOV", 'F'}, - {"0EVO((", 'F'}, - {"0EVO(1", 'F'}, - {"0EVO(E", 'F'}, - {"0EVO(F", 'F'}, - {"0EVO(N", 'F'}, - {"0EVO(S", 'F'}, - {"0EVO(V", 'F'}, - {"0EVOF(", 'F'}, - {"0EVOS&", 'F'}, - {"0EVOS(", 'F'}, - {"0EVOS)", 'F'}, - {"0EVOS,", 'F'}, - {"0EVOS1", 'F'}, - {"0EVOSF", 'F'}, - {"0EVOSK", 'F'}, - {"0EVOSO", 'F'}, - {"0EVOSU", 'F'}, - {"0EVOSV", 'F'}, - {"0EVS", 'F'}, - {"0EVS;", 'F'}, - {"0EVS;C", 'F'}, - {"0EVSC", 'F'}, - {"0EVSO(", 'F'}, - {"0EVSO1", 'F'}, - {"0EVSOF", 'F'}, - {"0EVSON", 'F'}, - {"0EVSOS", 'F'}, - {"0EVSOV", 'F'}, - {"0EVUE(", 'F'}, - {"0EVUE1", 'F'}, - {"0EVUEF", 'F'}, - {"0EVUEK", 'F'}, - {"0EVUEN", 'F'}, - {"0EVUES", 'F'}, - {"0EVUEV", 'F'}, - {"0F((((", 'F'}, - {"0F((()", 'F'}, - {"0F(((1", 'F'}, - {"0F(((E", 'F'}, - {"0F(((F", 'F'}, - {"0F(((K", 'F'}, - {"0F(((N", 'F'}, - {"0F(((S", 'F'}, - {"0F(((T", 'F'}, - {"0F(((V", 'F'}, - {"0F(()&", 'F'}, - {"0F((),", 'F'}, - {"0F(()1", 'F'}, - {"0F(();", 'F'}, - {"0F(()A", 'F'}, - {"0F(()B", 'F'}, - {"0F(()C", 'F'}, - {"0F(()E", 'F'}, - {"0F(()F", 'F'}, - {"0F(()K", 'F'}, - {"0F(()N", 'F'}, - {"0F(()O", 'F'}, - {"0F(()S", 'F'}, - {"0F(()U", 'F'}, - {"0F(()V", 'F'}, - {"0F((1)", 'F'}, - {"0F((1,", 'F'}, - {"0F((1O", 'F'}, - {"0F((E(", 'F'}, - {"0F((E1", 'F'}, - {"0F((EF", 'F'}, - {"0F((EK", 'F'}, - {"0F((EN", 'F'}, - {"0F((ES", 'F'}, - {"0F((EV", 'F'}, - {"0F((F(", 'F'}, - {"0F((K(", 'F'}, - {"0F((K,", 'F'}, - {"0F((N)", 'F'}, - {"0F((N,", 'F'}, - {"0F((NO", 'F'}, - {"0F((S)", 'F'}, - {"0F((S,", 'F'}, - {"0F((SO", 'F'}, - {"0F((T)", 'F'}, - {"0F((T,", 'F'}, - {"0F((V)", 'F'}, - {"0F((V,", 'F'}, - {"0F((VO", 'F'}, - {"0F()&(", 'F'}, - {"0F()&1", 'F'}, - {"0F()&E", 'F'}, - {"0F()&F", 'F'}, - {"0F()&K", 'F'}, - {"0F()&N", 'F'}, - {"0F()&S", 'F'}, - {"0F()&T", 'F'}, - {"0F()&V", 'F'}, - {"0F(),(", 'F'}, - {"0F(),1", 'F'}, - {"0F(),F", 'F'}, - {"0F(),N", 'F'}, - {"0F(),S", 'F'}, - {"0F(),V", 'F'}, - {"0F()1(", 'F'}, - {"0F()1F", 'F'}, - {"0F()1N", 'F'}, - {"0F()1O", 'F'}, - {"0F()1S", 'F'}, - {"0F()1U", 'F'}, - {"0F()1V", 'F'}, - {"0F();E", 'F'}, - {"0F();N", 'F'}, - {"0F();T", 'F'}, - {"0F()A(", 'F'}, - {"0F()AF", 'F'}, - {"0F()AS", 'F'}, - {"0F()AT", 'F'}, - {"0F()AV", 'F'}, - {"0F()B(", 'F'}, - {"0F()B1", 'F'}, - {"0F()BE", 'F'}, - {"0F()BF", 'F'}, - {"0F()BN", 'F'}, - {"0F()BS", 'F'}, - {"0F()BV", 'F'}, - {"0F()C", 'F'}, - {"0F()E(", 'F'}, - {"0F()E1", 'F'}, - {"0F()EF", 'F'}, - {"0F()EK", 'F'}, - {"0F()EN", 'F'}, - {"0F()EO", 'F'}, - {"0F()ES", 'F'}, - {"0F()EU", 'F'}, - {"0F()EV", 'F'}, - {"0F()F(", 'F'}, - {"0F()K(", 'F'}, - {"0F()K)", 'F'}, - {"0F()K1", 'F'}, - {"0F()KF", 'F'}, - {"0F()KN", 'F'}, - {"0F()KS", 'F'}, - {"0F()KU", 'F'}, - {"0F()KV", 'F'}, - {"0F()N&", 'F'}, - {"0F()N(", 'F'}, - {"0F()N)", 'F'}, - {"0F()N,", 'F'}, - {"0F()N1", 'F'}, - {"0F()NE", 'F'}, - {"0F()NF", 'F'}, - {"0F()NO", 'F'}, - {"0F()NU", 'F'}, - {"0F()O(", 'F'}, - {"0F()O1", 'F'}, - {"0F()OF", 'F'}, - {"0F()OK", 'F'}, - {"0F()ON", 'F'}, - {"0F()OS", 'F'}, - {"0F()OT", 'F'}, - {"0F()OU", 'F'}, - {"0F()OV", 'F'}, - {"0F()S(", 'F'}, - {"0F()S1", 'F'}, - {"0F()SF", 'F'}, - {"0F()SO", 'F'}, - {"0F()SU", 'F'}, - {"0F()SV", 'F'}, - {"0F()U", 'F'}, - {"0F()U(", 'F'}, - {"0F()U1", 'F'}, - {"0F()U;", 'F'}, - {"0F()UC", 'F'}, - {"0F()UE", 'F'}, - {"0F()UF", 'F'}, - {"0F()UK", 'F'}, - {"0F()UN", 'F'}, - {"0F()UO", 'F'}, - {"0F()US", 'F'}, - {"0F()UT", 'F'}, - {"0F()UV", 'F'}, - {"0F()V(", 'F'}, - {"0F()VF", 'F'}, - {"0F()VO", 'F'}, - {"0F()VS", 'F'}, - {"0F()VU", 'F'}, - {"0F(1)", 'F'}, - {"0F(1)&", 'F'}, - {"0F(1),", 'F'}, - {"0F(1)1", 'F'}, - {"0F(1);", 'F'}, - {"0F(1)A", 'F'}, - {"0F(1)B", 'F'}, - {"0F(1)C", 'F'}, - {"0F(1)E", 'F'}, - {"0F(1)F", 'F'}, - {"0F(1)K", 'F'}, - {"0F(1)N", 'F'}, - {"0F(1)O", 'F'}, - {"0F(1)S", 'F'}, - {"0F(1)U", 'F'}, - {"0F(1)V", 'F'}, - {"0F(1,(", 'F'}, - {"0F(1,F", 'F'}, - {"0F(1,V", 'F'}, - {"0F(1O(", 'F'}, - {"0F(1OF", 'F'}, - {"0F(1OS", 'F'}, - {"0F(1OV", 'F'}, - {"0F(E((", 'F'}, - {"0F(E(1", 'F'}, - {"0F(E(F", 'F'}, - {"0F(E(N", 'F'}, - {"0F(E(S", 'F'}, - {"0F(E(V", 'F'}, - {"0F(E1)", 'F'}, - {"0F(E1K", 'F'}, - {"0F(E1O", 'F'}, - {"0F(EF(", 'F'}, - {"0F(EK(", 'F'}, - {"0F(EK1", 'F'}, - {"0F(EKF", 'F'}, - {"0F(EKN", 'F'}, - {"0F(EKS", 'F'}, - {"0F(EKV", 'F'}, - {"0F(EN)", 'F'}, - {"0F(ENK", 'F'}, - {"0F(ENO", 'F'}, - {"0F(ES)", 'F'}, - {"0F(ESK", 'F'}, - {"0F(ESO", 'F'}, - {"0F(EV)", 'F'}, - {"0F(EVK", 'F'}, - {"0F(EVO", 'F'}, - {"0F(F((", 'F'}, - {"0F(F()", 'F'}, - {"0F(F(1", 'F'}, - {"0F(F(F", 'F'}, - {"0F(F(N", 'F'}, - {"0F(F(S", 'F'}, - {"0F(F(V", 'F'}, - {"0F(K((", 'F'}, - {"0F(K()", 'F'}, - {"0F(K,(", 'F'}, - {"0F(K,F", 'F'}, - {"0F(N)", 'F'}, - {"0F(N)&", 'F'}, - {"0F(N),", 'F'}, - {"0F(N)1", 'F'}, - {"0F(N);", 'F'}, - {"0F(N)A", 'F'}, - {"0F(N)B", 'F'}, - {"0F(N)C", 'F'}, - {"0F(N)E", 'F'}, - {"0F(N)F", 'F'}, - {"0F(N)K", 'F'}, - {"0F(N)N", 'F'}, - {"0F(N)O", 'F'}, - {"0F(N)S", 'F'}, - {"0F(N)U", 'F'}, - {"0F(N)V", 'F'}, - {"0F(N,(", 'F'}, - {"0F(N,F", 'F'}, - {"0F(N,V", 'F'}, - {"0F(NO(", 'F'}, - {"0F(NOF", 'F'}, - {"0F(NOS", 'F'}, - {"0F(NOV", 'F'}, - {"0F(S)", 'F'}, - {"0F(S)&", 'F'}, - {"0F(S),", 'F'}, - {"0F(S)1", 'F'}, - {"0F(S);", 'F'}, - {"0F(S)A", 'F'}, - {"0F(S)B", 'F'}, - {"0F(S)C", 'F'}, - {"0F(S)E", 'F'}, - {"0F(S)F", 'F'}, - {"0F(S)K", 'F'}, - {"0F(S)N", 'F'}, - {"0F(S)O", 'F'}, - {"0F(S)S", 'F'}, - {"0F(S)U", 'F'}, - {"0F(S)V", 'F'}, - {"0F(S,(", 'F'}, - {"0F(S,F", 'F'}, - {"0F(S,V", 'F'}, - {"0F(SO(", 'F'}, - {"0F(SO1", 'F'}, - {"0F(SOF", 'F'}, - {"0F(SON", 'F'}, - {"0F(SOS", 'F'}, - {"0F(SOV", 'F'}, - {"0F(T),", 'F'}, - {"0F(T,(", 'F'}, - {"0F(T,1", 'F'}, - {"0F(T,F", 'F'}, - {"0F(T,N", 'F'}, - {"0F(T,S", 'F'}, - {"0F(T,V", 'F'}, - {"0F(V)", 'F'}, - {"0F(V)&", 'F'}, - {"0F(V),", 'F'}, - {"0F(V)1", 'F'}, - {"0F(V);", 'F'}, - {"0F(V)A", 'F'}, - {"0F(V)B", 'F'}, - {"0F(V)C", 'F'}, - {"0F(V)E", 'F'}, - {"0F(V)F", 'F'}, - {"0F(V)K", 'F'}, - {"0F(V)N", 'F'}, - {"0F(V)O", 'F'}, - {"0F(V)S", 'F'}, - {"0F(V)U", 'F'}, - {"0F(V)V", 'F'}, - {"0F(V,(", 'F'}, - {"0F(V,1", 'F'}, - {"0F(V,F", 'F'}, - {"0F(V,N", 'F'}, - {"0F(V,S", 'F'}, - {"0F(V,V", 'F'}, - {"0F(VO(", 'F'}, - {"0F(VOF", 'F'}, - {"0F(VOS", 'F'}, - {"0K((((", 'F'}, - {"0K(((1", 'F'}, - {"0K(((F", 'F'}, - {"0K(((N", 'F'}, - {"0K(((S", 'F'}, - {"0K(((V", 'F'}, - {"0K((1)", 'F'}, - {"0K((1O", 'F'}, - {"0K((F(", 'F'}, - {"0K((N)", 'F'}, - {"0K((NO", 'F'}, - {"0K((S)", 'F'}, - {"0K((SO", 'F'}, - {"0K((V)", 'F'}, - {"0K((VO", 'F'}, - {"0K(1),", 'F'}, - {"0K(1)A", 'F'}, - {"0K(1)K", 'F'}, - {"0K(1)O", 'F'}, - {"0K(1O(", 'F'}, - {"0K(1OF", 'F'}, - {"0K(1OS", 'F'}, - {"0K(1OV", 'F'}, - {"0K(F((", 'F'}, - {"0K(F()", 'F'}, - {"0K(F(1", 'F'}, - {"0K(F(F", 'F'}, - {"0K(F(N", 'F'}, - {"0K(F(S", 'F'}, - {"0K(F(V", 'F'}, - {"0K(N),", 'F'}, - {"0K(N)A", 'F'}, - {"0K(N)K", 'F'}, - {"0K(N)O", 'F'}, - {"0K(NO(", 'F'}, - {"0K(NOF", 'F'}, - {"0K(NOS", 'F'}, - {"0K(NOV", 'F'}, - {"0K(S),", 'F'}, - {"0K(S)A", 'F'}, - {"0K(S)K", 'F'}, - {"0K(S)O", 'F'}, - {"0K(SO(", 'F'}, - {"0K(SO1", 'F'}, - {"0K(SOF", 'F'}, - {"0K(SON", 'F'}, - {"0K(SOS", 'F'}, - {"0K(SOV", 'F'}, - {"0K(V),", 'F'}, - {"0K(V)A", 'F'}, - {"0K(V)K", 'F'}, - {"0K(V)O", 'F'}, - {"0K(VO(", 'F'}, - {"0K(VOF", 'F'}, - {"0K(VOS", 'F'}, - {"0K1,((", 'F'}, - {"0K1,(1", 'F'}, - {"0K1,(F", 'F'}, - {"0K1,(N", 'F'}, - {"0K1,(S", 'F'}, - {"0K1,(V", 'F'}, - {"0K1,F(", 'F'}, - {"0K1,VC", 'F'}, - {"0K1,VO", 'F'}, - {"0K1A((", 'F'}, - {"0K1A(F", 'F'}, - {"0K1A(N", 'F'}, - {"0K1A(S", 'F'}, - {"0K1A(V", 'F'}, - {"0K1AF(", 'F'}, - {"0K1ASO", 'F'}, - {"0K1ATO", 'F'}, - {"0K1AVO", 'F'}, - {"0K1K((", 'F'}, - {"0K1K(1", 'F'}, - {"0K1K(F", 'F'}, - {"0K1K(N", 'F'}, - {"0K1K(S", 'F'}, - {"0K1K(V", 'F'}, - {"0K1K1O", 'F'}, - {"0K1K1U", 'F'}, - {"0K1KF(", 'F'}, - {"0K1KNU", 'F'}, - {"0K1KSO", 'F'}, - {"0K1KSU", 'F'}, - {"0K1KVO", 'F'}, - {"0K1KVU", 'F'}, - {"0K1O((", 'F'}, - {"0K1O(1", 'F'}, - {"0K1O(F", 'F'}, - {"0K1O(N", 'F'}, - {"0K1O(S", 'F'}, - {"0K1O(V", 'F'}, - {"0K1OF(", 'F'}, - {"0K1OS(", 'F'}, - {"0K1OS,", 'F'}, - {"0K1OS1", 'F'}, - {"0K1OSA", 'F'}, - {"0K1OSF", 'F'}, - {"0K1OSK", 'F'}, - {"0K1OSO", 'F'}, - {"0K1OSV", 'F'}, - {"0K1OV(", 'F'}, - {"0K1OV,", 'F'}, - {"0K1OVA", 'F'}, - {"0K1OVF", 'F'}, - {"0K1OVK", 'F'}, - {"0K1OVO", 'F'}, - {"0K1OVS", 'F'}, - {"0KF(((", 'F'}, - {"0KF(()", 'F'}, - {"0KF((1", 'F'}, - {"0KF((F", 'F'}, - {"0KF((N", 'F'}, - {"0KF((S", 'F'}, - {"0KF((V", 'F'}, - {"0KF(),", 'F'}, - {"0KF()A", 'F'}, - {"0KF()K", 'F'}, - {"0KF()O", 'F'}, - {"0KF(1)", 'F'}, - {"0KF(1O", 'F'}, - {"0KF(F(", 'F'}, - {"0KF(N)", 'F'}, - {"0KF(NO", 'F'}, - {"0KF(S)", 'F'}, - {"0KF(SO", 'F'}, - {"0KF(V)", 'F'}, - {"0KF(VO", 'F'}, - {"0KN,((", 'F'}, - {"0KN,(1", 'F'}, - {"0KN,(F", 'F'}, - {"0KN,(N", 'F'}, - {"0KN,(S", 'F'}, - {"0KN,(V", 'F'}, - {"0KN,F(", 'F'}, - {"0KN,VC", 'F'}, - {"0KN,VO", 'F'}, - {"0KNA((", 'F'}, - {"0KNA(F", 'F'}, - {"0KNA(N", 'F'}, - {"0KNA(S", 'F'}, - {"0KNA(V", 'F'}, - {"0KNAF(", 'F'}, - {"0KNASO", 'F'}, - {"0KNATO", 'F'}, - {"0KNAVO", 'F'}, - {"0KNK((", 'F'}, - {"0KNK(1", 'F'}, - {"0KNK(F", 'F'}, - {"0KNK(N", 'F'}, - {"0KNK(S", 'F'}, - {"0KNK(V", 'F'}, - {"0KNK1O", 'F'}, - {"0KNK1U", 'F'}, - {"0KNKF(", 'F'}, - {"0KNKNU", 'F'}, - {"0KNKSO", 'F'}, - {"0KNKSU", 'F'}, - {"0KNKVO", 'F'}, - {"0KNKVU", 'F'}, - {"0KS,((", 'F'}, - {"0KS,(1", 'F'}, - {"0KS,(F", 'F'}, - {"0KS,(N", 'F'}, - {"0KS,(S", 'F'}, - {"0KS,(V", 'F'}, - {"0KS,F(", 'F'}, - {"0KS,VC", 'F'}, - {"0KS,VO", 'F'}, - {"0KSA((", 'F'}, - {"0KSA(F", 'F'}, - {"0KSA(N", 'F'}, - {"0KSA(S", 'F'}, - {"0KSA(V", 'F'}, - {"0KSAF(", 'F'}, - {"0KSASO", 'F'}, - {"0KSATO", 'F'}, - {"0KSAVO", 'F'}, - {"0KSK((", 'F'}, - {"0KSK(1", 'F'}, - {"0KSK(F", 'F'}, - {"0KSK(N", 'F'}, - {"0KSK(S", 'F'}, - {"0KSK(V", 'F'}, - {"0KSK1O", 'F'}, - {"0KSK1U", 'F'}, - {"0KSKF(", 'F'}, - {"0KSKNU", 'F'}, - {"0KSKSO", 'F'}, - {"0KSKSU", 'F'}, - {"0KSKVO", 'F'}, - {"0KSKVU", 'F'}, - {"0KSO((", 'F'}, - {"0KSO(1", 'F'}, - {"0KSO(F", 'F'}, - {"0KSO(N", 'F'}, - {"0KSO(S", 'F'}, - {"0KSO(V", 'F'}, - {"0KSO1(", 'F'}, - {"0KSO1,", 'F'}, - {"0KSO1A", 'F'}, - {"0KSO1F", 'F'}, - {"0KSO1K", 'F'}, - {"0KSO1N", 'F'}, - {"0KSO1O", 'F'}, - {"0KSO1S", 'F'}, - {"0KSO1V", 'F'}, - {"0KSOF(", 'F'}, - {"0KSON(", 'F'}, - {"0KSON,", 'F'}, - {"0KSON1", 'F'}, - {"0KSONA", 'F'}, - {"0KSONF", 'F'}, - {"0KSONK", 'F'}, - {"0KSONO", 'F'}, - {"0KSOS(", 'F'}, - {"0KSOS,", 'F'}, - {"0KSOS1", 'F'}, - {"0KSOSA", 'F'}, - {"0KSOSF", 'F'}, - {"0KSOSK", 'F'}, - {"0KSOSO", 'F'}, - {"0KSOSV", 'F'}, - {"0KSOV(", 'F'}, - {"0KSOV,", 'F'}, - {"0KSOVA", 'F'}, - {"0KSOVF", 'F'}, - {"0KSOVK", 'F'}, - {"0KSOVO", 'F'}, - {"0KSOVS", 'F'}, - {"0KV,((", 'F'}, - {"0KV,(1", 'F'}, - {"0KV,(F", 'F'}, - {"0KV,(N", 'F'}, - {"0KV,(S", 'F'}, - {"0KV,(V", 'F'}, - {"0KV,1C", 'F'}, - {"0KV,1O", 'F'}, - {"0KV,F(", 'F'}, - {"0KV,NC", 'F'}, - {"0KV,NO", 'F'}, - {"0KV,SC", 'F'}, - {"0KV,SO", 'F'}, - {"0KV,VC", 'F'}, - {"0KV,VO", 'F'}, - {"0KVA((", 'F'}, - {"0KVA(F", 'F'}, - {"0KVA(N", 'F'}, - {"0KVA(S", 'F'}, - {"0KVA(V", 'F'}, - {"0KVAF(", 'F'}, - {"0KVASO", 'F'}, - {"0KVATO", 'F'}, - {"0KVAVO", 'F'}, - {"0KVK((", 'F'}, - {"0KVK(1", 'F'}, - {"0KVK(F", 'F'}, - {"0KVK(N", 'F'}, - {"0KVK(S", 'F'}, - {"0KVK(V", 'F'}, - {"0KVK1O", 'F'}, - {"0KVK1U", 'F'}, - {"0KVKF(", 'F'}, - {"0KVKNU", 'F'}, - {"0KVKSO", 'F'}, - {"0KVKSU", 'F'}, - {"0KVKVO", 'F'}, - {"0KVKVU", 'F'}, - {"0KVO((", 'F'}, - {"0KVO(1", 'F'}, - {"0KVO(F", 'F'}, - {"0KVO(N", 'F'}, - {"0KVO(S", 'F'}, - {"0KVO(V", 'F'}, - {"0KVOF(", 'F'}, - {"0KVOS(", 'F'}, - {"0KVOS,", 'F'}, - {"0KVOS1", 'F'}, - {"0KVOSA", 'F'}, - {"0KVOSF", 'F'}, - {"0KVOSK", 'F'}, - {"0KVOSO", 'F'}, - {"0KVOSV", 'F'}, - {"0N&(((", 'F'}, - {"0N&((1", 'F'}, - {"0N&((E", 'F'}, - {"0N&((F", 'F'}, - {"0N&((N", 'F'}, - {"0N&((O", 'F'}, - {"0N&((S", 'F'}, - {"0N&((V", 'F'}, - {"0N&(1&", 'F'}, - {"0N&(1)", 'F'}, - {"0N&(1,", 'F'}, - {"0N&(1O", 'F'}, - {"0N&(E(", 'F'}, - {"0N&(E1", 'F'}, - {"0N&(EF", 'F'}, - {"0N&(EK", 'F'}, - {"0N&(EN", 'F'}, - {"0N&(EO", 'F'}, - {"0N&(ES", 'F'}, - {"0N&(EV", 'F'}, - {"0N&(F(", 'F'}, - {"0N&(N&", 'F'}, - {"0N&(N)", 'F'}, - {"0N&(N,", 'F'}, - {"0N&(NO", 'F'}, - {"0N&(O(", 'F'}, - {"0N&(O1", 'F'}, - {"0N&(OF", 'F'}, - {"0N&(ON", 'F'}, - {"0N&(OS", 'F'}, - {"0N&(OV", 'F'}, - {"0N&(S&", 'F'}, - {"0N&(S)", 'F'}, - {"0N&(S,", 'F'}, - {"0N&(SO", 'F'}, - {"0N&(V&", 'F'}, - {"0N&(V)", 'F'}, - {"0N&(V,", 'F'}, - {"0N&(VO", 'F'}, - {"0N&1", 'F'}, - {"0N&1&(", 'F'}, - {"0N&1&1", 'F'}, - {"0N&1&F", 'F'}, - {"0N&1&N", 'F'}, - {"0N&1&S", 'F'}, - {"0N&1&V", 'F'}, - {"0N&1)&", 'F'}, - {"0N&1)O", 'F'}, - {"0N&1)U", 'F'}, - {"0N&1;", 'F'}, - {"0N&1;C", 'F'}, - {"0N&1;E", 'F'}, - {"0N&1;T", 'F'}, - {"0N&1B(", 'F'}, - {"0N&1B1", 'F'}, - {"0N&1BF", 'F'}, - {"0N&1BN", 'F'}, - {"0N&1BS", 'F'}, - {"0N&1BV", 'F'}, - {"0N&1C", 'F'}, - {"0N&1EK", 'F'}, - {"0N&1EN", 'F'}, - {"0N&1F(", 'F'}, - {"0N&1K(", 'F'}, - {"0N&1K1", 'F'}, - {"0N&1KF", 'F'}, - {"0N&1KN", 'F'}, - {"0N&1KS", 'F'}, - {"0N&1KV", 'F'}, - {"0N&1O(", 'F'}, - {"0N&1OF", 'F'}, - {"0N&1OO", 'F'}, - {"0N&1OS", 'F'}, - {"0N&1OV", 'F'}, - {"0N&1TN", 'F'}, - {"0N&1U", 'F'}, - {"0N&1U(", 'F'}, - {"0N&1U;", 'F'}, - {"0N&1UC", 'F'}, - {"0N&1UE", 'F'}, - {"0N&E((", 'F'}, - {"0N&E(1", 'F'}, - {"0N&E(F", 'F'}, - {"0N&E(N", 'F'}, - {"0N&E(O", 'F'}, - {"0N&E(S", 'F'}, - {"0N&E(V", 'F'}, - {"0N&E1", 'F'}, - {"0N&E1;", 'F'}, - {"0N&E1C", 'F'}, - {"0N&E1K", 'F'}, - {"0N&E1O", 'F'}, - {"0N&EF(", 'F'}, - {"0N&EK(", 'F'}, - {"0N&EK1", 'F'}, - {"0N&EKF", 'F'}, - {"0N&EKN", 'F'}, - {"0N&EKS", 'F'}, - {"0N&EKV", 'F'}, - {"0N&EN;", 'F'}, - {"0N&ENC", 'F'}, - {"0N&ENK", 'F'}, - {"0N&ENO", 'F'}, - {"0N&ES", 'F'}, - {"0N&ES;", 'F'}, - {"0N&ESC", 'F'}, - {"0N&ESK", 'F'}, - {"0N&ESO", 'F'}, - {"0N&EV", 'F'}, - {"0N&EV;", 'F'}, - {"0N&EVC", 'F'}, - {"0N&EVK", 'F'}, - {"0N&EVO", 'F'}, - {"0N&F((", 'F'}, - {"0N&F()", 'F'}, - {"0N&F(1", 'F'}, - {"0N&F(E", 'F'}, - {"0N&F(F", 'F'}, - {"0N&F(N", 'F'}, - {"0N&F(S", 'F'}, - {"0N&F(V", 'F'}, - {"0N&K&(", 'F'}, - {"0N&K&1", 'F'}, - {"0N&K&F", 'F'}, - {"0N&K&N", 'F'}, - {"0N&K&S", 'F'}, - {"0N&K&V", 'F'}, - {"0N&K((", 'F'}, - {"0N&K(1", 'F'}, - {"0N&K(F", 'F'}, - {"0N&K(N", 'F'}, - {"0N&K(S", 'F'}, - {"0N&K(V", 'F'}, - {"0N&K1O", 'F'}, - {"0N&KF(", 'F'}, - {"0N&KNK", 'F'}, - {"0N&KO(", 'F'}, - {"0N&KO1", 'F'}, - {"0N&KOF", 'F'}, - {"0N&KOK", 'F'}, - {"0N&KON", 'F'}, - {"0N&KOS", 'F'}, - {"0N&KOV", 'F'}, - {"0N&KSO", 'F'}, - {"0N&KVO", 'F'}, - {"0N&N&(", 'F'}, - {"0N&N&1", 'F'}, - {"0N&N&F", 'F'}, - {"0N&N&S", 'F'}, - {"0N&N&V", 'F'}, - {"0N&N)&", 'F'}, - {"0N&N)O", 'F'}, - {"0N&N)U", 'F'}, - {"0N&N;C", 'F'}, - {"0N&N;E", 'F'}, - {"0N&N;T", 'F'}, - {"0N&NB(", 'F'}, - {"0N&NB1", 'F'}, - {"0N&NBF", 'F'}, - {"0N&NBN", 'F'}, - {"0N&NBS", 'F'}, - {"0N&NBV", 'F'}, - {"0N&NF(", 'F'}, - {"0N&NK(", 'F'}, - {"0N&NK1", 'F'}, - {"0N&NKF", 'F'}, - {"0N&NKS", 'F'}, - {"0N&NKV", 'F'}, - {"0N&NO(", 'F'}, - {"0N&NOF", 'F'}, - {"0N&NOS", 'F'}, - {"0N&NOV", 'F'}, - {"0N&NU", 'F'}, - {"0N&NU(", 'F'}, - {"0N&NU;", 'F'}, - {"0N&NUC", 'F'}, - {"0N&NUE", 'F'}, - {"0N&S&(", 'F'}, - {"0N&S&1", 'F'}, - {"0N&S&F", 'F'}, - {"0N&S&N", 'F'}, - {"0N&S&S", 'F'}, - {"0N&S&V", 'F'}, - {"0N&S)&", 'F'}, - {"0N&S)O", 'F'}, - {"0N&S)U", 'F'}, - {"0N&S1", 'F'}, - {"0N&S1;", 'F'}, - {"0N&S1C", 'F'}, - {"0N&S1O", 'F'}, - {"0N&S;", 'F'}, - {"0N&S;C", 'F'}, - {"0N&S;E", 'F'}, - {"0N&S;T", 'F'}, - {"0N&SB(", 'F'}, - {"0N&SB1", 'F'}, - {"0N&SBF", 'F'}, - {"0N&SBN", 'F'}, - {"0N&SBS", 'F'}, - {"0N&SBV", 'F'}, - {"0N&SC", 'F'}, - {"0N&SEK", 'F'}, - {"0N&SEN", 'F'}, - {"0N&SF(", 'F'}, - {"0N&SK(", 'F'}, - {"0N&SK1", 'F'}, - {"0N&SKF", 'F'}, - {"0N&SKN", 'F'}, - {"0N&SKS", 'F'}, - {"0N&SKV", 'F'}, - {"0N&SO(", 'F'}, - {"0N&SO1", 'F'}, - {"0N&SOF", 'F'}, - {"0N&SON", 'F'}, - {"0N&SOO", 'F'}, - {"0N&SOS", 'F'}, - {"0N&SOV", 'F'}, - {"0N&STN", 'F'}, - {"0N&SU", 'F'}, - {"0N&SU(", 'F'}, - {"0N&SU;", 'F'}, - {"0N&SUC", 'F'}, - {"0N&SUE", 'F'}, - {"0N&SV", 'F'}, - {"0N&SV;", 'F'}, - {"0N&SVC", 'F'}, - {"0N&SVO", 'F'}, - {"0N&T((", 'F'}, - {"0N&T(1", 'F'}, - {"0N&T(F", 'F'}, - {"0N&T(N", 'F'}, - {"0N&T(S", 'F'}, - {"0N&T(V", 'F'}, - {"0N&V", 'F'}, - {"0N&V&(", 'F'}, - {"0N&V&1", 'F'}, - {"0N&V&F", 'F'}, - {"0N&V&N", 'F'}, - {"0N&V&S", 'F'}, - {"0N&V&V", 'F'}, - {"0N&V)&", 'F'}, - {"0N&V)O", 'F'}, - {"0N&V)U", 'F'}, - {"0N&V;", 'F'}, - {"0N&V;C", 'F'}, - {"0N&V;E", 'F'}, - {"0N&V;T", 'F'}, - {"0N&VB(", 'F'}, - {"0N&VB1", 'F'}, - {"0N&VBF", 'F'}, - {"0N&VBN", 'F'}, - {"0N&VBS", 'F'}, - {"0N&VBV", 'F'}, - {"0N&VC", 'F'}, - {"0N&VEK", 'F'}, - {"0N&VEN", 'F'}, - {"0N&VF(", 'F'}, - {"0N&VK(", 'F'}, - {"0N&VK1", 'F'}, - {"0N&VKF", 'F'}, - {"0N&VKN", 'F'}, - {"0N&VKS", 'F'}, - {"0N&VKV", 'F'}, - {"0N&VO(", 'F'}, - {"0N&VOF", 'F'}, - {"0N&VOO", 'F'}, - {"0N&VOS", 'F'}, - {"0N&VS", 'F'}, - {"0N&VS;", 'F'}, - {"0N&VSC", 'F'}, - {"0N&VSO", 'F'}, - {"0N&VTN", 'F'}, - {"0N&VU", 'F'}, - {"0N&VU(", 'F'}, - {"0N&VU;", 'F'}, - {"0N&VUC", 'F'}, - {"0N&VUE", 'F'}, - {"0N((((", 'F'}, - {"0N(((1", 'F'}, - {"0N(((E", 'F'}, - {"0N(((F", 'F'}, - {"0N(((N", 'F'}, - {"0N(((S", 'F'}, - {"0N(((U", 'F'}, - {"0N(((V", 'F'}, - {"0N((1)", 'F'}, - {"0N((1O", 'F'}, - {"0N((EK", 'F'}, - {"0N((EN", 'F'}, - {"0N((F(", 'F'}, - {"0N((N)", 'F'}, - {"0N((NO", 'F'}, - {"0N((S)", 'F'}, - {"0N((SO", 'F'}, - {"0N((U(", 'F'}, - {"0N((V)", 'F'}, - {"0N((VO", 'F'}, - {"0N(1)F", 'F'}, - {"0N(1)O", 'F'}, - {"0N(1)U", 'F'}, - {"0N(1)V", 'F'}, - {"0N(1O(", 'F'}, - {"0N(1OF", 'F'}, - {"0N(1OS", 'F'}, - {"0N(1OV", 'F'}, - {"0N(EKN", 'F'}, - {"0N(ENK", 'F'}, - {"0N(F((", 'F'}, - {"0N(F()", 'F'}, - {"0N(F(1", 'F'}, - {"0N(F(F", 'F'}, - {"0N(F(N", 'F'}, - {"0N(F(S", 'F'}, - {"0N(F(V", 'F'}, - {"0N(S)1", 'F'}, - {"0N(S)F", 'F'}, - {"0N(S)N", 'F'}, - {"0N(S)O", 'F'}, - {"0N(S)S", 'F'}, - {"0N(S)U", 'F'}, - {"0N(S)V", 'F'}, - {"0N(SO(", 'F'}, - {"0N(SO1", 'F'}, - {"0N(SOF", 'F'}, - {"0N(SON", 'F'}, - {"0N(SOS", 'F'}, - {"0N(SOV", 'F'}, - {"0N(U((", 'F'}, - {"0N(U(E", 'F'}, - {"0N(V)1", 'F'}, - {"0N(V)F", 'F'}, - {"0N(V)N", 'F'}, - {"0N(V)O", 'F'}, - {"0N(V)S", 'F'}, - {"0N(V)U", 'F'}, - {"0N(V)V", 'F'}, - {"0N(VO(", 'F'}, - {"0N(VOF", 'F'}, - {"0N(VOS", 'F'}, - {"0N)&((", 'F'}, - {"0N)&(1", 'F'}, - {"0N)&(E", 'F'}, - {"0N)&(F", 'F'}, - {"0N)&(N", 'F'}, - {"0N)&(S", 'F'}, - {"0N)&(V", 'F'}, - {"0N)&1", 'F'}, - {"0N)&1&", 'F'}, - {"0N)&1)", 'F'}, - {"0N)&1;", 'F'}, - {"0N)&1B", 'F'}, - {"0N)&1C", 'F'}, - {"0N)&1F", 'F'}, - {"0N)&1O", 'F'}, - {"0N)&1U", 'F'}, - {"0N)&F(", 'F'}, - {"0N)&N", 'F'}, - {"0N)&N&", 'F'}, - {"0N)&N)", 'F'}, - {"0N)&N;", 'F'}, - {"0N)&NB", 'F'}, - {"0N)&NC", 'F'}, - {"0N)&NF", 'F'}, - {"0N)&NO", 'F'}, - {"0N)&NU", 'F'}, - {"0N)&S", 'F'}, - {"0N)&S&", 'F'}, - {"0N)&S)", 'F'}, - {"0N)&S;", 'F'}, - {"0N)&SB", 'F'}, - {"0N)&SC", 'F'}, - {"0N)&SF", 'F'}, - {"0N)&SO", 'F'}, - {"0N)&SU", 'F'}, - {"0N)&V", 'F'}, - {"0N)&V&", 'F'}, - {"0N)&V)", 'F'}, - {"0N)&V;", 'F'}, - {"0N)&VB", 'F'}, - {"0N)&VC", 'F'}, - {"0N)&VF", 'F'}, - {"0N)&VO", 'F'}, - {"0N)&VU", 'F'}, - {"0N),((", 'F'}, - {"0N),(1", 'F'}, - {"0N),(F", 'F'}, - {"0N),(N", 'F'}, - {"0N),(S", 'F'}, - {"0N),(V", 'F'}, - {"0N);E(", 'F'}, - {"0N);E1", 'F'}, - {"0N);EF", 'F'}, - {"0N);EK", 'F'}, - {"0N);EN", 'F'}, - {"0N);EO", 'F'}, - {"0N);ES", 'F'}, - {"0N);EV", 'F'}, - {"0N);T(", 'F'}, - {"0N);T1", 'F'}, - {"0N);TF", 'F'}, - {"0N);TK", 'F'}, - {"0N);TN", 'F'}, - {"0N);TO", 'F'}, - {"0N);TS", 'F'}, - {"0N);TV", 'F'}, - {"0N)B((", 'F'}, - {"0N)B(1", 'F'}, - {"0N)B(F", 'F'}, - {"0N)B(N", 'F'}, - {"0N)B(S", 'F'}, - {"0N)B(V", 'F'}, - {"0N)B1", 'F'}, - {"0N)B1&", 'F'}, - {"0N)B1;", 'F'}, - {"0N)B1C", 'F'}, - {"0N)B1K", 'F'}, - {"0N)B1N", 'F'}, - {"0N)B1O", 'F'}, - {"0N)B1U", 'F'}, - {"0N)BF(", 'F'}, - {"0N)BN", 'F'}, - {"0N)BN&", 'F'}, - {"0N)BN;", 'F'}, - {"0N)BNC", 'F'}, - {"0N)BNK", 'F'}, - {"0N)BNO", 'F'}, - {"0N)BNU", 'F'}, - {"0N)BS", 'F'}, - {"0N)BS&", 'F'}, - {"0N)BS;", 'F'}, - {"0N)BSC", 'F'}, - {"0N)BSK", 'F'}, - {"0N)BSO", 'F'}, - {"0N)BSU", 'F'}, - {"0N)BV", 'F'}, - {"0N)BV&", 'F'}, - {"0N)BV;", 'F'}, - {"0N)BVC", 'F'}, - {"0N)BVK", 'F'}, - {"0N)BVO", 'F'}, - {"0N)BVU", 'F'}, - {"0N)E((", 'F'}, - {"0N)E(1", 'F'}, - {"0N)E(F", 'F'}, - {"0N)E(N", 'F'}, - {"0N)E(S", 'F'}, - {"0N)E(V", 'F'}, - {"0N)E1C", 'F'}, - {"0N)E1O", 'F'}, - {"0N)EF(", 'F'}, - {"0N)EK(", 'F'}, - {"0N)EK1", 'F'}, - {"0N)EKF", 'F'}, - {"0N)EKN", 'F'}, - {"0N)EKS", 'F'}, - {"0N)EKV", 'F'}, - {"0N)ENC", 'F'}, - {"0N)ENO", 'F'}, - {"0N)ESC", 'F'}, - {"0N)ESO", 'F'}, - {"0N)EVC", 'F'}, - {"0N)EVO", 'F'}, - {"0N)K((", 'F'}, - {"0N)K(1", 'F'}, - {"0N)K(F", 'F'}, - {"0N)K(N", 'F'}, - {"0N)K(S", 'F'}, - {"0N)K(V", 'F'}, - {"0N)K1&", 'F'}, - {"0N)K1;", 'F'}, - {"0N)K1B", 'F'}, - {"0N)K1E", 'F'}, - {"0N)K1O", 'F'}, - {"0N)K1U", 'F'}, - {"0N)KB(", 'F'}, - {"0N)KB1", 'F'}, - {"0N)KBF", 'F'}, - {"0N)KBN", 'F'}, - {"0N)KBS", 'F'}, - {"0N)KBV", 'F'}, - {"0N)KF(", 'F'}, - {"0N)KN&", 'F'}, - {"0N)KN;", 'F'}, - {"0N)KNB", 'F'}, - {"0N)KNE", 'F'}, - {"0N)KNK", 'F'}, - {"0N)KNU", 'F'}, - {"0N)KS&", 'F'}, - {"0N)KS;", 'F'}, - {"0N)KSB", 'F'}, - {"0N)KSE", 'F'}, - {"0N)KSO", 'F'}, - {"0N)KSU", 'F'}, - {"0N)KUE", 'F'}, - {"0N)KV&", 'F'}, - {"0N)KV;", 'F'}, - {"0N)KVB", 'F'}, - {"0N)KVE", 'F'}, - {"0N)KVO", 'F'}, - {"0N)KVU", 'F'}, - {"0N)O((", 'F'}, - {"0N)O(1", 'F'}, - {"0N)O(E", 'F'}, - {"0N)O(F", 'F'}, - {"0N)O(N", 'F'}, - {"0N)O(S", 'F'}, - {"0N)O(V", 'F'}, - {"0N)O1&", 'F'}, - {"0N)O1)", 'F'}, - {"0N)O1;", 'F'}, - {"0N)O1B", 'F'}, - {"0N)O1C", 'F'}, - {"0N)O1K", 'F'}, - {"0N)O1O", 'F'}, - {"0N)O1U", 'F'}, - {"0N)OF(", 'F'}, - {"0N)ON", 'F'}, - {"0N)ON&", 'F'}, - {"0N)ON)", 'F'}, - {"0N)ON;", 'F'}, - {"0N)ONB", 'F'}, - {"0N)ONC", 'F'}, - {"0N)ONK", 'F'}, - {"0N)ONO", 'F'}, - {"0N)ONU", 'F'}, - {"0N)OS", 'F'}, - {"0N)OS&", 'F'}, - {"0N)OS)", 'F'}, - {"0N)OS;", 'F'}, - {"0N)OSB", 'F'}, - {"0N)OSC", 'F'}, - {"0N)OSK", 'F'}, - {"0N)OSO", 'F'}, - {"0N)OSU", 'F'}, - {"0N)OV", 'F'}, - {"0N)OV&", 'F'}, - {"0N)OV)", 'F'}, - {"0N)OV;", 'F'}, - {"0N)OVB", 'F'}, - {"0N)OVC", 'F'}, - {"0N)OVK", 'F'}, - {"0N)OVO", 'F'}, - {"0N)OVU", 'F'}, - {"0N)U((", 'F'}, - {"0N)U(E", 'F'}, - {"0N)UE(", 'F'}, - {"0N)UE1", 'F'}, - {"0N)UEF", 'F'}, - {"0N)UEK", 'F'}, - {"0N)UEN", 'F'}, - {"0N)UES", 'F'}, - {"0N)UEV", 'F'}, - {"0N,(((", 'F'}, - {"0N,((1", 'F'}, - {"0N,((E", 'F'}, - {"0N,((F", 'F'}, - {"0N,((N", 'F'}, - {"0N,((S", 'F'}, - {"0N,((V", 'F'}, - {"0N,(1)", 'F'}, - {"0N,(1O", 'F'}, - {"0N,(E(", 'F'}, - {"0N,(E1", 'F'}, - {"0N,(EF", 'F'}, - {"0N,(EK", 'F'}, - {"0N,(EN", 'F'}, - {"0N,(ES", 'F'}, - {"0N,(EV", 'F'}, - {"0N,(F(", 'F'}, - {"0N,(NO", 'F'}, - {"0N,(S)", 'F'}, - {"0N,(SO", 'F'}, - {"0N,(V)", 'F'}, - {"0N,(VO", 'F'}, - {"0N,F((", 'F'}, - {"0N,F()", 'F'}, - {"0N,F(1", 'F'}, - {"0N,F(F", 'F'}, - {"0N,F(N", 'F'}, - {"0N,F(S", 'F'}, - {"0N,F(T", 'F'}, - {"0N,F(V", 'F'}, - {"0N,V),", 'F'}, - {"0N,V)O", 'F'}, - {"0N,V,(", 'F'}, - {"0N,V,1", 'F'}, - {"0N,V,F", 'F'}, - {"0N,V,N", 'F'}, - {"0N,V,S", 'F'}, - {"0N,V,V", 'F'}, - {"0N,VB(", 'F'}, - {"0N,VB1", 'F'}, - {"0N,VBF", 'F'}, - {"0N,VBN", 'F'}, - {"0N,VBS", 'F'}, - {"0N,VBV", 'F'}, - {"0N,VO(", 'F'}, - {"0N,VOF", 'F'}, - {"0N,VOS", 'F'}, - {"0N,VUE", 'F'}, - {"0N1F((", 'F'}, - {"0N1F()", 'F'}, - {"0N1F(1", 'F'}, - {"0N1F(F", 'F'}, - {"0N1F(N", 'F'}, - {"0N1F(S", 'F'}, - {"0N1F(V", 'F'}, - {"0N1O((", 'F'}, - {"0N1O(1", 'F'}, - {"0N1O(F", 'F'}, - {"0N1O(N", 'F'}, - {"0N1O(S", 'F'}, - {"0N1O(V", 'F'}, - {"0N1OF(", 'F'}, - {"0N1OS(", 'F'}, - {"0N1OS1", 'F'}, - {"0N1OSF", 'F'}, - {"0N1OSO", 'F'}, - {"0N1OSU", 'F'}, - {"0N1OSV", 'F'}, - {"0N1OV(", 'F'}, - {"0N1OVF", 'F'}, - {"0N1OVO", 'F'}, - {"0N1OVS", 'F'}, - {"0N1OVU", 'F'}, - {"0N1S;", 'F'}, - {"0N1S;C", 'F'}, - {"0N1SC", 'F'}, - {"0N1UE", 'F'}, - {"0N1UE;", 'F'}, - {"0N1UEC", 'F'}, - {"0N1UEK", 'F'}, - {"0N1V;", 'F'}, - {"0N1V;C", 'F'}, - {"0N1VC", 'F'}, - {"0N1VO(", 'F'}, - {"0N1VOF", 'F'}, - {"0N1VOS", 'F'}, - {"0N;E((", 'F'}, - {"0N;E(1", 'F'}, - {"0N;E(E", 'F'}, - {"0N;E(F", 'F'}, - {"0N;E(N", 'F'}, - {"0N;E(S", 'F'}, - {"0N;E(V", 'F'}, - {"0N;E1,", 'F'}, - {"0N;E1;", 'F'}, - {"0N;E1C", 'F'}, - {"0N;E1K", 'F'}, - {"0N;E1O", 'F'}, - {"0N;E1T", 'F'}, - {"0N;EF(", 'F'}, - {"0N;EK(", 'F'}, - {"0N;EK1", 'F'}, - {"0N;EKF", 'F'}, - {"0N;EKN", 'F'}, - {"0N;EKO", 'F'}, - {"0N;EKS", 'F'}, - {"0N;EKV", 'F'}, - {"0N;EN,", 'F'}, - {"0N;EN;", 'F'}, - {"0N;ENC", 'F'}, - {"0N;ENK", 'F'}, - {"0N;ENO", 'F'}, - {"0N;ENT", 'F'}, - {"0N;EO(", 'F'}, - {"0N;ES,", 'F'}, - {"0N;ES;", 'F'}, - {"0N;ESC", 'F'}, - {"0N;ESK", 'F'}, - {"0N;ESO", 'F'}, - {"0N;EST", 'F'}, - {"0N;EV,", 'F'}, - {"0N;EV;", 'F'}, - {"0N;EVC", 'F'}, - {"0N;EVK", 'F'}, - {"0N;EVO", 'F'}, - {"0N;EVT", 'F'}, - {"0N;N:T", 'F'}, - {"0N;T((", 'F'}, - {"0N;T(1", 'F'}, - {"0N;T(E", 'F'}, - {"0N;T(F", 'F'}, - {"0N;T(N", 'F'}, - {"0N;T(S", 'F'}, - {"0N;T(V", 'F'}, - {"0N;T1,", 'F'}, - {"0N;T1;", 'F'}, - {"0N;T1C", 'F'}, - {"0N;T1F", 'F'}, - {"0N;T1K", 'F'}, - {"0N;T1O", 'F'}, - {"0N;T1T", 'F'}, - {"0N;T;", 'F'}, - {"0N;T;C", 'F'}, - {"0N;TF(", 'F'}, - {"0N;TK(", 'F'}, - {"0N;TK1", 'F'}, - {"0N;TKF", 'F'}, - {"0N;TKK", 'F'}, - {"0N;TKN", 'F'}, - {"0N;TKO", 'F'}, - {"0N;TKS", 'F'}, - {"0N;TKV", 'F'}, - {"0N;TN(", 'F'}, - {"0N;TN,", 'F'}, - {"0N;TN1", 'F'}, - {"0N;TN;", 'F'}, - {"0N;TNC", 'F'}, - {"0N;TNE", 'F'}, - {"0N;TNF", 'F'}, - {"0N;TNK", 'F'}, - {"0N;TNN", 'F'}, - {"0N;TNO", 'F'}, - {"0N;TNS", 'F'}, - {"0N;TNT", 'F'}, - {"0N;TNV", 'F'}, - {"0N;TO(", 'F'}, - {"0N;TS,", 'F'}, - {"0N;TS;", 'F'}, - {"0N;TSC", 'F'}, - {"0N;TSF", 'F'}, - {"0N;TSK", 'F'}, - {"0N;TSO", 'F'}, - {"0N;TST", 'F'}, - {"0N;TT(", 'F'}, - {"0N;TT1", 'F'}, - {"0N;TTF", 'F'}, - {"0N;TTN", 'F'}, - {"0N;TTS", 'F'}, - {"0N;TTV", 'F'}, - {"0N;TV,", 'F'}, - {"0N;TV;", 'F'}, - {"0N;TVC", 'F'}, - {"0N;TVF", 'F'}, - {"0N;TVK", 'F'}, - {"0N;TVO", 'F'}, - {"0N;TVT", 'F'}, - {"0NA(((", 'F'}, - {"0NA((1", 'F'}, - {"0NA((F", 'F'}, - {"0NA((N", 'F'}, - {"0NA((S", 'F'}, - {"0NA((V", 'F'}, - {"0NA(F(", 'F'}, - {"0NA(N)", 'F'}, - {"0NA(NO", 'F'}, - {"0NA(S)", 'F'}, - {"0NA(SO", 'F'}, - {"0NA(V)", 'F'}, - {"0NA(VO", 'F'}, - {"0NAF((", 'F'}, - {"0NAF()", 'F'}, - {"0NAF(1", 'F'}, - {"0NAF(F", 'F'}, - {"0NAF(N", 'F'}, - {"0NAF(S", 'F'}, - {"0NAF(V", 'F'}, - {"0NASO(", 'F'}, - {"0NASO1", 'F'}, - {"0NASOF", 'F'}, - {"0NASON", 'F'}, - {"0NASOS", 'F'}, - {"0NASOV", 'F'}, - {"0NASUE", 'F'}, - {"0NATO(", 'F'}, - {"0NATO1", 'F'}, - {"0NATOF", 'F'}, - {"0NATON", 'F'}, - {"0NATOS", 'F'}, - {"0NATOV", 'F'}, - {"0NATUE", 'F'}, - {"0NAVO(", 'F'}, - {"0NAVOF", 'F'}, - {"0NAVOS", 'F'}, - {"0NAVUE", 'F'}, - {"0NB(((", 'F'}, - {"0NB((1", 'F'}, - {"0NB((F", 'F'}, - {"0NB((N", 'F'}, - {"0NB((S", 'F'}, - {"0NB((V", 'F'}, - {"0NB(1)", 'F'}, - {"0NB(1O", 'F'}, - {"0NB(F(", 'F'}, - {"0NB(N)", 'F'}, - {"0NB(NO", 'F'}, - {"0NB(S)", 'F'}, - {"0NB(SO", 'F'}, - {"0NB(V)", 'F'}, - {"0NB(VO", 'F'}, - {"0NB1", 'F'}, - {"0NB1&(", 'F'}, - {"0NB1&1", 'F'}, - {"0NB1&F", 'F'}, - {"0NB1&N", 'F'}, - {"0NB1&S", 'F'}, - {"0NB1&V", 'F'}, - {"0NB1,(", 'F'}, - {"0NB1,F", 'F'}, - {"0NB1,V", 'F'}, - {"0NB1;", 'F'}, - {"0NB1;C", 'F'}, - {"0NB1B(", 'F'}, - {"0NB1B1", 'F'}, - {"0NB1BF", 'F'}, - {"0NB1BN", 'F'}, - {"0NB1BS", 'F'}, - {"0NB1BV", 'F'}, - {"0NB1C", 'F'}, - {"0NB1K(", 'F'}, - {"0NB1K1", 'F'}, - {"0NB1KF", 'F'}, - {"0NB1KN", 'F'}, - {"0NB1KS", 'F'}, - {"0NB1KV", 'F'}, - {"0NB1O(", 'F'}, - {"0NB1OF", 'F'}, - {"0NB1OS", 'F'}, - {"0NB1OV", 'F'}, - {"0NB1U(", 'F'}, - {"0NB1UE", 'F'}, - {"0NBE((", 'F'}, - {"0NBE(1", 'F'}, - {"0NBE(F", 'F'}, - {"0NBE(N", 'F'}, - {"0NBE(S", 'F'}, - {"0NBE(V", 'F'}, - {"0NBEK(", 'F'}, - {"0NBF((", 'F'}, - {"0NBF()", 'F'}, - {"0NBF(1", 'F'}, - {"0NBF(F", 'F'}, - {"0NBF(N", 'F'}, - {"0NBF(S", 'F'}, - {"0NBF(V", 'F'}, - {"0NBN&(", 'F'}, - {"0NBN&1", 'F'}, - {"0NBN&F", 'F'}, - {"0NBN&N", 'F'}, - {"0NBN&S", 'F'}, - {"0NBN&V", 'F'}, - {"0NBN,(", 'F'}, - {"0NBN,F", 'F'}, - {"0NBN,V", 'F'}, - {"0NBN;", 'F'}, - {"0NBN;C", 'F'}, - {"0NBNB(", 'F'}, - {"0NBNB1", 'F'}, - {"0NBNBF", 'F'}, - {"0NBNBN", 'F'}, - {"0NBNBS", 'F'}, - {"0NBNBV", 'F'}, - {"0NBNC", 'F'}, - {"0NBNK(", 'F'}, - {"0NBNK1", 'F'}, - {"0NBNKF", 'F'}, - {"0NBNKN", 'F'}, - {"0NBNKS", 'F'}, - {"0NBNKV", 'F'}, - {"0NBNO(", 'F'}, - {"0NBNOF", 'F'}, - {"0NBNOS", 'F'}, - {"0NBNOV", 'F'}, - {"0NBNU(", 'F'}, - {"0NBNUE", 'F'}, - {"0NBS", 'F'}, - {"0NBS&(", 'F'}, - {"0NBS&1", 'F'}, - {"0NBS&F", 'F'}, - {"0NBS&N", 'F'}, - {"0NBS&S", 'F'}, - {"0NBS&V", 'F'}, - {"0NBS,(", 'F'}, - {"0NBS,F", 'F'}, - {"0NBS,V", 'F'}, - {"0NBS;", 'F'}, - {"0NBS;C", 'F'}, - {"0NBSB(", 'F'}, - {"0NBSB1", 'F'}, - {"0NBSBF", 'F'}, - {"0NBSBN", 'F'}, - {"0NBSBS", 'F'}, - {"0NBSBV", 'F'}, - {"0NBSC", 'F'}, - {"0NBSK(", 'F'}, - {"0NBSK1", 'F'}, - {"0NBSKF", 'F'}, - {"0NBSKN", 'F'}, - {"0NBSKS", 'F'}, - {"0NBSKV", 'F'}, - {"0NBSO(", 'F'}, - {"0NBSO1", 'F'}, - {"0NBSOF", 'F'}, - {"0NBSON", 'F'}, - {"0NBSOS", 'F'}, - {"0NBSOV", 'F'}, - {"0NBSU(", 'F'}, - {"0NBSUE", 'F'}, - {"0NBV", 'F'}, - {"0NBV&(", 'F'}, - {"0NBV&1", 'F'}, - {"0NBV&F", 'F'}, - {"0NBV&N", 'F'}, - {"0NBV&S", 'F'}, - {"0NBV&V", 'F'}, - {"0NBV,(", 'F'}, - {"0NBV,1", 'F'}, - {"0NBV,F", 'F'}, - {"0NBV,N", 'F'}, - {"0NBV,S", 'F'}, - {"0NBV,V", 'F'}, - {"0NBV;", 'F'}, - {"0NBV;C", 'F'}, - {"0NBVB(", 'F'}, - {"0NBVB1", 'F'}, - {"0NBVBF", 'F'}, - {"0NBVBN", 'F'}, - {"0NBVBS", 'F'}, - {"0NBVBV", 'F'}, - {"0NBVC", 'F'}, - {"0NBVK(", 'F'}, - {"0NBVK1", 'F'}, - {"0NBVKF", 'F'}, - {"0NBVKN", 'F'}, - {"0NBVKS", 'F'}, - {"0NBVKV", 'F'}, - {"0NBVO(", 'F'}, - {"0NBVOF", 'F'}, - {"0NBVOS", 'F'}, - {"0NBVU(", 'F'}, - {"0NBVUE", 'F'}, - {"0NC", 'F'}, - {"0NE(((", 'F'}, - {"0NE((1", 'F'}, - {"0NE((F", 'F'}, - {"0NE((N", 'F'}, - {"0NE((S", 'F'}, - {"0NE((V", 'F'}, - {"0NE(1)", 'F'}, - {"0NE(1O", 'F'}, - {"0NE(F(", 'F'}, - {"0NE(N)", 'F'}, - {"0NE(NO", 'F'}, - {"0NE(S)", 'F'}, - {"0NE(SO", 'F'}, - {"0NE(V)", 'F'}, - {"0NE(VO", 'F'}, - {"0NE1C", 'F'}, - {"0NE1O(", 'F'}, - {"0NE1OF", 'F'}, - {"0NE1OS", 'F'}, - {"0NE1OV", 'F'}, - {"0NE1UE", 'F'}, - {"0NEF((", 'F'}, - {"0NEF()", 'F'}, - {"0NEF(1", 'F'}, - {"0NEF(F", 'F'}, - {"0NEF(N", 'F'}, - {"0NEF(S", 'F'}, - {"0NEF(V", 'F'}, - {"0NENC", 'F'}, - {"0NENO(", 'F'}, - {"0NENOF", 'F'}, - {"0NENOS", 'F'}, - {"0NENOV", 'F'}, - {"0NENUE", 'F'}, - {"0NEOKN", 'F'}, - {"0NESC", 'F'}, - {"0NESO(", 'F'}, - {"0NESO1", 'F'}, - {"0NESOF", 'F'}, - {"0NESON", 'F'}, - {"0NESOS", 'F'}, - {"0NESOV", 'F'}, - {"0NESUE", 'F'}, - {"0NEU((", 'F'}, - {"0NEU(1", 'F'}, - {"0NEU(F", 'F'}, - {"0NEU(N", 'F'}, - {"0NEU(S", 'F'}, - {"0NEU(V", 'F'}, - {"0NEU1,", 'F'}, - {"0NEU1C", 'F'}, - {"0NEU1O", 'F'}, - {"0NEUEF", 'F'}, - {"0NEUEK", 'F'}, - {"0NEUF(", 'F'}, - {"0NEUN,", 'F'}, - {"0NEUNC", 'F'}, - {"0NEUNO", 'F'}, - {"0NEUS,", 'F'}, - {"0NEUSC", 'F'}, - {"0NEUSO", 'F'}, - {"0NEUV,", 'F'}, - {"0NEUVC", 'F'}, - {"0NEUVO", 'F'}, - {"0NEVC", 'F'}, - {"0NEVO(", 'F'}, - {"0NEVOF", 'F'}, - {"0NEVOS", 'F'}, - {"0NEVUE", 'F'}, - {"0NF(((", 'F'}, - {"0NF(()", 'F'}, - {"0NF((1", 'F'}, - {"0NF((F", 'F'}, - {"0NF((N", 'F'}, - {"0NF((S", 'F'}, - {"0NF((V", 'F'}, - {"0NF()1", 'F'}, - {"0NF()F", 'F'}, - {"0NF()K", 'F'}, - {"0NF()N", 'F'}, - {"0NF()O", 'F'}, - {"0NF()S", 'F'}, - {"0NF()U", 'F'}, - {"0NF()V", 'F'}, - {"0NF(1)", 'F'}, - {"0NF(1N", 'F'}, - {"0NF(1O", 'F'}, - {"0NF(E1", 'F'}, - {"0NF(F(", 'F'}, - {"0NF(N)", 'F'}, - {"0NF(N,", 'F'}, - {"0NF(NO", 'F'}, - {"0NF(S)", 'F'}, - {"0NF(SO", 'F'}, - {"0NF(V)", 'F'}, - {"0NF(VO", 'F'}, - {"0NK(((", 'F'}, - {"0NK((1", 'F'}, - {"0NK((F", 'F'}, - {"0NK((N", 'F'}, - {"0NK((S", 'F'}, - {"0NK((V", 'F'}, - {"0NK(1)", 'F'}, - {"0NK(1O", 'F'}, - {"0NK(F(", 'F'}, - {"0NK(N)", 'F'}, - {"0NK(NO", 'F'}, - {"0NK(S)", 'F'}, - {"0NK(SO", 'F'}, - {"0NK(V)", 'F'}, - {"0NK(VO", 'F'}, - {"0NK)&(", 'F'}, - {"0NK)&1", 'F'}, - {"0NK)&F", 'F'}, - {"0NK)&N", 'F'}, - {"0NK)&S", 'F'}, - {"0NK)&V", 'F'}, - {"0NK);E", 'F'}, - {"0NK);T", 'F'}, - {"0NK)B(", 'F'}, - {"0NK)B1", 'F'}, - {"0NK)BF", 'F'}, - {"0NK)BN", 'F'}, - {"0NK)BS", 'F'}, - {"0NK)BV", 'F'}, - {"0NK)E(", 'F'}, - {"0NK)E1", 'F'}, - {"0NK)EF", 'F'}, - {"0NK)EK", 'F'}, - {"0NK)EN", 'F'}, - {"0NK)ES", 'F'}, - {"0NK)EV", 'F'}, - {"0NK)OF", 'F'}, - {"0NK)UE", 'F'}, - {"0NK1", 'F'}, - {"0NK1&(", 'F'}, - {"0NK1&1", 'F'}, - {"0NK1&F", 'F'}, - {"0NK1&N", 'F'}, - {"0NK1&S", 'F'}, - {"0NK1&V", 'F'}, - {"0NK1;C", 'F'}, - {"0NK1;E", 'F'}, - {"0NK1;T", 'F'}, - {"0NK1B(", 'F'}, - {"0NK1B1", 'F'}, - {"0NK1BF", 'F'}, - {"0NK1BN", 'F'}, - {"0NK1BS", 'F'}, - {"0NK1BV", 'F'}, - {"0NK1C", 'F'}, - {"0NK1E(", 'F'}, - {"0NK1E1", 'F'}, - {"0NK1EF", 'F'}, - {"0NK1EK", 'F'}, - {"0NK1EN", 'F'}, - {"0NK1ES", 'F'}, - {"0NK1EV", 'F'}, - {"0NK1O(", 'F'}, - {"0NK1OF", 'F'}, - {"0NK1OS", 'F'}, - {"0NK1OV", 'F'}, - {"0NK1U(", 'F'}, - {"0NK1UE", 'F'}, - {"0NKF((", 'F'}, - {"0NKF()", 'F'}, - {"0NKF(1", 'F'}, - {"0NKF(F", 'F'}, - {"0NKF(N", 'F'}, - {"0NKF(S", 'F'}, - {"0NKF(V", 'F'}, - {"0NKN", 'F'}, - {"0NKN&(", 'F'}, - {"0NKN&1", 'F'}, - {"0NKN&F", 'F'}, - {"0NKN&S", 'F'}, - {"0NKN&V", 'F'}, - {"0NKN;C", 'F'}, - {"0NKN;E", 'F'}, - {"0NKN;T", 'F'}, - {"0NKNB(", 'F'}, - {"0NKNB1", 'F'}, - {"0NKNBF", 'F'}, - {"0NKNBN", 'F'}, - {"0NKNBS", 'F'}, - {"0NKNBV", 'F'}, - {"0NKNC", 'F'}, - {"0NKNE(", 'F'}, - {"0NKNE1", 'F'}, - {"0NKNEF", 'F'}, - {"0NKNES", 'F'}, - {"0NKNEV", 'F'}, - {"0NKNU(", 'F'}, - {"0NKNUE", 'F'}, - {"0NKS", 'F'}, - {"0NKS&(", 'F'}, - {"0NKS&1", 'F'}, - {"0NKS&F", 'F'}, - {"0NKS&N", 'F'}, - {"0NKS&S", 'F'}, - {"0NKS&V", 'F'}, - {"0NKS;", 'F'}, - {"0NKS;C", 'F'}, - {"0NKS;E", 'F'}, - {"0NKS;T", 'F'}, - {"0NKSB(", 'F'}, - {"0NKSB1", 'F'}, - {"0NKSBF", 'F'}, - {"0NKSBN", 'F'}, - {"0NKSBS", 'F'}, - {"0NKSBV", 'F'}, - {"0NKSC", 'F'}, - {"0NKSE(", 'F'}, - {"0NKSE1", 'F'}, - {"0NKSEF", 'F'}, - {"0NKSEK", 'F'}, - {"0NKSEN", 'F'}, - {"0NKSES", 'F'}, - {"0NKSEV", 'F'}, - {"0NKSO(", 'F'}, - {"0NKSO1", 'F'}, - {"0NKSOF", 'F'}, - {"0NKSON", 'F'}, - {"0NKSOS", 'F'}, - {"0NKSOV", 'F'}, - {"0NKSU(", 'F'}, - {"0NKSUE", 'F'}, - {"0NKUE(", 'F'}, - {"0NKUE1", 'F'}, - {"0NKUEF", 'F'}, - {"0NKUEK", 'F'}, - {"0NKUEN", 'F'}, - {"0NKUES", 'F'}, - {"0NKUEV", 'F'}, - {"0NKV", 'F'}, - {"0NKV&(", 'F'}, - {"0NKV&1", 'F'}, - {"0NKV&F", 'F'}, - {"0NKV&N", 'F'}, - {"0NKV&S", 'F'}, - {"0NKV&V", 'F'}, - {"0NKV;", 'F'}, - {"0NKV;C", 'F'}, - {"0NKV;E", 'F'}, - {"0NKV;T", 'F'}, - {"0NKVB(", 'F'}, - {"0NKVB1", 'F'}, - {"0NKVBF", 'F'}, - {"0NKVBN", 'F'}, - {"0NKVBS", 'F'}, - {"0NKVBV", 'F'}, - {"0NKVC", 'F'}, - {"0NKVE(", 'F'}, - {"0NKVE1", 'F'}, - {"0NKVEF", 'F'}, - {"0NKVEK", 'F'}, - {"0NKVEN", 'F'}, - {"0NKVES", 'F'}, - {"0NKVEV", 'F'}, - {"0NKVO(", 'F'}, - {"0NKVOF", 'F'}, - {"0NKVOS", 'F'}, - {"0NKVU(", 'F'}, - {"0NKVUE", 'F'}, - {"0NO(((", 'F'}, - {"0NO((1", 'F'}, - {"0NO((E", 'F'}, - {"0NO((F", 'F'}, - {"0NO((N", 'F'}, - {"0NO((O", 'F'}, - {"0NO((S", 'F'}, - {"0NO((T", 'F'}, - {"0NO((V", 'F'}, - {"0NO(1&", 'F'}, - {"0NO(1)", 'F'}, - {"0NO(1,", 'F'}, - {"0NO(1O", 'F'}, - {"0NO(E(", 'F'}, - {"0NO(E1", 'F'}, - {"0NO(EE", 'F'}, - {"0NO(EF", 'F'}, - {"0NO(EK", 'F'}, - {"0NO(EN", 'F'}, - {"0NO(ES", 'F'}, - {"0NO(EV", 'F'}, - {"0NO(F(", 'F'}, - {"0NO(N&", 'F'}, - {"0NO(N)", 'F'}, - {"0NO(N,", 'F'}, - {"0NO(NO", 'F'}, - {"0NO(O(", 'F'}, - {"0NO(O1", 'F'}, - {"0NO(OF", 'F'}, - {"0NO(ON", 'F'}, - {"0NO(OS", 'F'}, - {"0NO(OV", 'F'}, - {"0NO(S&", 'F'}, - {"0NO(S)", 'F'}, - {"0NO(S,", 'F'}, - {"0NO(SO", 'F'}, - {"0NO(T(", 'F'}, - {"0NO(V&", 'F'}, - {"0NO(V)", 'F'}, - {"0NO(V,", 'F'}, - {"0NO(VO", 'F'}, - {"0NOF((", 'F'}, - {"0NOF()", 'F'}, - {"0NOF(1", 'F'}, - {"0NOF(E", 'F'}, - {"0NOF(F", 'F'}, - {"0NOF(N", 'F'}, - {"0NOF(S", 'F'}, - {"0NOF(V", 'F'}, - {"0NOK&(", 'F'}, - {"0NOK&1", 'F'}, - {"0NOK&F", 'F'}, - {"0NOK&N", 'F'}, - {"0NOK&S", 'F'}, - {"0NOK&V", 'F'}, - {"0NOK((", 'F'}, - {"0NOK(1", 'F'}, - {"0NOK(F", 'F'}, - {"0NOK(N", 'F'}, - {"0NOK(S", 'F'}, - {"0NOK(V", 'F'}, - {"0NOK1C", 'F'}, - {"0NOK1O", 'F'}, - {"0NOKF(", 'F'}, - {"0NOKNC", 'F'}, - {"0NOKO(", 'F'}, - {"0NOKO1", 'F'}, - {"0NOKOF", 'F'}, - {"0NOKON", 'F'}, - {"0NOKOS", 'F'}, - {"0NOKOV", 'F'}, - {"0NOKSC", 'F'}, - {"0NOKSO", 'F'}, - {"0NOKVC", 'F'}, - {"0NOKVO", 'F'}, - {"0NONSU", 'F'}, - {"0NOS&(", 'F'}, - {"0NOS&1", 'F'}, - {"0NOS&E", 'F'}, - {"0NOS&F", 'F'}, - {"0NOS&K", 'F'}, - {"0NOS&N", 'F'}, - {"0NOS&S", 'F'}, - {"0NOS&T", 'F'}, - {"0NOS&U", 'F'}, - {"0NOS&V", 'F'}, - {"0NOS((", 'F'}, - {"0NOS(E", 'F'}, - {"0NOS(U", 'F'}, - {"0NOS)&", 'F'}, - {"0NOS),", 'F'}, - {"0NOS);", 'F'}, - {"0NOS)B", 'F'}, - {"0NOS)E", 'F'}, - {"0NOS)K", 'F'}, - {"0NOS)O", 'F'}, - {"0NOS)U", 'F'}, - {"0NOS,(", 'F'}, - {"0NOS,F", 'F'}, - {"0NOS,V", 'F'}, - {"0NOS1(", 'F'}, - {"0NOS1F", 'F'}, - {"0NOS1N", 'F'}, - {"0NOS1O", 'F'}, - {"0NOS1S", 'F'}, - {"0NOS1U", 'F'}, - {"0NOS1V", 'F'}, - {"0NOS;", 'F'}, - {"0NOS;C", 'F'}, - {"0NOS;E", 'F'}, - {"0NOS;T", 'F'}, - {"0NOSA(", 'F'}, - {"0NOSAF", 'F'}, - {"0NOSAS", 'F'}, - {"0NOSAT", 'F'}, - {"0NOSAV", 'F'}, - {"0NOSB(", 'F'}, - {"0NOSB1", 'F'}, - {"0NOSBE", 'F'}, - {"0NOSBF", 'F'}, - {"0NOSBN", 'F'}, - {"0NOSBS", 'F'}, - {"0NOSBV", 'F'}, - {"0NOSC", 'F'}, - {"0NOSE(", 'F'}, - {"0NOSE1", 'F'}, - {"0NOSEF", 'F'}, - {"0NOSEK", 'F'}, - {"0NOSEN", 'F'}, - {"0NOSEO", 'F'}, - {"0NOSES", 'F'}, - {"0NOSEU", 'F'}, - {"0NOSEV", 'F'}, - {"0NOSF(", 'F'}, - {"0NOSK(", 'F'}, - {"0NOSK)", 'F'}, - {"0NOSK1", 'F'}, - {"0NOSKB", 'F'}, - {"0NOSKF", 'F'}, - {"0NOSKN", 'F'}, - {"0NOSKS", 'F'}, - {"0NOSKU", 'F'}, - {"0NOSKV", 'F'}, - {"0NOSO(", 'F'}, - {"0NOSO1", 'F'}, - {"0NOSOF", 'F'}, - {"0NOSOK", 'F'}, - {"0NOSON", 'F'}, - {"0NOSOS", 'F'}, - {"0NOSOT", 'F'}, - {"0NOSOU", 'F'}, - {"0NOSOV", 'F'}, - {"0NOSU", 'F'}, - {"0NOSU(", 'F'}, - {"0NOSU1", 'F'}, - {"0NOSU;", 'F'}, - {"0NOSUC", 'F'}, - {"0NOSUE", 'F'}, - {"0NOSUF", 'F'}, - {"0NOSUK", 'F'}, - {"0NOSUN", 'F'}, - {"0NOSUO", 'F'}, - {"0NOSUS", 'F'}, - {"0NOSUT", 'F'}, - {"0NOSUV", 'F'}, - {"0NOSV(", 'F'}, - {"0NOSVF", 'F'}, - {"0NOSVO", 'F'}, - {"0NOSVS", 'F'}, - {"0NOSVU", 'F'}, - {"0NOT((", 'F'}, - {"0NOT(1", 'F'}, - {"0NOT(F", 'F'}, - {"0NOT(N", 'F'}, - {"0NOT(S", 'F'}, - {"0NOT(T", 'F'}, - {"0NOT(V", 'F'}, - {"0NOU((", 'F'}, - {"0NOU(E", 'F'}, - {"0NOUEK", 'F'}, - {"0NOUEN", 'F'}, - {"0NOV&(", 'F'}, - {"0NOV&1", 'F'}, - {"0NOV&E", 'F'}, - {"0NOV&F", 'F'}, - {"0NOV&K", 'F'}, - {"0NOV&N", 'F'}, - {"0NOV&S", 'F'}, - {"0NOV&T", 'F'}, - {"0NOV&U", 'F'}, - {"0NOV&V", 'F'}, - {"0NOV((", 'F'}, - {"0NOV(E", 'F'}, - {"0NOV(U", 'F'}, - {"0NOV)&", 'F'}, - {"0NOV),", 'F'}, - {"0NOV);", 'F'}, - {"0NOV)B", 'F'}, - {"0NOV)E", 'F'}, - {"0NOV)K", 'F'}, - {"0NOV)O", 'F'}, - {"0NOV)U", 'F'}, - {"0NOV,(", 'F'}, - {"0NOV,1", 'F'}, - {"0NOV,F", 'F'}, - {"0NOV,N", 'F'}, - {"0NOV,S", 'F'}, - {"0NOV,V", 'F'}, - {"0NOV;", 'F'}, - {"0NOV;C", 'F'}, - {"0NOV;E", 'F'}, - {"0NOV;N", 'F'}, - {"0NOV;T", 'F'}, - {"0NOVA(", 'F'}, - {"0NOVAF", 'F'}, - {"0NOVAS", 'F'}, - {"0NOVAT", 'F'}, - {"0NOVAV", 'F'}, - {"0NOVB(", 'F'}, - {"0NOVB1", 'F'}, - {"0NOVBE", 'F'}, - {"0NOVBF", 'F'}, - {"0NOVBN", 'F'}, - {"0NOVBS", 'F'}, - {"0NOVBV", 'F'}, - {"0NOVC", 'F'}, - {"0NOVE(", 'F'}, - {"0NOVE1", 'F'}, - {"0NOVEF", 'F'}, - {"0NOVEK", 'F'}, - {"0NOVEN", 'F'}, - {"0NOVEO", 'F'}, - {"0NOVES", 'F'}, - {"0NOVEU", 'F'}, - {"0NOVEV", 'F'}, - {"0NOVF(", 'F'}, - {"0NOVK(", 'F'}, - {"0NOVK)", 'F'}, - {"0NOVK1", 'F'}, - {"0NOVKB", 'F'}, - {"0NOVKF", 'F'}, - {"0NOVKN", 'F'}, - {"0NOVKS", 'F'}, - {"0NOVKU", 'F'}, - {"0NOVKV", 'F'}, - {"0NOVO(", 'F'}, - {"0NOVOF", 'F'}, - {"0NOVOK", 'F'}, - {"0NOVOS", 'F'}, - {"0NOVOT", 'F'}, - {"0NOVOU", 'F'}, - {"0NOVS(", 'F'}, - {"0NOVS1", 'F'}, - {"0NOVSF", 'F'}, - {"0NOVSO", 'F'}, - {"0NOVSU", 'F'}, - {"0NOVSV", 'F'}, - {"0NOVU", 'F'}, - {"0NOVU(", 'F'}, - {"0NOVU1", 'F'}, - {"0NOVU;", 'F'}, - {"0NOVUC", 'F'}, - {"0NOVUE", 'F'}, - {"0NOVUF", 'F'}, - {"0NOVUK", 'F'}, - {"0NOVUN", 'F'}, - {"0NOVUO", 'F'}, - {"0NOVUS", 'F'}, - {"0NOVUT", 'F'}, - {"0NOVUV", 'F'}, - {"0NSO1U", 'F'}, - {"0NSONU", 'F'}, - {"0NSOSU", 'F'}, - {"0NSOVU", 'F'}, - {"0NSUE", 'F'}, - {"0NSUE;", 'F'}, - {"0NSUEC", 'F'}, - {"0NSUEK", 'F'}, - {"0NU(((", 'F'}, - {"0NU((1", 'F'}, - {"0NU((E", 'F'}, - {"0NU((F", 'F'}, - {"0NU((N", 'F'}, - {"0NU((S", 'F'}, - {"0NU((V", 'F'}, - {"0NU(1)", 'F'}, - {"0NU(1O", 'F'}, - {"0NU(E(", 'F'}, - {"0NU(E1", 'F'}, - {"0NU(EF", 'F'}, - {"0NU(EK", 'F'}, - {"0NU(EN", 'F'}, - {"0NU(ES", 'F'}, - {"0NU(EV", 'F'}, - {"0NU(F(", 'F'}, - {"0NU(N)", 'F'}, - {"0NU(NO", 'F'}, - {"0NU(S)", 'F'}, - {"0NU(SO", 'F'}, - {"0NU(V)", 'F'}, - {"0NU(VO", 'F'}, - {"0NU1,(", 'F'}, - {"0NU1,F", 'F'}, - {"0NU1,V", 'F'}, - {"0NU1C", 'F'}, - {"0NU1O(", 'F'}, - {"0NU1OF", 'F'}, - {"0NU1OS", 'F'}, - {"0NU1OV", 'F'}, - {"0NU;", 'F'}, - {"0NU;C", 'F'}, - {"0NUC", 'F'}, - {"0NUE", 'F'}, - {"0NUE((", 'F'}, - {"0NUE(1", 'F'}, - {"0NUE(E", 'F'}, - {"0NUE(F", 'F'}, - {"0NUE(N", 'F'}, - {"0NUE(O", 'F'}, - {"0NUE(S", 'F'}, - {"0NUE(V", 'F'}, - {"0NUE1", 'F'}, - {"0NUE1&", 'F'}, - {"0NUE1(", 'F'}, - {"0NUE1)", 'F'}, - {"0NUE1,", 'F'}, - {"0NUE1;", 'F'}, - {"0NUE1B", 'F'}, - {"0NUE1C", 'F'}, - {"0NUE1F", 'F'}, - {"0NUE1K", 'F'}, - {"0NUE1N", 'F'}, - {"0NUE1O", 'F'}, - {"0NUE1S", 'F'}, - {"0NUE1U", 'F'}, - {"0NUE1V", 'F'}, - {"0NUE;", 'F'}, - {"0NUE;C", 'F'}, - {"0NUEC", 'F'}, - {"0NUEF", 'F'}, - {"0NUEF(", 'F'}, - {"0NUEF,", 'F'}, - {"0NUEF;", 'F'}, - {"0NUEFC", 'F'}, - {"0NUEK", 'F'}, - {"0NUEK(", 'F'}, - {"0NUEK1", 'F'}, - {"0NUEK;", 'F'}, - {"0NUEKC", 'F'}, - {"0NUEKF", 'F'}, - {"0NUEKN", 'F'}, - {"0NUEKO", 'F'}, - {"0NUEKS", 'F'}, - {"0NUEKV", 'F'}, - {"0NUEN", 'F'}, - {"0NUEN&", 'F'}, - {"0NUEN(", 'F'}, - {"0NUEN)", 'F'}, - {"0NUEN,", 'F'}, - {"0NUEN1", 'F'}, - {"0NUEN;", 'F'}, - {"0NUENB", 'F'}, - {"0NUENC", 'F'}, - {"0NUENF", 'F'}, - {"0NUENK", 'F'}, - {"0NUENO", 'F'}, - {"0NUENS", 'F'}, - {"0NUENU", 'F'}, - {"0NUEOK", 'F'}, - {"0NUEON", 'F'}, - {"0NUEOO", 'F'}, - {"0NUES", 'F'}, - {"0NUES&", 'F'}, - {"0NUES(", 'F'}, - {"0NUES)", 'F'}, - {"0NUES,", 'F'}, - {"0NUES1", 'F'}, - {"0NUES;", 'F'}, - {"0NUESB", 'F'}, - {"0NUESC", 'F'}, - {"0NUESF", 'F'}, - {"0NUESK", 'F'}, - {"0NUESO", 'F'}, - {"0NUESU", 'F'}, - {"0NUESV", 'F'}, - {"0NUEV", 'F'}, - {"0NUEV&", 'F'}, - {"0NUEV(", 'F'}, - {"0NUEV)", 'F'}, - {"0NUEV,", 'F'}, - {"0NUEV;", 'F'}, - {"0NUEVB", 'F'}, - {"0NUEVC", 'F'}, - {"0NUEVF", 'F'}, - {"0NUEVK", 'F'}, - {"0NUEVN", 'F'}, - {"0NUEVO", 'F'}, - {"0NUEVS", 'F'}, - {"0NUEVU", 'F'}, - {"0NUF((", 'F'}, - {"0NUF()", 'F'}, - {"0NUF(1", 'F'}, - {"0NUF(F", 'F'}, - {"0NUF(N", 'F'}, - {"0NUF(S", 'F'}, - {"0NUF(V", 'F'}, - {"0NUK((", 'F'}, - {"0NUK(E", 'F'}, - {"0NUN((", 'F'}, - {"0NUN(1", 'F'}, - {"0NUN(F", 'F'}, - {"0NUN(S", 'F'}, - {"0NUN(V", 'F'}, - {"0NUN,(", 'F'}, - {"0NUN,F", 'F'}, - {"0NUN,V", 'F'}, - {"0NUN1(", 'F'}, - {"0NUN1,", 'F'}, - {"0NUN1O", 'F'}, - {"0NUNC", 'F'}, - {"0NUNE(", 'F'}, - {"0NUNE1", 'F'}, - {"0NUNEF", 'F'}, - {"0NUNEN", 'F'}, - {"0NUNES", 'F'}, - {"0NUNEV", 'F'}, - {"0NUNF(", 'F'}, - {"0NUNO(", 'F'}, - {"0NUNOF", 'F'}, - {"0NUNOS", 'F'}, - {"0NUNOV", 'F'}, - {"0NUNS(", 'F'}, - {"0NUNS,", 'F'}, - {"0NUNSO", 'F'}, - {"0NUO((", 'F'}, - {"0NUO(E", 'F'}, - {"0NUON(", 'F'}, - {"0NUON1", 'F'}, - {"0NUONF", 'F'}, - {"0NUONS", 'F'}, - {"0NUS,(", 'F'}, - {"0NUS,F", 'F'}, - {"0NUS,V", 'F'}, - {"0NUSC", 'F'}, - {"0NUSO(", 'F'}, - {"0NUSO1", 'F'}, - {"0NUSOF", 'F'}, - {"0NUSON", 'F'}, - {"0NUSOS", 'F'}, - {"0NUSOV", 'F'}, - {"0NUTN(", 'F'}, - {"0NUTN1", 'F'}, - {"0NUTNF", 'F'}, - {"0NUTNS", 'F'}, - {"0NUV,(", 'F'}, - {"0NUV,1", 'F'}, - {"0NUV,F", 'F'}, - {"0NUV,N", 'F'}, - {"0NUV,S", 'F'}, - {"0NUV,V", 'F'}, - {"0NUVC", 'F'}, - {"0NUVO(", 'F'}, - {"0NUVOF", 'F'}, - {"0NUVOS", 'F'}, - {"0O((((", 'F'}, - {"0O(((1", 'F'}, - {"0O(((F", 'F'}, - {"0O(((N", 'F'}, - {"0O(((S", 'F'}, - {"0O(((V", 'F'}, - {"0O((1)", 'F'}, - {"0O((1O", 'F'}, - {"0O((F(", 'F'}, - {"0O((N)", 'F'}, - {"0O((NO", 'F'}, - {"0O((S)", 'F'}, - {"0O((SO", 'F'}, - {"0O((V)", 'F'}, - {"0O((VO", 'F'}, - {"0O(1)O", 'F'}, - {"0O(1)U", 'F'}, - {"0O(1O(", 'F'}, - {"0O(1OF", 'F'}, - {"0O(1OS", 'F'}, - {"0O(1OV", 'F'}, - {"0O(F((", 'F'}, - {"0O(F()", 'F'}, - {"0O(F(1", 'F'}, - {"0O(F(F", 'F'}, - {"0O(F(N", 'F'}, - {"0O(F(S", 'F'}, - {"0O(F(V", 'F'}, - {"0O(N)O", 'F'}, - {"0O(N)U", 'F'}, - {"0O(NO(", 'F'}, - {"0O(NOF", 'F'}, - {"0O(NOS", 'F'}, - {"0O(NOV", 'F'}, - {"0O(S)O", 'F'}, - {"0O(S)U", 'F'}, - {"0O(SO(", 'F'}, - {"0O(SO1", 'F'}, - {"0O(SOF", 'F'}, - {"0O(SON", 'F'}, - {"0O(SOS", 'F'}, - {"0O(SOV", 'F'}, - {"0O(V)O", 'F'}, - {"0O(V)U", 'F'}, - {"0O(VO(", 'F'}, - {"0O(VOF", 'F'}, - {"0O(VOS", 'F'}, - {"0O1O((", 'F'}, - {"0O1O(1", 'F'}, - {"0O1O(F", 'F'}, - {"0O1O(N", 'F'}, - {"0O1O(S", 'F'}, - {"0O1O(V", 'F'}, - {"0O1OF(", 'F'}, - {"0O1OSO", 'F'}, - {"0O1OSU", 'F'}, - {"0O1OVO", 'F'}, - {"0O1OVU", 'F'}, - {"0O1UE(", 'F'}, - {"0O1UE1", 'F'}, - {"0O1UEF", 'F'}, - {"0O1UEK", 'F'}, - {"0O1UEN", 'F'}, - {"0O1UES", 'F'}, - {"0O1UEV", 'F'}, - {"0OF(((", 'F'}, - {"0OF(()", 'F'}, - {"0OF((1", 'F'}, - {"0OF((F", 'F'}, - {"0OF((N", 'F'}, - {"0OF((S", 'F'}, - {"0OF((V", 'F'}, - {"0OF()O", 'F'}, - {"0OF()U", 'F'}, - {"0OF(1)", 'F'}, - {"0OF(1O", 'F'}, - {"0OF(F(", 'F'}, - {"0OF(N)", 'F'}, - {"0OF(NO", 'F'}, - {"0OF(S)", 'F'}, - {"0OF(SO", 'F'}, - {"0OF(V)", 'F'}, - {"0OF(VO", 'F'}, - {"0ONO((", 'F'}, - {"0ONO(1", 'F'}, - {"0ONO(F", 'F'}, - {"0ONO(N", 'F'}, - {"0ONO(S", 'F'}, - {"0ONO(V", 'F'}, - {"0ONOF(", 'F'}, - {"0ONOSO", 'F'}, - {"0ONOSU", 'F'}, - {"0ONOVO", 'F'}, - {"0ONOVU", 'F'}, - {"0ONUE(", 'F'}, - {"0ONUE1", 'F'}, - {"0ONUEF", 'F'}, - {"0ONUEK", 'F'}, - {"0ONUEN", 'F'}, - {"0ONUES", 'F'}, - {"0ONUEV", 'F'}, - {"0OSO((", 'F'}, - {"0OSO(1", 'F'}, - {"0OSO(F", 'F'}, - {"0OSO(N", 'F'}, - {"0OSO(S", 'F'}, - {"0OSO(V", 'F'}, - {"0OSO1O", 'F'}, - {"0OSO1U", 'F'}, - {"0OSOF(", 'F'}, - {"0OSONO", 'F'}, - {"0OSONU", 'F'}, - {"0OSOSO", 'F'}, - {"0OSOSU", 'F'}, - {"0OSOVO", 'F'}, - {"0OSOVU", 'F'}, - {"0OSUE(", 'F'}, - {"0OSUE1", 'F'}, - {"0OSUEF", 'F'}, - {"0OSUEK", 'F'}, - {"0OSUEN", 'F'}, - {"0OSUES", 'F'}, - {"0OSUEV", 'F'}, - {"0OUE((", 'F'}, - {"0OUE(1", 'F'}, - {"0OUE(F", 'F'}, - {"0OUE(N", 'F'}, - {"0OUE(S", 'F'}, - {"0OUE(V", 'F'}, - {"0OUE1,", 'F'}, - {"0OUE1O", 'F'}, - {"0OUEF(", 'F'}, - {"0OUEK(", 'F'}, - {"0OUEK1", 'F'}, - {"0OUEKF", 'F'}, - {"0OUEKN", 'F'}, - {"0OUEKS", 'F'}, - {"0OUEKV", 'F'}, - {"0OUEN,", 'F'}, - {"0OUENO", 'F'}, - {"0OUES,", 'F'}, - {"0OUESO", 'F'}, - {"0OUEV,", 'F'}, - {"0OUEVO", 'F'}, - {"0OVO((", 'F'}, - {"0OVO(1", 'F'}, - {"0OVO(F", 'F'}, - {"0OVO(N", 'F'}, - {"0OVO(S", 'F'}, - {"0OVO(V", 'F'}, - {"0OVOF(", 'F'}, - {"0OVOSO", 'F'}, - {"0OVOSU", 'F'}, - {"0OVUE(", 'F'}, - {"0OVUE1", 'F'}, - {"0OVUEF", 'F'}, - {"0OVUEK", 'F'}, - {"0OVUEN", 'F'}, - {"0OVUES", 'F'}, - {"0OVUEV", 'F'}, - {"0S&(((", 'F'}, - {"0S&((1", 'F'}, - {"0S&((E", 'F'}, - {"0S&((F", 'F'}, - {"0S&((N", 'F'}, - {"0S&((S", 'F'}, - {"0S&((V", 'F'}, - {"0S&(1&", 'F'}, - {"0S&(1)", 'F'}, - {"0S&(1,", 'F'}, - {"0S&(1O", 'F'}, - {"0S&(E(", 'F'}, - {"0S&(E1", 'F'}, - {"0S&(EF", 'F'}, - {"0S&(EK", 'F'}, - {"0S&(EN", 'F'}, - {"0S&(EO", 'F'}, - {"0S&(ES", 'F'}, - {"0S&(EV", 'F'}, - {"0S&(F(", 'F'}, - {"0S&(N&", 'F'}, - {"0S&(N)", 'F'}, - {"0S&(N,", 'F'}, - {"0S&(NO", 'F'}, - {"0S&(S&", 'F'}, - {"0S&(S)", 'F'}, - {"0S&(S,", 'F'}, - {"0S&(SO", 'F'}, - {"0S&(V&", 'F'}, - {"0S&(V)", 'F'}, - {"0S&(V,", 'F'}, - {"0S&(VO", 'F'}, - {"0S&1", 'F'}, - {"0S&1&(", 'F'}, - {"0S&1&1", 'F'}, - {"0S&1&F", 'F'}, - {"0S&1&N", 'F'}, - {"0S&1&S", 'F'}, - {"0S&1&V", 'F'}, - {"0S&1)&", 'F'}, - {"0S&1)O", 'F'}, - {"0S&1)U", 'F'}, - {"0S&1;", 'F'}, - {"0S&1;C", 'F'}, - {"0S&1;E", 'F'}, - {"0S&1;T", 'F'}, - {"0S&1B(", 'F'}, - {"0S&1B1", 'F'}, - {"0S&1BF", 'F'}, - {"0S&1BN", 'F'}, - {"0S&1BS", 'F'}, - {"0S&1BV", 'F'}, - {"0S&1C", 'F'}, - {"0S&1EK", 'F'}, - {"0S&1EN", 'F'}, - {"0S&1F(", 'F'}, - {"0S&1K(", 'F'}, - {"0S&1K1", 'F'}, - {"0S&1KF", 'F'}, - {"0S&1KN", 'F'}, - {"0S&1KS", 'F'}, - {"0S&1KV", 'F'}, - {"0S&1O(", 'F'}, - {"0S&1OF", 'F'}, - {"0S&1OO", 'F'}, - {"0S&1OS", 'F'}, - {"0S&1OV", 'F'}, - {"0S&1TN", 'F'}, - {"0S&1U", 'F'}, - {"0S&1U(", 'F'}, - {"0S&1U;", 'F'}, - {"0S&1UC", 'F'}, - {"0S&1UE", 'F'}, - {"0S&E((", 'F'}, - {"0S&E(1", 'F'}, - {"0S&E(F", 'F'}, - {"0S&E(N", 'F'}, - {"0S&E(O", 'F'}, - {"0S&E(S", 'F'}, - {"0S&E(V", 'F'}, - {"0S&E1", 'F'}, - {"0S&E1;", 'F'}, - {"0S&E1C", 'F'}, - {"0S&E1K", 'F'}, - {"0S&E1O", 'F'}, - {"0S&EF(", 'F'}, - {"0S&EK(", 'F'}, - {"0S&EK1", 'F'}, - {"0S&EKF", 'F'}, - {"0S&EKN", 'F'}, - {"0S&EKS", 'F'}, - {"0S&EKV", 'F'}, - {"0S&EN", 'F'}, - {"0S&EN;", 'F'}, - {"0S&ENC", 'F'}, - {"0S&ENK", 'F'}, - {"0S&ENO", 'F'}, - {"0S&ES", 'F'}, - {"0S&ES;", 'F'}, - {"0S&ESC", 'F'}, - {"0S&ESK", 'F'}, - {"0S&ESO", 'F'}, - {"0S&EV", 'F'}, - {"0S&EV;", 'F'}, - {"0S&EVC", 'F'}, - {"0S&EVK", 'F'}, - {"0S&EVO", 'F'}, - {"0S&F((", 'F'}, - {"0S&F()", 'F'}, - {"0S&F(1", 'F'}, - {"0S&F(E", 'F'}, - {"0S&F(F", 'F'}, - {"0S&F(N", 'F'}, - {"0S&F(S", 'F'}, - {"0S&F(V", 'F'}, - {"0S&K&(", 'F'}, - {"0S&K&1", 'F'}, - {"0S&K&F", 'F'}, - {"0S&K&N", 'F'}, - {"0S&K&S", 'F'}, - {"0S&K&V", 'F'}, - {"0S&K((", 'F'}, - {"0S&K(1", 'F'}, - {"0S&K(F", 'F'}, - {"0S&K(N", 'F'}, - {"0S&K(S", 'F'}, - {"0S&K(V", 'F'}, - {"0S&K1O", 'F'}, - {"0S&KF(", 'F'}, - {"0S&KNK", 'F'}, - {"0S&KO(", 'F'}, - {"0S&KO1", 'F'}, - {"0S&KOF", 'F'}, - {"0S&KOK", 'F'}, - {"0S&KON", 'F'}, - {"0S&KOS", 'F'}, - {"0S&KOV", 'F'}, - {"0S&KSO", 'F'}, - {"0S&KVO", 'F'}, - {"0S&N", 'F'}, - {"0S&N&(", 'F'}, - {"0S&N&1", 'F'}, - {"0S&N&F", 'F'}, - {"0S&N&N", 'F'}, - {"0S&N&S", 'F'}, - {"0S&N&V", 'F'}, - {"0S&N)&", 'F'}, - {"0S&N)O", 'F'}, - {"0S&N)U", 'F'}, - {"0S&N;", 'F'}, - {"0S&N;C", 'F'}, - {"0S&N;E", 'F'}, - {"0S&N;T", 'F'}, - {"0S&NB(", 'F'}, - {"0S&NB1", 'F'}, - {"0S&NBF", 'F'}, - {"0S&NBN", 'F'}, - {"0S&NBS", 'F'}, - {"0S&NBV", 'F'}, - {"0S&NC", 'F'}, - {"0S&NEN", 'F'}, - {"0S&NF(", 'F'}, - {"0S&NK(", 'F'}, - {"0S&NK1", 'F'}, - {"0S&NKF", 'F'}, - {"0S&NKN", 'F'}, - {"0S&NKS", 'F'}, - {"0S&NKV", 'F'}, - {"0S&NO(", 'F'}, - {"0S&NOF", 'F'}, - {"0S&NOS", 'F'}, - {"0S&NOV", 'F'}, - {"0S&NTN", 'F'}, - {"0S&NU", 'F'}, - {"0S&NU(", 'F'}, - {"0S&NU;", 'F'}, - {"0S&NUC", 'F'}, - {"0S&NUE", 'F'}, - {"0S&S", 'F'}, - {"0S&S&(", 'F'}, - {"0S&S&1", 'F'}, - {"0S&S&F", 'F'}, - {"0S&S&N", 'F'}, - {"0S&S&S", 'F'}, - {"0S&S&V", 'F'}, - {"0S&S)&", 'F'}, - {"0S&S)O", 'F'}, - {"0S&S)U", 'F'}, - {"0S&S1", 'F'}, - {"0S&S1;", 'F'}, - {"0S&S1C", 'F'}, - {"0S&S1O", 'F'}, - {"0S&S;", 'F'}, - {"0S&S;C", 'F'}, - {"0S&S;E", 'F'}, - {"0S&S;T", 'F'}, - {"0S&SB(", 'F'}, - {"0S&SB1", 'F'}, - {"0S&SBF", 'F'}, - {"0S&SBN", 'F'}, - {"0S&SBS", 'F'}, - {"0S&SBV", 'F'}, - {"0S&SC", 'F'}, - {"0S&SEK", 'F'}, - {"0S&SEN", 'F'}, - {"0S&SF(", 'F'}, - {"0S&SK(", 'F'}, - {"0S&SK1", 'F'}, - {"0S&SKF", 'F'}, - {"0S&SKN", 'F'}, - {"0S&SKS", 'F'}, - {"0S&SKV", 'F'}, - {"0S&SO(", 'F'}, - {"0S&SO1", 'F'}, - {"0S&SOF", 'F'}, - {"0S&SON", 'F'}, - {"0S&SOO", 'F'}, - {"0S&SOS", 'F'}, - {"0S&SOV", 'F'}, - {"0S&STN", 'F'}, - {"0S&SU", 'F'}, - {"0S&SU(", 'F'}, - {"0S&SU;", 'F'}, - {"0S&SUC", 'F'}, - {"0S&SUE", 'F'}, - {"0S&SV", 'F'}, - {"0S&SV;", 'F'}, - {"0S&SVC", 'F'}, - {"0S&SVO", 'F'}, - {"0S&T((", 'F'}, - {"0S&T(1", 'F'}, - {"0S&T(F", 'F'}, - {"0S&T(N", 'F'}, - {"0S&T(S", 'F'}, - {"0S&T(V", 'F'}, - {"0S&V", 'F'}, - {"0S&V&(", 'F'}, - {"0S&V&1", 'F'}, - {"0S&V&F", 'F'}, - {"0S&V&N", 'F'}, - {"0S&V&S", 'F'}, - {"0S&V&V", 'F'}, - {"0S&V)&", 'F'}, - {"0S&V)O", 'F'}, - {"0S&V)U", 'F'}, - {"0S&V;", 'F'}, - {"0S&V;C", 'F'}, - {"0S&V;E", 'F'}, - {"0S&V;T", 'F'}, - {"0S&VB(", 'F'}, - {"0S&VB1", 'F'}, - {"0S&VBF", 'F'}, - {"0S&VBN", 'F'}, - {"0S&VBS", 'F'}, - {"0S&VBV", 'F'}, - {"0S&VC", 'F'}, - {"0S&VEK", 'F'}, - {"0S&VEN", 'F'}, - {"0S&VF(", 'F'}, - {"0S&VK(", 'F'}, - {"0S&VK1", 'F'}, - {"0S&VKF", 'F'}, - {"0S&VKN", 'F'}, - {"0S&VKS", 'F'}, - {"0S&VKV", 'F'}, - {"0S&VO(", 'F'}, - {"0S&VOF", 'F'}, - {"0S&VOO", 'F'}, - {"0S&VOS", 'F'}, - {"0S&VS", 'F'}, - {"0S&VS;", 'F'}, - {"0S&VSC", 'F'}, - {"0S&VSO", 'F'}, - {"0S&VTN", 'F'}, - {"0S&VU", 'F'}, - {"0S&VU(", 'F'}, - {"0S&VU;", 'F'}, - {"0S&VUC", 'F'}, - {"0S&VUE", 'F'}, - {"0S((((", 'F'}, - {"0S(((E", 'F'}, - {"0S(((U", 'F'}, - {"0S((EK", 'F'}, - {"0S((EN", 'F'}, - {"0S((U(", 'F'}, - {"0S(EKN", 'F'}, - {"0S(ENK", 'F'}, - {"0S(U((", 'F'}, - {"0S(U(E", 'F'}, - {"0S)&((", 'F'}, - {"0S)&(1", 'F'}, - {"0S)&(E", 'F'}, - {"0S)&(F", 'F'}, - {"0S)&(N", 'F'}, - {"0S)&(S", 'F'}, - {"0S)&(V", 'F'}, - {"0S)&1", 'F'}, - {"0S)&1&", 'F'}, - {"0S)&1)", 'F'}, - {"0S)&1;", 'F'}, - {"0S)&1B", 'F'}, - {"0S)&1C", 'F'}, - {"0S)&1F", 'F'}, - {"0S)&1O", 'F'}, - {"0S)&1U", 'F'}, - {"0S)&F(", 'F'}, - {"0S)&N", 'F'}, - {"0S)&N&", 'F'}, - {"0S)&N)", 'F'}, - {"0S)&N;", 'F'}, - {"0S)&NB", 'F'}, - {"0S)&NC", 'F'}, - {"0S)&NF", 'F'}, - {"0S)&NO", 'F'}, - {"0S)&NU", 'F'}, - {"0S)&S", 'F'}, - {"0S)&S&", 'F'}, - {"0S)&S)", 'F'}, - {"0S)&S;", 'F'}, - {"0S)&SB", 'F'}, - {"0S)&SC", 'F'}, - {"0S)&SF", 'F'}, - {"0S)&SO", 'F'}, - {"0S)&SU", 'F'}, - {"0S)&V", 'F'}, - {"0S)&V&", 'F'}, - {"0S)&V)", 'F'}, - {"0S)&V;", 'F'}, - {"0S)&VB", 'F'}, - {"0S)&VC", 'F'}, - {"0S)&VF", 'F'}, - {"0S)&VO", 'F'}, - {"0S)&VU", 'F'}, - {"0S),((", 'F'}, - {"0S),(1", 'F'}, - {"0S),(F", 'F'}, - {"0S),(N", 'F'}, - {"0S),(S", 'F'}, - {"0S),(V", 'F'}, - {"0S);E(", 'F'}, - {"0S);E1", 'F'}, - {"0S);EF", 'F'}, - {"0S);EK", 'F'}, - {"0S);EN", 'F'}, - {"0S);EO", 'F'}, - {"0S);ES", 'F'}, - {"0S);EV", 'F'}, - {"0S);T(", 'F'}, - {"0S);T1", 'F'}, - {"0S);TF", 'F'}, - {"0S);TK", 'F'}, - {"0S);TN", 'F'}, - {"0S);TO", 'F'}, - {"0S);TS", 'F'}, - {"0S);TV", 'F'}, - {"0S)B((", 'F'}, - {"0S)B(1", 'F'}, - {"0S)B(F", 'F'}, - {"0S)B(N", 'F'}, - {"0S)B(S", 'F'}, - {"0S)B(V", 'F'}, - {"0S)B1", 'F'}, - {"0S)B1&", 'F'}, - {"0S)B1;", 'F'}, - {"0S)B1C", 'F'}, - {"0S)B1K", 'F'}, - {"0S)B1N", 'F'}, - {"0S)B1O", 'F'}, - {"0S)B1U", 'F'}, - {"0S)BF(", 'F'}, - {"0S)BN", 'F'}, - {"0S)BN&", 'F'}, - {"0S)BN;", 'F'}, - {"0S)BNC", 'F'}, - {"0S)BNK", 'F'}, - {"0S)BNO", 'F'}, - {"0S)BNU", 'F'}, - {"0S)BS", 'F'}, - {"0S)BS&", 'F'}, - {"0S)BS;", 'F'}, - {"0S)BSC", 'F'}, - {"0S)BSK", 'F'}, - {"0S)BSO", 'F'}, - {"0S)BSU", 'F'}, - {"0S)BV", 'F'}, - {"0S)BV&", 'F'}, - {"0S)BV;", 'F'}, - {"0S)BVC", 'F'}, - {"0S)BVK", 'F'}, - {"0S)BVO", 'F'}, - {"0S)BVU", 'F'}, - {"0S)E((", 'F'}, - {"0S)E(1", 'F'}, - {"0S)E(F", 'F'}, - {"0S)E(N", 'F'}, - {"0S)E(S", 'F'}, - {"0S)E(V", 'F'}, - {"0S)E1C", 'F'}, - {"0S)E1O", 'F'}, - {"0S)EF(", 'F'}, - {"0S)EK(", 'F'}, - {"0S)EK1", 'F'}, - {"0S)EKF", 'F'}, - {"0S)EKN", 'F'}, - {"0S)EKS", 'F'}, - {"0S)EKV", 'F'}, - {"0S)ENC", 'F'}, - {"0S)ENO", 'F'}, - {"0S)ESC", 'F'}, - {"0S)ESO", 'F'}, - {"0S)EVC", 'F'}, - {"0S)EVO", 'F'}, - {"0S)K((", 'F'}, - {"0S)K(1", 'F'}, - {"0S)K(F", 'F'}, - {"0S)K(N", 'F'}, - {"0S)K(S", 'F'}, - {"0S)K(V", 'F'}, - {"0S)K1&", 'F'}, - {"0S)K1;", 'F'}, - {"0S)K1B", 'F'}, - {"0S)K1E", 'F'}, - {"0S)K1O", 'F'}, - {"0S)K1U", 'F'}, - {"0S)KB(", 'F'}, - {"0S)KB1", 'F'}, - {"0S)KBF", 'F'}, - {"0S)KBN", 'F'}, - {"0S)KBS", 'F'}, - {"0S)KBV", 'F'}, - {"0S)KF(", 'F'}, - {"0S)KN&", 'F'}, - {"0S)KN;", 'F'}, - {"0S)KNB", 'F'}, - {"0S)KNE", 'F'}, - {"0S)KNK", 'F'}, - {"0S)KNU", 'F'}, - {"0S)KS&", 'F'}, - {"0S)KS;", 'F'}, - {"0S)KSB", 'F'}, - {"0S)KSE", 'F'}, - {"0S)KSO", 'F'}, - {"0S)KSU", 'F'}, - {"0S)KUE", 'F'}, - {"0S)KV&", 'F'}, - {"0S)KV;", 'F'}, - {"0S)KVB", 'F'}, - {"0S)KVE", 'F'}, - {"0S)KVO", 'F'}, - {"0S)KVU", 'F'}, - {"0S)O((", 'F'}, - {"0S)O(1", 'F'}, - {"0S)O(E", 'F'}, - {"0S)O(F", 'F'}, - {"0S)O(N", 'F'}, - {"0S)O(S", 'F'}, - {"0S)O(V", 'F'}, - {"0S)O1", 'F'}, - {"0S)O1&", 'F'}, - {"0S)O1)", 'F'}, - {"0S)O1;", 'F'}, - {"0S)O1B", 'F'}, - {"0S)O1C", 'F'}, - {"0S)O1K", 'F'}, - {"0S)O1O", 'F'}, - {"0S)O1U", 'F'}, - {"0S)OF(", 'F'}, - {"0S)ON&", 'F'}, - {"0S)ON)", 'F'}, - {"0S)ON;", 'F'}, - {"0S)ONB", 'F'}, - {"0S)ONC", 'F'}, - {"0S)ONK", 'F'}, - {"0S)ONO", 'F'}, - {"0S)ONU", 'F'}, - {"0S)OS", 'F'}, - {"0S)OS&", 'F'}, - {"0S)OS)", 'F'}, - {"0S)OS;", 'F'}, - {"0S)OSB", 'F'}, - {"0S)OSC", 'F'}, - {"0S)OSK", 'F'}, - {"0S)OSO", 'F'}, - {"0S)OSU", 'F'}, - {"0S)OV", 'F'}, - {"0S)OV&", 'F'}, - {"0S)OV)", 'F'}, - {"0S)OV;", 'F'}, - {"0S)OVB", 'F'}, - {"0S)OVC", 'F'}, - {"0S)OVK", 'F'}, - {"0S)OVO", 'F'}, - {"0S)OVU", 'F'}, - {"0S)U((", 'F'}, - {"0S)U(E", 'F'}, - {"0S)UE(", 'F'}, - {"0S)UE1", 'F'}, - {"0S)UEF", 'F'}, - {"0S)UEK", 'F'}, - {"0S)UEN", 'F'}, - {"0S)UES", 'F'}, - {"0S)UEV", 'F'}, - {"0S,(((", 'F'}, - {"0S,((1", 'F'}, - {"0S,((E", 'F'}, - {"0S,((F", 'F'}, - {"0S,((N", 'F'}, - {"0S,((S", 'F'}, - {"0S,((V", 'F'}, - {"0S,(1)", 'F'}, - {"0S,(1O", 'F'}, - {"0S,(E(", 'F'}, - {"0S,(E1", 'F'}, - {"0S,(EF", 'F'}, - {"0S,(EK", 'F'}, - {"0S,(EN", 'F'}, - {"0S,(ES", 'F'}, - {"0S,(EV", 'F'}, - {"0S,(F(", 'F'}, - {"0S,(N)", 'F'}, - {"0S,(NO", 'F'}, - {"0S,(S)", 'F'}, - {"0S,(SO", 'F'}, - {"0S,(V)", 'F'}, - {"0S,(VO", 'F'}, - {"0S,F((", 'F'}, - {"0S,F()", 'F'}, - {"0S,F(1", 'F'}, - {"0S,F(F", 'F'}, - {"0S,F(N", 'F'}, - {"0S,F(S", 'F'}, - {"0S,F(T", 'F'}, - {"0S,F(V", 'F'}, - {"0S,V),", 'F'}, - {"0S,V)O", 'F'}, - {"0S,VB(", 'F'}, - {"0S,VB1", 'F'}, - {"0S,VBF", 'F'}, - {"0S,VBN", 'F'}, - {"0S,VBS", 'F'}, - {"0S,VBV", 'F'}, - {"0S,VO(", 'F'}, - {"0S,VOF", 'F'}, - {"0S,VOS", 'F'}, - {"0S,VUE", 'F'}, - {"0S1F((", 'F'}, - {"0S1F()", 'F'}, - {"0S1F(1", 'F'}, - {"0S1F(F", 'F'}, - {"0S1F(N", 'F'}, - {"0S1F(S", 'F'}, - {"0S1F(V", 'F'}, - {"0S1NC", 'F'}, - {"0S1O((", 'F'}, - {"0S1O(1", 'F'}, - {"0S1O(F", 'F'}, - {"0S1O(N", 'F'}, - {"0S1O(S", 'F'}, - {"0S1O(V", 'F'}, - {"0S1OF(", 'F'}, - {"0S1OS(", 'F'}, - {"0S1OS1", 'F'}, - {"0S1OSF", 'F'}, - {"0S1OSO", 'F'}, - {"0S1OSU", 'F'}, - {"0S1OSV", 'F'}, - {"0S1OV(", 'F'}, - {"0S1OVF", 'F'}, - {"0S1OVO", 'F'}, - {"0S1OVS", 'F'}, - {"0S1OVU", 'F'}, - {"0S1S;", 'F'}, - {"0S1S;C", 'F'}, - {"0S1SC", 'F'}, - {"0S1UE", 'F'}, - {"0S1UE;", 'F'}, - {"0S1UEC", 'F'}, - {"0S1UEK", 'F'}, - {"0S1V", 'F'}, - {"0S1V;", 'F'}, - {"0S1V;C", 'F'}, - {"0S1VC", 'F'}, - {"0S1VO(", 'F'}, - {"0S1VOF", 'F'}, - {"0S1VOS", 'F'}, - {"0S;E((", 'F'}, - {"0S;E(1", 'F'}, - {"0S;E(E", 'F'}, - {"0S;E(F", 'F'}, - {"0S;E(N", 'F'}, - {"0S;E(S", 'F'}, - {"0S;E(V", 'F'}, - {"0S;E1,", 'F'}, - {"0S;E1;", 'F'}, - {"0S;E1C", 'F'}, - {"0S;E1K", 'F'}, - {"0S;E1O", 'F'}, - {"0S;E1T", 'F'}, - {"0S;EF(", 'F'}, - {"0S;EK(", 'F'}, - {"0S;EK1", 'F'}, - {"0S;EKF", 'F'}, - {"0S;EKN", 'F'}, - {"0S;EKO", 'F'}, - {"0S;EKS", 'F'}, - {"0S;EKV", 'F'}, - {"0S;EN,", 'F'}, - {"0S;EN;", 'F'}, - {"0S;ENC", 'F'}, - {"0S;ENK", 'F'}, - {"0S;ENO", 'F'}, - {"0S;ENT", 'F'}, - {"0S;EO(", 'F'}, - {"0S;ES,", 'F'}, - {"0S;ES;", 'F'}, - {"0S;ESC", 'F'}, - {"0S;ESK", 'F'}, - {"0S;ESO", 'F'}, - {"0S;EST", 'F'}, - {"0S;EV,", 'F'}, - {"0S;EV;", 'F'}, - {"0S;EVC", 'F'}, - {"0S;EVK", 'F'}, - {"0S;EVO", 'F'}, - {"0S;EVT", 'F'}, - {"0S;N:T", 'F'}, - {"0S;T((", 'F'}, - {"0S;T(1", 'F'}, - {"0S;T(E", 'F'}, - {"0S;T(F", 'F'}, - {"0S;T(N", 'F'}, - {"0S;T(S", 'F'}, - {"0S;T(V", 'F'}, - {"0S;T1,", 'F'}, - {"0S;T1;", 'F'}, - {"0S;T1C", 'F'}, - {"0S;T1F", 'F'}, - {"0S;T1K", 'F'}, - {"0S;T1O", 'F'}, - {"0S;T1T", 'F'}, - {"0S;T;", 'F'}, - {"0S;T;C", 'F'}, - {"0S;TF(", 'F'}, - {"0S;TK(", 'F'}, - {"0S;TK1", 'F'}, - {"0S;TKF", 'F'}, - {"0S;TKK", 'F'}, - {"0S;TKN", 'F'}, - {"0S;TKO", 'F'}, - {"0S;TKS", 'F'}, - {"0S;TKV", 'F'}, - {"0S;TN(", 'F'}, - {"0S;TN,", 'F'}, - {"0S;TN1", 'F'}, - {"0S;TN;", 'F'}, - {"0S;TNC", 'F'}, - {"0S;TNE", 'F'}, - {"0S;TNF", 'F'}, - {"0S;TNK", 'F'}, - {"0S;TNN", 'F'}, - {"0S;TNO", 'F'}, - {"0S;TNS", 'F'}, - {"0S;TNT", 'F'}, - {"0S;TNV", 'F'}, - {"0S;TO(", 'F'}, - {"0S;TS,", 'F'}, - {"0S;TS;", 'F'}, - {"0S;TSC", 'F'}, - {"0S;TSF", 'F'}, - {"0S;TSK", 'F'}, - {"0S;TSO", 'F'}, - {"0S;TST", 'F'}, - {"0S;TT(", 'F'}, - {"0S;TT1", 'F'}, - {"0S;TTF", 'F'}, - {"0S;TTN", 'F'}, - {"0S;TTS", 'F'}, - {"0S;TTV", 'F'}, - {"0S;TV,", 'F'}, - {"0S;TV;", 'F'}, - {"0S;TVC", 'F'}, - {"0S;TVF", 'F'}, - {"0S;TVK", 'F'}, - {"0S;TVO", 'F'}, - {"0S;TVT", 'F'}, - {"0SA(((", 'F'}, - {"0SA((1", 'F'}, - {"0SA((F", 'F'}, - {"0SA((N", 'F'}, - {"0SA((S", 'F'}, - {"0SA((V", 'F'}, - {"0SA(F(", 'F'}, - {"0SA(N)", 'F'}, - {"0SA(NO", 'F'}, - {"0SA(S)", 'F'}, - {"0SA(SO", 'F'}, - {"0SA(V)", 'F'}, - {"0SA(VO", 'F'}, - {"0SAF((", 'F'}, - {"0SAF()", 'F'}, - {"0SAF(1", 'F'}, - {"0SAF(F", 'F'}, - {"0SAF(N", 'F'}, - {"0SAF(S", 'F'}, - {"0SAF(V", 'F'}, - {"0SASO(", 'F'}, - {"0SASO1", 'F'}, - {"0SASOF", 'F'}, - {"0SASON", 'F'}, - {"0SASOS", 'F'}, - {"0SASOV", 'F'}, - {"0SASUE", 'F'}, - {"0SATO(", 'F'}, - {"0SATO1", 'F'}, - {"0SATOF", 'F'}, - {"0SATON", 'F'}, - {"0SATOS", 'F'}, - {"0SATOV", 'F'}, - {"0SATUE", 'F'}, - {"0SAVO(", 'F'}, - {"0SAVOF", 'F'}, - {"0SAVOS", 'F'}, - {"0SAVUE", 'F'}, - {"0SB(((", 'F'}, - {"0SB((1", 'F'}, - {"0SB((F", 'F'}, - {"0SB((N", 'F'}, - {"0SB((S", 'F'}, - {"0SB((V", 'F'}, - {"0SB(1)", 'F'}, - {"0SB(1O", 'F'}, - {"0SB(F(", 'F'}, - {"0SB(N)", 'F'}, - {"0SB(NO", 'F'}, - {"0SB(S)", 'F'}, - {"0SB(SO", 'F'}, - {"0SB(V)", 'F'}, - {"0SB(VO", 'F'}, - {"0SB1", 'F'}, - {"0SB1&(", 'F'}, - {"0SB1&1", 'F'}, - {"0SB1&F", 'F'}, - {"0SB1&N", 'F'}, - {"0SB1&S", 'F'}, - {"0SB1&V", 'F'}, - {"0SB1,(", 'F'}, - {"0SB1,F", 'F'}, - {"0SB1,V", 'F'}, - {"0SB1;", 'F'}, - {"0SB1;C", 'F'}, - {"0SB1B(", 'F'}, - {"0SB1B1", 'F'}, - {"0SB1BF", 'F'}, - {"0SB1BN", 'F'}, - {"0SB1BS", 'F'}, - {"0SB1BV", 'F'}, - {"0SB1C", 'F'}, - {"0SB1K(", 'F'}, - {"0SB1K1", 'F'}, - {"0SB1KF", 'F'}, - {"0SB1KN", 'F'}, - {"0SB1KS", 'F'}, - {"0SB1KV", 'F'}, - {"0SB1O(", 'F'}, - {"0SB1OF", 'F'}, - {"0SB1OS", 'F'}, - {"0SB1OV", 'F'}, - {"0SB1U(", 'F'}, - {"0SB1UE", 'F'}, - {"0SBE((", 'F'}, - {"0SBE(1", 'F'}, - {"0SBE(F", 'F'}, - {"0SBE(N", 'F'}, - {"0SBE(S", 'F'}, - {"0SBE(V", 'F'}, - {"0SBEK(", 'F'}, - {"0SBF((", 'F'}, - {"0SBF()", 'F'}, - {"0SBF(1", 'F'}, - {"0SBF(F", 'F'}, - {"0SBF(N", 'F'}, - {"0SBF(S", 'F'}, - {"0SBF(V", 'F'}, - {"0SBN", 'F'}, - {"0SBN&(", 'F'}, - {"0SBN&1", 'F'}, - {"0SBN&F", 'F'}, - {"0SBN&N", 'F'}, - {"0SBN&S", 'F'}, - {"0SBN&V", 'F'}, - {"0SBN,(", 'F'}, - {"0SBN,F", 'F'}, - {"0SBN,V", 'F'}, - {"0SBN;", 'F'}, - {"0SBN;C", 'F'}, - {"0SBNB(", 'F'}, - {"0SBNB1", 'F'}, - {"0SBNBF", 'F'}, - {"0SBNBN", 'F'}, - {"0SBNBS", 'F'}, - {"0SBNBV", 'F'}, - {"0SBNC", 'F'}, - {"0SBNK(", 'F'}, - {"0SBNK1", 'F'}, - {"0SBNKF", 'F'}, - {"0SBNKN", 'F'}, - {"0SBNKS", 'F'}, - {"0SBNKV", 'F'}, - {"0SBNO(", 'F'}, - {"0SBNOF", 'F'}, - {"0SBNOS", 'F'}, - {"0SBNOV", 'F'}, - {"0SBNU(", 'F'}, - {"0SBNUE", 'F'}, - {"0SBS", 'F'}, - {"0SBS&(", 'F'}, - {"0SBS&1", 'F'}, - {"0SBS&F", 'F'}, - {"0SBS&N", 'F'}, - {"0SBS&S", 'F'}, - {"0SBS&V", 'F'}, - {"0SBS,(", 'F'}, - {"0SBS,F", 'F'}, - {"0SBS,V", 'F'}, - {"0SBS;", 'F'}, - {"0SBS;C", 'F'}, - {"0SBSB(", 'F'}, - {"0SBSB1", 'F'}, - {"0SBSBF", 'F'}, - {"0SBSBN", 'F'}, - {"0SBSBS", 'F'}, - {"0SBSBV", 'F'}, - {"0SBSC", 'F'}, - {"0SBSK(", 'F'}, - {"0SBSK1", 'F'}, - {"0SBSKF", 'F'}, - {"0SBSKN", 'F'}, - {"0SBSKS", 'F'}, - {"0SBSKV", 'F'}, - {"0SBSO(", 'F'}, - {"0SBSO1", 'F'}, - {"0SBSOF", 'F'}, - {"0SBSON", 'F'}, - {"0SBSOS", 'F'}, - {"0SBSOV", 'F'}, - {"0SBSU(", 'F'}, - {"0SBSUE", 'F'}, - {"0SBV", 'F'}, - {"0SBV&(", 'F'}, - {"0SBV&1", 'F'}, - {"0SBV&F", 'F'}, - {"0SBV&N", 'F'}, - {"0SBV&S", 'F'}, - {"0SBV&V", 'F'}, - {"0SBV,(", 'F'}, - {"0SBV,1", 'F'}, - {"0SBV,F", 'F'}, - {"0SBV,N", 'F'}, - {"0SBV,S", 'F'}, - {"0SBV,V", 'F'}, - {"0SBV;", 'F'}, - {"0SBV;C", 'F'}, - {"0SBVB(", 'F'}, - {"0SBVB1", 'F'}, - {"0SBVBF", 'F'}, - {"0SBVBN", 'F'}, - {"0SBVBS", 'F'}, - {"0SBVBV", 'F'}, - {"0SBVC", 'F'}, - {"0SBVK(", 'F'}, - {"0SBVK1", 'F'}, - {"0SBVKF", 'F'}, - {"0SBVKN", 'F'}, - {"0SBVKS", 'F'}, - {"0SBVKV", 'F'}, - {"0SBVO(", 'F'}, - {"0SBVOF", 'F'}, - {"0SBVOS", 'F'}, - {"0SBVU(", 'F'}, - {"0SBVUE", 'F'}, - {"0SC", 'F'}, - {"0SE(((", 'F'}, - {"0SE((1", 'F'}, - {"0SE((F", 'F'}, - {"0SE((N", 'F'}, - {"0SE((S", 'F'}, - {"0SE((V", 'F'}, - {"0SE(1)", 'F'}, - {"0SE(1O", 'F'}, - {"0SE(F(", 'F'}, - {"0SE(N)", 'F'}, - {"0SE(NO", 'F'}, - {"0SE(S)", 'F'}, - {"0SE(SO", 'F'}, - {"0SE(V)", 'F'}, - {"0SE(VO", 'F'}, - {"0SE1C", 'F'}, - {"0SE1O(", 'F'}, - {"0SE1OF", 'F'}, - {"0SE1OS", 'F'}, - {"0SE1OV", 'F'}, - {"0SE1UE", 'F'}, - {"0SEF((", 'F'}, - {"0SEF()", 'F'}, - {"0SEF(1", 'F'}, - {"0SEF(F", 'F'}, - {"0SEF(N", 'F'}, - {"0SEF(S", 'F'}, - {"0SEF(V", 'F'}, - {"0SEK((", 'F'}, - {"0SEK(1", 'F'}, - {"0SEK(E", 'F'}, - {"0SEK(F", 'F'}, - {"0SEK(N", 'F'}, - {"0SEK(S", 'F'}, - {"0SEK(V", 'F'}, - {"0SEK1C", 'F'}, - {"0SEK1O", 'F'}, - {"0SEK1U", 'F'}, - {"0SEKF(", 'F'}, - {"0SEKNC", 'F'}, - {"0SEKNE", 'F'}, - {"0SEKNU", 'F'}, - {"0SEKOK", 'F'}, - {"0SEKSC", 'F'}, - {"0SEKSO", 'F'}, - {"0SEKSU", 'F'}, - {"0SEKU(", 'F'}, - {"0SEKU1", 'F'}, - {"0SEKUE", 'F'}, - {"0SEKUF", 'F'}, - {"0SEKUN", 'F'}, - {"0SEKUS", 'F'}, - {"0SEKUV", 'F'}, - {"0SEKVC", 'F'}, - {"0SEKVO", 'F'}, - {"0SEKVU", 'F'}, - {"0SENC", 'F'}, - {"0SENEN", 'F'}, - {"0SENO(", 'F'}, - {"0SENOF", 'F'}, - {"0SENOS", 'F'}, - {"0SENOV", 'F'}, - {"0SENUE", 'F'}, - {"0SEOKN", 'F'}, - {"0SESC", 'F'}, - {"0SESO(", 'F'}, - {"0SESO1", 'F'}, - {"0SESOF", 'F'}, - {"0SESON", 'F'}, - {"0SESOS", 'F'}, - {"0SESOV", 'F'}, - {"0SESUE", 'F'}, - {"0SEU((", 'F'}, - {"0SEU(1", 'F'}, - {"0SEU(F", 'F'}, - {"0SEU(N", 'F'}, - {"0SEU(S", 'F'}, - {"0SEU(V", 'F'}, - {"0SEU1,", 'F'}, - {"0SEU1C", 'F'}, - {"0SEU1O", 'F'}, - {"0SEUEF", 'F'}, - {"0SEUEK", 'F'}, - {"0SEUF(", 'F'}, - {"0SEUN,", 'F'}, - {"0SEUNC", 'F'}, - {"0SEUNO", 'F'}, - {"0SEUS,", 'F'}, - {"0SEUSC", 'F'}, - {"0SEUSO", 'F'}, - {"0SEUV,", 'F'}, - {"0SEUVC", 'F'}, - {"0SEUVO", 'F'}, - {"0SEVC", 'F'}, - {"0SEVO(", 'F'}, - {"0SEVOF", 'F'}, - {"0SEVOS", 'F'}, - {"0SEVUE", 'F'}, - {"0SF(((", 'F'}, - {"0SF(()", 'F'}, - {"0SF((1", 'F'}, - {"0SF((E", 'F'}, - {"0SF((F", 'F'}, - {"0SF((N", 'F'}, - {"0SF((S", 'F'}, - {"0SF((V", 'F'}, - {"0SF()1", 'F'}, - {"0SF()F", 'F'}, - {"0SF()K", 'F'}, - {"0SF()N", 'F'}, - {"0SF()O", 'F'}, - {"0SF()S", 'F'}, - {"0SF()U", 'F'}, - {"0SF()V", 'F'}, - {"0SF(1)", 'F'}, - {"0SF(1N", 'F'}, - {"0SF(1O", 'F'}, - {"0SF(E(", 'F'}, - {"0SF(E1", 'F'}, - {"0SF(EF", 'F'}, - {"0SF(EK", 'F'}, - {"0SF(EN", 'F'}, - {"0SF(ES", 'F'}, - {"0SF(EV", 'F'}, - {"0SF(F(", 'F'}, - {"0SF(N)", 'F'}, - {"0SF(N,", 'F'}, - {"0SF(NO", 'F'}, - {"0SF(S)", 'F'}, - {"0SF(SO", 'F'}, - {"0SF(V)", 'F'}, - {"0SF(VO", 'F'}, - {"0SK(((", 'F'}, - {"0SK((1", 'F'}, - {"0SK((F", 'F'}, - {"0SK((N", 'F'}, - {"0SK((S", 'F'}, - {"0SK((V", 'F'}, - {"0SK(1)", 'F'}, - {"0SK(1O", 'F'}, - {"0SK(F(", 'F'}, - {"0SK(N)", 'F'}, - {"0SK(NO", 'F'}, - {"0SK(S)", 'F'}, - {"0SK(SO", 'F'}, - {"0SK(V)", 'F'}, - {"0SK(VO", 'F'}, - {"0SK)&(", 'F'}, - {"0SK)&1", 'F'}, - {"0SK)&F", 'F'}, - {"0SK)&N", 'F'}, - {"0SK)&S", 'F'}, - {"0SK)&V", 'F'}, - {"0SK);E", 'F'}, - {"0SK);T", 'F'}, - {"0SK)B(", 'F'}, - {"0SK)B1", 'F'}, - {"0SK)BF", 'F'}, - {"0SK)BN", 'F'}, - {"0SK)BS", 'F'}, - {"0SK)BV", 'F'}, - {"0SK)E(", 'F'}, - {"0SK)E1", 'F'}, - {"0SK)EF", 'F'}, - {"0SK)EK", 'F'}, - {"0SK)EN", 'F'}, - {"0SK)ES", 'F'}, - {"0SK)EV", 'F'}, - {"0SK)OF", 'F'}, - {"0SK)UE", 'F'}, - {"0SK1", 'F'}, - {"0SK1&(", 'F'}, - {"0SK1&1", 'F'}, - {"0SK1&F", 'F'}, - {"0SK1&N", 'F'}, - {"0SK1&S", 'F'}, - {"0SK1&V", 'F'}, - {"0SK1;", 'F'}, - {"0SK1;C", 'F'}, - {"0SK1;E", 'F'}, - {"0SK1;T", 'F'}, - {"0SK1B(", 'F'}, - {"0SK1B1", 'F'}, - {"0SK1BF", 'F'}, - {"0SK1BN", 'F'}, - {"0SK1BS", 'F'}, - {"0SK1BV", 'F'}, - {"0SK1C", 'F'}, - {"0SK1E(", 'F'}, - {"0SK1E1", 'F'}, - {"0SK1EF", 'F'}, - {"0SK1EK", 'F'}, - {"0SK1EN", 'F'}, - {"0SK1ES", 'F'}, - {"0SK1EV", 'F'}, - {"0SK1O(", 'F'}, - {"0SK1OF", 'F'}, - {"0SK1OS", 'F'}, - {"0SK1OV", 'F'}, - {"0SK1U(", 'F'}, - {"0SK1UE", 'F'}, - {"0SKF((", 'F'}, - {"0SKF()", 'F'}, - {"0SKF(1", 'F'}, - {"0SKF(F", 'F'}, - {"0SKF(N", 'F'}, - {"0SKF(S", 'F'}, - {"0SKF(V", 'F'}, - {"0SKN", 'F'}, - {"0SKN&(", 'F'}, - {"0SKN&1", 'F'}, - {"0SKN&F", 'F'}, - {"0SKN&N", 'F'}, - {"0SKN&S", 'F'}, - {"0SKN&V", 'F'}, - {"0SKN;", 'F'}, - {"0SKN;C", 'F'}, - {"0SKN;E", 'F'}, - {"0SKN;T", 'F'}, - {"0SKNB(", 'F'}, - {"0SKNB1", 'F'}, - {"0SKNBF", 'F'}, - {"0SKNBN", 'F'}, - {"0SKNBS", 'F'}, - {"0SKNBV", 'F'}, - {"0SKNC", 'F'}, - {"0SKNE(", 'F'}, - {"0SKNE1", 'F'}, - {"0SKNEF", 'F'}, - {"0SKNEN", 'F'}, - {"0SKNES", 'F'}, - {"0SKNEV", 'F'}, - {"0SKNU(", 'F'}, - {"0SKNUE", 'F'}, - {"0SKS", 'F'}, - {"0SKS&(", 'F'}, - {"0SKS&1", 'F'}, - {"0SKS&F", 'F'}, - {"0SKS&N", 'F'}, - {"0SKS&S", 'F'}, - {"0SKS&V", 'F'}, - {"0SKS;", 'F'}, - {"0SKS;C", 'F'}, - {"0SKS;E", 'F'}, - {"0SKS;T", 'F'}, - {"0SKSB(", 'F'}, - {"0SKSB1", 'F'}, - {"0SKSBF", 'F'}, - {"0SKSBN", 'F'}, - {"0SKSBS", 'F'}, - {"0SKSBV", 'F'}, - {"0SKSC", 'F'}, - {"0SKSE(", 'F'}, - {"0SKSE1", 'F'}, - {"0SKSEF", 'F'}, - {"0SKSEK", 'F'}, - {"0SKSEN", 'F'}, - {"0SKSES", 'F'}, - {"0SKSEV", 'F'}, - {"0SKSO(", 'F'}, - {"0SKSO1", 'F'}, - {"0SKSOF", 'F'}, - {"0SKSON", 'F'}, - {"0SKSOS", 'F'}, - {"0SKSOV", 'F'}, - {"0SKSU(", 'F'}, - {"0SKSUE", 'F'}, - {"0SKUE(", 'F'}, - {"0SKUE1", 'F'}, - {"0SKUEF", 'F'}, - {"0SKUEK", 'F'}, - {"0SKUEN", 'F'}, - {"0SKUES", 'F'}, - {"0SKUEV", 'F'}, - {"0SKV", 'F'}, - {"0SKV&(", 'F'}, - {"0SKV&1", 'F'}, - {"0SKV&F", 'F'}, - {"0SKV&N", 'F'}, - {"0SKV&S", 'F'}, - {"0SKV&V", 'F'}, - {"0SKV;", 'F'}, - {"0SKV;C", 'F'}, - {"0SKV;E", 'F'}, - {"0SKV;T", 'F'}, - {"0SKVB(", 'F'}, - {"0SKVB1", 'F'}, - {"0SKVBF", 'F'}, - {"0SKVBN", 'F'}, - {"0SKVBS", 'F'}, - {"0SKVBV", 'F'}, - {"0SKVC", 'F'}, - {"0SKVE(", 'F'}, - {"0SKVE1", 'F'}, - {"0SKVEF", 'F'}, - {"0SKVEK", 'F'}, - {"0SKVEN", 'F'}, - {"0SKVES", 'F'}, - {"0SKVEV", 'F'}, - {"0SKVO(", 'F'}, - {"0SKVOF", 'F'}, - {"0SKVOS", 'F'}, - {"0SKVU(", 'F'}, - {"0SKVUE", 'F'}, - {"0SO(((", 'F'}, - {"0SO((1", 'F'}, - {"0SO((E", 'F'}, - {"0SO((F", 'F'}, - {"0SO((N", 'F'}, - {"0SO((O", 'F'}, - {"0SO((S", 'F'}, - {"0SO((T", 'F'}, - {"0SO((V", 'F'}, - {"0SO(1&", 'F'}, - {"0SO(1)", 'F'}, - {"0SO(1,", 'F'}, - {"0SO(1O", 'F'}, - {"0SO(E(", 'F'}, - {"0SO(E1", 'F'}, - {"0SO(EE", 'F'}, - {"0SO(EF", 'F'}, - {"0SO(EK", 'F'}, - {"0SO(EN", 'F'}, - {"0SO(ES", 'F'}, - {"0SO(EV", 'F'}, - {"0SO(F(", 'F'}, - {"0SO(N&", 'F'}, - {"0SO(N)", 'F'}, - {"0SO(N,", 'F'}, - {"0SO(NO", 'F'}, - {"0SO(O(", 'F'}, - {"0SO(O1", 'F'}, - {"0SO(OF", 'F'}, - {"0SO(ON", 'F'}, - {"0SO(OS", 'F'}, - {"0SO(OV", 'F'}, - {"0SO(S&", 'F'}, - {"0SO(S)", 'F'}, - {"0SO(S,", 'F'}, - {"0SO(SO", 'F'}, - {"0SO(T(", 'F'}, - {"0SO(V&", 'F'}, - {"0SO(V)", 'F'}, - {"0SO(V,", 'F'}, - {"0SO(VO", 'F'}, - {"0SO1&(", 'F'}, - {"0SO1&1", 'F'}, - {"0SO1&E", 'F'}, - {"0SO1&F", 'F'}, - {"0SO1&K", 'F'}, - {"0SO1&N", 'F'}, - {"0SO1&S", 'F'}, - {"0SO1&T", 'F'}, - {"0SO1&U", 'F'}, - {"0SO1&V", 'F'}, - {"0SO1((", 'F'}, - {"0SO1(E", 'F'}, - {"0SO1(U", 'F'}, - {"0SO1)&", 'F'}, - {"0SO1),", 'F'}, - {"0SO1);", 'F'}, - {"0SO1)B", 'F'}, - {"0SO1)E", 'F'}, - {"0SO1)K", 'F'}, - {"0SO1)O", 'F'}, - {"0SO1)U", 'F'}, - {"0SO1,(", 'F'}, - {"0SO1,F", 'F'}, - {"0SO1,V", 'F'}, - {"0SO1;", 'F'}, - {"0SO1;C", 'F'}, - {"0SO1;E", 'F'}, - {"0SO1;N", 'F'}, - {"0SO1;T", 'F'}, - {"0SO1A(", 'F'}, - {"0SO1AF", 'F'}, - {"0SO1AS", 'F'}, - {"0SO1AT", 'F'}, - {"0SO1AV", 'F'}, - {"0SO1B(", 'F'}, - {"0SO1B1", 'F'}, - {"0SO1BE", 'F'}, - {"0SO1BF", 'F'}, - {"0SO1BN", 'F'}, - {"0SO1BS", 'F'}, - {"0SO1BV", 'F'}, - {"0SO1C", 'F'}, - {"0SO1E(", 'F'}, - {"0SO1E1", 'F'}, - {"0SO1EF", 'F'}, - {"0SO1EK", 'F'}, - {"0SO1EN", 'F'}, - {"0SO1EO", 'F'}, - {"0SO1ES", 'F'}, - {"0SO1EU", 'F'}, - {"0SO1EV", 'F'}, - {"0SO1F(", 'F'}, - {"0SO1K(", 'F'}, - {"0SO1K)", 'F'}, - {"0SO1K1", 'F'}, - {"0SO1KB", 'F'}, - {"0SO1KF", 'F'}, - {"0SO1KN", 'F'}, - {"0SO1KS", 'F'}, - {"0SO1KU", 'F'}, - {"0SO1KV", 'F'}, - {"0SO1N&", 'F'}, - {"0SO1N(", 'F'}, - {"0SO1N,", 'F'}, - {"0SO1NE", 'F'}, - {"0SO1NF", 'F'}, - {"0SO1NU", 'F'}, - {"0SO1O(", 'F'}, - {"0SO1OF", 'F'}, - {"0SO1OK", 'F'}, - {"0SO1OS", 'F'}, - {"0SO1OT", 'F'}, - {"0SO1OU", 'F'}, - {"0SO1OV", 'F'}, - {"0SO1S(", 'F'}, - {"0SO1SF", 'F'}, - {"0SO1SU", 'F'}, - {"0SO1SV", 'F'}, - {"0SO1U", 'F'}, - {"0SO1U(", 'F'}, - {"0SO1U1", 'F'}, - {"0SO1U;", 'F'}, - {"0SO1UC", 'F'}, - {"0SO1UE", 'F'}, - {"0SO1UF", 'F'}, - {"0SO1UK", 'F'}, - {"0SO1UN", 'F'}, - {"0SO1UO", 'F'}, - {"0SO1US", 'F'}, - {"0SO1UT", 'F'}, - {"0SO1UV", 'F'}, - {"0SO1V(", 'F'}, - {"0SO1VF", 'F'}, - {"0SO1VO", 'F'}, - {"0SO1VS", 'F'}, - {"0SO1VU", 'F'}, - {"0SOF((", 'F'}, - {"0SOF()", 'F'}, - {"0SOF(1", 'F'}, - {"0SOF(E", 'F'}, - {"0SOF(F", 'F'}, - {"0SOF(N", 'F'}, - {"0SOF(S", 'F'}, - {"0SOF(V", 'F'}, - {"0SOK&(", 'F'}, - {"0SOK&1", 'F'}, - {"0SOK&F", 'F'}, - {"0SOK&N", 'F'}, - {"0SOK&S", 'F'}, - {"0SOK&V", 'F'}, - {"0SOK((", 'F'}, - {"0SOK(1", 'F'}, - {"0SOK(F", 'F'}, - {"0SOK(N", 'F'}, - {"0SOK(S", 'F'}, - {"0SOK(V", 'F'}, - {"0SOK1C", 'F'}, - {"0SOK1O", 'F'}, - {"0SOKF(", 'F'}, - {"0SOKNC", 'F'}, - {"0SOKO(", 'F'}, - {"0SOKO1", 'F'}, - {"0SOKOF", 'F'}, - {"0SOKON", 'F'}, - {"0SOKOS", 'F'}, - {"0SOKOV", 'F'}, - {"0SOKSC", 'F'}, - {"0SOKSO", 'F'}, - {"0SOKVC", 'F'}, - {"0SOKVO", 'F'}, - {"0SON&(", 'F'}, - {"0SON&1", 'F'}, - {"0SON&E", 'F'}, - {"0SON&F", 'F'}, - {"0SON&K", 'F'}, - {"0SON&N", 'F'}, - {"0SON&S", 'F'}, - {"0SON&T", 'F'}, - {"0SON&U", 'F'}, - {"0SON&V", 'F'}, - {"0SON((", 'F'}, - {"0SON(1", 'F'}, - {"0SON(E", 'F'}, - {"0SON(F", 'F'}, - {"0SON(S", 'F'}, - {"0SON(U", 'F'}, - {"0SON(V", 'F'}, - {"0SON)&", 'F'}, - {"0SON),", 'F'}, - {"0SON);", 'F'}, - {"0SON)B", 'F'}, - {"0SON)E", 'F'}, - {"0SON)K", 'F'}, - {"0SON)O", 'F'}, - {"0SON)U", 'F'}, - {"0SON,(", 'F'}, - {"0SON,F", 'F'}, - {"0SON,V", 'F'}, - {"0SON1(", 'F'}, - {"0SON1F", 'F'}, - {"0SON1N", 'F'}, - {"0SON1O", 'F'}, - {"0SON1S", 'F'}, - {"0SON1U", 'F'}, - {"0SON1V", 'F'}, - {"0SON;", 'F'}, - {"0SON;C", 'F'}, - {"0SON;E", 'F'}, - {"0SON;N", 'F'}, - {"0SON;T", 'F'}, - {"0SONA(", 'F'}, - {"0SONAF", 'F'}, - {"0SONAS", 'F'}, - {"0SONAT", 'F'}, - {"0SONAV", 'F'}, - {"0SONB(", 'F'}, - {"0SONB1", 'F'}, - {"0SONBE", 'F'}, - {"0SONBF", 'F'}, - {"0SONBN", 'F'}, - {"0SONBS", 'F'}, - {"0SONBV", 'F'}, - {"0SONE(", 'F'}, - {"0SONE1", 'F'}, - {"0SONEF", 'F'}, - {"0SONEN", 'F'}, - {"0SONEO", 'F'}, - {"0SONES", 'F'}, - {"0SONEU", 'F'}, - {"0SONEV", 'F'}, - {"0SONF(", 'F'}, - {"0SONK(", 'F'}, - {"0SONK)", 'F'}, - {"0SONK1", 'F'}, - {"0SONKB", 'F'}, - {"0SONKF", 'F'}, - {"0SONKS", 'F'}, - {"0SONKU", 'F'}, - {"0SONKV", 'F'}, - {"0SONO(", 'F'}, - {"0SONOF", 'F'}, - {"0SONOK", 'F'}, - {"0SONOS", 'F'}, - {"0SONOT", 'F'}, - {"0SONOU", 'F'}, - {"0SONOV", 'F'}, - {"0SONSU", 'F'}, - {"0SONU", 'F'}, - {"0SONU(", 'F'}, - {"0SONU1", 'F'}, - {"0SONU;", 'F'}, - {"0SONUC", 'F'}, - {"0SONUE", 'F'}, - {"0SONUF", 'F'}, - {"0SONUK", 'F'}, - {"0SONUN", 'F'}, - {"0SONUO", 'F'}, - {"0SONUS", 'F'}, - {"0SONUT", 'F'}, - {"0SONUV", 'F'}, - {"0SOS", 'F'}, - {"0SOS&(", 'F'}, - {"0SOS&1", 'F'}, - {"0SOS&E", 'F'}, - {"0SOS&F", 'F'}, - {"0SOS&K", 'F'}, - {"0SOS&N", 'F'}, - {"0SOS&S", 'F'}, - {"0SOS&T", 'F'}, - {"0SOS&U", 'F'}, - {"0SOS&V", 'F'}, - {"0SOS((", 'F'}, - {"0SOS(E", 'F'}, - {"0SOS(U", 'F'}, - {"0SOS)&", 'F'}, - {"0SOS),", 'F'}, - {"0SOS);", 'F'}, - {"0SOS)B", 'F'}, - {"0SOS)E", 'F'}, - {"0SOS)K", 'F'}, - {"0SOS)O", 'F'}, - {"0SOS)U", 'F'}, - {"0SOS,(", 'F'}, - {"0SOS,F", 'F'}, - {"0SOS,V", 'F'}, - {"0SOS1(", 'F'}, - {"0SOS1F", 'F'}, - {"0SOS1N", 'F'}, - {"0SOS1O", 'F'}, - {"0SOS1S", 'F'}, - {"0SOS1U", 'F'}, - {"0SOS1V", 'F'}, - {"0SOS;", 'F'}, - {"0SOS;C", 'F'}, - {"0SOS;E", 'F'}, - {"0SOS;N", 'F'}, - {"0SOS;T", 'F'}, - {"0SOSA(", 'F'}, - {"0SOSAF", 'F'}, - {"0SOSAS", 'F'}, - {"0SOSAT", 'F'}, - {"0SOSAV", 'F'}, - {"0SOSB(", 'F'}, - {"0SOSB1", 'F'}, - {"0SOSBE", 'F'}, - {"0SOSBF", 'F'}, - {"0SOSBN", 'F'}, - {"0SOSBS", 'F'}, - {"0SOSBV", 'F'}, - {"0SOSC", 'F'}, - {"0SOSE(", 'F'}, - {"0SOSE1", 'F'}, - {"0SOSEF", 'F'}, - {"0SOSEK", 'F'}, - {"0SOSEN", 'F'}, - {"0SOSEO", 'F'}, - {"0SOSES", 'F'}, - {"0SOSEU", 'F'}, - {"0SOSEV", 'F'}, - {"0SOSF(", 'F'}, - {"0SOSK(", 'F'}, - {"0SOSK)", 'F'}, - {"0SOSK1", 'F'}, - {"0SOSKB", 'F'}, - {"0SOSKF", 'F'}, - {"0SOSKN", 'F'}, - {"0SOSKS", 'F'}, - {"0SOSKU", 'F'}, - {"0SOSKV", 'F'}, - {"0SOSO(", 'F'}, - {"0SOSO1", 'F'}, - {"0SOSOF", 'F'}, - {"0SOSOK", 'F'}, - {"0SOSON", 'F'}, - {"0SOSOS", 'F'}, - {"0SOSOT", 'F'}, - {"0SOSOU", 'F'}, - {"0SOSOV", 'F'}, - {"0SOSU", 'F'}, - {"0SOSU(", 'F'}, - {"0SOSU1", 'F'}, - {"0SOSU;", 'F'}, - {"0SOSUC", 'F'}, - {"0SOSUE", 'F'}, - {"0SOSUF", 'F'}, - {"0SOSUK", 'F'}, - {"0SOSUN", 'F'}, - {"0SOSUO", 'F'}, - {"0SOSUS", 'F'}, - {"0SOSUT", 'F'}, - {"0SOSUV", 'F'}, - {"0SOSV(", 'F'}, - {"0SOSVF", 'F'}, - {"0SOSVO", 'F'}, - {"0SOSVS", 'F'}, - {"0SOSVU", 'F'}, - {"0SOT((", 'F'}, - {"0SOT(1", 'F'}, - {"0SOT(F", 'F'}, - {"0SOT(N", 'F'}, - {"0SOT(S", 'F'}, - {"0SOT(T", 'F'}, - {"0SOT(V", 'F'}, - {"0SOU((", 'F'}, - {"0SOU(E", 'F'}, - {"0SOUEK", 'F'}, - {"0SOUEN", 'F'}, - {"0SOV", 'F'}, - {"0SOV&(", 'F'}, - {"0SOV&1", 'F'}, - {"0SOV&E", 'F'}, - {"0SOV&F", 'F'}, - {"0SOV&K", 'F'}, - {"0SOV&N", 'F'}, - {"0SOV&S", 'F'}, - {"0SOV&T", 'F'}, - {"0SOV&U", 'F'}, - {"0SOV&V", 'F'}, - {"0SOV((", 'F'}, - {"0SOV(E", 'F'}, - {"0SOV(U", 'F'}, - {"0SOV)&", 'F'}, - {"0SOV),", 'F'}, - {"0SOV);", 'F'}, - {"0SOV)B", 'F'}, - {"0SOV)E", 'F'}, - {"0SOV)K", 'F'}, - {"0SOV)O", 'F'}, - {"0SOV)U", 'F'}, - {"0SOV,(", 'F'}, - {"0SOV,1", 'F'}, - {"0SOV,F", 'F'}, - {"0SOV,N", 'F'}, - {"0SOV,S", 'F'}, - {"0SOV,V", 'F'}, - {"0SOV;", 'F'}, - {"0SOV;C", 'F'}, - {"0SOV;E", 'F'}, - {"0SOV;N", 'F'}, - {"0SOV;T", 'F'}, - {"0SOVA(", 'F'}, - {"0SOVAF", 'F'}, - {"0SOVAS", 'F'}, - {"0SOVAT", 'F'}, - {"0SOVAV", 'F'}, - {"0SOVB(", 'F'}, - {"0SOVB1", 'F'}, - {"0SOVBE", 'F'}, - {"0SOVBF", 'F'}, - {"0SOVBN", 'F'}, - {"0SOVBS", 'F'}, - {"0SOVBV", 'F'}, - {"0SOVC", 'F'}, - {"0SOVE(", 'F'}, - {"0SOVE1", 'F'}, - {"0SOVEF", 'F'}, - {"0SOVEK", 'F'}, - {"0SOVEN", 'F'}, - {"0SOVEO", 'F'}, - {"0SOVES", 'F'}, - {"0SOVEU", 'F'}, - {"0SOVEV", 'F'}, - {"0SOVF(", 'F'}, - {"0SOVK(", 'F'}, - {"0SOVK)", 'F'}, - {"0SOVK1", 'F'}, - {"0SOVKB", 'F'}, - {"0SOVKF", 'F'}, - {"0SOVKN", 'F'}, - {"0SOVKS", 'F'}, - {"0SOVKU", 'F'}, - {"0SOVKV", 'F'}, - {"0SOVO(", 'F'}, - {"0SOVOF", 'F'}, - {"0SOVOK", 'F'}, - {"0SOVOS", 'F'}, - {"0SOVOT", 'F'}, - {"0SOVOU", 'F'}, - {"0SOVS(", 'F'}, - {"0SOVS1", 'F'}, - {"0SOVSF", 'F'}, - {"0SOVSO", 'F'}, - {"0SOVSU", 'F'}, - {"0SOVSV", 'F'}, - {"0SOVU", 'F'}, - {"0SOVU(", 'F'}, - {"0SOVU1", 'F'}, - {"0SOVU;", 'F'}, - {"0SOVUC", 'F'}, - {"0SOVUE", 'F'}, - {"0SOVUF", 'F'}, - {"0SOVUK", 'F'}, - {"0SOVUN", 'F'}, - {"0SOVUO", 'F'}, - {"0SOVUS", 'F'}, - {"0SOVUT", 'F'}, - {"0SOVUV", 'F'}, - {"0SU(((", 'F'}, - {"0SU((1", 'F'}, - {"0SU((E", 'F'}, - {"0SU((F", 'F'}, - {"0SU((N", 'F'}, - {"0SU((S", 'F'}, - {"0SU((V", 'F'}, - {"0SU(1)", 'F'}, - {"0SU(1O", 'F'}, - {"0SU(E(", 'F'}, - {"0SU(E1", 'F'}, - {"0SU(EF", 'F'}, - {"0SU(EK", 'F'}, - {"0SU(EN", 'F'}, - {"0SU(ES", 'F'}, - {"0SU(EV", 'F'}, - {"0SU(F(", 'F'}, - {"0SU(N)", 'F'}, - {"0SU(NO", 'F'}, - {"0SU(S)", 'F'}, - {"0SU(SO", 'F'}, - {"0SU(V)", 'F'}, - {"0SU(VO", 'F'}, - {"0SU1,(", 'F'}, - {"0SU1,F", 'F'}, - {"0SU1,V", 'F'}, - {"0SU1C", 'F'}, - {"0SU1O(", 'F'}, - {"0SU1OF", 'F'}, - {"0SU1OS", 'F'}, - {"0SU1OV", 'F'}, - {"0SU;", 'F'}, - {"0SU;C", 'F'}, - {"0SUC", 'F'}, - {"0SUE", 'F'}, - {"0SUE((", 'F'}, - {"0SUE(1", 'F'}, - {"0SUE(E", 'F'}, - {"0SUE(F", 'F'}, - {"0SUE(N", 'F'}, - {"0SUE(O", 'F'}, - {"0SUE(S", 'F'}, - {"0SUE(V", 'F'}, - {"0SUE1", 'F'}, - {"0SUE1&", 'F'}, - {"0SUE1(", 'F'}, - {"0SUE1)", 'F'}, - {"0SUE1,", 'F'}, - {"0SUE1;", 'F'}, - {"0SUE1B", 'F'}, - {"0SUE1C", 'F'}, - {"0SUE1F", 'F'}, - {"0SUE1K", 'F'}, - {"0SUE1N", 'F'}, - {"0SUE1O", 'F'}, - {"0SUE1S", 'F'}, - {"0SUE1U", 'F'}, - {"0SUE1V", 'F'}, - {"0SUE;", 'F'}, - {"0SUE;C", 'F'}, - {"0SUEC", 'F'}, - {"0SUEF", 'F'}, - {"0SUEF(", 'F'}, - {"0SUEF,", 'F'}, - {"0SUEF;", 'F'}, - {"0SUEFC", 'F'}, - {"0SUEK", 'F'}, - {"0SUEK(", 'F'}, - {"0SUEK1", 'F'}, - {"0SUEK;", 'F'}, - {"0SUEKC", 'F'}, - {"0SUEKF", 'F'}, - {"0SUEKN", 'F'}, - {"0SUEKO", 'F'}, - {"0SUEKS", 'F'}, - {"0SUEKV", 'F'}, - {"0SUEN", 'F'}, - {"0SUEN&", 'F'}, - {"0SUEN(", 'F'}, - {"0SUEN)", 'F'}, - {"0SUEN,", 'F'}, - {"0SUEN1", 'F'}, - {"0SUEN;", 'F'}, - {"0SUENB", 'F'}, - {"0SUENC", 'F'}, - {"0SUENF", 'F'}, - {"0SUENK", 'F'}, - {"0SUENO", 'F'}, - {"0SUENS", 'F'}, - {"0SUENU", 'F'}, - {"0SUEOK", 'F'}, - {"0SUEON", 'F'}, - {"0SUEOO", 'F'}, - {"0SUES", 'F'}, - {"0SUES&", 'F'}, - {"0SUES(", 'F'}, - {"0SUES)", 'F'}, - {"0SUES,", 'F'}, - {"0SUES1", 'F'}, - {"0SUES;", 'F'}, - {"0SUESB", 'F'}, - {"0SUESC", 'F'}, - {"0SUESF", 'F'}, - {"0SUESK", 'F'}, - {"0SUESO", 'F'}, - {"0SUESU", 'F'}, - {"0SUESV", 'F'}, - {"0SUEV", 'F'}, - {"0SUEV&", 'F'}, - {"0SUEV(", 'F'}, - {"0SUEV)", 'F'}, - {"0SUEV,", 'F'}, - {"0SUEV;", 'F'}, - {"0SUEVB", 'F'}, - {"0SUEVC", 'F'}, - {"0SUEVF", 'F'}, - {"0SUEVK", 'F'}, - {"0SUEVN", 'F'}, - {"0SUEVO", 'F'}, - {"0SUEVS", 'F'}, - {"0SUEVU", 'F'}, - {"0SUF((", 'F'}, - {"0SUF()", 'F'}, - {"0SUF(1", 'F'}, - {"0SUF(F", 'F'}, - {"0SUF(N", 'F'}, - {"0SUF(S", 'F'}, - {"0SUF(V", 'F'}, - {"0SUK((", 'F'}, - {"0SUK(E", 'F'}, - {"0SUN((", 'F'}, - {"0SUN(1", 'F'}, - {"0SUN(F", 'F'}, - {"0SUN(S", 'F'}, - {"0SUN(V", 'F'}, - {"0SUN,(", 'F'}, - {"0SUN,F", 'F'}, - {"0SUN,V", 'F'}, - {"0SUN1(", 'F'}, - {"0SUN1,", 'F'}, - {"0SUN1O", 'F'}, - {"0SUNC", 'F'}, - {"0SUNE(", 'F'}, - {"0SUNE1", 'F'}, - {"0SUNEF", 'F'}, - {"0SUNEN", 'F'}, - {"0SUNES", 'F'}, - {"0SUNEV", 'F'}, - {"0SUNF(", 'F'}, - {"0SUNO(", 'F'}, - {"0SUNOF", 'F'}, - {"0SUNOS", 'F'}, - {"0SUNOV", 'F'}, - {"0SUNS(", 'F'}, - {"0SUNS,", 'F'}, - {"0SUNSO", 'F'}, - {"0SUO((", 'F'}, - {"0SUO(E", 'F'}, - {"0SUON(", 'F'}, - {"0SUON1", 'F'}, - {"0SUONF", 'F'}, - {"0SUONS", 'F'}, - {"0SUS,(", 'F'}, - {"0SUS,F", 'F'}, - {"0SUS,V", 'F'}, - {"0SUSC", 'F'}, - {"0SUSO(", 'F'}, - {"0SUSO1", 'F'}, - {"0SUSOF", 'F'}, - {"0SUSON", 'F'}, - {"0SUSOS", 'F'}, - {"0SUSOV", 'F'}, - {"0SUTN(", 'F'}, - {"0SUTN1", 'F'}, - {"0SUTNF", 'F'}, - {"0SUTNS", 'F'}, - {"0SUV,(", 'F'}, - {"0SUV,1", 'F'}, - {"0SUV,F", 'F'}, - {"0SUV,N", 'F'}, - {"0SUV,S", 'F'}, - {"0SUV,V", 'F'}, - {"0SUVC", 'F'}, - {"0SUVO(", 'F'}, - {"0SUVOF", 'F'}, - {"0SUVOS", 'F'}, - {"0SVF((", 'F'}, - {"0SVF()", 'F'}, - {"0SVF(1", 'F'}, - {"0SVF(F", 'F'}, - {"0SVF(N", 'F'}, - {"0SVF(S", 'F'}, - {"0SVF(V", 'F'}, - {"0SVO((", 'F'}, - {"0SVO(1", 'F'}, - {"0SVO(F", 'F'}, - {"0SVO(N", 'F'}, - {"0SVO(S", 'F'}, - {"0SVO(V", 'F'}, - {"0SVOF(", 'F'}, - {"0SVOS(", 'F'}, - {"0SVOS1", 'F'}, - {"0SVOSF", 'F'}, - {"0SVOSO", 'F'}, - {"0SVOSU", 'F'}, - {"0SVOSV", 'F'}, - {"0SVS", 'F'}, - {"0SVS;", 'F'}, - {"0SVS;C", 'F'}, - {"0SVSC", 'F'}, - {"0SVSO(", 'F'}, - {"0SVSO1", 'F'}, - {"0SVSOF", 'F'}, - {"0SVSON", 'F'}, - {"0SVSOS", 'F'}, - {"0SVSOV", 'F'}, - {"0SVUE", 'F'}, - {"0SVUE;", 'F'}, - {"0SVUEC", 'F'}, - {"0SVUEK", 'F'}, - {"0T((((", 'F'}, - {"0T(((1", 'F'}, - {"0T(((F", 'F'}, - {"0T(((N", 'F'}, - {"0T(((S", 'F'}, - {"0T(((T", 'F'}, - {"0T(((V", 'F'}, - {"0T((1(", 'F'}, - {"0T((1)", 'F'}, - {"0T((1F", 'F'}, - {"0T((1N", 'F'}, - {"0T((1O", 'F'}, - {"0T((1S", 'F'}, - {"0T((1V", 'F'}, - {"0T((F(", 'F'}, - {"0T((N(", 'F'}, - {"0T((N)", 'F'}, - {"0T((N1", 'F'}, - {"0T((NF", 'F'}, - {"0T((NO", 'F'}, - {"0T((NS", 'F'}, - {"0T((S(", 'F'}, - {"0T((S)", 'F'}, - {"0T((S1", 'F'}, - {"0T((SF", 'F'}, - {"0T((SN", 'F'}, - {"0T((SO", 'F'}, - {"0T((SV", 'F'}, - {"0T((T(", 'F'}, - {"0T((V(", 'F'}, - {"0T((V)", 'F'}, - {"0T((VF", 'F'}, - {"0T((VO", 'F'}, - {"0T((VS", 'F'}, - {"0T(1)F", 'F'}, - {"0T(1)O", 'F'}, - {"0T(1)U", 'F'}, - {"0T(1F(", 'F'}, - {"0T(1N)", 'F'}, - {"0T(1O(", 'F'}, - {"0T(1OF", 'F'}, - {"0T(1OS", 'F'}, - {"0T(1OT", 'F'}, - {"0T(1OV", 'F'}, - {"0T(1S)", 'F'}, - {"0T(1V)", 'F'}, - {"0T(1VO", 'F'}, - {"0T(F((", 'F'}, - {"0T(F()", 'F'}, - {"0T(F(1", 'F'}, - {"0T(F(F", 'F'}, - {"0T(F(N", 'F'}, - {"0T(F(S", 'F'}, - {"0T(F(V", 'F'}, - {"0T(N((", 'F'}, - {"0T(N(1", 'F'}, - {"0T(N(F", 'F'}, - {"0T(N(S", 'F'}, - {"0T(N(V", 'F'}, - {"0T(N)F", 'F'}, - {"0T(N)O", 'F'}, - {"0T(N)U", 'F'}, - {"0T(N1)", 'F'}, - {"0T(N1O", 'F'}, - {"0T(NF(", 'F'}, - {"0T(NO(", 'F'}, - {"0T(NOF", 'F'}, - {"0T(NOS", 'F'}, - {"0T(NOT", 'F'}, - {"0T(NOV", 'F'}, - {"0T(NS)", 'F'}, - {"0T(NSO", 'F'}, - {"0T(S)F", 'F'}, - {"0T(S)O", 'F'}, - {"0T(S)U", 'F'}, - {"0T(S1)", 'F'}, - {"0T(S1O", 'F'}, - {"0T(SF(", 'F'}, - {"0T(SN)", 'F'}, - {"0T(SNO", 'F'}, - {"0T(SO(", 'F'}, - {"0T(SO1", 'F'}, - {"0T(SOF", 'F'}, - {"0T(SON", 'F'}, - {"0T(SOS", 'F'}, - {"0T(SOT", 'F'}, - {"0T(SOV", 'F'}, - {"0T(SV)", 'F'}, - {"0T(SVO", 'F'}, - {"0T(T((", 'F'}, - {"0T(T(1", 'F'}, - {"0T(T(F", 'F'}, - {"0T(T(N", 'F'}, - {"0T(T(S", 'F'}, - {"0T(T(V", 'F'}, - {"0T(V)F", 'F'}, - {"0T(V)O", 'F'}, - {"0T(V)U", 'F'}, - {"0T(VF(", 'F'}, - {"0T(VO(", 'F'}, - {"0T(VOF", 'F'}, - {"0T(VOS", 'F'}, - {"0T(VOT", 'F'}, - {"0T(VS)", 'F'}, - {"0T(VSO", 'F'}, - {"0T(VV)", 'F'}, - {"0T1F((", 'F'}, - {"0T1F(1", 'F'}, - {"0T1F(F", 'F'}, - {"0T1F(N", 'F'}, - {"0T1F(S", 'F'}, - {"0T1F(V", 'F'}, - {"0T1O((", 'F'}, - {"0T1O(1", 'F'}, - {"0T1O(F", 'F'}, - {"0T1O(N", 'F'}, - {"0T1O(S", 'F'}, - {"0T1O(V", 'F'}, - {"0T1OF(", 'F'}, - {"0T1OSF", 'F'}, - {"0T1OSO", 'F'}, - {"0T1OVF", 'F'}, - {"0T1OVO", 'F'}, - {"0TF(((", 'F'}, - {"0TF(()", 'F'}, - {"0TF((1", 'F'}, - {"0TF((F", 'F'}, - {"0TF((N", 'F'}, - {"0TF((S", 'F'}, - {"0TF((V", 'F'}, - {"0TF()F", 'F'}, - {"0TF()O", 'F'}, - {"0TF(1)", 'F'}, - {"0TF(1O", 'F'}, - {"0TF(F(", 'F'}, - {"0TF(N)", 'F'}, - {"0TF(NO", 'F'}, - {"0TF(S)", 'F'}, - {"0TF(SO", 'F'}, - {"0TF(V)", 'F'}, - {"0TF(VO", 'F'}, - {"0TN(((", 'F'}, - {"0TN((1", 'F'}, - {"0TN((F", 'F'}, - {"0TN((N", 'F'}, - {"0TN((S", 'F'}, - {"0TN((V", 'F'}, - {"0TN(1)", 'F'}, - {"0TN(1O", 'F'}, - {"0TN(F(", 'F'}, - {"0TN(S)", 'F'}, - {"0TN(SO", 'F'}, - {"0TN(V)", 'F'}, - {"0TN(VO", 'F'}, - {"0TN1;", 'F'}, - {"0TN1;C", 'F'}, - {"0TN1O(", 'F'}, - {"0TN1OF", 'F'}, - {"0TN1OS", 'F'}, - {"0TN1OV", 'F'}, - {"0TNF((", 'F'}, - {"0TNF()", 'F'}, - {"0TNF(1", 'F'}, - {"0TNF(F", 'F'}, - {"0TNF(N", 'F'}, - {"0TNF(S", 'F'}, - {"0TNF(V", 'F'}, - {"0TNO((", 'F'}, - {"0TNO(1", 'F'}, - {"0TNO(F", 'F'}, - {"0TNO(N", 'F'}, - {"0TNO(S", 'F'}, - {"0TNO(V", 'F'}, - {"0TNOF(", 'F'}, - {"0TNOSF", 'F'}, - {"0TNOSO", 'F'}, - {"0TNOVF", 'F'}, - {"0TNOVO", 'F'}, - {"0TNS;", 'F'}, - {"0TNS;C", 'F'}, - {"0TNSO(", 'F'}, - {"0TNSO1", 'F'}, - {"0TNSOF", 'F'}, - {"0TNSON", 'F'}, - {"0TNSOS", 'F'}, - {"0TNSOV", 'F'}, - {"0TNV;", 'F'}, - {"0TNVOS", 'F'}, - {"0TO(((", 'F'}, - {"0TO((1", 'F'}, - {"0TO((F", 'F'}, - {"0TO((N", 'F'}, - {"0TO((S", 'F'}, - {"0TO((V", 'F'}, - {"0TO(1)", 'F'}, - {"0TO(1O", 'F'}, - {"0TO(F(", 'F'}, - {"0TO(N)", 'F'}, - {"0TO(NO", 'F'}, - {"0TO(S)", 'F'}, - {"0TO(SO", 'F'}, - {"0TO(V)", 'F'}, - {"0TO(VO", 'F'}, - {"0TO1O(", 'F'}, - {"0TO1OF", 'F'}, - {"0TO1OS", 'F'}, - {"0TO1OV", 'F'}, - {"0TO1UE", 'F'}, - {"0TOF((", 'F'}, - {"0TOF()", 'F'}, - {"0TOF(1", 'F'}, - {"0TOF(F", 'F'}, - {"0TOF(N", 'F'}, - {"0TOF(S", 'F'}, - {"0TOF(V", 'F'}, - {"0TONO(", 'F'}, - {"0TONOF", 'F'}, - {"0TONOS", 'F'}, - {"0TONOV", 'F'}, - {"0TONUE", 'F'}, - {"0TOSO(", 'F'}, - {"0TOSO1", 'F'}, - {"0TOSOF", 'F'}, - {"0TOSON", 'F'}, - {"0TOSOS", 'F'}, - {"0TOSOV", 'F'}, - {"0TOSUE", 'F'}, - {"0TOVO(", 'F'}, - {"0TOVOF", 'F'}, - {"0TOVOS", 'F'}, - {"0TOVUE", 'F'}, - {"0TSF((", 'F'}, - {"0TSF(1", 'F'}, - {"0TSF(F", 'F'}, - {"0TSF(N", 'F'}, - {"0TSF(S", 'F'}, - {"0TSF(V", 'F'}, - {"0TSO((", 'F'}, - {"0TSO(1", 'F'}, - {"0TSO(F", 'F'}, - {"0TSO(N", 'F'}, - {"0TSO(S", 'F'}, - {"0TSO(V", 'F'}, - {"0TSO1F", 'F'}, - {"0TSO1O", 'F'}, - {"0TSOF(", 'F'}, - {"0TSONF", 'F'}, - {"0TSONO", 'F'}, - {"0TSOSF", 'F'}, - {"0TSOSO", 'F'}, - {"0TSOVF", 'F'}, - {"0TSOVO", 'F'}, - {"0TVF((", 'F'}, - {"0TVF(1", 'F'}, - {"0TVF(F", 'F'}, - {"0TVF(N", 'F'}, - {"0TVF(S", 'F'}, - {"0TVF(V", 'F'}, - {"0TVO((", 'F'}, - {"0TVO(1", 'F'}, - {"0TVO(F", 'F'}, - {"0TVO(N", 'F'}, - {"0TVO(S", 'F'}, - {"0TVO(V", 'F'}, - {"0TVOF(", 'F'}, - {"0TVOSF", 'F'}, - {"0TVOSO", 'F'}, - {"0U((((", 'F'}, - {"0U(((E", 'F'}, - {"0U((E(", 'F'}, - {"0U((E1", 'F'}, - {"0U((EF", 'F'}, - {"0U((EK", 'F'}, - {"0U((EN", 'F'}, - {"0U((EO", 'F'}, - {"0U((ES", 'F'}, - {"0U((EV", 'F'}, - {"0U(E((", 'F'}, - {"0U(E(1", 'F'}, - {"0U(E(F", 'F'}, - {"0U(E(K", 'F'}, - {"0U(E(N", 'F'}, - {"0U(E(S", 'F'}, - {"0U(E(V", 'F'}, - {"0U(E1)", 'F'}, - {"0U(E1O", 'F'}, - {"0U(EF(", 'F'}, - {"0U(EK(", 'F'}, - {"0U(EK1", 'F'}, - {"0U(EKF", 'F'}, - {"0U(EKN", 'F'}, - {"0U(EKO", 'F'}, - {"0U(EKS", 'F'}, - {"0U(EKV", 'F'}, - {"0U(EN)", 'F'}, - {"0U(ENK", 'F'}, - {"0U(ENO", 'F'}, - {"0U(EOK", 'F'}, - {"0U(ES)", 'F'}, - {"0U(ESO", 'F'}, - {"0U(EV)", 'F'}, - {"0U(EVO", 'F'}, - {"0UE(((", 'F'}, - {"0UE((1", 'F'}, - {"0UE((F", 'F'}, - {"0UE((N", 'F'}, - {"0UE((S", 'F'}, - {"0UE((V", 'F'}, - {"0UE(1)", 'F'}, - {"0UE(1,", 'F'}, - {"0UE(1O", 'F'}, - {"0UE(F(", 'F'}, - {"0UE(N)", 'F'}, - {"0UE(N,", 'F'}, - {"0UE(NO", 'F'}, - {"0UE(S)", 'F'}, - {"0UE(S,", 'F'}, - {"0UE(SO", 'F'}, - {"0UE(V)", 'F'}, - {"0UE(V,", 'F'}, - {"0UE(VO", 'F'}, - {"0UE1", 'F'}, - {"0UE1,(", 'F'}, - {"0UE1,F", 'F'}, - {"0UE1,V", 'F'}, - {"0UE1;", 'F'}, - {"0UE1;C", 'F'}, - {"0UE1C", 'F'}, - {"0UE1K(", 'F'}, - {"0UE1K1", 'F'}, - {"0UE1KF", 'F'}, - {"0UE1KN", 'F'}, - {"0UE1KS", 'F'}, - {"0UE1KV", 'F'}, - {"0UE1O(", 'F'}, - {"0UE1OF", 'F'}, - {"0UE1OS", 'F'}, - {"0UE1OV", 'F'}, - {"0UEF((", 'F'}, - {"0UEF()", 'F'}, - {"0UEF(1", 'F'}, - {"0UEF(F", 'F'}, - {"0UEF(N", 'F'}, - {"0UEF(S", 'F'}, - {"0UEF(V", 'F'}, - {"0UEK((", 'F'}, - {"0UEK(1", 'F'}, - {"0UEK(F", 'F'}, - {"0UEK(N", 'F'}, - {"0UEK(S", 'F'}, - {"0UEK(V", 'F'}, - {"0UEK1", 'F'}, - {"0UEK1,", 'F'}, - {"0UEK1;", 'F'}, - {"0UEK1C", 'F'}, - {"0UEK1K", 'F'}, - {"0UEK1O", 'F'}, - {"0UEKF(", 'F'}, - {"0UEKN", 'F'}, - {"0UEKN(", 'F'}, - {"0UEKN,", 'F'}, - {"0UEKN;", 'F'}, - {"0UEKNC", 'F'}, - {"0UEKNK", 'F'}, - {"0UEKS", 'F'}, - {"0UEKS,", 'F'}, - {"0UEKS;", 'F'}, - {"0UEKSC", 'F'}, - {"0UEKSK", 'F'}, - {"0UEKSO", 'F'}, - {"0UEKV", 'F'}, - {"0UEKV,", 'F'}, - {"0UEKV;", 'F'}, - {"0UEKVC", 'F'}, - {"0UEKVK", 'F'}, - {"0UEKVO", 'F'}, - {"0UEN((", 'F'}, - {"0UEN()", 'F'}, - {"0UEN,(", 'F'}, - {"0UEN,F", 'F'}, - {"0UEN,V", 'F'}, - {"0UEN;", 'F'}, - {"0UEN;C", 'F'}, - {"0UENC", 'F'}, - {"0UENK(", 'F'}, - {"0UENK1", 'F'}, - {"0UENKF", 'F'}, - {"0UENKN", 'F'}, - {"0UENKS", 'F'}, - {"0UENKV", 'F'}, - {"0UENO(", 'F'}, - {"0UENOF", 'F'}, - {"0UENOS", 'F'}, - {"0UENOV", 'F'}, - {"0UES", 'F'}, - {"0UES,(", 'F'}, - {"0UES,F", 'F'}, - {"0UES,V", 'F'}, - {"0UES;", 'F'}, - {"0UES;C", 'F'}, - {"0UESC", 'F'}, - {"0UESK(", 'F'}, - {"0UESK1", 'F'}, - {"0UESKF", 'F'}, - {"0UESKN", 'F'}, - {"0UESKS", 'F'}, - {"0UESKV", 'F'}, - {"0UESO(", 'F'}, - {"0UESO1", 'F'}, - {"0UESOF", 'F'}, - {"0UESON", 'F'}, - {"0UESOS", 'F'}, - {"0UESOV", 'F'}, - {"0UEV", 'F'}, - {"0UEV,(", 'F'}, - {"0UEV,1", 'F'}, - {"0UEV,F", 'F'}, - {"0UEV,N", 'F'}, - {"0UEV,S", 'F'}, - {"0UEV,V", 'F'}, - {"0UEV;", 'F'}, - {"0UEV;C", 'F'}, - {"0UEVC", 'F'}, - {"0UEVK(", 'F'}, - {"0UEVK1", 'F'}, - {"0UEVKF", 'F'}, - {"0UEVKN", 'F'}, - {"0UEVKS", 'F'}, - {"0UEVKV", 'F'}, - {"0UEVO(", 'F'}, - {"0UEVOF", 'F'}, - {"0UEVOS", 'F'}, - {"0UF(((", 'F'}, - {"0UF((1", 'F'}, - {"0UF((F", 'F'}, - {"0UF((N", 'F'}, - {"0UF((S", 'F'}, - {"0UF((V", 'F'}, - {"0UF(1O", 'F'}, - {"0UF(F(", 'F'}, - {"0UF(NO", 'F'}, - {"0UF(SO", 'F'}, - {"0UF(VO", 'F'}, - {"0V&(((", 'F'}, - {"0V&((1", 'F'}, - {"0V&((E", 'F'}, - {"0V&((F", 'F'}, - {"0V&((N", 'F'}, - {"0V&((S", 'F'}, - {"0V&((V", 'F'}, - {"0V&(1&", 'F'}, - {"0V&(1)", 'F'}, - {"0V&(1,", 'F'}, - {"0V&(1O", 'F'}, - {"0V&(E(", 'F'}, - {"0V&(E1", 'F'}, - {"0V&(EF", 'F'}, - {"0V&(EK", 'F'}, - {"0V&(EN", 'F'}, - {"0V&(EO", 'F'}, - {"0V&(ES", 'F'}, - {"0V&(EV", 'F'}, - {"0V&(F(", 'F'}, - {"0V&(N&", 'F'}, - {"0V&(N)", 'F'}, - {"0V&(N,", 'F'}, - {"0V&(NO", 'F'}, - {"0V&(S&", 'F'}, - {"0V&(S)", 'F'}, - {"0V&(S,", 'F'}, - {"0V&(SO", 'F'}, - {"0V&(V&", 'F'}, - {"0V&(V)", 'F'}, - {"0V&(V,", 'F'}, - {"0V&(VO", 'F'}, - {"0V&1", 'F'}, - {"0V&1&(", 'F'}, - {"0V&1&1", 'F'}, - {"0V&1&F", 'F'}, - {"0V&1&N", 'F'}, - {"0V&1&S", 'F'}, - {"0V&1&V", 'F'}, - {"0V&1)&", 'F'}, - {"0V&1)O", 'F'}, - {"0V&1)U", 'F'}, - {"0V&1;", 'F'}, - {"0V&1;C", 'F'}, - {"0V&1;E", 'F'}, - {"0V&1;T", 'F'}, - {"0V&1B(", 'F'}, - {"0V&1B1", 'F'}, - {"0V&1BF", 'F'}, - {"0V&1BN", 'F'}, - {"0V&1BS", 'F'}, - {"0V&1BV", 'F'}, - {"0V&1C", 'F'}, - {"0V&1EK", 'F'}, - {"0V&1EN", 'F'}, - {"0V&1F(", 'F'}, - {"0V&1K(", 'F'}, - {"0V&1K1", 'F'}, - {"0V&1KF", 'F'}, - {"0V&1KN", 'F'}, - {"0V&1KS", 'F'}, - {"0V&1KV", 'F'}, - {"0V&1O(", 'F'}, - {"0V&1OF", 'F'}, - {"0V&1OO", 'F'}, - {"0V&1OS", 'F'}, - {"0V&1OV", 'F'}, - {"0V&1TN", 'F'}, - {"0V&1U", 'F'}, - {"0V&1U(", 'F'}, - {"0V&1U;", 'F'}, - {"0V&1UC", 'F'}, - {"0V&1UE", 'F'}, - {"0V&E((", 'F'}, - {"0V&E(1", 'F'}, - {"0V&E(F", 'F'}, - {"0V&E(N", 'F'}, - {"0V&E(O", 'F'}, - {"0V&E(S", 'F'}, - {"0V&E(V", 'F'}, - {"0V&E1", 'F'}, - {"0V&E1;", 'F'}, - {"0V&E1C", 'F'}, - {"0V&E1K", 'F'}, - {"0V&E1O", 'F'}, - {"0V&EF(", 'F'}, - {"0V&EK(", 'F'}, - {"0V&EK1", 'F'}, - {"0V&EKF", 'F'}, - {"0V&EKN", 'F'}, - {"0V&EKS", 'F'}, - {"0V&EKV", 'F'}, - {"0V&EN", 'F'}, - {"0V&EN;", 'F'}, - {"0V&ENC", 'F'}, - {"0V&ENK", 'F'}, - {"0V&ENO", 'F'}, - {"0V&ES", 'F'}, - {"0V&ES;", 'F'}, - {"0V&ESC", 'F'}, - {"0V&ESK", 'F'}, - {"0V&ESO", 'F'}, - {"0V&EV", 'F'}, - {"0V&EV;", 'F'}, - {"0V&EVC", 'F'}, - {"0V&EVK", 'F'}, - {"0V&EVO", 'F'}, - {"0V&F((", 'F'}, - {"0V&F()", 'F'}, - {"0V&F(1", 'F'}, - {"0V&F(E", 'F'}, - {"0V&F(F", 'F'}, - {"0V&F(N", 'F'}, - {"0V&F(S", 'F'}, - {"0V&F(V", 'F'}, - {"0V&K&(", 'F'}, - {"0V&K&1", 'F'}, - {"0V&K&F", 'F'}, - {"0V&K&N", 'F'}, - {"0V&K&S", 'F'}, - {"0V&K&V", 'F'}, - {"0V&K((", 'F'}, - {"0V&K(1", 'F'}, - {"0V&K(F", 'F'}, - {"0V&K(N", 'F'}, - {"0V&K(S", 'F'}, - {"0V&K(V", 'F'}, - {"0V&K1O", 'F'}, - {"0V&KF(", 'F'}, - {"0V&KNK", 'F'}, - {"0V&KO(", 'F'}, - {"0V&KO1", 'F'}, - {"0V&KOF", 'F'}, - {"0V&KOK", 'F'}, - {"0V&KON", 'F'}, - {"0V&KOS", 'F'}, - {"0V&KOV", 'F'}, - {"0V&KSO", 'F'}, - {"0V&KVO", 'F'}, - {"0V&N", 'F'}, - {"0V&N&(", 'F'}, - {"0V&N&1", 'F'}, - {"0V&N&F", 'F'}, - {"0V&N&N", 'F'}, - {"0V&N&S", 'F'}, - {"0V&N&V", 'F'}, - {"0V&N)&", 'F'}, - {"0V&N)O", 'F'}, - {"0V&N)U", 'F'}, - {"0V&N;", 'F'}, - {"0V&N;C", 'F'}, - {"0V&N;E", 'F'}, - {"0V&N;T", 'F'}, - {"0V&NB(", 'F'}, - {"0V&NB1", 'F'}, - {"0V&NBF", 'F'}, - {"0V&NBN", 'F'}, - {"0V&NBS", 'F'}, - {"0V&NBV", 'F'}, - {"0V&NC", 'F'}, - {"0V&NEN", 'F'}, - {"0V&NF(", 'F'}, - {"0V&NK(", 'F'}, - {"0V&NK1", 'F'}, - {"0V&NKF", 'F'}, - {"0V&NKN", 'F'}, - {"0V&NKS", 'F'}, - {"0V&NKV", 'F'}, - {"0V&NO(", 'F'}, - {"0V&NOF", 'F'}, - {"0V&NOS", 'F'}, - {"0V&NOV", 'F'}, - {"0V&NTN", 'F'}, - {"0V&NU", 'F'}, - {"0V&NU(", 'F'}, - {"0V&NU;", 'F'}, - {"0V&NUC", 'F'}, - {"0V&NUE", 'F'}, - {"0V&S", 'F'}, - {"0V&S&(", 'F'}, - {"0V&S&1", 'F'}, - {"0V&S&F", 'F'}, - {"0V&S&N", 'F'}, - {"0V&S&S", 'F'}, - {"0V&S&V", 'F'}, - {"0V&S)&", 'F'}, - {"0V&S)O", 'F'}, - {"0V&S)U", 'F'}, - {"0V&S1", 'F'}, - {"0V&S1;", 'F'}, - {"0V&S1C", 'F'}, - {"0V&S1O", 'F'}, - {"0V&S;", 'F'}, - {"0V&S;C", 'F'}, - {"0V&S;E", 'F'}, - {"0V&S;T", 'F'}, - {"0V&SB(", 'F'}, - {"0V&SB1", 'F'}, - {"0V&SBF", 'F'}, - {"0V&SBN", 'F'}, - {"0V&SBS", 'F'}, - {"0V&SBV", 'F'}, - {"0V&SC", 'F'}, - {"0V&SEK", 'F'}, - {"0V&SEN", 'F'}, - {"0V&SF(", 'F'}, - {"0V&SK(", 'F'}, - {"0V&SK1", 'F'}, - {"0V&SKF", 'F'}, - {"0V&SKN", 'F'}, - {"0V&SKS", 'F'}, - {"0V&SKV", 'F'}, - {"0V&SO(", 'F'}, - {"0V&SO1", 'F'}, - {"0V&SOF", 'F'}, - {"0V&SON", 'F'}, - {"0V&SOO", 'F'}, - {"0V&SOS", 'F'}, - {"0V&SOV", 'F'}, - {"0V&STN", 'F'}, - {"0V&SU", 'F'}, - {"0V&SU(", 'F'}, - {"0V&SU;", 'F'}, - {"0V&SUC", 'F'}, - {"0V&SUE", 'F'}, - {"0V&SV", 'F'}, - {"0V&SV;", 'F'}, - {"0V&SVC", 'F'}, - {"0V&SVO", 'F'}, - {"0V&T((", 'F'}, - {"0V&T(1", 'F'}, - {"0V&T(F", 'F'}, - {"0V&T(N", 'F'}, - {"0V&T(S", 'F'}, - {"0V&T(V", 'F'}, - {"0V&V", 'F'}, - {"0V&V&(", 'F'}, - {"0V&V&1", 'F'}, - {"0V&V&F", 'F'}, - {"0V&V&N", 'F'}, - {"0V&V&S", 'F'}, - {"0V&V&V", 'F'}, - {"0V&V)&", 'F'}, - {"0V&V)O", 'F'}, - {"0V&V)U", 'F'}, - {"0V&V;", 'F'}, - {"0V&V;C", 'F'}, - {"0V&V;E", 'F'}, - {"0V&V;T", 'F'}, - {"0V&VB(", 'F'}, - {"0V&VB1", 'F'}, - {"0V&VBF", 'F'}, - {"0V&VBN", 'F'}, - {"0V&VBS", 'F'}, - {"0V&VBV", 'F'}, - {"0V&VC", 'F'}, - {"0V&VEK", 'F'}, - {"0V&VEN", 'F'}, - {"0V&VF(", 'F'}, - {"0V&VK(", 'F'}, - {"0V&VK1", 'F'}, - {"0V&VKF", 'F'}, - {"0V&VKN", 'F'}, - {"0V&VKS", 'F'}, - {"0V&VKV", 'F'}, - {"0V&VO(", 'F'}, - {"0V&VOF", 'F'}, - {"0V&VOO", 'F'}, - {"0V&VOS", 'F'}, - {"0V&VS", 'F'}, - {"0V&VS;", 'F'}, - {"0V&VSC", 'F'}, - {"0V&VSO", 'F'}, - {"0V&VTN", 'F'}, - {"0V&VU", 'F'}, - {"0V&VU(", 'F'}, - {"0V&VU;", 'F'}, - {"0V&VUC", 'F'}, - {"0V&VUE", 'F'}, - {"0V((((", 'F'}, - {"0V(((E", 'F'}, - {"0V(((U", 'F'}, - {"0V((EK", 'F'}, - {"0V((EN", 'F'}, - {"0V((U(", 'F'}, - {"0V(EKN", 'F'}, - {"0V(ENK", 'F'}, - {"0V(U((", 'F'}, - {"0V(U(E", 'F'}, - {"0V)&((", 'F'}, - {"0V)&(1", 'F'}, - {"0V)&(E", 'F'}, - {"0V)&(F", 'F'}, - {"0V)&(N", 'F'}, - {"0V)&(S", 'F'}, - {"0V)&(V", 'F'}, - {"0V)&1", 'F'}, - {"0V)&1&", 'F'}, - {"0V)&1)", 'F'}, - {"0V)&1;", 'F'}, - {"0V)&1B", 'F'}, - {"0V)&1C", 'F'}, - {"0V)&1F", 'F'}, - {"0V)&1O", 'F'}, - {"0V)&1U", 'F'}, - {"0V)&F(", 'F'}, - {"0V)&N", 'F'}, - {"0V)&N&", 'F'}, - {"0V)&N)", 'F'}, - {"0V)&N;", 'F'}, - {"0V)&NB", 'F'}, - {"0V)&NC", 'F'}, - {"0V)&NF", 'F'}, - {"0V)&NO", 'F'}, - {"0V)&NU", 'F'}, - {"0V)&S", 'F'}, - {"0V)&S&", 'F'}, - {"0V)&S)", 'F'}, - {"0V)&S;", 'F'}, - {"0V)&SB", 'F'}, - {"0V)&SC", 'F'}, - {"0V)&SF", 'F'}, - {"0V)&SO", 'F'}, - {"0V)&SU", 'F'}, - {"0V)&V", 'F'}, - {"0V)&V&", 'F'}, - {"0V)&V)", 'F'}, - {"0V)&V;", 'F'}, - {"0V)&VB", 'F'}, - {"0V)&VC", 'F'}, - {"0V)&VF", 'F'}, - {"0V)&VO", 'F'}, - {"0V)&VU", 'F'}, - {"0V),((", 'F'}, - {"0V),(1", 'F'}, - {"0V),(F", 'F'}, - {"0V),(N", 'F'}, - {"0V),(S", 'F'}, - {"0V),(V", 'F'}, - {"0V);E(", 'F'}, - {"0V);E1", 'F'}, - {"0V);EF", 'F'}, - {"0V);EK", 'F'}, - {"0V);EN", 'F'}, - {"0V);EO", 'F'}, - {"0V);ES", 'F'}, - {"0V);EV", 'F'}, - {"0V);T(", 'F'}, - {"0V);T1", 'F'}, - {"0V);TF", 'F'}, - {"0V);TK", 'F'}, - {"0V);TN", 'F'}, - {"0V);TO", 'F'}, - {"0V);TS", 'F'}, - {"0V);TV", 'F'}, - {"0V)B((", 'F'}, - {"0V)B(1", 'F'}, - {"0V)B(F", 'F'}, - {"0V)B(N", 'F'}, - {"0V)B(S", 'F'}, - {"0V)B(V", 'F'}, - {"0V)B1", 'F'}, - {"0V)B1&", 'F'}, - {"0V)B1;", 'F'}, - {"0V)B1C", 'F'}, - {"0V)B1K", 'F'}, - {"0V)B1N", 'F'}, - {"0V)B1O", 'F'}, - {"0V)B1U", 'F'}, - {"0V)BF(", 'F'}, - {"0V)BN", 'F'}, - {"0V)BN&", 'F'}, - {"0V)BN;", 'F'}, - {"0V)BNC", 'F'}, - {"0V)BNK", 'F'}, - {"0V)BNO", 'F'}, - {"0V)BNU", 'F'}, - {"0V)BS", 'F'}, - {"0V)BS&", 'F'}, - {"0V)BS;", 'F'}, - {"0V)BSC", 'F'}, - {"0V)BSK", 'F'}, - {"0V)BSO", 'F'}, - {"0V)BSU", 'F'}, - {"0V)BV", 'F'}, - {"0V)BV&", 'F'}, - {"0V)BV;", 'F'}, - {"0V)BVC", 'F'}, - {"0V)BVK", 'F'}, - {"0V)BVO", 'F'}, - {"0V)BVU", 'F'}, - {"0V)E((", 'F'}, - {"0V)E(1", 'F'}, - {"0V)E(F", 'F'}, - {"0V)E(N", 'F'}, - {"0V)E(S", 'F'}, - {"0V)E(V", 'F'}, - {"0V)E1C", 'F'}, - {"0V)E1O", 'F'}, - {"0V)EF(", 'F'}, - {"0V)EK(", 'F'}, - {"0V)EK1", 'F'}, - {"0V)EKF", 'F'}, - {"0V)EKN", 'F'}, - {"0V)EKS", 'F'}, - {"0V)EKV", 'F'}, - {"0V)ENC", 'F'}, - {"0V)ENO", 'F'}, - {"0V)ESC", 'F'}, - {"0V)ESO", 'F'}, - {"0V)EVC", 'F'}, - {"0V)EVO", 'F'}, - {"0V)K((", 'F'}, - {"0V)K(1", 'F'}, - {"0V)K(F", 'F'}, - {"0V)K(N", 'F'}, - {"0V)K(S", 'F'}, - {"0V)K(V", 'F'}, - {"0V)K1&", 'F'}, - {"0V)K1;", 'F'}, - {"0V)K1B", 'F'}, - {"0V)K1E", 'F'}, - {"0V)K1O", 'F'}, - {"0V)K1U", 'F'}, - {"0V)KB(", 'F'}, - {"0V)KB1", 'F'}, - {"0V)KBF", 'F'}, - {"0V)KBN", 'F'}, - {"0V)KBS", 'F'}, - {"0V)KBV", 'F'}, - {"0V)KF(", 'F'}, - {"0V)KN&", 'F'}, - {"0V)KN;", 'F'}, - {"0V)KNB", 'F'}, - {"0V)KNE", 'F'}, - {"0V)KNK", 'F'}, - {"0V)KNU", 'F'}, - {"0V)KS&", 'F'}, - {"0V)KS;", 'F'}, - {"0V)KSB", 'F'}, - {"0V)KSE", 'F'}, - {"0V)KSO", 'F'}, - {"0V)KSU", 'F'}, - {"0V)KUE", 'F'}, - {"0V)KV&", 'F'}, - {"0V)KV;", 'F'}, - {"0V)KVB", 'F'}, - {"0V)KVE", 'F'}, - {"0V)KVO", 'F'}, - {"0V)KVU", 'F'}, - {"0V)O((", 'F'}, - {"0V)O(1", 'F'}, - {"0V)O(E", 'F'}, - {"0V)O(F", 'F'}, - {"0V)O(N", 'F'}, - {"0V)O(S", 'F'}, - {"0V)O(V", 'F'}, - {"0V)O1", 'F'}, - {"0V)O1&", 'F'}, - {"0V)O1)", 'F'}, - {"0V)O1;", 'F'}, - {"0V)O1B", 'F'}, - {"0V)O1C", 'F'}, - {"0V)O1K", 'F'}, - {"0V)O1O", 'F'}, - {"0V)O1U", 'F'}, - {"0V)OF(", 'F'}, - {"0V)ON", 'F'}, - {"0V)ON&", 'F'}, - {"0V)ON)", 'F'}, - {"0V)ON;", 'F'}, - {"0V)ONB", 'F'}, - {"0V)ONC", 'F'}, - {"0V)ONK", 'F'}, - {"0V)ONO", 'F'}, - {"0V)ONU", 'F'}, - {"0V)OS", 'F'}, - {"0V)OS&", 'F'}, - {"0V)OS)", 'F'}, - {"0V)OS;", 'F'}, - {"0V)OSB", 'F'}, - {"0V)OSC", 'F'}, - {"0V)OSK", 'F'}, - {"0V)OSO", 'F'}, - {"0V)OSU", 'F'}, - {"0V)OV", 'F'}, - {"0V)OV&", 'F'}, - {"0V)OV)", 'F'}, - {"0V)OV;", 'F'}, - {"0V)OVB", 'F'}, - {"0V)OVC", 'F'}, - {"0V)OVK", 'F'}, - {"0V)OVO", 'F'}, - {"0V)OVU", 'F'}, - {"0V)U((", 'F'}, - {"0V)U(E", 'F'}, - {"0V)UE(", 'F'}, - {"0V)UE1", 'F'}, - {"0V)UEF", 'F'}, - {"0V)UEK", 'F'}, - {"0V)UEN", 'F'}, - {"0V)UES", 'F'}, - {"0V)UEV", 'F'}, - {"0V,(((", 'F'}, - {"0V,((1", 'F'}, - {"0V,((E", 'F'}, - {"0V,((F", 'F'}, - {"0V,((N", 'F'}, - {"0V,((S", 'F'}, - {"0V,((V", 'F'}, - {"0V,(1)", 'F'}, - {"0V,(1O", 'F'}, - {"0V,(E(", 'F'}, - {"0V,(E1", 'F'}, - {"0V,(EF", 'F'}, - {"0V,(EK", 'F'}, - {"0V,(EN", 'F'}, - {"0V,(ES", 'F'}, - {"0V,(EV", 'F'}, - {"0V,(F(", 'F'}, - {"0V,(N)", 'F'}, - {"0V,(NO", 'F'}, - {"0V,(S)", 'F'}, - {"0V,(SO", 'F'}, - {"0V,(V)", 'F'}, - {"0V,(VO", 'F'}, - {"0V,1),", 'F'}, - {"0V,1)O", 'F'}, - {"0V,1B(", 'F'}, - {"0V,1B1", 'F'}, - {"0V,1BF", 'F'}, - {"0V,1BN", 'F'}, - {"0V,1BS", 'F'}, - {"0V,1BV", 'F'}, - {"0V,1O(", 'F'}, - {"0V,1OF", 'F'}, - {"0V,1OS", 'F'}, - {"0V,1OV", 'F'}, - {"0V,1UE", 'F'}, - {"0V,F((", 'F'}, - {"0V,F()", 'F'}, - {"0V,F(1", 'F'}, - {"0V,F(F", 'F'}, - {"0V,F(N", 'F'}, - {"0V,F(S", 'F'}, - {"0V,F(T", 'F'}, - {"0V,F(V", 'F'}, - {"0V,N),", 'F'}, - {"0V,N)O", 'F'}, - {"0V,NB(", 'F'}, - {"0V,NB1", 'F'}, - {"0V,NBF", 'F'}, - {"0V,NBN", 'F'}, - {"0V,NBS", 'F'}, - {"0V,NBV", 'F'}, - {"0V,NO(", 'F'}, - {"0V,NOF", 'F'}, - {"0V,NOS", 'F'}, - {"0V,NOV", 'F'}, - {"0V,NUE", 'F'}, - {"0V,S),", 'F'}, - {"0V,S)O", 'F'}, - {"0V,SB(", 'F'}, - {"0V,SB1", 'F'}, - {"0V,SBF", 'F'}, - {"0V,SBN", 'F'}, - {"0V,SBS", 'F'}, - {"0V,SBV", 'F'}, - {"0V,SO(", 'F'}, - {"0V,SO1", 'F'}, - {"0V,SOF", 'F'}, - {"0V,SON", 'F'}, - {"0V,SOS", 'F'}, - {"0V,SOV", 'F'}, - {"0V,SUE", 'F'}, - {"0V,V),", 'F'}, - {"0V,V)O", 'F'}, - {"0V,VB(", 'F'}, - {"0V,VB1", 'F'}, - {"0V,VBF", 'F'}, - {"0V,VBN", 'F'}, - {"0V,VBS", 'F'}, - {"0V,VBV", 'F'}, - {"0V,VO(", 'F'}, - {"0V,VOF", 'F'}, - {"0V,VOS", 'F'}, - {"0V,VUE", 'F'}, - {"0V;E((", 'F'}, - {"0V;E(1", 'F'}, - {"0V;E(E", 'F'}, - {"0V;E(F", 'F'}, - {"0V;E(N", 'F'}, - {"0V;E(S", 'F'}, - {"0V;E(V", 'F'}, - {"0V;E1,", 'F'}, - {"0V;E1;", 'F'}, - {"0V;E1C", 'F'}, - {"0V;E1K", 'F'}, - {"0V;E1O", 'F'}, - {"0V;E1T", 'F'}, - {"0V;EF(", 'F'}, - {"0V;EK(", 'F'}, - {"0V;EK1", 'F'}, - {"0V;EKF", 'F'}, - {"0V;EKN", 'F'}, - {"0V;EKO", 'F'}, - {"0V;EKS", 'F'}, - {"0V;EKV", 'F'}, - {"0V;EN,", 'F'}, - {"0V;EN;", 'F'}, - {"0V;ENC", 'F'}, - {"0V;ENK", 'F'}, - {"0V;ENO", 'F'}, - {"0V;ENT", 'F'}, - {"0V;EO(", 'F'}, - {"0V;ES,", 'F'}, - {"0V;ES;", 'F'}, - {"0V;ESC", 'F'}, - {"0V;ESK", 'F'}, - {"0V;ESO", 'F'}, - {"0V;EST", 'F'}, - {"0V;EV,", 'F'}, - {"0V;EV;", 'F'}, - {"0V;EVC", 'F'}, - {"0V;EVK", 'F'}, - {"0V;EVO", 'F'}, - {"0V;EVT", 'F'}, - {"0V;N:T", 'F'}, - {"0V;T((", 'F'}, - {"0V;T(1", 'F'}, - {"0V;T(E", 'F'}, - {"0V;T(F", 'F'}, - {"0V;T(N", 'F'}, - {"0V;T(S", 'F'}, - {"0V;T(V", 'F'}, - {"0V;T1,", 'F'}, - {"0V;T1;", 'F'}, - {"0V;T1C", 'F'}, - {"0V;T1F", 'F'}, - {"0V;T1K", 'F'}, - {"0V;T1O", 'F'}, - {"0V;T1T", 'F'}, - {"0V;T;", 'F'}, - {"0V;T;C", 'F'}, - {"0V;TF(", 'F'}, - {"0V;TK(", 'F'}, - {"0V;TK1", 'F'}, - {"0V;TKF", 'F'}, - {"0V;TKK", 'F'}, - {"0V;TKN", 'F'}, - {"0V;TKO", 'F'}, - {"0V;TKS", 'F'}, - {"0V;TKV", 'F'}, - {"0V;TN(", 'F'}, - {"0V;TN,", 'F'}, - {"0V;TN1", 'F'}, - {"0V;TN;", 'F'}, - {"0V;TNC", 'F'}, - {"0V;TNE", 'F'}, - {"0V;TNF", 'F'}, - {"0V;TNK", 'F'}, - {"0V;TNN", 'F'}, - {"0V;TNO", 'F'}, - {"0V;TNS", 'F'}, - {"0V;TNT", 'F'}, - {"0V;TNV", 'F'}, - {"0V;TO(", 'F'}, - {"0V;TS,", 'F'}, - {"0V;TS;", 'F'}, - {"0V;TSC", 'F'}, - {"0V;TSF", 'F'}, - {"0V;TSK", 'F'}, - {"0V;TSO", 'F'}, - {"0V;TST", 'F'}, - {"0V;TT(", 'F'}, - {"0V;TT1", 'F'}, - {"0V;TTF", 'F'}, - {"0V;TTN", 'F'}, - {"0V;TTS", 'F'}, - {"0V;TTV", 'F'}, - {"0V;TV,", 'F'}, - {"0V;TV;", 'F'}, - {"0V;TVC", 'F'}, - {"0V;TVF", 'F'}, - {"0V;TVK", 'F'}, - {"0V;TVO", 'F'}, - {"0V;TVT", 'F'}, - {"0VA(((", 'F'}, - {"0VA((1", 'F'}, - {"0VA((F", 'F'}, - {"0VA((N", 'F'}, - {"0VA((S", 'F'}, - {"0VA((V", 'F'}, - {"0VA(F(", 'F'}, - {"0VA(N)", 'F'}, - {"0VA(NO", 'F'}, - {"0VA(S)", 'F'}, - {"0VA(SO", 'F'}, - {"0VA(V)", 'F'}, - {"0VA(VO", 'F'}, - {"0VAF((", 'F'}, - {"0VAF()", 'F'}, - {"0VAF(1", 'F'}, - {"0VAF(F", 'F'}, - {"0VAF(N", 'F'}, - {"0VAF(S", 'F'}, - {"0VAF(V", 'F'}, - {"0VASO(", 'F'}, - {"0VASO1", 'F'}, - {"0VASOF", 'F'}, - {"0VASON", 'F'}, - {"0VASOS", 'F'}, - {"0VASOV", 'F'}, - {"0VASUE", 'F'}, - {"0VATO(", 'F'}, - {"0VATO1", 'F'}, - {"0VATOF", 'F'}, - {"0VATON", 'F'}, - {"0VATOS", 'F'}, - {"0VATOV", 'F'}, - {"0VATUE", 'F'}, - {"0VAVO(", 'F'}, - {"0VAVOF", 'F'}, - {"0VAVOS", 'F'}, - {"0VAVUE", 'F'}, - {"0VB(((", 'F'}, - {"0VB((1", 'F'}, - {"0VB((F", 'F'}, - {"0VB((N", 'F'}, - {"0VB((S", 'F'}, - {"0VB((V", 'F'}, - {"0VB(1)", 'F'}, - {"0VB(1O", 'F'}, - {"0VB(F(", 'F'}, - {"0VB(N)", 'F'}, - {"0VB(NO", 'F'}, - {"0VB(S)", 'F'}, - {"0VB(SO", 'F'}, - {"0VB(V)", 'F'}, - {"0VB(VO", 'F'}, - {"0VB1", 'F'}, - {"0VB1&(", 'F'}, - {"0VB1&1", 'F'}, - {"0VB1&F", 'F'}, - {"0VB1&N", 'F'}, - {"0VB1&S", 'F'}, - {"0VB1&V", 'F'}, - {"0VB1,(", 'F'}, - {"0VB1,F", 'F'}, - {"0VB1,V", 'F'}, - {"0VB1;", 'F'}, - {"0VB1;C", 'F'}, - {"0VB1B(", 'F'}, - {"0VB1B1", 'F'}, - {"0VB1BF", 'F'}, - {"0VB1BN", 'F'}, - {"0VB1BS", 'F'}, - {"0VB1BV", 'F'}, - {"0VB1C", 'F'}, - {"0VB1K(", 'F'}, - {"0VB1K1", 'F'}, - {"0VB1KF", 'F'}, - {"0VB1KN", 'F'}, - {"0VB1KS", 'F'}, - {"0VB1KV", 'F'}, - {"0VB1O(", 'F'}, - {"0VB1OF", 'F'}, - {"0VB1OS", 'F'}, - {"0VB1OV", 'F'}, - {"0VB1U(", 'F'}, - {"0VB1UE", 'F'}, - {"0VBE((", 'F'}, - {"0VBE(1", 'F'}, - {"0VBE(F", 'F'}, - {"0VBE(N", 'F'}, - {"0VBE(S", 'F'}, - {"0VBE(V", 'F'}, - {"0VBEK(", 'F'}, - {"0VBF((", 'F'}, - {"0VBF()", 'F'}, - {"0VBF(1", 'F'}, - {"0VBF(F", 'F'}, - {"0VBF(N", 'F'}, - {"0VBF(S", 'F'}, - {"0VBF(V", 'F'}, - {"0VBN", 'F'}, - {"0VBN&(", 'F'}, - {"0VBN&1", 'F'}, - {"0VBN&F", 'F'}, - {"0VBN&N", 'F'}, - {"0VBN&S", 'F'}, - {"0VBN&V", 'F'}, - {"0VBN,(", 'F'}, - {"0VBN,F", 'F'}, - {"0VBN,V", 'F'}, - {"0VBN;", 'F'}, - {"0VBN;C", 'F'}, - {"0VBNB(", 'F'}, - {"0VBNB1", 'F'}, - {"0VBNBF", 'F'}, - {"0VBNBN", 'F'}, - {"0VBNBS", 'F'}, - {"0VBNBV", 'F'}, - {"0VBNC", 'F'}, - {"0VBNK(", 'F'}, - {"0VBNK1", 'F'}, - {"0VBNKF", 'F'}, - {"0VBNKN", 'F'}, - {"0VBNKS", 'F'}, - {"0VBNKV", 'F'}, - {"0VBNO(", 'F'}, - {"0VBNOF", 'F'}, - {"0VBNOS", 'F'}, - {"0VBNOV", 'F'}, - {"0VBNU(", 'F'}, - {"0VBNUE", 'F'}, - {"0VBS", 'F'}, - {"0VBS&(", 'F'}, - {"0VBS&1", 'F'}, - {"0VBS&F", 'F'}, - {"0VBS&N", 'F'}, - {"0VBS&S", 'F'}, - {"0VBS&V", 'F'}, - {"0VBS,(", 'F'}, - {"0VBS,F", 'F'}, - {"0VBS,V", 'F'}, - {"0VBS;", 'F'}, - {"0VBS;C", 'F'}, - {"0VBSB(", 'F'}, - {"0VBSB1", 'F'}, - {"0VBSBF", 'F'}, - {"0VBSBN", 'F'}, - {"0VBSBS", 'F'}, - {"0VBSBV", 'F'}, - {"0VBSC", 'F'}, - {"0VBSK(", 'F'}, - {"0VBSK1", 'F'}, - {"0VBSKF", 'F'}, - {"0VBSKN", 'F'}, - {"0VBSKS", 'F'}, - {"0VBSKV", 'F'}, - {"0VBSO(", 'F'}, - {"0VBSO1", 'F'}, - {"0VBSOF", 'F'}, - {"0VBSON", 'F'}, - {"0VBSOS", 'F'}, - {"0VBSOV", 'F'}, - {"0VBSU(", 'F'}, - {"0VBSUE", 'F'}, - {"0VBV", 'F'}, - {"0VBV&(", 'F'}, - {"0VBV&1", 'F'}, - {"0VBV&F", 'F'}, - {"0VBV&N", 'F'}, - {"0VBV&S", 'F'}, - {"0VBV&V", 'F'}, - {"0VBV,(", 'F'}, - {"0VBV,1", 'F'}, - {"0VBV,F", 'F'}, - {"0VBV,N", 'F'}, - {"0VBV,S", 'F'}, - {"0VBV,V", 'F'}, - {"0VBV;", 'F'}, - {"0VBV;C", 'F'}, - {"0VBVB(", 'F'}, - {"0VBVB1", 'F'}, - {"0VBVBF", 'F'}, - {"0VBVBN", 'F'}, - {"0VBVBS", 'F'}, - {"0VBVBV", 'F'}, - {"0VBVC", 'F'}, - {"0VBVK(", 'F'}, - {"0VBVK1", 'F'}, - {"0VBVKF", 'F'}, - {"0VBVKN", 'F'}, - {"0VBVKS", 'F'}, - {"0VBVKV", 'F'}, - {"0VBVO(", 'F'}, - {"0VBVOF", 'F'}, - {"0VBVOS", 'F'}, - {"0VBVU(", 'F'}, - {"0VBVUE", 'F'}, - {"0VC", 'F'}, - {"0VE(((", 'F'}, - {"0VE((1", 'F'}, - {"0VE((F", 'F'}, - {"0VE((N", 'F'}, - {"0VE((S", 'F'}, - {"0VE((V", 'F'}, - {"0VE(1)", 'F'}, - {"0VE(1O", 'F'}, - {"0VE(F(", 'F'}, - {"0VE(N)", 'F'}, - {"0VE(NO", 'F'}, - {"0VE(S)", 'F'}, - {"0VE(SO", 'F'}, - {"0VE(V)", 'F'}, - {"0VE(VO", 'F'}, - {"0VE1C", 'F'}, - {"0VE1O(", 'F'}, - {"0VE1OF", 'F'}, - {"0VE1OS", 'F'}, - {"0VE1OV", 'F'}, - {"0VE1UE", 'F'}, - {"0VEF((", 'F'}, - {"0VEF()", 'F'}, - {"0VEF(1", 'F'}, - {"0VEF(F", 'F'}, - {"0VEF(N", 'F'}, - {"0VEF(S", 'F'}, - {"0VEF(V", 'F'}, - {"0VEK((", 'F'}, - {"0VEK(1", 'F'}, - {"0VEK(E", 'F'}, - {"0VEK(F", 'F'}, - {"0VEK(N", 'F'}, - {"0VEK(S", 'F'}, - {"0VEK(V", 'F'}, - {"0VEK1C", 'F'}, - {"0VEK1O", 'F'}, - {"0VEK1U", 'F'}, - {"0VEKF(", 'F'}, - {"0VEKNC", 'F'}, - {"0VEKNE", 'F'}, - {"0VEKNU", 'F'}, - {"0VEKOK", 'F'}, - {"0VEKSC", 'F'}, - {"0VEKSO", 'F'}, - {"0VEKSU", 'F'}, - {"0VEKU(", 'F'}, - {"0VEKU1", 'F'}, - {"0VEKUE", 'F'}, - {"0VEKUF", 'F'}, - {"0VEKUN", 'F'}, - {"0VEKUS", 'F'}, - {"0VEKUV", 'F'}, - {"0VEKVC", 'F'}, - {"0VEKVO", 'F'}, - {"0VEKVU", 'F'}, - {"0VENC", 'F'}, - {"0VENEN", 'F'}, - {"0VENO(", 'F'}, - {"0VENOF", 'F'}, - {"0VENOS", 'F'}, - {"0VENOV", 'F'}, - {"0VENUE", 'F'}, - {"0VEOKN", 'F'}, - {"0VESC", 'F'}, - {"0VESO(", 'F'}, - {"0VESO1", 'F'}, - {"0VESOF", 'F'}, - {"0VESON", 'F'}, - {"0VESOS", 'F'}, - {"0VESOV", 'F'}, - {"0VESUE", 'F'}, - {"0VEU((", 'F'}, - {"0VEU(1", 'F'}, - {"0VEU(F", 'F'}, - {"0VEU(N", 'F'}, - {"0VEU(S", 'F'}, - {"0VEU(V", 'F'}, - {"0VEU1,", 'F'}, - {"0VEU1C", 'F'}, - {"0VEU1O", 'F'}, - {"0VEUEF", 'F'}, - {"0VEUEK", 'F'}, - {"0VEUF(", 'F'}, - {"0VEUN,", 'F'}, - {"0VEUNC", 'F'}, - {"0VEUNO", 'F'}, - {"0VEUS,", 'F'}, - {"0VEUSC", 'F'}, - {"0VEUSO", 'F'}, - {"0VEUV,", 'F'}, - {"0VEUVC", 'F'}, - {"0VEUVO", 'F'}, - {"0VEVC", 'F'}, - {"0VEVO(", 'F'}, - {"0VEVOF", 'F'}, - {"0VEVOS", 'F'}, - {"0VEVUE", 'F'}, - {"0VF(((", 'F'}, - {"0VF(()", 'F'}, - {"0VF((1", 'F'}, - {"0VF((E", 'F'}, - {"0VF((F", 'F'}, - {"0VF((N", 'F'}, - {"0VF((S", 'F'}, - {"0VF((V", 'F'}, - {"0VF()1", 'F'}, - {"0VF()F", 'F'}, - {"0VF()K", 'F'}, - {"0VF()N", 'F'}, - {"0VF()O", 'F'}, - {"0VF()S", 'F'}, - {"0VF()U", 'F'}, - {"0VF()V", 'F'}, - {"0VF(1)", 'F'}, - {"0VF(1N", 'F'}, - {"0VF(1O", 'F'}, - {"0VF(E(", 'F'}, - {"0VF(E1", 'F'}, - {"0VF(EF", 'F'}, - {"0VF(EK", 'F'}, - {"0VF(EN", 'F'}, - {"0VF(ES", 'F'}, - {"0VF(EV", 'F'}, - {"0VF(F(", 'F'}, - {"0VF(N)", 'F'}, - {"0VF(N,", 'F'}, - {"0VF(NO", 'F'}, - {"0VF(S)", 'F'}, - {"0VF(SO", 'F'}, - {"0VF(V)", 'F'}, - {"0VF(VO", 'F'}, - {"0VK(((", 'F'}, - {"0VK((1", 'F'}, - {"0VK((F", 'F'}, - {"0VK((N", 'F'}, - {"0VK((S", 'F'}, - {"0VK((V", 'F'}, - {"0VK(1)", 'F'}, - {"0VK(1O", 'F'}, - {"0VK(F(", 'F'}, - {"0VK(N)", 'F'}, - {"0VK(NO", 'F'}, - {"0VK(S)", 'F'}, - {"0VK(SO", 'F'}, - {"0VK(V)", 'F'}, - {"0VK(VO", 'F'}, - {"0VK)&(", 'F'}, - {"0VK)&1", 'F'}, - {"0VK)&F", 'F'}, - {"0VK)&N", 'F'}, - {"0VK)&S", 'F'}, - {"0VK)&V", 'F'}, - {"0VK);E", 'F'}, - {"0VK);T", 'F'}, - {"0VK)B(", 'F'}, - {"0VK)B1", 'F'}, - {"0VK)BF", 'F'}, - {"0VK)BN", 'F'}, - {"0VK)BS", 'F'}, - {"0VK)BV", 'F'}, - {"0VK)E(", 'F'}, - {"0VK)E1", 'F'}, - {"0VK)EF", 'F'}, - {"0VK)EK", 'F'}, - {"0VK)EN", 'F'}, - {"0VK)ES", 'F'}, - {"0VK)EV", 'F'}, - {"0VK)OF", 'F'}, - {"0VK)UE", 'F'}, - {"0VK1", 'F'}, - {"0VK1&(", 'F'}, - {"0VK1&1", 'F'}, - {"0VK1&F", 'F'}, - {"0VK1&N", 'F'}, - {"0VK1&S", 'F'}, - {"0VK1&V", 'F'}, - {"0VK1;", 'F'}, - {"0VK1;C", 'F'}, - {"0VK1;E", 'F'}, - {"0VK1;T", 'F'}, - {"0VK1B(", 'F'}, - {"0VK1B1", 'F'}, - {"0VK1BF", 'F'}, - {"0VK1BN", 'F'}, - {"0VK1BS", 'F'}, - {"0VK1BV", 'F'}, - {"0VK1C", 'F'}, - {"0VK1E(", 'F'}, - {"0VK1E1", 'F'}, - {"0VK1EF", 'F'}, - {"0VK1EK", 'F'}, - {"0VK1EN", 'F'}, - {"0VK1ES", 'F'}, - {"0VK1EV", 'F'}, - {"0VK1O(", 'F'}, - {"0VK1OF", 'F'}, - {"0VK1OS", 'F'}, - {"0VK1OV", 'F'}, - {"0VK1U(", 'F'}, - {"0VK1UE", 'F'}, - {"0VKF((", 'F'}, - {"0VKF()", 'F'}, - {"0VKF(1", 'F'}, - {"0VKF(F", 'F'}, - {"0VKF(N", 'F'}, - {"0VKF(S", 'F'}, - {"0VKF(V", 'F'}, - {"0VKN", 'F'}, - {"0VKN&(", 'F'}, - {"0VKN&1", 'F'}, - {"0VKN&F", 'F'}, - {"0VKN&N", 'F'}, - {"0VKN&S", 'F'}, - {"0VKN&V", 'F'}, - {"0VKN;", 'F'}, - {"0VKN;C", 'F'}, - {"0VKN;E", 'F'}, - {"0VKN;T", 'F'}, - {"0VKNB(", 'F'}, - {"0VKNB1", 'F'}, - {"0VKNBF", 'F'}, - {"0VKNBN", 'F'}, - {"0VKNBS", 'F'}, - {"0VKNBV", 'F'}, - {"0VKNC", 'F'}, - {"0VKNE(", 'F'}, - {"0VKNE1", 'F'}, - {"0VKNEF", 'F'}, - {"0VKNEN", 'F'}, - {"0VKNES", 'F'}, - {"0VKNEV", 'F'}, - {"0VKNU(", 'F'}, - {"0VKNUE", 'F'}, - {"0VKS", 'F'}, - {"0VKS&(", 'F'}, - {"0VKS&1", 'F'}, - {"0VKS&F", 'F'}, - {"0VKS&N", 'F'}, - {"0VKS&S", 'F'}, - {"0VKS&V", 'F'}, - {"0VKS;", 'F'}, - {"0VKS;C", 'F'}, - {"0VKS;E", 'F'}, - {"0VKS;T", 'F'}, - {"0VKSB(", 'F'}, - {"0VKSB1", 'F'}, - {"0VKSBF", 'F'}, - {"0VKSBN", 'F'}, - {"0VKSBS", 'F'}, - {"0VKSBV", 'F'}, - {"0VKSC", 'F'}, - {"0VKSE(", 'F'}, - {"0VKSE1", 'F'}, - {"0VKSEF", 'F'}, - {"0VKSEK", 'F'}, - {"0VKSEN", 'F'}, - {"0VKSES", 'F'}, - {"0VKSEV", 'F'}, - {"0VKSO(", 'F'}, - {"0VKSO1", 'F'}, - {"0VKSOF", 'F'}, - {"0VKSON", 'F'}, - {"0VKSOS", 'F'}, - {"0VKSOV", 'F'}, - {"0VKSU(", 'F'}, - {"0VKSUE", 'F'}, - {"0VKUE(", 'F'}, - {"0VKUE1", 'F'}, - {"0VKUEF", 'F'}, - {"0VKUEK", 'F'}, - {"0VKUEN", 'F'}, - {"0VKUES", 'F'}, - {"0VKUEV", 'F'}, - {"0VKV", 'F'}, - {"0VKV&(", 'F'}, - {"0VKV&1", 'F'}, - {"0VKV&F", 'F'}, - {"0VKV&N", 'F'}, - {"0VKV&S", 'F'}, - {"0VKV&V", 'F'}, - {"0VKV;", 'F'}, - {"0VKV;C", 'F'}, - {"0VKV;E", 'F'}, - {"0VKV;T", 'F'}, - {"0VKVB(", 'F'}, - {"0VKVB1", 'F'}, - {"0VKVBF", 'F'}, - {"0VKVBN", 'F'}, - {"0VKVBS", 'F'}, - {"0VKVBV", 'F'}, - {"0VKVC", 'F'}, - {"0VKVE(", 'F'}, - {"0VKVE1", 'F'}, - {"0VKVEF", 'F'}, - {"0VKVEK", 'F'}, - {"0VKVEN", 'F'}, - {"0VKVES", 'F'}, - {"0VKVEV", 'F'}, - {"0VKVO(", 'F'}, - {"0VKVOF", 'F'}, - {"0VKVOS", 'F'}, - {"0VKVU(", 'F'}, - {"0VKVUE", 'F'}, - {"0VO(((", 'F'}, - {"0VO((1", 'F'}, - {"0VO((E", 'F'}, - {"0VO((F", 'F'}, - {"0VO((N", 'F'}, - {"0VO((O", 'F'}, - {"0VO((S", 'F'}, - {"0VO((T", 'F'}, - {"0VO((V", 'F'}, - {"0VO(1&", 'F'}, - {"0VO(1)", 'F'}, - {"0VO(1,", 'F'}, - {"0VO(1O", 'F'}, - {"0VO(E(", 'F'}, - {"0VO(E1", 'F'}, - {"0VO(EE", 'F'}, - {"0VO(EF", 'F'}, - {"0VO(EK", 'F'}, - {"0VO(EN", 'F'}, - {"0VO(ES", 'F'}, - {"0VO(EV", 'F'}, - {"0VO(F(", 'F'}, - {"0VO(N&", 'F'}, - {"0VO(N)", 'F'}, - {"0VO(N,", 'F'}, - {"0VO(NO", 'F'}, - {"0VO(O(", 'F'}, - {"0VO(O1", 'F'}, - {"0VO(OF", 'F'}, - {"0VO(ON", 'F'}, - {"0VO(OS", 'F'}, - {"0VO(OV", 'F'}, - {"0VO(S&", 'F'}, - {"0VO(S)", 'F'}, - {"0VO(S,", 'F'}, - {"0VO(SO", 'F'}, - {"0VO(T(", 'F'}, - {"0VO(V&", 'F'}, - {"0VO(V)", 'F'}, - {"0VO(V,", 'F'}, - {"0VO(VO", 'F'}, - {"0VOF((", 'F'}, - {"0VOF()", 'F'}, - {"0VOF(1", 'F'}, - {"0VOF(E", 'F'}, - {"0VOF(F", 'F'}, - {"0VOF(N", 'F'}, - {"0VOF(S", 'F'}, - {"0VOF(V", 'F'}, - {"0VOK&(", 'F'}, - {"0VOK&1", 'F'}, - {"0VOK&F", 'F'}, - {"0VOK&N", 'F'}, - {"0VOK&S", 'F'}, - {"0VOK&V", 'F'}, - {"0VOK((", 'F'}, - {"0VOK(1", 'F'}, - {"0VOK(F", 'F'}, - {"0VOK(N", 'F'}, - {"0VOK(S", 'F'}, - {"0VOK(V", 'F'}, - {"0VOK1C", 'F'}, - {"0VOK1O", 'F'}, - {"0VOKF(", 'F'}, - {"0VOKNC", 'F'}, - {"0VOKO(", 'F'}, - {"0VOKO1", 'F'}, - {"0VOKOF", 'F'}, - {"0VOKON", 'F'}, - {"0VOKOS", 'F'}, - {"0VOKOV", 'F'}, - {"0VOKSC", 'F'}, - {"0VOKSO", 'F'}, - {"0VOKVC", 'F'}, - {"0VOKVO", 'F'}, - {"0VOS", 'F'}, - {"0VOS&(", 'F'}, - {"0VOS&1", 'F'}, - {"0VOS&E", 'F'}, - {"0VOS&F", 'F'}, - {"0VOS&K", 'F'}, - {"0VOS&N", 'F'}, - {"0VOS&S", 'F'}, - {"0VOS&T", 'F'}, - {"0VOS&U", 'F'}, - {"0VOS&V", 'F'}, - {"0VOS((", 'F'}, - {"0VOS(E", 'F'}, - {"0VOS(U", 'F'}, - {"0VOS)&", 'F'}, - {"0VOS),", 'F'}, - {"0VOS);", 'F'}, - {"0VOS)B", 'F'}, - {"0VOS)E", 'F'}, - {"0VOS)K", 'F'}, - {"0VOS)O", 'F'}, - {"0VOS)U", 'F'}, - {"0VOS,(", 'F'}, - {"0VOS,F", 'F'}, - {"0VOS,V", 'F'}, - {"0VOS1(", 'F'}, - {"0VOS1F", 'F'}, - {"0VOS1N", 'F'}, - {"0VOS1O", 'F'}, - {"0VOS1S", 'F'}, - {"0VOS1U", 'F'}, - {"0VOS1V", 'F'}, - {"0VOS;", 'F'}, - {"0VOS;C", 'F'}, - {"0VOS;E", 'F'}, - {"0VOS;N", 'F'}, - {"0VOS;T", 'F'}, - {"0VOSA(", 'F'}, - {"0VOSAF", 'F'}, - {"0VOSAS", 'F'}, - {"0VOSAT", 'F'}, - {"0VOSAV", 'F'}, - {"0VOSB(", 'F'}, - {"0VOSB1", 'F'}, - {"0VOSBE", 'F'}, - {"0VOSBF", 'F'}, - {"0VOSBN", 'F'}, - {"0VOSBS", 'F'}, - {"0VOSBV", 'F'}, - {"0VOSC", 'F'}, - {"0VOSE(", 'F'}, - {"0VOSE1", 'F'}, - {"0VOSEF", 'F'}, - {"0VOSEK", 'F'}, - {"0VOSEN", 'F'}, - {"0VOSEO", 'F'}, - {"0VOSES", 'F'}, - {"0VOSEU", 'F'}, - {"0VOSEV", 'F'}, - {"0VOSF(", 'F'}, - {"0VOSK(", 'F'}, - {"0VOSK)", 'F'}, - {"0VOSK1", 'F'}, - {"0VOSKB", 'F'}, - {"0VOSKF", 'F'}, - {"0VOSKN", 'F'}, - {"0VOSKS", 'F'}, - {"0VOSKU", 'F'}, - {"0VOSKV", 'F'}, - {"0VOSO(", 'F'}, - {"0VOSO1", 'F'}, - {"0VOSOF", 'F'}, - {"0VOSOK", 'F'}, - {"0VOSON", 'F'}, - {"0VOSOS", 'F'}, - {"0VOSOT", 'F'}, - {"0VOSOU", 'F'}, - {"0VOSOV", 'F'}, - {"0VOSU", 'F'}, - {"0VOSU(", 'F'}, - {"0VOSU1", 'F'}, - {"0VOSU;", 'F'}, - {"0VOSUC", 'F'}, - {"0VOSUE", 'F'}, - {"0VOSUF", 'F'}, - {"0VOSUK", 'F'}, - {"0VOSUN", 'F'}, - {"0VOSUO", 'F'}, - {"0VOSUS", 'F'}, - {"0VOSUT", 'F'}, - {"0VOSUV", 'F'}, - {"0VOSV(", 'F'}, - {"0VOSVF", 'F'}, - {"0VOSVO", 'F'}, - {"0VOSVS", 'F'}, - {"0VOSVU", 'F'}, - {"0VOT((", 'F'}, - {"0VOT(1", 'F'}, - {"0VOT(F", 'F'}, - {"0VOT(N", 'F'}, - {"0VOT(S", 'F'}, - {"0VOT(T", 'F'}, - {"0VOT(V", 'F'}, - {"0VOU((", 'F'}, - {"0VOU(E", 'F'}, - {"0VOUEK", 'F'}, - {"0VOUEN", 'F'}, - {"0VU", 'F'}, - {"0VU(((", 'F'}, - {"0VU((1", 'F'}, - {"0VU((E", 'F'}, - {"0VU((F", 'F'}, - {"0VU((N", 'F'}, - {"0VU((S", 'F'}, - {"0VU((V", 'F'}, - {"0VU(1)", 'F'}, - {"0VU(1O", 'F'}, - {"0VU(E(", 'F'}, - {"0VU(E1", 'F'}, - {"0VU(EF", 'F'}, - {"0VU(EK", 'F'}, - {"0VU(EN", 'F'}, - {"0VU(ES", 'F'}, - {"0VU(EV", 'F'}, - {"0VU(F(", 'F'}, - {"0VU(N)", 'F'}, - {"0VU(NO", 'F'}, - {"0VU(S)", 'F'}, - {"0VU(SO", 'F'}, - {"0VU(V)", 'F'}, - {"0VU(VO", 'F'}, - {"0VU1,(", 'F'}, - {"0VU1,F", 'F'}, - {"0VU1,V", 'F'}, - {"0VU1C", 'F'}, - {"0VU1O(", 'F'}, - {"0VU1OF", 'F'}, - {"0VU1OS", 'F'}, - {"0VU1OV", 'F'}, - {"0VU;", 'F'}, - {"0VU;C", 'F'}, - {"0VUC", 'F'}, - {"0VUE", 'F'}, - {"0VUE((", 'F'}, - {"0VUE(1", 'F'}, - {"0VUE(E", 'F'}, - {"0VUE(F", 'F'}, - {"0VUE(N", 'F'}, - {"0VUE(O", 'F'}, - {"0VUE(S", 'F'}, - {"0VUE(V", 'F'}, - {"0VUE1", 'F'}, - {"0VUE1&", 'F'}, - {"0VUE1(", 'F'}, - {"0VUE1)", 'F'}, - {"0VUE1,", 'F'}, - {"0VUE1;", 'F'}, - {"0VUE1B", 'F'}, - {"0VUE1C", 'F'}, - {"0VUE1F", 'F'}, - {"0VUE1K", 'F'}, - {"0VUE1N", 'F'}, - {"0VUE1O", 'F'}, - {"0VUE1S", 'F'}, - {"0VUE1U", 'F'}, - {"0VUE1V", 'F'}, - {"0VUE;", 'F'}, - {"0VUE;C", 'F'}, - {"0VUEC", 'F'}, - {"0VUEF", 'F'}, - {"0VUEF(", 'F'}, - {"0VUEF,", 'F'}, - {"0VUEF;", 'F'}, - {"0VUEFC", 'F'}, - {"0VUEK", 'F'}, - {"0VUEK(", 'F'}, - {"0VUEK1", 'F'}, - {"0VUEK;", 'F'}, - {"0VUEKC", 'F'}, - {"0VUEKF", 'F'}, - {"0VUEKN", 'F'}, - {"0VUEKO", 'F'}, - {"0VUEKS", 'F'}, - {"0VUEKV", 'F'}, - {"0VUEN", 'F'}, - {"0VUEN&", 'F'}, - {"0VUEN(", 'F'}, - {"0VUEN)", 'F'}, - {"0VUEN,", 'F'}, - {"0VUEN1", 'F'}, - {"0VUEN;", 'F'}, - {"0VUENB", 'F'}, - {"0VUENC", 'F'}, - {"0VUENF", 'F'}, - {"0VUENK", 'F'}, - {"0VUENO", 'F'}, - {"0VUENS", 'F'}, - {"0VUENU", 'F'}, - {"0VUEOK", 'F'}, - {"0VUEON", 'F'}, - {"0VUEOO", 'F'}, - {"0VUES", 'F'}, - {"0VUES&", 'F'}, - {"0VUES(", 'F'}, - {"0VUES)", 'F'}, - {"0VUES,", 'F'}, - {"0VUES1", 'F'}, - {"0VUES;", 'F'}, - {"0VUESB", 'F'}, - {"0VUESC", 'F'}, - {"0VUESF", 'F'}, - {"0VUESK", 'F'}, - {"0VUESO", 'F'}, - {"0VUESU", 'F'}, - {"0VUESV", 'F'}, - {"0VUEV", 'F'}, - {"0VUEV&", 'F'}, - {"0VUEV(", 'F'}, - {"0VUEV)", 'F'}, - {"0VUEV,", 'F'}, - {"0VUEV;", 'F'}, - {"0VUEVB", 'F'}, - {"0VUEVC", 'F'}, - {"0VUEVF", 'F'}, - {"0VUEVK", 'F'}, - {"0VUEVN", 'F'}, - {"0VUEVO", 'F'}, - {"0VUEVS", 'F'}, - {"0VUEVU", 'F'}, - {"0VUF((", 'F'}, - {"0VUF()", 'F'}, - {"0VUF(1", 'F'}, - {"0VUF(F", 'F'}, - {"0VUF(N", 'F'}, - {"0VUF(S", 'F'}, - {"0VUF(V", 'F'}, - {"0VUK((", 'F'}, - {"0VUK(E", 'F'}, - {"0VUN((", 'F'}, - {"0VUN(1", 'F'}, - {"0VUN(F", 'F'}, - {"0VUN(S", 'F'}, - {"0VUN(V", 'F'}, - {"0VUN,(", 'F'}, - {"0VUN,F", 'F'}, - {"0VUN,V", 'F'}, - {"0VUN1(", 'F'}, - {"0VUN1,", 'F'}, - {"0VUN1O", 'F'}, - {"0VUNC", 'F'}, - {"0VUNE(", 'F'}, - {"0VUNE1", 'F'}, - {"0VUNEF", 'F'}, - {"0VUNEN", 'F'}, - {"0VUNES", 'F'}, - {"0VUNEV", 'F'}, - {"0VUNF(", 'F'}, - {"0VUNO(", 'F'}, - {"0VUNOF", 'F'}, - {"0VUNOS", 'F'}, - {"0VUNOV", 'F'}, - {"0VUNS(", 'F'}, - {"0VUNS,", 'F'}, - {"0VUNSO", 'F'}, - {"0VUO((", 'F'}, - {"0VUO(E", 'F'}, - {"0VUON(", 'F'}, - {"0VUON1", 'F'}, - {"0VUONF", 'F'}, - {"0VUONS", 'F'}, - {"0VUS,(", 'F'}, - {"0VUS,F", 'F'}, - {"0VUS,V", 'F'}, - {"0VUSC", 'F'}, - {"0VUSO(", 'F'}, - {"0VUSO1", 'F'}, - {"0VUSOF", 'F'}, - {"0VUSON", 'F'}, - {"0VUSOS", 'F'}, - {"0VUSOV", 'F'}, - {"0VUTN(", 'F'}, - {"0VUTN1", 'F'}, - {"0VUTNF", 'F'}, - {"0VUTNS", 'F'}, - {"0VUV,(", 'F'}, - {"0VUV,1", 'F'}, - {"0VUV,F", 'F'}, - {"0VUV,N", 'F'}, - {"0VUV,S", 'F'}, - {"0VUV,V", 'F'}, - {"0VUVC", 'F'}, - {"0VUVO(", 'F'}, - {"0VUVOF", 'F'}, - {"0VUVOS", 'F'}, - {"0X", 'F'}, - {"::", 'o'}, - {":=", 'o'}, - {"<<", 'o'}, - {"<=", 'o'}, - {"<>", 'o'}, - {"<@", 'o'}, - {">=", 'o'}, - {">>", 'o'}, - {"@>", 'o'}, - {"ABORT", 'k'}, - {"ABS", 'f'}, - {"ACCESSIBLE", 'k'}, - {"ACOS", 'f'}, - {"ADD", 'k'}, - {"ADDDATE", 'f'}, - {"ADDTIME", 'f'}, - {"AES_DECRYPT", 'f'}, - {"AES_ENCRYPT", 'f'}, - {"AGAINST", 'k'}, - {"AGE", 'f'}, - {"ALL_USERS", 'k'}, - {"ALTER", 'k'}, - {"ALTER DOMAIN", 'k'}, - {"ALTER TABLE", 'k'}, - {"ANALYZE", 'k'}, - {"AND", '&'}, - {"ANY", 'f'}, - {"ANYARRAY", 't'}, - {"ANYELEMENT", 't'}, - {"ANYNONARRY", 't'}, - {"APPLOCK_MODE", 'f'}, - {"APPLOCK_TEST", 'f'}, - {"APP_NAME", 'f'}, - {"ARRAY_AGG", 'f'}, - {"ARRAY_CAT", 'f'}, - {"ARRAY_DIM", 'f'}, - {"ARRAY_FILL", 'f'}, - {"ARRAY_LENGTH", 'f'}, - {"ARRAY_LOWER", 'f'}, - {"ARRAY_NDIMS", 'f'}, - {"ARRAY_PREPEND", 'f'}, - {"ARRAY_TO_JSON", 'f'}, - {"ARRAY_TO_STRING", 'f'}, - {"ARRAY_UPPER", 'f'}, - {"AS", 'k'}, - {"ASC", 'k'}, - {"ASCII", 'f'}, - {"ASENSITIVE", 'k'}, - {"ASIN", 'f'}, - {"ASSEMBLYPROPERTY", 'f'}, - {"ASYMKEY_ID", 'f'}, - {"AT TIME", 'n'}, - {"AT TIME ZONE", 'k'}, - {"ATAN", 'f'}, - {"ATAN2", 'f'}, - {"AUTOINCREMENT", 'k'}, - {"AVG", 'f'}, - {"BEFORE", 'k'}, - {"BEGIN", 'T'}, - {"BENCHMARK", 'f'}, - {"BETWEEN", 'o'}, - {"BIGINT", 't'}, - {"BIGSERIAL", 't'}, - {"BIN", 'f'}, - {"BINARY", 't'}, - {"BINARY_DOUBLE_INFINITY", '1'}, - {"BINARY_DOUBLE_NAN", '1'}, - {"BINARY_FLOAT_INFINITY", '1'}, - {"BINARY_FLOAT_NAN", '1'}, - {"BINBINARY", 'f'}, - {"BIT_AND", 'f'}, - {"BIT_COUNT", 'f'}, - {"BIT_LENGTH", 'f'}, - {"BIT_OR", 'f'}, - {"BIT_XOR", 'f'}, - {"BLOB", 'k'}, - {"BOOLEAN", 't'}, - {"BOOL_AND", 'f'}, - {"BOOL_OR", 'f'}, - {"BOTH", 'k'}, - {"BTRIM", 'f'}, - {"BY", 'n'}, - {"BYTEA", 't'}, - {"CALL", 'T'}, - {"CASCADE", 'k'}, - {"CASE", 'E'}, - {"CAST", 'f'}, - {"CBOOL", 'f'}, - {"CBRT", 'f'}, - {"CBYTE", 'f'}, - {"CCUR", 'f'}, - {"CDATE", 'f'}, - {"CDBL", 'f'}, - {"CEIL", 'f'}, - {"CEILING", 'f'}, - {"CERTENCODED", 'f'}, - {"CERTPRIVATEKEY", 'f'}, - {"CERT_ID", 'f'}, - {"CERT_PROPERTY", 'f'}, - {"CHANGE", 'k'}, - {"CHANGES", 'f'}, - {"CHAR", 'f'}, - {"CHARACTER", 't'}, - {"CHARACTER VARYING", 't'}, - {"CHARACTER_LENGTH", 'f'}, - {"CHARINDEX", 'f'}, - {"CHARSET", 'f'}, - {"CHAR_LENGTH", 'f'}, - {"CHDIR", 'f'}, - {"CHDRIVE", 'f'}, - {"CHECK", 'k'}, - {"CHECKSUM_AGG", 'f'}, - {"CHOOSE", 'f'}, - {"CHR", 'f'}, - {"CINT", 'f'}, - {"CLNG", 'f'}, - {"CLOCK_TIMESTAMP", 'f'}, - {"COALESCE", 'f'}, - {"COERCIBILITY", 'f'}, - {"COLLATE", 'A'}, - {"COLLATION", 'f'}, - {"COLLATIONPROPERTY", 'f'}, - {"COLUMN", 'k'}, - {"COLUMNPROPERTY", 'f'}, - {"COLUMNS_UPDATED", 'f'}, - {"COL_LENGTH", 'f'}, - {"COL_NAME", 'f'}, - {"COMPRESS", 'f'}, - {"CONCAT", 'f'}, - {"CONCAT_WS", 'f'}, - {"CONDITION", 'k'}, - {"CONNECTION_ID", 'f'}, - {"CONSTRAINT", 'k'}, - {"CONTINUE", 'k'}, - {"CONV", 'f'}, - {"CONVERT", 'f'}, - {"CONVERT_FROM", 'f'}, - {"CONVERT_TO", 'f'}, - {"CONVERT_TZ", 'f'}, - {"COS", 'f'}, - {"COT", 'f'}, - {"COUNT", 'f'}, - {"COUNT_BIG", 'k'}, - {"CRC32", 'f'}, - {"CREATE", 'E'}, - {"CREATE OR", 'n'}, - {"CREATE OR REPLACE", 'T'}, - {"CROSS", 'n'}, - {"CROSS JOIN", 'k'}, - {"CSNG", 'f'}, - {"CSTRING", 't'}, - {"CTXSYS.DRITHSX.SN", 'f'}, - {"CUME_DIST", 'f'}, - {"CURDATE", 'f'}, - {"CURDIR", 'f'}, - {"CURRENT DATE", 'v'}, - {"CURRENT DEGREE", 'v'}, - {"CURRENT FUNCTION", 'v'}, - {"CURRENT FUNCTION PATH", 'v'}, - {"CURRENT PATH", 'v'}, - {"CURRENT SCHEMA", 'v'}, - {"CURRENT SERVER", 'v'}, - {"CURRENT TIME", 'v'}, - {"CURRENT TIMEZONE", 'v'}, - {"CURRENTUSER", 'f'}, - {"CURRENT_DATABASE", 'f'}, - {"CURRENT_DATE", 'v'}, - {"CURRENT_PATH", 'v'}, - {"CURRENT_QUERY", 'f'}, - {"CURRENT_SCHEMA", 'f'}, - {"CURRENT_SCHEMAS", 'f'}, - {"CURRENT_SERVER", 'v'}, - {"CURRENT_SETTING", 'f'}, - {"CURRENT_TIME", 'v'}, - {"CURRENT_TIMESTAMP", 'v'}, - {"CURRENT_TIMEZONE", 'v'}, - {"CURRENT_USER", 'v'}, - {"CURRVAL", 'f'}, - {"CURSOR", 'k'}, - {"CURSOR_STATUS", 'f'}, - {"CURTIME", 'f'}, - {"CVAR", 'f'}, - {"DATABASE", 'n'}, - {"DATABASEPROPERTYEX", 'f'}, - {"DATABASES", 'k'}, - {"DATABASE_PRINCIPAL_ID", 'f'}, - {"DATALENGTH", 'f'}, - {"DATE", 'f'}, - {"DATEADD", 'f'}, - {"DATEDIFF", 'f'}, - {"DATEFROMPARTS", 'f'}, - {"DATENAME", 'f'}, - {"DATEPART", 'f'}, - {"DATESERIAL", 'f'}, - {"DATETIME2FROMPARTS", 'f'}, - {"DATETIMEFROMPARTS", 'f'}, - {"DATETIMEOFFSETFROMPARTS", 'f'}, - {"DATEVALUE", 'f'}, - {"DATE_ADD", 'f'}, - {"DATE_FORMAT", 'f'}, - {"DATE_PART", 'f'}, - {"DATE_SUB", 'f'}, - {"DATE_TRUNC", 'f'}, - {"DAVG", 'f'}, - {"DAY", 'f'}, - {"DAYNAME", 'f'}, - {"DAYOFMONTH", 'f'}, - {"DAYOFWEEK", 'f'}, - {"DAYOFYEAR", 'f'}, - {"DAY_HOUR", 'k'}, - {"DAY_MICROSECOND", 'k'}, - {"DAY_MINUTE", 'k'}, - {"DAY_SECOND", 'k'}, - {"DBMS_LOCK.SLEEP", 'f'}, - {"DBMS_PIPE.RECEIVE_MESSAGE", 'f'}, - {"DB_ID", 'f'}, - {"DB_NAME", 'f'}, - {"DCOUNT", 'f'}, - {"DEC", 'k'}, - {"DECIMAL", 't'}, - {"DECLARE", 'T'}, - {"DECODE", 'f'}, - {"DECRYPTBYASMKEY", 'f'}, - {"DECRYPTBYCERT", 'f'}, - {"DECRYPTBYKEY", 'f'}, - {"DECRYPTBYKEYAUTOCERT", 'f'}, - {"DECRYPTBYPASSPHRASE", 'f'}, - {"DEFAULT", 'k'}, - {"DEGREES", 'f'}, - {"DELAY", 'k'}, - {"DELAYED", 'k'}, - {"DELETE", 'T'}, - {"DENSE_RANK", 'f'}, - {"DESC", 'k'}, - {"DESCRIBE", 'k'}, - {"DES_DECRYPT", 'f'}, - {"DES_ENCRYPT", 'f'}, - {"DETERMINISTIC", 'k'}, - {"DFIRST", 'f'}, - {"DIFFERENCE", 'f'}, - {"DISTINCT", 'k'}, - {"DISTINCTROW", 'k'}, - {"DIV", 'o'}, - {"DLAST", 'f'}, - {"DLOOKUP", 'f'}, - {"DMAX", 'f'}, - {"DMIN", 'f'}, - {"DO", 'n'}, - {"DOUBLE", 't'}, - {"DOUBLE PRECISION", 't'}, - {"DROP", 'T'}, - {"DSUM", 'f'}, - {"DUAL", 'n'}, - {"EACH", 'k'}, - {"ELSE", 'k'}, - {"ELSEIF", 'k'}, - {"ELT", 'f'}, - {"ENCLOSED", 'k'}, - {"ENCODE", 'f'}, - {"ENCRYPT", 'f'}, - {"ENCRYPTBYASMKEY", 'f'}, - {"ENCRYPTBYCERT", 'f'}, - {"ENCRYPTBYKEY", 'f'}, - {"ENCRYPTBYPASSPHRASE", 'f'}, - {"ENUM_FIRST", 'f'}, - {"ENUM_LAST", 'f'}, - {"ENUM_RANGE", 'f'}, - {"EOMONTH", 'f'}, - {"EQV", 'o'}, - {"ESCAPED", 'k'}, - {"EVENTDATA", 'f'}, - {"EXCEPT", 'U'}, - {"EXEC", 'T'}, - {"EXECUTE", 'T'}, - {"EXECUTE AS", 'E'}, - {"EXECUTE AS LOGIN", 'E'}, - {"EXISTS", 'f'}, - {"EXIT", 'k'}, - {"EXP", 'f'}, - {"EXPLAIN", 'k'}, - {"EXPORT_SET", 'f'}, - {"EXTRACT", 'f'}, - {"EXTRACTVALUE", 'f'}, - {"EXTRACT_VALUE", 'f'}, - {"FALSE", '1'}, - {"FETCH", 'k'}, - {"FIELD", 'f'}, - {"FILEDATETIME", 'f'}, - {"FILEGROUPPROPERTY", 'f'}, - {"FILEGROUP_ID", 'f'}, - {"FILEGROUP_NAME", 'f'}, - {"FILELEN", 'f'}, - {"FILEPROPERTY", 'f'}, - {"FILE_ID", 'f'}, - {"FILE_IDEX", 'f'}, - {"FILE_NAME", 'f'}, - {"FIND_IN_SET", 'f'}, - {"FIRST_VALUE", 'f'}, - {"FLOAT", 't'}, - {"FLOAT4", 't'}, - {"FLOAT8", 't'}, - {"FLOOR", 'f'}, - {"FN_VIRTUALFILESTATS", 'f'}, - {"FOR", 'n'}, - {"FOR UPDATE", 'k'}, - {"FOR UPDATE NOWAIT", 'k'}, - {"FOR UPDATE OF", 'k'}, - {"FOR UPDATE SKIP", 'k'}, - {"FOR UPDATE SKIP LOCKED", 'k'}, - {"FOR UPDATE WAIT", 'k'}, - {"FORCE", 'k'}, - {"FOREIGN", 'k'}, - {"FORMAT", 'f'}, - {"FOUND_ROWS", 'f'}, - {"FROM", 'k'}, - {"FROM_DAYS", 'f'}, - {"FROM_UNIXTIME", 'f'}, - {"FULL JOIN", 'k'}, - {"FULL OUTER", 'k'}, - {"FULL OUTER JOIN", 'k'}, - {"FULLTEXT", 'k'}, - {"FULLTEXTCATALOGPROPERTY", 'f'}, - {"FULLTEXTSERVICEPROPERTY", 'f'}, - {"FUNCTION", 'k'}, - {"GENERATE_SERIES", 'f'}, - {"GENERATE_SUBSCRIPTS", 'f'}, - {"GETATTR", 'f'}, - {"GETDATE", 'f'}, - {"GETUTCDATE", 'f'}, - {"GET_BIT", 'f'}, - {"GET_BYTE", 'f'}, - {"GET_FORMAT", 'f'}, - {"GET_LOCK", 'f'}, - {"GO", 'T'}, - {"GOTO", 'T'}, - {"GRANT", 'k'}, - {"GREATEST", 'f'}, - {"GROUP", 'n'}, - {"GROUP BY", 'B'}, - {"GROUPING", 'f'}, - {"GROUPING_ID", 'f'}, - {"GROUP_CONCAT", 'f'}, - {"HANDLER", 'T'}, - {"HASHBYTES", 'f'}, - {"HAS_PERMS_BY_NAME", 'f'}, - {"HAVING", 'B'}, - {"HEX", 'f'}, - {"HIGH_PRIORITY", 'k'}, - {"HOST_NAME", 'f'}, - {"HOUR", 'f'}, - {"HOUR_MICROSECOND", 'k'}, - {"HOUR_MINUTE", 'k'}, - {"HOUR_SECOND", 'k'}, - {"IDENTIFY", 'f'}, - {"IDENT_CURRENT", 'f'}, - {"IDENT_INCR", 'f'}, - {"IDENT_SEED", 'f'}, - {"IF", 'f'}, - {"IF EXISTS", 'f'}, - {"IF NOT", 'n'}, - {"IF NOT EXISTS", 'f'}, - {"IFF", 'f'}, - {"IFNULL", 'f'}, - {"IGNORE", 'k'}, - {"IIF", 'f'}, - {"IN", 'k'}, - {"IN BOOLEAN", 'n'}, - {"IN BOOLEAN MODE", 'k'}, - {"INDEX", 'k'}, - {"INDEXKEY_PROPERTY", 'f'}, - {"INDEXPROPERTY", 'f'}, - {"INDEX_COL", 'f'}, - {"INET_ATON", 'f'}, - {"INET_NTOA", 'f'}, - {"INFILE", 'k'}, - {"INITCAP", 'f'}, - {"INNER", 'k'}, - {"INNER JOIN", 'k'}, - {"INOUT", 'k'}, - {"INSENSITIVE", 'k'}, - {"INSERT", 'E'}, - {"INSERT DELAYED", 'E'}, - {"INSERT DELAYED INTO", 'T'}, - {"INSERT HIGH_PRIORITY", 'E'}, - {"INSERT HIGH_PRIORITY INTO", 'T'}, - {"INSERT IGNORE", 'E'}, - {"INSERT IGNORE INTO", 'T'}, - {"INSERT INTO", 'T'}, - {"INSERT LOW_PRIORITY", 'E'}, - {"INSERT LOW_PRIORITY INTO", 'T'}, - {"INSTR", 'f'}, - {"INSTRREV", 'f'}, - {"INT", 't'}, - {"INT1", 't'}, - {"INT2", 't'}, - {"INT3", 't'}, - {"INT4", 't'}, - {"INT8", 't'}, - {"INTEGER", 't'}, - {"INTERSECT", 'U'}, - {"INTERSECT ALL", 'U'}, - {"INTERVAL", 'k'}, - {"INTO", 'k'}, - {"INTO DUMPFILE", 'k'}, - {"INTO OUTFILE", 'k'}, - {"IS", 'o'}, - {"IS DISTINCT", 'n'}, - {"IS DISTINCT FROM", 'o'}, - {"IS NOT", 'o'}, - {"IS NOT DISTINCT", 'n'}, - {"IS NOT DISTINCT FROM", 'o'}, - {"ISDATE", 'f'}, - {"ISEMPTY", 'f'}, - {"ISFINITE", 'f'}, - {"ISNULL", 'f'}, - {"ISNUMERIC", 'f'}, - {"IS_FREE_LOCK", 'f'}, - {"IS_MEMBER", 'f'}, - {"IS_OBJECTSIGNED", 'f'}, - {"IS_ROLEMEMBER", 'f'}, - {"IS_SRVROLEMEMBER", 'f'}, - {"IS_USED_LOCK", 'f'}, - {"ITERATE", 'k'}, - {"JOIN", 'k'}, - {"JULIANDAY", 'f'}, - {"JUSTIFY_DAYS", 'f'}, - {"JUSTIFY_HOURS", 'f'}, - {"JUSTIFY_INTERVAL", 'f'}, - {"KEYS", 'k'}, - {"KEY_GUID", 'f'}, - {"KEY_ID", 'f'}, - {"KILL", 'k'}, - {"LAG", 'f'}, - {"LASTVAL", 'f'}, - {"LAST_INSERT_ID", 'f'}, - {"LAST_INSERT_ROWID", 'f'}, - {"LAST_VALUE", 'f'}, - {"LCASE", 'f'}, - {"LEAD", 'f'}, - {"LEADING", 'k'}, - {"LEAST", 'f'}, - {"LEAVE", 'k'}, - {"LEFT", 'n'}, - {"LEFT JOIN", 'k'}, - {"LEFT OUTER", 'k'}, - {"LEFT OUTER JOIN", 'k'}, - {"LENGTH", 'f'}, - {"LIKE", 'o'}, - {"LIMIT", 'B'}, - {"LINEAR", 'k'}, - {"LINES", 'k'}, - {"LN", 'f'}, - {"LOAD", 'k'}, - {"LOAD DATA", 'T'}, - {"LOAD XML", 'T'}, - {"LOAD_EXTENSION", 'f'}, - {"LOAD_FILE", 'f'}, - {"LOCALTIME", 'v'}, - {"LOCALTIMESTAMP", 'v'}, - {"LOCATE", 'f'}, - {"LOCK", 'n'}, - {"LOCK IN", 'n'}, - {"LOCK IN SHARE", 'n'}, - {"LOCK IN SHARE MODE", 'k'}, - {"LOCK TABLE", 'k'}, - {"LOCK TABLES", 'k'}, - {"LOG", 'f'}, - {"LOG10", 'f'}, - {"LOG2", 'f'}, - {"LONGBLOB", 'k'}, - {"LONGTEXT", 'k'}, - {"LOOP", 'k'}, - {"LOWER", 'f'}, - {"LOWER_INC", 'f'}, - {"LOWER_INF", 'f'}, - {"LOW_PRIORITY", 'k'}, - {"LPAD", 'f'}, - {"LTRIM", 'f'}, - {"MAKEDATE", 'f'}, - {"MAKE_SET", 'f'}, - {"MASKLEN", 'f'}, - {"MASTER_BIND", 'k'}, - {"MASTER_POS_WAIT", 'f'}, - {"MASTER_SSL_VERIFY_SERVER_CERT", 'k'}, - {"MATCH", 'k'}, - {"MAX", 'f'}, - {"MAXVALUE", 'k'}, - {"MD5", 'f'}, - {"MEDIUMBLOB", 'k'}, - {"MEDIUMINT", 'k'}, - {"MEDIUMTEXT", 'k'}, - {"MERGE", 'k'}, - {"MICROSECOND", 'f'}, - {"MID", 'f'}, - {"MIDDLEINT", 'k'}, - {"MIN", 'f'}, - {"MINUTE", 'f'}, - {"MINUTE_MICROSECOND", 'k'}, - {"MINUTE_SECOND", 'k'}, - {"MKDIR", 'f'}, - {"MOD", 'o'}, - {"MODE", 'n'}, - {"MODIFIES", 'k'}, - {"MONEY", 't'}, - {"MONTH", 'f'}, - {"MONTHNAME", 'f'}, - {"NAME_CONST", 'f'}, - {"NATURAL", 'n'}, - {"NATURAL FULL", 'k'}, - {"NATURAL FULL OUTER JOIN", 'k'}, - {"NATURAL INNER", 'k'}, - {"NATURAL JOIN", 'k'}, - {"NATURAL LEFT", 'k'}, - {"NATURAL LEFT OUTER", 'k'}, - {"NATURAL LEFT OUTER JOIN", 'k'}, - {"NATURAL OUTER", 'k'}, - {"NATURAL RIGHT", 'k'}, - {"NATURAL RIGHT OUTER JOIN", 'k'}, - {"NETMASK", 'f'}, - {"NEXT VALUE", 'n'}, - {"NEXT VALUE FOR", 'k'}, - {"NEXTVAL", 'f'}, - {"NOT", 'o'}, - {"NOT BETWEEN", 'o'}, - {"NOT IN", 'k'}, - {"NOT LIKE", 'o'}, - {"NOT REGEXP", 'o'}, - {"NOT RLIKE", 'o'}, - {"NOT SIMILAR", 'o'}, - {"NOT SIMILAR TO", 'o'}, - {"NOTNULL", 'k'}, - {"NOW", 'f'}, - {"NOWAIT", 'k'}, - {"NO_WRITE_TO_BINLOG", 'k'}, - {"NTH_VALUE", 'f'}, - {"NTILE", 'f'}, - {"NULL", 'v'}, - {"NULLIF", 'f'}, - {"NUMERIC", 't'}, - {"NZ", 'f'}, - {"OBJECTPROPERTY", 'f'}, - {"OBJECTPROPERTYEX", 'f'}, - {"OBJECT_DEFINITION", 'f'}, - {"OBJECT_ID", 'f'}, - {"OBJECT_NAME", 'f'}, - {"OBJECT_SCHEMA_NAME", 'f'}, - {"OCT", 'f'}, - {"OCTET_LENGTH", 'f'}, - {"OFFSET", 'k'}, - {"OID", 't'}, - {"OLD_PASSWORD", 'f'}, - {"ONE_SHOT", 'k'}, - {"OPEN", 'k'}, - {"OPENDATASOURCE", 'f'}, - {"OPENQUERY", 'f'}, - {"OPENROWSET", 'f'}, - {"OPENXML", 'f'}, - {"OPTIMIZE", 'k'}, - {"OPTION", 'k'}, - {"OPTIONALLY", 'k'}, - {"OR", '&'}, - {"ORD", 'f'}, - {"ORDER", 'n'}, - {"ORDER BY", 'B'}, - {"ORIGINAL_DB_NAME", 'f'}, - {"ORIGINAL_LOGIN", 'f'}, - {"OUT", 'k'}, - {"OUTER", 'n'}, - {"OUTFILE", 'k'}, - {"OVERLAPS", 'f'}, - {"OVERLAY", 'f'}, - {"OWN3D", 'k'}, - {"OWN3D BY", 'B'}, - {"PARSENAME", 'f'}, - {"PARTITION", 'k'}, - {"PARTITION BY", 'B'}, - {"PASSWORD", 'n'}, - {"PATHINDEX", 'f'}, - {"PATINDEX", 'f'}, - {"PERCENTILE_COUNT", 'f'}, - {"PERCENTILE_DISC", 'f'}, - {"PERCENTILE_RANK", 'f'}, - {"PERCENT_RANK", 'f'}, - {"PERIOD_ADD", 'f'}, - {"PERIOD_DIFF", 'f'}, - {"PERMISSIONS", 'f'}, - {"PG_ADVISORY_LOCK", 'f'}, - {"PG_BACKEND_PID", 'f'}, - {"PG_CANCEL_BACKEND", 'f'}, - {"PG_CLIENT_ENCODING", 'f'}, - {"PG_CONF_LOAD_TIME", 'f'}, - {"PG_CREATE_RESTORE_POINT", 'f'}, - {"PG_HAS_ROLE", 'f'}, - {"PG_IS_IN_RECOVERY", 'f'}, - {"PG_IS_OTHER_TEMP_SCHEMA", 'f'}, - {"PG_LISTENING_CHANNELS", 'f'}, - {"PG_LS_DIR", 'f'}, - {"PG_MY_TEMP_SCHEMA", 'f'}, - {"PG_POSTMASTER_START_TIME", 'f'}, - {"PG_READ_BINARY_FILE", 'f'}, - {"PG_READ_FILE", 'f'}, - {"PG_RELOAD_CONF", 'f'}, - {"PG_ROTATE_LOGFILE", 'f'}, - {"PG_SLEEP", 'f'}, - {"PG_START_BACKUP", 'f'}, - {"PG_STAT_FILE", 'f'}, - {"PG_STOP_BACKUP", 'f'}, - {"PG_SWITCH_XLOG", 'f'}, - {"PG_TERMINATE_BACKEND", 'f'}, - {"PG_TRIGGER_DEPTH", 'f'}, - {"PI", 'f'}, - {"POSITION", 'f'}, - {"POW", 'f'}, - {"POWER", 'f'}, - {"PRECISION", 'k'}, - {"PREVIOUS VALUE", 'n'}, - {"PREVIOUS VALUE FOR", 'k'}, - {"PRIMARY", 'k'}, - {"PRINT", 'T'}, - {"PROCEDURE", 'k'}, - {"PROCEDURE ANALYSE", 'f'}, - {"PUBLISHINGSERVERNAME", 'f'}, - {"PURGE", 'k'}, - {"PWDCOMPARE", 'f'}, - {"PWDENCRYPT", 'f'}, - {"QUARTER", 'f'}, - {"QUOTE", 'f'}, - {"QUOTENAME", 'f'}, - {"QUOTE_IDENT", 'f'}, - {"QUOTE_LITERAL", 'f'}, - {"QUOTE_NULLABLE", 'f'}, - {"RADIANS", 'f'}, - {"RAISEERROR", 'E'}, - {"RAND", 'f'}, - {"RANDOM", 'f'}, - {"RANDOMBLOB", 'f'}, - {"RANGE", 'k'}, - {"RANK", 'f'}, - {"READ", 'k'}, - {"READ WRITE", 'k'}, - {"READS", 'k'}, - {"READ_WRITE", 'k'}, - {"REAL", 't'}, - {"REFERENCES", 'k'}, - {"REGCLASS", 't'}, - {"REGCONFIG", 't'}, - {"REGDICTIONARY", 't'}, - {"REGEXP", 'o'}, - {"REGEXP_MATCHES", 'f'}, - {"REGEXP_REPLACE", 'f'}, - {"REGEXP_SPLIT_TO_ARRAY", 'f'}, - {"REGEXP_SPLIT_TO_TABLE", 'f'}, - {"REGOPER", 't'}, - {"REGOPERATOR", 't'}, - {"REGPROC", 't'}, - {"REGPROCEDURE", 't'}, - {"REGTYPE", 't'}, - {"RELEASE", 'k'}, - {"RELEASE_LOCK", 'f'}, - {"RENAME", 'k'}, - {"REPEAT", 'k'}, - {"REPLACE", 'k'}, - {"REPLICATE", 'f'}, - {"REQUIRE", 'k'}, - {"RESIGNAL", 'k'}, - {"RESTRICT", 'k'}, - {"RETURN", 'k'}, - {"REVERSE", 'f'}, - {"REVOKE", 'k'}, - {"RIGHT", 'n'}, - {"RIGHT JOIN", 'k'}, - {"RIGHT OUTER", 'k'}, - {"RIGHT OUTER JOIN", 'k'}, - {"RLIKE", 'o'}, - {"ROUND", 'f'}, - {"ROW", 'f'}, - {"ROW_COUNT", 'f'}, - {"ROW_NUMBER", 'f'}, - {"ROW_TO_JSON", 'f'}, - {"RPAD", 'f'}, - {"RTRIM", 'f'}, - {"SCHAMA_NAME", 'f'}, - {"SCHEMA", 'k'}, - {"SCHEMAS", 'k'}, - {"SCHEMA_ID", 'f'}, - {"SCOPE_IDENTITY", 'f'}, - {"SECOND_MICROSECOND", 'k'}, - {"SEC_TO_TIME", 'f'}, - {"SELECT", 'E'}, - {"SELECT ALL", 'E'}, - {"SELECT DISTINCT", 'E'}, - {"SENSITIVE", 'k'}, - {"SEPARATOR", 'k'}, - {"SERIAL", 't'}, - {"SERIAL2", 't'}, - {"SERIAL4", 't'}, - {"SERIAL8", 't'}, - {"SERVERPROPERTY", 'f'}, - {"SESSION_USER", 'f'}, - {"SET", 'E'}, - {"SETATTR", 'f'}, - {"SETSEED", 'f'}, - {"SETVAL", 'f'}, - {"SET_BIT", 'f'}, - {"SET_BYTE", 'f'}, - {"SET_CONFIG", 'f'}, - {"SET_MASKLEN", 'f'}, - {"SHA", 'f'}, - {"SHA1", 'f'}, - {"SHA2", 'f'}, - {"SHOW", 'n'}, - {"SHUTDOWN", 'T'}, - {"SIGN", 'f'}, - {"SIGNAL", 'k'}, - {"SIGNBYASMKEY", 'f'}, - {"SIGNBYCERT", 'f'}, - {"SIMILAR", 'k'}, - {"SIMILAR TO", 'o'}, - {"SIN", 'f'}, - {"SLEEP", 'f'}, - {"SMALLDATETIMEFROMPARTS", 'f'}, - {"SMALLINT", 't'}, - {"SMALLSERIAL", 't'}, - {"SOME", 'f'}, - {"SOUNDEX", 'f'}, - {"SOUNDS", 'o'}, - {"SOUNDS LIKE", 'o'}, - {"SPACE", 'f'}, - {"SPATIAL", 'k'}, - {"SPECIFIC", 'k'}, - {"SPLIT_PART", 'f'}, - {"SQL", 'k'}, - {"SQLEXCEPTION", 'k'}, - {"SQLSTATE", 'k'}, - {"SQLWARNING", 'k'}, - {"SQL_BIG_RESULT", 'k'}, - {"SQL_BUFFER_RESULT", 'k'}, - {"SQL_CACHE", 'k'}, - {"SQL_CALC_FOUND_ROWS", 'k'}, - {"SQL_NO_CACHE", 'k'}, - {"SQL_SMALL_RESULT", 'k'}, - {"SQL_VARIANT_PROPERTY", 'f'}, - {"SQRT", 'f'}, - {"SSL", 'k'}, - {"STARTING", 'k'}, - {"STATEMENT_TIMESTAMP", 'f'}, - {"STATS_DATE", 'f'}, - {"STDDEV", 'f'}, - {"STDDEV_POP", 'f'}, - {"STDDEV_SAMP", 'f'}, - {"STRAIGHT_JOIN", 'k'}, - {"STRCMP", 'f'}, - {"STRCOMP", 'f'}, - {"STRCONV", 'f'}, - {"STRING_AGG", 'f'}, - {"STRING_TO_ARRAY", 'f'}, - {"STRPOS", 'f'}, - {"STR_TO_DATE", 'f'}, - {"STUFF", 'f'}, - {"SUBDATE", 'f'}, - {"SUBSTR", 'f'}, - {"SUBSTRING", 'f'}, - {"SUBSTRING_INDEX", 'f'}, - {"SUBTIME", 'f'}, - {"SUM", 'f'}, - {"SUSER_ID", 'f'}, - {"SUSER_NAME", 'f'}, - {"SUSER_SID", 'f'}, - {"SUSER_SNAME", 'f'}, - {"SWITCHOFFET", 'f'}, - {"SYS.DATABASE_NAME", 'n'}, - {"SYS.FN_BUILTIN_PERMISSIONS", 'f'}, - {"SYS.FN_GET_AUDIT_FILE", 'f'}, - {"SYS.FN_MY_PERMISSIONS", 'f'}, - {"SYS.STRAGG", 'f'}, - {"SYSCOLUMNS", 'k'}, - {"SYSDATE", 'f'}, - {"SYSDATETIME", 'f'}, - {"SYSDATETIMEOFFSET", 'f'}, - {"SYSOBJECTS", 'k'}, - {"SYSTEM_USER", 'f'}, - {"SYSUSERS", 'k'}, - {"SYSUTCDATETME", 'f'}, - {"TABLE", 'k'}, - {"TAN", 'f'}, - {"TERMINATED", 'k'}, - {"TERTIARY_WEIGHTS", 'f'}, - {"TEXT", 't'}, - {"TEXTPOS", 'f'}, - {"TEXTPTR", 'f'}, - {"TEXTVALID", 'f'}, - {"THEN", 'k'}, - {"TIME", 'k'}, - {"TIMEDIFF", 'f'}, - {"TIMEFROMPARTS", 'f'}, - {"TIMEOFDAY", 'f'}, - {"TIMESERIAL", 'f'}, - {"TIMESTAMP", 't'}, - {"TIMESTAMPADD", 'f'}, - {"TIMEVALUE", 'f'}, - {"TIME_FORMAT", 'f'}, - {"TIME_TO_SEC", 'f'}, - {"TINYBLOB", 'k'}, - {"TINYINT", 'k'}, - {"TINYTEXT", 'k'}, - {"TODATETIMEOFFSET", 'f'}, - {"TOP", 'k'}, - {"TOTAL", 'f'}, - {"TOTAL_CHANGES", 'f'}, - {"TO_ASCII", 'f'}, - {"TO_CHAR", 'f'}, - {"TO_DATE", 'f'}, - {"TO_DAYS", 'f'}, - {"TO_HEX", 'f'}, - {"TO_NUMBER", 'f'}, - {"TO_SECONDS", 'f'}, - {"TO_TIMESTAMP", 'f'}, - {"TRAILING", 'n'}, - {"TRANSACTION_TIMESTAMP", 'f'}, - {"TRANSLATE", 'f'}, - {"TRIGGER", 'k'}, - {"TRIGGER_NESTLEVEL", 'f'}, - {"TRIM", 'f'}, - {"TRUE", '1'}, - {"TRUNC", 'f'}, - {"TRUNCATE", 'f'}, - {"TRY_CAST", 'f'}, - {"TRY_CONVERT", 'f'}, - {"TRY_PARSE", 'f'}, - {"TYPEOF", 'f'}, - {"TYPEPROPERTY", 'f'}, - {"TYPE_ID", 'f'}, - {"TYPE_NAME", 'f'}, - {"UCASE", 'f'}, - {"UESCAPE", 'o'}, - {"UNCOMPRESS", 'f'}, - {"UNCOMPRESS_LENGTH", 'f'}, - {"UNDO", 'k'}, - {"UNHEX", 'f'}, - {"UNICODE", 'f'}, - {"UNION", 'U'}, - {"UNION ALL", 'U'}, - {"UNION ALL DISTINCT", 'U'}, - {"UNION DISTINCT", 'U'}, - {"UNION DISTINCT ALL", 'U'}, - {"UNIQUE", 'n'}, - {"UNIX_TIMESTAMP", 'f'}, - {"UNI_ON", 'U'}, - {"UNKNOWN", 'v'}, - {"UNLOCK", 'k'}, - {"UNNEST", 'f'}, - {"UNSIGNED", 'k'}, - {"UPDATE", 'E'}, - {"UPDATEXML", 'f'}, - {"UPPER", 'f'}, - {"UPPER_INC", 'f'}, - {"UPPER_INF", 'f'}, - {"USAGE", 'k'}, - {"USE", 'T'}, - {"USER", 'n'}, - {"USER_ID", 'n'}, - {"USER_LOCK.SLEEP", 'f'}, - {"USER_NAME", 'n'}, - {"USING", 'f'}, - {"UTC_DATE", 'k'}, - {"UTC_TIME", 'k'}, - {"UTC_TIMESTAMP", 'k'}, - {"UTL_HTTP.REQUEST", 'f'}, - {"UTL_INADDR.GET_HOST_ADDRESS", 'f'}, - {"UTL_INADDR.GET_HOST_NAME", 'f'}, - {"UUID", 'f'}, - {"UUID_SHORT", 'f'}, - {"VALUES", 'k'}, - {"VAR", 'f'}, - {"VARBINARY", 'k'}, - {"VARCHAR", 't'}, - {"VARCHARACTER", 'k'}, - {"VARIANCE", 'f'}, - {"VARP", 'f'}, - {"VARYING", 'k'}, - {"VAR_POP", 'f'}, - {"VAR_SAMP", 'f'}, - {"VERIFYSIGNEDBYASMKEY", 'f'}, - {"VERIFYSIGNEDBYCERT", 'f'}, - {"VERSION", 'f'}, - {"VOID", 't'}, - {"WAIT", 'k'}, - {"WAITFOR", 'n'}, - {"WAITFOR DELAY", 'E'}, - {"WAITFOR RECEIVE", 'E'}, - {"WAITFOR TIME", 'E'}, - {"WEEK", 'f'}, - {"WEEKDAY", 'f'}, - {"WEEKDAYNAME", 'f'}, - {"WEEKOFYEAR", 'f'}, - {"WHEN", 'k'}, - {"WHERE", 'k'}, - {"WHILE", 'T'}, - {"WIDTH_BUCKET", 'f'}, - {"WITH", 'n'}, - {"WITH ROLLUP", 'k'}, - {"XMLAGG", 'f'}, - {"XMLCOMMENT", 'f'}, - {"XMLCONCAT", 'f'}, - {"XMLELEMENT", 'f'}, - {"XMLEXISTS", 'f'}, - {"XMLFOREST", 'f'}, - {"XMLFORMAT", 'f'}, - {"XMLPI", 'f'}, - {"XMLROOT", 'f'}, - {"XMLTYPE", 'f'}, - {"XML_IS_WELL_FORMED", 'f'}, - {"XOR", '&'}, - {"XPATH", 'f'}, - {"XPATH_EXISTS", 'f'}, - {"XP_EXECRESULTSET", 'k'}, - {"YEAR", 'f'}, - {"YEARWEEK", 'f'}, - {"YEAR_MONTH", 'k'}, - {"ZEROBLOB", 'f'}, - {"ZEROFILL", 'k'}, - {"^=", 'o'}, - {"_ARMSCII8", 't'}, - {"_ASCII", 't'}, - {"_BIG5", 't'}, - {"_BINARY", 't'}, - {"_CP1250", 't'}, - {"_CP1251", 't'}, - {"_CP1257", 't'}, - {"_CP850", 't'}, - {"_CP852", 't'}, - {"_CP866", 't'}, - {"_CP932", 't'}, - {"_DEC8", 't'}, - {"_EUCJPMS", 't'}, - {"_EUCKR", 't'}, - {"_GB2312", 't'}, - {"_GBK", 't'}, - {"_GEOSTD8", 't'}, - {"_GREEK", 't'}, - {"_HEBREW", 't'}, - {"_HP8", 't'}, - {"_KEYBCS2", 't'}, - {"_KOI8R", 't'}, - {"_KOI8U", 't'}, - {"_LATIN1", 't'}, - {"_LATIN2", 't'}, - {"_LATIN5", 't'}, - {"_LATIN7", 't'}, - {"_MACCE", 't'}, - {"_MACROMAN", 't'}, - {"_SJIS", 't'}, - {"_SWE7", 't'}, - {"_TIS620", 't'}, - {"_UJIS", 't'}, - {"_USC2", 't'}, - {"_UTF8", 't'}, - {"|/", 'o'}, - {"|=", 'o'}, - {"||", '&'}, - {"~*", 'o'}, -}; -static const size_t sql_keywords_sz = 9963; -#endif diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c deleted file mode 100644 index 6ad6b1a0ad..0000000000 --- a/apache2/mod_security2.c +++ /dev/null @@ -1,1571 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include - -#include "http_core.h" -#include "http_request.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "http_main.h" -#include "http_connection.h" - -#include "apr_optional.h" -#include "mod_log_config.h" - -#include "msc_logging.h" -#include "msc_util.h" - -#include "ap_mpm.h" -#include "scoreboard.h" - -#include "apr_version.h" - -#if defined(WITH_LUA) -#include "msc_lua.h" -#endif - - -/* ModSecurity structure */ - -msc_engine DSOLOCAL *modsecurity = NULL; - -/* Global module variables; these are used for the Apache-specific functionality */ - -char DSOLOCAL *chroot_dir = NULL; - -char DSOLOCAL *new_server_signature = NULL; - -static char *real_server_signature = NULL; - -char DSOLOCAL *guardianlog_name = NULL; - -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; - -unsigned long int DSOLOCAL conn_read_state_limit = 0; - -unsigned long int DSOLOCAL conn_write_state_limit = 0; - -#if defined(WIN32) || defined(VERSION_NGINX) -int (*modsecDropAction)(request_rec *r) = NULL; -#endif -static int server_limit, thread_limit; - -typedef struct { - int child_num; - int thread_num; -} sb_handle; - -/* -- Miscellaneous functions -- */ - -/** -* \brief Print informations from used libraries -* -* \param mp Pointer to memory pool -*/ -static void version(apr_pool_t *mp) { - char *pcre_vrs = NULL; - - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, - "ModSecurity: APR compiled version=\"%s\"; " - "loaded version=\"%s\"", APR_VERSION_STRING, apr_version_string()); - - if (strstr(apr_version_string(),APR_VERSION_STRING) == NULL) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Loaded APR do not match with compiled!"); - } - - pcre_vrs = apr_psprintf(mp,"%d.%d ", PCRE_MAJOR, PCRE_MINOR); - - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, - "ModSecurity: PCRE compiled version=\"%s\"; " - "loaded version=\"%s\"", pcre_vrs, pcre_version()); - - if (strstr(pcre_version(),pcre_vrs) == NULL) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Loaded PCRE do not match with compiled!"); - } - - /* Lua version function was removed in current 5.1. Need to check in future versions if it's back */ -#if defined(WITH_LUA) - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, - "ModSecurity: LUA compiled version=\"%s\"",LUA_VERSION); -#endif /* WITH_LUA */ - - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, - "ModSecurity: LIBXML compiled version=\"%s\"",LIBXML_DOTTED_VERSION); -} - - -/** - * Intercepts transaction, using the method specified - * in the structure itself. MUST return an HTTP status code, - * which will be used to terminate the transaction. - */ -int perform_interception(modsec_rec *msr) { - msre_actionset *actionset = NULL; - const char *message = NULL; - const char *phase_text = ""; - unsigned int pause = 0; - int status = DECLINED; - int log_level = 1; - - /* Sanity checks first. */ - - if (msr->was_intercepted == 0) { - msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero"); - return DECLINED; - } - - if (msr->phase > 4) { - msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %d.", msr->phase); - msr->was_intercepted = 0; - return DECLINED; - } - - /* OK, we're good to go. */ - - actionset = msr->intercept_actionset; - phase_text = apr_psprintf(msr->mp, " (phase %d)", msr->phase); - - /* By default we log at level 1 but we switch to 4 - * if a nolog action was used or this is not the initial request - * to hide the message. - */ - log_level = (actionset->log != 1) ? 4 : 1; - - /* Pause the request first (if configured and the initial request). */ - if (actionset->intercept_pause != NULL) { - if(strstr(actionset->intercept_pause,"%{") != NULL) { - msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - - var->value = (char *)actionset->intercept_pause; - var->value_len = strlen(actionset->intercept_pause); - expand_macros(msr, var, NULL, msr->mp); - - pause = atoi(var->value); - if ((pause == LONG_MAX)||(pause == LONG_MIN)||(pause <= 0)) - pause = 0; - - msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " - "%d msec.", pause); - /* apr_sleep accepts microseconds */ - apr_sleep((apr_interval_time_t)(pause * 1000)); - } else { - pause = atoi(actionset->intercept_pause); - if ((pause == LONG_MAX)||(pause == LONG_MIN)||(pause <= 0)) - pause = 0; - msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " - "%d msec.", pause); - /* apr_sleep accepts microseconds */ - apr_sleep((apr_interval_time_t)(pause * 1000)); - } - } - - /* Determine how to respond and prepare the log message. */ - switch(actionset->intercept_action) { - case ACTION_DENY : - if (actionset->intercept_status != 0) { - status = actionset->intercept_status; - message = apr_psprintf(msr->mp, "Access denied with code %d%s.", - status, phase_text); - } else { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Internal Error: Invalid status code requested %d).", - phase_text, actionset->intercept_status); - } - break; - - case ACTION_PROXY : - if (msr->phase < 3) { - if (ap_find_linked_module("mod_proxy.c") == NULL) { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Configuration Error: Proxy action to %s requested but mod_proxy not found).", - phase_text, - log_escape_nq(msr->mp, actionset->intercept_uri)); - } else { - msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri); - msr->r->proxyreq = PROXYREQ_REVERSE; - msr->r->handler = "proxy-server"; - status = OK; - message = apr_psprintf(msr->mp, "Access denied using proxy to%s %s.", - phase_text, - log_escape_nq(msr->mp, actionset->intercept_uri)); - } - } else { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Configuration Error: Proxy action requested but it does not work in output phases).", - phase_text); - } - break; - - case ACTION_DROP : - /* ENH This does not seem to work on Windows. Is there a - * better way to drop a connection anyway? - */ - #if !defined(WIN32) && !defined(VERSION_NGINX) - { - extern module core_module; - apr_socket_t *csd = ap_get_module_config(msr->r->connection->conn_config, - &core_module); - - if (csd) { - if (apr_socket_close(csd) == APR_SUCCESS) { - status = HTTP_FORBIDDEN; - message = apr_psprintf(msr->mp, "Access denied with connection close%s.", - phase_text); - } else { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Error: Connection drop requested but failed to close the " - " socket).", - phase_text); - } - } else { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Error: Connection drop requested but socket not found.", - phase_text); - } - } - #else - { - if (modsecDropAction == NULL) { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Error: Connection drop not implemented on this platform.", - phase_text); - } else if (modsecDropAction(msr->r) == 0) { - status = HTTP_FORBIDDEN; - message = apr_psprintf(msr->mp, "Access denied with connection close%s.", - phase_text); - } else { - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Error: Connection drop request failed.", - phase_text); - } - } - #endif - break; - - case ACTION_REDIRECT : - if(strstr(actionset->intercept_uri,"%{") != NULL) { - msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - - var->value = (char *)actionset->intercept_uri; - var->value_len = strlen(actionset->intercept_uri); - expand_macros(msr, var, NULL, msr->mp); - - apr_table_setn(msr->r->headers_out, "Location", var->value); - if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302) - ||(actionset->intercept_status == 303)||(actionset->intercept_status == 307)) - { - status = actionset->intercept_status; - } else { - status = HTTP_MOVED_TEMPORARILY; - } - message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " - "status %d%s.", - log_escape_nq(msr->mp, var->value), status, - phase_text); - } else { - apr_table_setn(msr->r->headers_out, "Location", actionset->intercept_uri); - if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302) - ||(actionset->intercept_status == 303)||(actionset->intercept_status == 307)) - { - status = actionset->intercept_status; - } else { - status = HTTP_MOVED_TEMPORARILY; - } - message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " - "status %d%s.", - log_escape_nq(msr->mp, actionset->intercept_uri), status, - phase_text); - } - break; - - case ACTION_ALLOW : - status = DECLINED; - message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text); - msr->was_intercepted = 0; - msr->allow_scope = ACTION_ALLOW; - break; - - case ACTION_PAUSE : - status = DECLINED; - message = apr_psprintf(msr->mp, "Paused Access%s.", phase_text); - msr->was_intercepted = 0; - msr->allow_scope = ACTION_ALLOW; - break; - - case ACTION_ALLOW_PHASE : - status = DECLINED; - message = apr_psprintf(msr->mp, "Access to phase allowed%s.", phase_text); - msr->was_intercepted = 0; - msr->allow_scope = ACTION_ALLOW_PHASE; - break; - - case ACTION_ALLOW_REQUEST : - status = DECLINED; - message = apr_psprintf(msr->mp, "Access to request allowed%s.", phase_text); - msr->was_intercepted = 0; - msr->allow_scope = ACTION_ALLOW_REQUEST; - break; - - default : - log_level = 1; - status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Internal Error: invalid interception action %d).", - phase_text, actionset->intercept_action); - 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; -} - -/** - * Retrieves a previously stored transaction context by - * looking at the main request, and the previous requests. - */ -static modsec_rec *retrieve_tx_context(request_rec *r) { - modsec_rec *msr = NULL; - request_rec *rx = NULL; - - /* Look in the current request first. */ - msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - - /* If this is a subrequest then look in the main request. */ - if (r->main != NULL) { - msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - } - - /* If the request was redirected then look in the previous requests. */ - rx = r->prev; - while(rx != NULL) { - msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - rx = rx->prev; - } - - return NULL; -} - -/** - * Stores transaction context where it can be found in subsequent - * phases, redirections, or subrequests. - */ -static void store_tx_context(modsec_rec *msr, request_rec *r) { - apr_table_setn(r->notes, NOTE_MSR, (void *)msr); -} - -/** - * Creates a new transaction context. - */ -static modsec_rec *create_tx_context(request_rec *r) { - apr_allocator_t *allocator = NULL; - modsec_rec *msr = NULL; - - msr = (modsec_rec *)apr_pcalloc(r->pool, sizeof(modsec_rec)); - if (msr == NULL) return NULL; - - apr_allocator_create(&allocator); - apr_allocator_max_free_set(allocator, 1024); - apr_pool_create_ex(&msr->mp, r->pool, NULL, allocator); - if (msr->mp == NULL) return NULL; - apr_allocator_owner_set(allocator, msr->mp); - - msr->modsecurity = modsecurity; - msr->r = r; - msr->r_early = r; - msr->request_time = r->request_time; - msr->dcfg1 = (directory_config *)ap_get_module_config(r->per_dir_config, - &security2_module); - -#if defined(WITH_LUA) - #ifdef CACHE_LUA -#if LUA_VERSION_NUM > 501 - msr->L = luaL_newstate(); -#else - msr->L = lua_open(); -#endif - luaL_openlibs(msr->L); - #endif -#endif - - - /** - * Create a special user configuration. This is where - * explicit instructions will be stored. This will be used - * to override the default settings (and to override the - * configuration in the second phase, dcfg2, with the user - * setting executed in the first phase. - */ - msr->usercfg = create_directory_config(msr->mp, NULL); - if (msr->usercfg == NULL) return NULL; - - /* Create a transaction context and populate - * it using the directory config we just - * got from Apache. - */ - msr->txcfg = create_directory_config(msr->mp, NULL); - if (msr->txcfg == NULL) return NULL; - - if (msr->dcfg1 != NULL) { - msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg1); - if (msr->txcfg == NULL) return NULL; - } - init_directory_config(msr->txcfg); - - msr->txid = get_env_var(r, "UNIQUE_ID"); - if (msr->txid == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, - "ModSecurity: ModSecurity requires mod_unique_id to be installed."); - return NULL; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Initialising transaction (txid %s).", msr->txid); - } - - /* Populate tx fields */ - msr->error_messages = apr_array_make(msr->mp, 5, sizeof(error_message_t *)); - msr->alerts = apr_array_make(msr->mp, 5, sizeof(char *)); - - msr->server_software = real_server_signature; - msr->local_addr = r->connection->local_ip; - msr->local_port = r->connection->local_addr->port; - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - msr->remote_addr = r->connection->remote_ip; - msr->remote_port = r->connection->remote_addr->port; -#else - msr->remote_addr = r->connection->client_ip; - msr->remote_port = r->connection->client_addr->port; - msr->useragent_ip = r->useragent_ip; -#endif - - msr->request_line = r->the_request; - msr->request_uri = r->uri; - msr->request_method = r->method; - msr->query_string = r->args; - msr->request_protocol = r->protocol; - msr->request_headers = apr_table_copy(msr->mp, r->headers_in); - msr->hostname = ap_get_server_name(r); - - msr->msc_rule_mptmp = NULL; - - /* Invoke the engine to continue with initialisation */ - if (modsecurity_tx_init(msr) < 0) { - msr_log(msr, 1, "Failed to initialise transaction (txid %s).", msr->txid); - return NULL; - } - - store_tx_context(msr, r); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Transaction context created (dcfg %pp).", msr->dcfg1); - } - - return msr; -} - - -/* -- Hooks -- */ - -/* - * Change the signature of the server if change was requested in - * configuration. We do this by locating the signature in server - * memory and writing over it. - */ -static apr_status_t change_server_signature(server_rec *s) { - char *server_version = NULL; - - if (new_server_signature == NULL) return 0; - - server_version = (char *)apache_get_server_version(); - - if (server_version == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, - "SecServerSignature: Apache returned null as signature."); - return -1; - } - - if (strlen(server_version) >= strlen(new_server_signature)) { - strcpy(server_version, new_server_signature); - } - else { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, - "SecServerSignature: original signature too short. Please set " - "ServerTokens to Full."); - return -1; - } - - /* Check that it really changed. This assumes that what we got was - * not a copy and this could change in future versions of Apache. - */ - server_version = (char *)apache_get_server_version(); - if ((server_version == NULL) || (strcmp(server_version, new_server_signature) != 0)) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, "SecServerSignature: Failed to change server signature to \"%s\".", new_server_signature); - return 0; - } - else { - ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, s, "SecServerSignature: Changed server signature to \"%s\".", server_version); - } - - return 1; -} - -/** - * Executed at the end of server lifetime to cleanup the allocated resources. - */ -static apr_status_t module_cleanup(void *data) { - modsecurity_shutdown(modsecurity); - return APR_SUCCESS; -} - -/** - * Generate a single variable for use with mod_log_config. - */ -static const char *modsec_var_log_handler(request_rec *r, char *name) { - modsec_rec *msr = NULL; - - if (name == NULL) return NULL; - - msr = retrieve_tx_context(r); - if (msr == NULL) return NULL; - - return construct_single_var(msr, name); -} - -/** - * Pre-configuration initialisation hook. - */ -static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) { - static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register; - - /* Initialise ModSecurity engine */ - modsecurity = modsecurity_create(mp, MODSEC_ONLINE); - if (modsecurity == NULL) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, - "ModSecurity: Failed to initialise engine."); - return HTTP_INTERNAL_SERVER_ERROR; - } - - log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); - if (log_pfn_register) { - log_pfn_register(mp, "M", modsec_var_log_handler, 0); - } - - return OK; -} - -/** - * Main (post-configuration) module initialisation. - */ -static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp, server_rec *s) { - void *init_flag = NULL; - int first_time = 0; - - /* ENH Figure out a way to validate config before we start - * - skipafter: need to make sure we found all of our targets - */ - - /* Figure out if we are here for the first time */ - apr_pool_userdata_get(&init_flag, "modsecurity-init-flag", s->process->pool); - if (init_flag == NULL) { - first_time = 1; - apr_pool_userdata_set((const void *)1, "modsecurity-init-flag", - apr_pool_cleanup_null, s->process->pool); - } else { - modsecurity_init(modsecurity, mp); - } - - /* Store the original server signature */ - real_server_signature = apr_pstrdup(mp, apache_get_server_version()); - - /* Make some space in the server signature for later */ - if (new_server_signature != NULL) { - ap_add_version_component(mp, new_server_signature); - change_server_signature(s); - } - -#if (!(defined(WIN32) || defined(NETWARE))) - - /* Internal chroot functionality */ - - if (chroot_dir != NULL) { - - /* ENH Is it safe to simply return with an error, instead - * of using exit()? - */ - - if (first_time == 0) { - ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot checkpoint #2 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid()); - - if (chdir(chroot_dir) < 0) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot failed, unable to chdir to %s, errno=%d (%s)", - chroot_dir, errno, strerror(errno)); - exit(1); - } - - if (chroot(chroot_dir) < 0) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot failed, path=%s, errno=%d(%s)", - chroot_dir, errno, strerror(errno)); - exit(1); - } - - if (chdir("/") < 0) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, - "ModSecurity: chdoot failed, unable to chdir to /, errno=%d (%s)", - errno, strerror(errno)); - exit(1); - } - - ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot successful, path=%s", chroot_dir); - } else { - ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot checkpoint #1 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid()); - } - } -#endif - - /* Schedule main cleanup for later, when the main pool is destroyed. */ - apr_pool_cleanup_register(mp, (void *)s, module_cleanup, apr_pool_cleanup_null); - - /* Log our presence to the error log. */ - if (first_time) { - ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "%s configured.", MODSEC_MODULE_NAME_FULL); - - version(mp); - - /* If we've changed the server signature make note of the original. */ - if (new_server_signature != NULL) { - ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "Original server signature: %s", real_server_signature); - } - } - - srand((unsigned int)(time(NULL) * getpid())); - - return OK; -} - -/** - * Initialisation performed for every new child process. - */ -static void hook_child_init(apr_pool_t *mp, server_rec *s) { - modsecurity_child_init(modsecurity); -} - -/** - * Initial request processing, executed immediatelly after - * Apache receives the request headers. This function wil create - * a transaction context. - */ -static int hook_request_early(request_rec *r) { - modsec_rec *msr = NULL; - int rc = DECLINED; - - /* This function needs to run only once per transaction - * (i.e. subrequests and redirects are excluded). - */ - if ((r->main != NULL)||(r->prev != NULL)) { - return DECLINED; - } - - /* Initialise transaction context and - * create the initial configuration. - */ - msr = create_tx_context(r); - if (msr == NULL) return DECLINED; - -#ifdef REQUEST_EARLY - - /* Are we allowed to continue? */ - if (msr->txcfg->is_enabled == MODSEC_DISABLED) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Processing disabled, skipping (hook request_early)."); - } - return DECLINED; - } - - /* Process phase REQUEST_HEADERS */ - if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) { - rc = perform_interception(msr); - } - - if ( (msr->txcfg->is_enabled != MODSEC_DISABLED) - && (msr->txcfg->reqbody_access == 1) - && (rc == DECLINED)) - { - /* Check request body limit (non-chunked requests only). */ - if (msr->request_content_length > msr->txcfg->reqbody_limit) { - msr_log(msr, 1, "Request body (Content-Length) is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - if(msr->txcfg->is_enabled != MODSEC_DETECTION_ONLY) - return HTTP_REQUEST_ENTITY_TOO_LARGE; - } - } -#endif - return rc; -} - -/** - * Invoked as the first hook in the handler chain, this function - * executes the second phase of ModSecurity request processing. - */ -static int hook_request_late(request_rec *r) { - char *my_error_msg = NULL; - modsec_rec *msr = NULL; - int rc; - - /* This function needs to run only once per transaction - * (i.e. subrequests and redirects are excluded). - */ - if ((r->main != NULL)||(r->prev != NULL)) { - return DECLINED; - } - - /* Find the transaction context and make sure - * we are supposed to proceed. - */ - msr = retrieve_tx_context(r); - if (msr == NULL) { - /* If we can't find the context that probably means it's - * a subrequest that was not initiated from the outside. - */ - return DECLINED; - } - - /* Has this phase been completed already? */ - if (msr->phase_request_body_complete) { - msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once."); - return DECLINED; - } - - msr->phase_request_body_complete = 1; - msr->remote_user = r->user; - - /* Get the second configuration context. */ - msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config, - &security2_module); - - /* Create a transaction context. */ - msr->txcfg = create_directory_config(msr->mp, NULL); - if (msr->txcfg == NULL) return DECLINED; - if (msr->dcfg2 != NULL) { - msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg2); - if (msr->txcfg == NULL) return DECLINED; - } - - /* Update with the explicit user settings. */ - msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->usercfg); - - init_directory_config(msr->txcfg); - - if (msr->txcfg->is_enabled == 0) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Processing disabled, skipping (hook request_late)."); - } - return DECLINED; - } - -#ifndef REQUEST_EARLY - /* Phase 1 */ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "First phase starting (dcfg %pp).", msr->dcfg2); - } - - /* Process phase REQUEST_HEADERS */ - if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) { - /* There was a match; see if we need to intercept. */ - rc = perform_interception(msr); - if (rc != DECLINED) { - /* Intercepted */ - return rc; - } - } -#endif - - /* The rule engine could have been disabled in phase 1. */ - if (msr->txcfg->is_enabled == MODSEC_DISABLED) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping phase 2 as the rule engine was disabled by a rule in phase 1."); - } - - return DECLINED; - } - - /* Phase 2 */ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2); - } - - /* Check that the request body is not too long, but only - * if configuration allows for request body access. - */ - msr->inbound_error = 0; - if (msr->txcfg->reqbody_access == 1) { - /* Check request body limit (non-chunked requests only). */ - if (msr->request_content_length > msr->txcfg->reqbody_limit) { - - if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { - msr->inbound_error = 1; - msr_log(msr, 1, "Request body (Content-Length) is larger than the " - "configured limit (%ld). Deny with status (%d)", msr->txcfg->reqbody_limit, HTTP_REQUEST_ENTITY_TOO_LARGE); - return HTTP_REQUEST_ENTITY_TOO_LARGE; - } else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){ - msr->inbound_error = 1; - msr_log(msr, 1, "Request body (Content-Length) is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - } else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){ - msr_log(msr, 1, "Request body (Content-Length) is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - msr->inbound_error = 1; - } else { - msr_log(msr, 1, "Request body (Content-Length) is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - msr->inbound_error = 1; - } - } - } - - /* Figure out whether to extract multipart files. */ - if ((msr->txcfg->upload_keep_files != KEEP_FILES_OFF) /* user might want to keep them */ - || (msr->txcfg->upload_validates_files)) /* user might want to validate them */ - { - msr->upload_extract_files = 1; - msr->upload_remove_files = 1; - } - - rc = read_request_body(msr, &my_error_msg); - if (rc < 0) { - switch(rc) { - case -1 : - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - return HTTP_INTERNAL_SERVER_ERROR; - break; - case -4 : /* Timeout. */ - if (my_error_msg != NULL) { - msr_log(msr, 4, "%s", my_error_msg); - } - r->connection->keepalive = AP_CONN_CLOSE; - return HTTP_REQUEST_TIME_OUT; - break; - case -5 : /* Request body limit reached. */ - msr->inbound_error = 1; - if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { - r->connection->keepalive = AP_CONN_CLOSE; - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s. Deny with code (%d)", my_error_msg, HTTP_REQUEST_ENTITY_TOO_LARGE); - } - return HTTP_REQUEST_ENTITY_TOO_LARGE; - } else { - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - } - break; - case -6 : /* EOF when reading request body. */ - if (my_error_msg != NULL) { - msr_log(msr, 4, "%s", my_error_msg); - } - r->connection->keepalive = AP_CONN_CLOSE; - return HTTP_BAD_REQUEST; - break; - default : - /* allow through */ - break; - } - - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - } - - /* Update the request headers. They might have changed after - * the body was read (trailers). - * - * TODO We might still want to hold onto the original headers - * so that we can log them. Keeping them is probably not - * going to increase our memory requirements (because all - * headers are allocated from the request memory pool - * anyway). - */ - msr->request_headers = apr_table_copy(msr->mp, r->headers_in); - - /* Process phase REQUEST_BODY */ - rc = DECLINED; - if (modsecurity_process_phase(msr, PHASE_REQUEST_BODY) > 0) { - rc = perform_interception(msr); - } - - if(msr->txcfg->stream_inbody_inspection && msr->msc_reqbody_read) { - const char *clen = NULL; - clen = apr_psprintf(msr->mp,"%"APR_SIZE_T_FMT,msr->stream_input_length); - if(clen) - apr_table_setn(r->headers_in, "Content-Length",clen); - } - - /* Remove the compression ability indications the client set, - * but only if we need to disable backend compression. - */ - if (msr->txcfg->disable_backend_compression) { - apr_table_unset(r->headers_in, "Accept-Encoding"); - apr_table_unset(r->headers_in, "TE"); - } - - return rc; -} - -/** - * Invoked every time Apache has something to write to the error log. - */ -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 -static void hook_error_log(const ap_errorlog_info *info, const char *errstr) -#else -static void hook_error_log(const char *file, int line, int level, apr_status_t status, - const server_rec *s, const request_rec *r, apr_pool_t *mp, const char *fmt) -#endif -{ - modsec_rec *msr = NULL; - error_message_t *em = NULL; - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - if (info == NULL) return; - if (info->r == NULL) return; -#else - if (r == NULL) return; -#endif - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - msr = retrieve_tx_context((request_rec *)info->r); -#else - msr = retrieve_tx_context((request_rec *)r); -#endif - - /* Create a context for requests we never had the chance to process */ -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - if ((msr == NULL) - && ((info->level & APLOG_LEVELMASK) < APLOG_DEBUG) - && apr_table_get(info->r->subprocess_env, "UNIQUE_ID")) -#else - if ((msr == NULL) - && ((level & APLOG_LEVELMASK) < APLOG_DEBUG) - && apr_table_get(r->subprocess_env, "UNIQUE_ID")) -#endif - { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - msr = create_tx_context((request_rec *)info->r); -#else - msr = create_tx_context((request_rec *)r); -#endif - if (msr->txcfg->debuglog_level >= 9) { - if (msr == NULL) { - msr_log(msr, 9, "Failed to create context after request failure."); - } - else { - msr_log(msr, 9, "Context created after request failure."); - } - } - } - if (msr == NULL) return; - - /* Store the error message for later */ - em = (error_message_t *)apr_pcalloc(msr->mp, sizeof(error_message_t)); - if (em == NULL) return; - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - if (info->file != NULL) em->file = apr_pstrdup(msr->mp, info->file); - em->line = info->line; - em->level = info->level; - em->status = info->status; - if (info->format != NULL) em->message = apr_pstrdup(msr->mp, info->format); -#else - if (file != NULL) em->file = apr_pstrdup(msr->mp, file); - em->line = line; - em->level = level; - em->status = status; - if (fmt != NULL) em->message = apr_pstrdup(msr->mp, fmt); -#endif - /* Remove \n from the end of the message */ - if (em->message != NULL) { - char *p = (char *)em->message; - while(*p != '\0') { - if ((*(p + 1) == '\0')&&(*p == '\n')) { - *p = '\0'; - break; - } - p++; - } - } - - *(const error_message_t **)apr_array_push(msr->error_messages) = em; -} - - -/** - * Guardian logger is used to interface to the external - * script for web server protection - httpd_guardian. - */ -static void sec_guardian_logger(request_rec *r, request_rec *origr, modsec_rec *msr) { - char *str1, *str2, *text; - char *modsec_message = "-"; - int modsec_rating = 0; /* not used yet */ - apr_size_t nbytes, nbytes_written; - apr_time_t duration = (apr_time_now() - origr->request_time); - int limit, was_limited; - - /* bail out if we do not have where to write */ - if ((guardianlog_name == NULL)||(guardianlog_fd == NULL)) return; - - /* process the condition, if we have one */ - if (guardianlog_condition != NULL) { - if (*guardianlog_condition == '!') { - if (apr_table_get(r->subprocess_env, guardianlog_condition + 1) != NULL) { - return; - } - } - else { - if (apr_table_get(r->subprocess_env, guardianlog_condition) == NULL) { - return; - } - } - } - - /* - * Log format is as follows: - * - * %V %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %{UNIQUE_ID}e - * "SESSION_ID" %T %D "MODSEC_MESSAGE" MODSEC_RATING - * - * The fields SESSION_ID, MODSEC_MESSAGE, and MODSEC_RATING are not used at the moment. - */ - - str2 = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT " %" APR_TIME_T_FMT " \"%s\" %d", - duration, apr_time_sec(duration), log_escape(msr->mp, modsec_message), modsec_rating); - if (str2 == NULL) return; - - /* We do not want the index line to be longer than 3980 bytes. */ - limit = 3980; - was_limited = 0; - - /* If we are logging to a pipe we need to observe and - * obey the pipe atomic write limit - PIPE_BUF. For - * more details see the discussion in sec_guardian_logger, - * above. - */ - if (msr->txcfg->auditlog_name[0] == '|') { - if (PIPE_BUF < limit) { - limit = PIPE_BUF; - } - } - - limit = limit - strlen(str2) - 5; - if (limit <= 0) { - msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF); - return; - } - - str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited); - if (str1 == NULL) return; - - if (was_limited == 0) { - text = apr_psprintf(msr->mp, "%s %s \n", str1, str2); - } else { - text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2); - } - if (text == NULL) return; - - nbytes = strlen(text); - apr_file_write_full(guardianlog_fd, text, nbytes, &nbytes_written); -} - -/** - * Invoked at the end of each transaction. - */ -static int hook_log_transaction(request_rec *r) { - const apr_array_header_t *arr = NULL; - request_rec *origr = NULL; - modsec_rec *msr = NULL; - - msr = retrieve_tx_context(r); - if (msr == NULL) { - return DECLINED; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Initialising logging."); - } - - /* Find the first (origr) and the last (r) request */ - origr = r; - while(origr->prev) { - origr = origr->prev; - } - while(r->next) { - r = r->next; - } - - /* At this point r is the last request in the - * chain. However, we now need to detect a case when - * a bad ErrorDocument was used and back out of it. That's - * how Apache does it internally. Except where Apache knows - * exactly what is happening we will have to rely on the missing - * headers in the final request to detect this condition. - */ - arr = apr_table_elts(r->headers_out); - while ((arr->nelts == 0)&&(r->prev != NULL)) { - r = r->prev; - arr = apr_table_elts(r->headers_out); - } - - msr->r = r; - msr->response_status = r->status; - msr->status_line = ((r->status_line != NULL) - ? r->status_line : ap_get_status_line(r->status)); - msr->response_protocol = get_response_protocol(origr); - msr->response_headers = apr_table_copy(msr->mp, r->headers_out); - if (!r->assbackwards) msr->response_headers_sent = 1; - msr->bytes_sent = r->bytes_sent; - msr->local_user = r->user; - msr->remote_user = r->connection->remote_logname; - - /* -- Guardian -- */ - - sec_guardian_logger(r, origr, msr); - - /* Invoke the engine to do the rest of the work now. */ - modsecurity_process_phase(msr, PHASE_LOGGING); - - return DECLINED; -} - -/** - * Invoked right before request processing begins. This is - * when we need to decide if we want to hook into the output - * filter chain. - */ -static void hook_insert_filter(request_rec *r) { - modsec_rec *msr = NULL; - - /* Find the transaction context first. */ - msr = retrieve_tx_context(r); - if (msr == NULL) return; - - /* Add the input filter, but only if we need it to run. */ - if (msr->if_status == IF_STATUS_WANTS_TO_RUN) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %pp).", - (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); - } - - ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); - } - - /* The output filters only need to be added only once per transaction - * (i.e. subrequests and redirects are excluded). - */ - if ((r->main != NULL)||(r->prev != NULL)) { - return; - } - - /* Only proceed to add the second filter if the engine is enabled. */ - if (msr->txcfg->is_enabled == 0) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping."); - } - - return; - } - - /* We always add the output filter because that's where we need to - * initiate our 3rd and 4th processing phases from. The filter is - * smart enough not to buffer the data if it is not supposed to. - */ - if (msr->of_status != OF_STATUS_COMPLETE) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %pp).", r); - } - - ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); - } -} - -// TODO: Holding off on this for now (needs more testing) -/** - * Invoked whenever Apache starts processing an error. A chance - * to insert ourselves into the output filter chain. - */ -static void hook_insert_error_filter(request_rec *r) { - modsec_rec *msr = NULL; - - /* Find the transaction context and make sure we are - * supposed to proceed. - */ - msr = retrieve_tx_context(r); - if (msr == NULL) return; - - /* Do not run if not enabled. */ - if (msr->txcfg->is_enabled == 0) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_error_filter: Processing disabled, skipping."); - } - return; - } - - /* Do not run if the output filter already completed. This will - * happen if we intercept in phase 4. - */ - if (msr->of_status != OF_STATUS_COMPLETE) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %pp).", r); - } - - /* Make a note that the output we will be receiving is a - * result of error processing. - */ - msr->of_is_error = 1; - - ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_error_filter: Output buffering already complete."); - } - } -} - -#if (!defined(NO_MODSEC_API)) -/** - * This function is exported for other Apache modules to - * register new transformation functions. - */ -static void modsec_register_tfn(const char *name, void *fn) { - if (modsecurity != NULL) { - msre_engine_tfn_register(modsecurity->msre, name, (fn_tfn_execute_t)fn); - } -} - -/** - * This function is exported for other Apache modules to - * register new operators. - */ -static void modsec_register_operator(const char *name, void *fn_init, void *fn_exec) { - if (modsecurity != NULL) { - msre_engine_op_register(modsecurity->msre, name, (fn_op_param_init_t)fn_init, (fn_op_execute_t)fn_exec); - } -} - -/** - * \brief Connetion hook to limit the number of - * connections in BUSY state - * - * \param conn Pointer to connection struct - * - * \retval DECLINED On failure - * \retval OK On Success - */ -static int hook_connection_early(conn_rec *conn) -{ - sb_handle *sb = conn->sbh; - int i, j; - unsigned long int ip_count = 0, ip_count_w = 0; - worker_score *ws_record = NULL; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - ap_sb_handle_t *sbh = NULL; -#endif - - if(sb != NULL && (conn_read_state_limit > 0 || conn_write_state_limit > 0)) { - - ws_record = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num]; - if(ws_record == NULL) - return DECLINED; - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - apr_cpystrn(ws_record->client, conn->client_ip, sizeof(ws_record->client)); -#else - apr_cpystrn(ws_record->client, conn->remote_ip, sizeof(ws_record->client)); -#endif - for (i = 0; i < server_limit; ++i) { - for (j = 0; j < thread_limit; ++j) { - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - sbh = conn->sbh; - if (sbh == NULL) { - return DECLINED; - } - - ws_record = ap_get_scoreboard_worker(sbh); -#else - ws_record = ap_get_scoreboard_worker(i, j); -#endif - - if(ws_record == NULL) - return DECLINED; - - switch (ws_record->status) { - case SERVER_BUSY_READ: -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - if (strcmp(conn->client_ip, ws_record->client) == 0) - ip_count++; -#else - if (strcmp(conn->remote_ip, ws_record->client) == 0) - ip_count++; -#endif - break; - case SERVER_BUSY_WRITE: -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - if (strcmp(conn->client_ip, ws_record->client) == 0) - ip_count_w++; -#else - if (strcmp(conn->remote_ip, ws_record->client) == 0) - ip_count_w++; -#endif - break; - default: - break; - } - } - } - - if ((conn_read_state_limit > 0) && (ip_count > conn_read_state_limit)) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Access denied with code 400. Too many threads [%ld] of %ld allowed in READ state from %s - Possible DoS Consumption Attack [Rejected]", ip_count,conn_read_state_limit,conn->client_ip); -#else - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Access denied with code 400. Too many threads [%ld] of %ld allowed in READ state from %s - Possible DoS Consumption Attack [Rejected]", ip_count,conn_read_state_limit,conn->remote_ip); -#endif - return OK; - } else if ((conn_write_state_limit > 0) && (ip_count_w > conn_write_state_limit)) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Access denied with code 400. Too many threads [%ld] of %ld allowed in WRITE state from %s - Possible DoS Consumption Attack [Rejected]", ip_count_w,conn_write_state_limit,conn->client_ip); -#else - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Access denied with code 400. Too many threads [%ld] of %ld allowed in WRITE state from %s - Possible DoS Consumption Attack [Rejected]", ip_count_w,conn_write_state_limit,conn->remote_ip); -#endif - return OK; - } else { - return DECLINED; - } - } - - return DECLINED; -} - -/** - * This function is exported for other Apache modules to - * register new variables. - */ -static void modsec_register_variable(const char *name, unsigned int type, - unsigned int argc_min, unsigned int argc_max, - void *fn_validate, void *fn_generate, - unsigned int is_cacheable, unsigned int availability) { - if (modsecurity != NULL) { - msre_engine_variable_register(modsecurity->msre, name, type, argc_min, argc_max, (fn_var_validate_t)fn_validate, (fn_var_generate_t)fn_generate, is_cacheable, availability); - } - else { - fprintf(stderr,"modsecurity is NULL\n"); - } -} - -/** - * This function is exported for other Apache modules to - * register new request body processors. - */ -static void modsec_register_reqbody_processor(const char *name, - void *fn_init, - void *fn_process, - void *fn_complete) -{ - if (modsecurity != NULL) { - - msre_engine_reqbody_processor_register(modsecurity->msre, name, - (fn_reqbody_processor_init_t)fn_init, - (fn_reqbody_processor_init_t)fn_process, - (fn_reqbody_processor_init_t)fn_complete); - } -} - -#endif - -/** - * Registers module hooks with Apache. - */ -static void register_hooks(apr_pool_t *mp) { - static const char *const postconfig_beforeme_list[] = { - "mod_unique_id.c", - "mod_ssl.c", - NULL - }; - - static const char *const postconfig_afterme_list[] = { - "mod_fcgid.c", - "mod_cgid.c", - NULL - }; - - static const char *const postread_beforeme_list[] = { - "mod_rpaf.c", - "mod_rpaf-2.0.c", - "mod_extract_forwarded2.c", - "mod_remoteip.c", - "mod_custom_header.c", - "mod_breach_realip.c", - "mod_breach_trans.c", - "mod_unique_id.c", - NULL - }; - - static const char *const postread_afterme_list[] = { - "mod_log_forensic.c", - NULL - }; - - static const char *const transaction_afterme_list[] = { - "mod_log_config.c", - NULL - }; - - static const char *const fixups_beforeme_list[] = { - "mod_env.c", - NULL - }; - - /* Add the MODSEC_2.x compatibility defines */ - *(char **)apr_array_push(ap_server_config_defines) = apr_pstrdup(mp, "MODSEC_2.5"); - - /* Add the MODSEC_a.b define */ - *(char **)apr_array_push(ap_server_config_defines) = apr_psprintf(mp, "MODSEC_%s.%s", MODSEC_VERSION_MAJOR, MODSEC_VERSION_MINOR); - -#if (!defined(NO_MODSEC_API)) - /* Export optional functions. */ - APR_REGISTER_OPTIONAL_FN(modsec_register_tfn); - APR_REGISTER_OPTIONAL_FN(modsec_register_operator); - APR_REGISTER_OPTIONAL_FN(modsec_register_variable); - APR_REGISTER_OPTIONAL_FN(modsec_register_reqbody_processor); -#endif - - /* For connection level hook */ - ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); - ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); - - /* Main hooks */ - ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST); - ap_hook_post_config(hook_post_config, postconfig_beforeme_list, - postconfig_afterme_list, APR_HOOK_REALLY_LAST); - ap_hook_child_init(hook_child_init, NULL, NULL, APR_HOOK_MIDDLE); - - /* Our own hook to handle RPC transactions (not used at the moment). - * // ap_hook_handler(hook_handler, NULL, NULL, APR_HOOK_MIDDLE); - */ - - /* Connection processing hooks */ - ap_hook_process_connection(hook_connection_early, NULL, NULL, APR_HOOK_FIRST); - - /* Transaction processing hooks */ - ap_hook_post_read_request(hook_request_early, - postread_beforeme_list, postread_afterme_list, APR_HOOK_REALLY_FIRST); - - ap_hook_fixups(hook_request_late, fixups_beforeme_list, NULL, APR_HOOK_REALLY_FIRST); - - /* Logging */ - ap_hook_error_log(hook_error_log, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_log_transaction(hook_log_transaction, NULL, transaction_afterme_list, APR_HOOK_MIDDLE); - - /* Filter hooks */ - ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST); - - ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); - - ap_register_input_filter("MODSECURITY_IN", input_filter, - NULL, AP_FTYPE_CONTENT_SET); - - /* Ensure that the output filter runs before other modules so that - * we get a request that has a better chance of not being modified: - * - * Currently: - * mod_expires = -2 - * mod_cache = -1 - * mod_deflate = -1 - * mod_headers = 0 - */ - - ap_register_output_filter("MODSECURITY_OUT", output_filter, - NULL, AP_FTYPE_CONTENT_SET - 3); - -} - -/* Defined in apache2_config.c */ -extern const command_rec module_directives[]; - -/* Module entry points */ -module AP_MODULE_DECLARE_DATA security2_module = { - STANDARD20_MODULE_STUFF, - create_directory_config, - merge_directory_configs, - NULL, /* create_server_config */ - NULL, /* merge_server_configs */ - module_directives, - register_hooks -}; diff --git a/apache2/mod_security2_config.hw b/apache2/mod_security2_config.hw deleted file mode 100644 index c95004462f..0000000000 --- a/apache2/mod_security2_config.hw +++ /dev/null @@ -1 +0,0 @@ -/* This file is left empty for building on Windows. */ diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c deleted file mode 100644 index b36775d223..0000000000 --- a/apache2/modsecurity.c +++ /dev/null @@ -1,802 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include - -#include "apr_global_mutex.h" - -#include "modsecurity.h" -#include "msc_parsers.h" -#include "msc_util.h" -#include "msc_xml.h" -#include "apr_version.h" - -unsigned long int DSOLOCAL unicode_codepage = 0; - -int DSOLOCAL *unicode_map_table = NULL; - -/** - * Format an alert message. - */ -const char * msc_alert_message(modsec_rec *msr, msre_actionset *actionset, const char *action_message, - const char *rule_message) -{ - const char *message = NULL; - - if (rule_message == NULL) rule_message = "Unknown error."; - - if (action_message == NULL) { - message = apr_psprintf(msr->mp, "%s%s", - rule_message, msre_format_metadata(msr, actionset)); - } - else { - message = apr_psprintf(msr->mp, "%s %s%s", action_message, - rule_message, msre_format_metadata(msr, actionset)); - } - - return message; -} - -/** - * Log an alert message to the log, adding the rule metadata at the end. - */ -void msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message, - const char *rule_message) -{ - const char *message = msc_alert_message(msr, actionset, action_message, rule_message); - - msr_log(msr, level, "%s", message); -} - -#if 0 -/** - * Return phase name associated with the given phase number. - */ -static const char *phase_name(int phase) { - switch(phase) { - case 1 : - return "REQUEST_HEADERS"; - break; - case 2 : - return "REQUEST_BODY"; - break; - case 3 : - return "RESPONSE_HEADERS"; - break; - case 4 : - return "RESPONSE_BODY"; - break; - case 5 : - return "LOGGING"; - break; - } - - return "INVALID"; -} -#endif - -/** - * Creates and initialises a ModSecurity engine instance. - */ -msc_engine *modsecurity_create(apr_pool_t *mp, int processing_mode) { - msc_engine *msce = NULL; - - msce = apr_pcalloc(mp, sizeof(msc_engine)); - if (msce == NULL) return NULL; - - msce->mp = mp; - msce->processing_mode = processing_mode; - - msce->msre = msre_engine_create(msce->mp); - if (msce->msre == NULL) return NULL; - msre_engine_register_default_variables(msce->msre); - msre_engine_register_default_operators(msce->msre); - msre_engine_register_default_tfns(msce->msre); - msre_engine_register_default_actions(msce->msre); - // TODO: msre_engine_register_default_reqbody_processors(msce->msre); - - return msce; -} - -/** - * Initialise the modsecurity engine. This function must be invoked - * after configuration processing is complete as Apache needs to know the - * username it is running as. - */ -int modsecurity_init(msc_engine *msce, apr_pool_t *mp) { - apr_status_t rc; - - /* Serial audit log mutext */ - rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp); - if (rc != APR_SUCCESS) { - //ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mod_security: Could not create modsec_auditlog_lock"); - //return HTTP_INTERNAL_SERVER_ERROR; - return -1; - } - -#if !defined(MSC_TEST) -#ifdef __SET_MUTEX_PERMS -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - rc = ap_unixd_set_global_mutex_perms(msce->auditlog_lock); -#else - rc = unixd_set_global_mutex_perms(msce->auditlog_lock); -#endif - if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, "mod_security: Could not set permissions on modsec_auditlog_lock; check User and Group directives"); - // return HTTP_INTERNAL_SERVER_ERROR; - return -1; - } -#endif /* SET_MUTEX_PERMS */ - - rc = apr_global_mutex_create(&msce->geo_lock, NULL, APR_LOCK_DEFAULT, mp); - if (rc != APR_SUCCESS) { - return -1; - } - -#ifdef __SET_MUTEX_PERMS -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - rc = ap_unixd_set_global_mutex_perms(msce->geo_lock); -#else - rc = unixd_set_global_mutex_perms(msce->geo_lock); -#endif - if (rc != APR_SUCCESS) { - return -1; - } -#endif /* SET_MUTEX_PERMS */ -#endif - - return 1; -} - -/** - * Performs per-child (new process) initialisation. - */ -void modsecurity_child_init(msc_engine *msce) { - /* Need to call this once per process before any other XML calls. */ - xmlInitParser(); - - if (msce->auditlog_lock != NULL) { - apr_status_t rc = apr_global_mutex_child_init(&msce->auditlog_lock, NULL, msce->mp); - if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init auditlog mutex"); - } - } - - if (msce->geo_lock != NULL) { - apr_status_t rc = apr_global_mutex_child_init(&msce->geo_lock, NULL, msce->mp); - if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init geo mutex"); - } - } - -} - -/** - * Releases resources held by engine instance. - */ -void modsecurity_shutdown(msc_engine *msce) { - if (msce == NULL) return; -} - -/** - * - */ -static void modsecurity_persist_data(modsec_rec *msr) { - const apr_array_header_t *arr; - apr_table_entry_t *te; - apr_time_t time_before, time_after; - int i; - - time_before = apr_time_now(); - - /* Collections, store & remove stale. */ - arr = apr_table_elts(msr->collections); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - apr_table_t *col = (apr_table_t *)te[i].val; - - /* Only store those collections that changed. */ - if (apr_table_get(msr->collections_dirty, te[i].key)) { - collection_store(msr, col); - } - } - - time_after = apr_time_now(); - - msr->time_storage_write += time_after - time_before; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Recording persistent data took %" APR_TIME_T_FMT - " microseconds.", msr->time_gc); - } - - /* Remove stale collections. */ - srand(time(NULL)); - - if (rand() < RAND_MAX/100) { - arr = apr_table_elts(msr->collections); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - collections_remove_stale(msr, te[i].key); - } - - msr->time_gc = apr_time_now() - time_after; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Garbage collection took %" APR_TIME_T_FMT - " microseconds.", msr->time_gc); - } - } -} - -/** - * - */ -static apr_status_t modsecurity_tx_cleanup(void *data) { - modsec_rec *msr = (modsec_rec *)data; - char *my_error_msg = NULL; - - if (msr == NULL) return APR_SUCCESS; - - /* Multipart processor cleanup. */ - if (msr->mpd != NULL) multipart_cleanup(msr); - - /* XML processor cleanup. */ - if (msr->xml != NULL) xml_cleanup(msr); - - // TODO: Why do we ignore return code here? - modsecurity_request_body_clear(msr, &my_error_msg); - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - -#if defined(WITH_LUA) - #ifdef CACHE_LUA - if(msr->L != NULL) lua_close(msr->L); - #endif -#endif - - return APR_SUCCESS; -} - -/** - * - */ -apr_status_t modsecurity_tx_init(modsec_rec *msr) { - const char *s = NULL; - const apr_array_header_t *arr; - char *semicolon = NULL; - char *comma = NULL; - apr_table_entry_t *te; - int i; - - /* Register TX cleanup */ - apr_pool_cleanup_register(msr->mp, msr, modsecurity_tx_cleanup, apr_pool_cleanup_null); - - /* Initialise C-L */ - msr->request_content_length = -1; - s = apr_table_get(msr->request_headers, "Content-Length"); - if (s != NULL) { - msr->request_content_length = strtol(s, NULL, 10); - } - - /* Figure out whether this request has a body */ - msr->reqbody_chunked = 0; - msr->reqbody_should_exist = 0; - if (msr->request_content_length == -1) { - /* There's no C-L, but is chunked encoding used? */ - char *transfer_encoding = (char *)apr_table_get(msr->request_headers, "Transfer-Encoding"); - if ((transfer_encoding != NULL)&&(m_strcasestr(transfer_encoding, "chunked") != NULL)) { - msr->reqbody_should_exist = 1; - msr->reqbody_chunked = 1; - } - } else { - /* C-L found */ - msr->reqbody_should_exist = 1; - } - - /* Initialise C-T */ - msr->request_content_type = NULL; - s = apr_table_get(msr->request_headers, "Content-Type"); - if (s != NULL) msr->request_content_type = s; - - /* Decide what to do with the request body. */ - if ((msr->request_content_type != NULL) - && (strncasecmp(msr->request_content_type, "application/x-www-form-urlencoded", 33) == 0)) - { - /* Always place POST requests with - * "application/x-www-form-urlencoded" payloads in memory. - */ - msr->msc_reqbody_storage = MSC_REQBODY_MEMORY; - msr->msc_reqbody_spilltodisk = 0; - msr->msc_reqbody_processor = "URLENCODED"; - } else { - /* If the C-L is known and there's more data than - * our limit go to disk straight away. - */ - if ((msr->request_content_length != -1) - && (msr->request_content_length > msr->txcfg->reqbody_inmemory_limit)) - { - msr->msc_reqbody_storage = MSC_REQBODY_DISK; - } - - /* In all other cases, try using the memory first - * but switch over to disk for larger bodies. - */ - msr->msc_reqbody_storage = MSC_REQBODY_MEMORY; - msr->msc_reqbody_spilltodisk = 1; - - if (msr->request_content_type != NULL) { - if (strncasecmp(msr->request_content_type, "multipart/form-data", 19) == 0) { - msr->msc_reqbody_processor = "MULTIPART"; - } - } - } - - /* Check if we are forcing buffering, then use memory only. */ - if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) { - msr->msc_reqbody_storage = MSC_REQBODY_MEMORY; - msr->msc_reqbody_spilltodisk = 0; - } - - /* Initialise arguments */ - msr->arguments = apr_table_make(msr->mp, 32); - if (msr->arguments == NULL) return -1; - if (msr->query_string != NULL) { - int invalid_count = 0; - - if (parse_arguments(msr, msr->query_string, strlen(msr->query_string), - msr->txcfg->argument_separator, "QUERY_STRING", msr->arguments, - &invalid_count) < 0) - { - msr_log(msr, 1, "Initialisation: Error occurred while parsing QUERY_STRING arguments."); - return -1; - } - - if (invalid_count) { - msr->urlencoded_error = 1; - } - } - - msr->arguments_to_sanitize = apr_table_make(msr->mp, 16); - if (msr->arguments_to_sanitize == NULL) return -1; - msr->request_headers_to_sanitize = apr_table_make(msr->mp, 16); - if (msr->request_headers_to_sanitize == NULL) return -1; - msr->response_headers_to_sanitize = apr_table_make(msr->mp, 16); - if (msr->response_headers_to_sanitize == NULL) return -1; - msr->pattern_to_sanitize = apr_table_make(msr->mp, 32); - if (msr->pattern_to_sanitize == NULL) return -1; - - /* remove targets */ - msr->removed_targets = apr_table_make(msr->mp, 16); - if (msr->removed_targets == NULL) return -1; - - /* Initialise cookies */ - msr->request_cookies = apr_table_make(msr->mp, 16); - if (msr->request_cookies == NULL) return -1; - - /* Initialize matched vars */ - msr->matched_vars = apr_table_make(msr->mp, 8); - if (msr->matched_vars == NULL) return -1; - apr_table_clear(msr->matched_vars); - - msr->perf_rules = apr_table_make(msr->mp, 8); - if (msr->perf_rules == NULL) return -1; - apr_table_clear(msr->perf_rules); - - /* Locate the cookie headers and parse them */ - arr = apr_table_elts(msr->request_headers); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - if (strcasecmp(te[i].key, "Cookie") == 0) { - if (msr->txcfg->cookie_format == COOKIES_V0) { - semicolon = apr_pstrdup(msr->mp, te[i].val); - while((*semicolon != 0)&&(*semicolon != ';')) semicolon++; - if(*semicolon == ';') { - parse_cookies_v0(msr, te[i].val, msr->request_cookies, ";"); - } else { - comma = apr_pstrdup(msr->mp, te[i].val); - while((*comma != 0)&&(*comma != ',')) comma++; - if(*comma == ',') { - comma++; - if(*comma == 0x20) {// looks like comma is the separator - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "Cookie v0 parser: Using comma as a separator. Semi-colon was not identified!"); - } - parse_cookies_v0(msr, te[i].val, msr->request_cookies, ","); - } else { - parse_cookies_v0(msr, te[i].val, msr->request_cookies, ";"); - } - } else { - parse_cookies_v0(msr, te[i].val, msr->request_cookies, ";"); - } - } - } else { - parse_cookies_v1(msr, te[i].val, msr->request_cookies); - } - } - } - - /* Collections. */ - msr->tx_vars = apr_table_make(msr->mp, 32); - if (msr->tx_vars == NULL) return -1; - - msr->geo_vars = apr_table_make(msr->mp, 8); - if (msr->geo_vars == NULL) return -1; - - msr->collections_original = apr_table_make(msr->mp, 8); - if (msr->collections_original == NULL) return -1; - msr->collections = apr_table_make(msr->mp, 8); - if (msr->collections == NULL) return -1; - msr->collections_dirty = apr_table_make(msr->mp, 8); - if (msr->collections_dirty == NULL) return -1; - - /* Other */ - msr->tcache = NULL; - msr->tcache_items = 0; - - msr->matched_rules = apr_array_make(msr->mp, 16, sizeof(void *)); - if (msr->matched_rules == NULL) return -1; - - msr->matched_var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - if (msr->matched_var == NULL) return -1; - - msr->highest_severity = 255; /* high, invalid value */ - - msr->removed_rules = apr_array_make(msr->mp, 16, sizeof(char *)); - if (msr->removed_rules == NULL) return -1; - - msr->removed_rules_tag = apr_array_make(msr->mp, 16, sizeof(char *)); - if (msr->removed_rules_tag == NULL) return -1; - - msr->removed_rules_msg = apr_array_make(msr->mp, 16, sizeof(char *)); - if (msr->removed_rules_msg == NULL) return -1; - - return 1; -} - -/** - * - */ -static int is_response_status_relevant(modsec_rec *msr, int status) { - char *my_error_msg = NULL; - apr_status_t rc; - char buf[32]; - - /* ENH: Setting is_relevant here will cause an audit even if noauditlog - * was set for the last rule that matched. Is this what we want? - */ - - if ((msr->txcfg->auditlog_relevant_regex == NULL) - ||(msr->txcfg->auditlog_relevant_regex == NOT_SET_P)) - { - return 0; - } - - apr_snprintf(buf, sizeof(buf), "%d", status); - - rc = msc_regexec(msr->txcfg->auditlog_relevant_regex, buf, strlen(buf), &my_error_msg); - if (rc >= 0) return 1; - if (rc == PCRE_ERROR_NOMATCH) return 0; - - msr_log(msr, 1, "Regex processing failed (rc %d): %s", rc, my_error_msg); - - return 0; -} - -/** - * - */ -static apr_status_t modsecurity_process_phase_request_headers(modsec_rec *msr) { - apr_time_t time_before; - apr_status_t rc = 0; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Starting phase REQUEST_HEADERS."); - } - - time_before = apr_time_now(); - - if (msr->txcfg->ruleset != NULL) { - rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr); - } - - msr->time_phase1 = apr_time_now() - time_before; - - return rc; -} - -/** - * - */ -static apr_status_t modsecurity_process_phase_request_body(modsec_rec *msr) { - apr_time_t time_before; - apr_status_t rc = 0; - - - if ((msr->allow_scope == ACTION_ALLOW_REQUEST)||(msr->allow_scope == ACTION_ALLOW)) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping phase REQUEST_BODY (allow used)."); - } - - return 0; - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Starting phase REQUEST_BODY."); - } - } - - time_before = apr_time_now(); - - if (msr->txcfg->ruleset != NULL) { - rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr); - } - - msr->time_phase2 = apr_time_now() - time_before; - - return rc; -} - -/** - * - */ -static apr_status_t modsecurity_process_phase_response_headers(modsec_rec *msr) { - apr_time_t time_before; - apr_status_t rc = 0; - - if (msr->allow_scope == ACTION_ALLOW) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping phase RESPONSE_HEADERS (allow used)."); - } - - return 0; - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Starting phase RESPONSE_HEADERS."); - } - } - - time_before = apr_time_now(); - - if (msr->txcfg->ruleset != NULL) { - rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr); - } - - msr->time_phase3 = apr_time_now() - time_before; - - return rc; -} - -/** - * - */ -static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) { - apr_time_t time_before; - apr_status_t rc = 0; - - if (msr->allow_scope == ACTION_ALLOW) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping phase RESPONSE_BODY (allow used)."); - } - - return 0; - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Starting phase RESPONSE_BODY."); - } - } - - time_before = apr_time_now(); - - if (msr->txcfg->ruleset != NULL) { - rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr); - } - - msr->time_phase4 = apr_time_now() - time_before; - - - return rc; -} - -/** - * - */ -static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { - apr_time_t time_before, time_after; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Starting phase LOGGING."); - } - - time_before = apr_time_now(); - - if (msr->txcfg->ruleset != NULL) { - msre_ruleset_process_phase(msr->txcfg->ruleset, msr); - } - - modsecurity_persist_data(msr); - - time_after = apr_time_now(); - msr->time_phase5 = time_after - time_before; - - /* Is this request relevant for logging purposes? */ - if (msr->is_relevant == 0) { - /* Check the status */ - msr->is_relevant += is_response_status_relevant(msr, msr->r->status); - - /* If we processed two requests and statuses are different then - * check the other status too. - */ - if (msr->r_early->status != msr->r->status) { - msr->is_relevant += is_response_status_relevant(msr, msr->r_early->status); - } - } - - /* Figure out if we want to keep the files (if there are any, of course). */ - if ((msr->txcfg->upload_keep_files == KEEP_FILES_ON) - || ((msr->txcfg->upload_keep_files == KEEP_FILES_RELEVANT_ONLY)&&(msr->is_relevant))) - { - msr->upload_remove_files = 0; - } else { - msr->upload_remove_files = 1; - } - - /* Are we configured for audit logging? */ - switch(msr->txcfg->auditlog_flag) { - case AUDITLOG_OFF : - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Audit log: Not configured to run for this request."); - } - - return DECLINED; - break; - - case AUDITLOG_RELEVANT : - if (msr->is_relevant == 0) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Audit log: Ignoring a non-relevant request."); - } - - return DECLINED; - } - break; - - case AUDITLOG_ON : - /* All right, do nothing */ - break; - - default : - msr_log(msr, 1, "Internal error: Could not determine if auditing is needed, so forcing auditing."); - break; - } - - /* Invoke the Audit logger */ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Audit log: Logging this transaction."); - } - - sec_audit_logger(msr); - - msr->time_logging = apr_time_now() - time_after; - - return 0; -} - -/** - * Processes one transaction phase. The phase number does not - * need to be explicitly provided since it's already available - * in the modsec_rec structure. - */ -apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { - /* Check if we should run. */ - if ((msr->was_intercepted)&&(phase != PHASE_LOGGING)) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping phase %d as request was already intercepted.", phase); - } - - return 0; - } - - /* Do not process the same phase twice. */ - if (msr->phase >= phase) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping phase %d because it was previously run (at %d now).", - phase, msr->phase); - } - - return 0; - } - - msr->phase = phase; - - /* Clear out the transformation cache at the start of each phase */ - if (msr->txcfg->cache_trans == MODSEC_CACHE_ENABLED) { - if (msr->tcache) { - apr_hash_index_t *hi; - void *dummy; - apr_table_t *tab; - const void *key; - apr_ssize_t klen; - #ifdef CACHE_DEBUG - apr_pool_t *mp = msr->msc_rule_mptmp; - const apr_array_header_t *ctarr; - const apr_table_entry_t *ctelts; - msre_cache_rec *rec; - int cn = 0; - int ri; - #else - apr_pool_t *mp = msr->mp; - #endif - - for (hi = apr_hash_first(mp, msr->tcache); hi; hi = apr_hash_next(hi)) { - apr_hash_this(hi, &key, &klen, &dummy); - tab = (apr_table_t *)dummy; - - if (tab == NULL) continue; - - #ifdef CACHE_DEBUG - /* Dump the cache out as we clear */ - ctarr = apr_table_elts(tab); - ctelts = (const apr_table_entry_t*)ctarr->elts; - for (ri = 0; ri < ctarr->nelts; ri++) { - cn++; - rec = (msre_cache_rec *)ctelts[ri].val; - if (rec->changed) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=\"%s\" (%pp - %pp)", - cn, rec->hits, key, rec->num, rec->path, log_escape_nq_ex(mp, rec->val, rec->val_len), - rec->val, rec->val + rec->val_len); - } - } - else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=", - cn, rec->hits, key, rec->num, rec->path); - } - } - } - #endif - - apr_table_clear(tab); - apr_hash_set(msr->tcache, key, klen, NULL); - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Cleared transformation cache for phase %d", msr->phase); - } - } - - msr->tcache_items = 0; - msr->tcache = apr_hash_make(msr->mp); - if (msr->tcache == NULL) return -1; - } - - switch(phase) { - case 1 : - return modsecurity_process_phase_request_headers(msr); - case 2 : - return modsecurity_process_phase_request_body(msr); - case 3 : - return modsecurity_process_phase_response_headers(msr); - case 4 : - return modsecurity_process_phase_response_body(msr); - case 5 : - return modsecurity_process_phase_logging(msr); - default : - msr_log(msr, 1, "Invalid processing phase: %d", msr->phase); - break; - } - - return -1; -} diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h deleted file mode 100644 index 72cdea16ec..0000000000 --- a/apache2/modsecurity.h +++ /dev/null @@ -1,700 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MODSECURITY_H_ -#define _MODSECURITY_H_ - -#include -#include - -#include -#include -#include - -typedef struct rule_exception rule_exception; -typedef struct rule_exception hash_method; -typedef struct modsec_rec modsec_rec; -typedef struct directory_config directory_config; -typedef struct error_message_t error_message_t; -typedef struct msc_engine msc_engine; -typedef struct msc_data_chunk msc_data_chunk; -typedef struct msc_arg msc_arg; -typedef struct msc_string msc_string; -typedef struct msc_parm msc_parm; - -#include "msc_release.h" -#include "msc_logging.h" -#include "msc_multipart.h" -#include "msc_pcre.h" -#include "msc_util.h" -#include "msc_xml.h" -#include "msc_geo.h" -#include "msc_gsb.h" -#include "msc_unicode.h" -#include "re.h" -#include "msc_crypt.h" - -#include "ap_config.h" -#include "apr_md5.h" -#include "apr_strings.h" -#include "apr_hash.h" -#include "httpd.h" -#include "http_config.h" -#include "http_log.h" -#include "http_protocol.h" - -#if defined(WITH_LUA) -#include "msc_lua.h" -#endif - - -#define PHASE_REQUEST_HEADERS 1 -#define PHASE_REQUEST_BODY 2 -#define PHASE_RESPONSE_HEADERS 3 -#define PHASE_RESPONSE_BODY 4 -#define PHASE_LOGGING 5 -#define PHASE_FIRST PHASE_REQUEST_HEADERS -#define PHASE_LAST PHASE_LOGGING - -#define NOT_SET -1l -#define NOT_SET_P ((void *)-1l) - -#define CREATEMODE ( APR_UREAD | APR_UWRITE | APR_GREAD ) -#define CREATEMODE_DIR ( APR_UREAD | APR_UWRITE | APR_UEXECUTE | APR_GREAD | APR_GEXECUTE ) - -#if defined(NETWARE) -#define CREATEMODE_UNISTD ( S_IREAD | S_IWRITE ) -#elif defined(WIN32) -#define CREATEMODE_UNISTD ( _S_IREAD | _S_IWRITE ) -#else -#define CREATEMODE_UNISTD ( S_IRUSR | S_IWUSR | S_IRGRP ) -#endif - -#if !defined(O_BINARY) -#define O_BINARY (0) -#endif - -#ifndef PIPE_BUF -#define PIPE_BUF (512) -#endif - -#define REQUEST_BODY_HARD_LIMIT 1073741824L -#define REQUEST_BODY_DEFAULT_INMEMORY_LIMIT 131072 -#define REQUEST_BODY_DEFAULT_LIMIT 134217728 -#define REQUEST_BODY_NO_FILES_DEFAULT_LIMIT 1048576 -#define RESPONSE_BODY_DEFAULT_LIMIT 524288 -#define RESPONSE_BODY_HARD_LIMIT 1073741824L - -#define RESPONSE_BODY_LIMIT_ACTION_REJECT 0 -#define RESPONSE_BODY_LIMIT_ACTION_PARTIAL 1 - -#define REQUEST_BODY_FORCEBUF_OFF 0 -#define REQUEST_BODY_FORCEBUF_ON 1 - -#define REQUEST_BODY_LIMIT_ACTION_REJECT 0 -#define REQUEST_BODY_LIMIT_ACTION_PARTIAL 1 - -#define SECACTION_TARGETS "REMOTE_ADDR" -#define SECACTION_ARGS "@unconditionalMatch" - -#define SECMARKER_TARGETS "REMOTE_ADDR" -#define SECMARKER_ARGS "@noMatch" -#define SECMARKER_BASE_ACTIONS "t:none,pass,marker:" - -#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) -#include "unixd.h" -#define __SET_MUTEX_PERMS -#endif - -#define COOKIES_V0 0 -#define COOKIES_V1 1 - -#ifdef WIN32 -#include -#else -#include -#include -#endif - -#define NOTE_MSR "modsecurity-tx-context" - -#define FATAL_ERROR "ModSecurity: Fatal error (memory allocation or unexpected internal error)!" - -extern DSOLOCAL char *new_server_signature; -extern DSOLOCAL char *chroot_dir; - -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; - -extern DSOLOCAL unsigned long int conn_read_state_limit; - -extern DSOLOCAL unsigned long int conn_write_state_limit; - -extern DSOLOCAL unsigned long int unicode_codepage; - -extern DSOLOCAL int *unicode_map_table; - -#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 */ -#define RESBODY_STATUS_READ_BRIGADE 3 /* body was read but not flattened */ -#define RESBODY_STATUS_READ 4 /* body was read and flattened */ - -#define IF_STATUS_NONE 0 -#define IF_STATUS_WANTS_TO_RUN 1 -#define IF_STATUS_COMPLETE 2 - -#define OF_STATUS_NOT_STARTED 0 -#define OF_STATUS_IN_PROGRESS 1 -#define OF_STATUS_COMPLETE 2 - -#define MSC_REQBODY_NONE 0 -#define MSC_REQBODY_MEMORY 1 -#define MSC_REQBODY_DISK 2 - -#define ACTION_NONE 0 -#define ACTION_DENY 1 -#define ACTION_REDIRECT 2 -#define ACTION_PROXY 3 -#define ACTION_DROP 4 -#define ACTION_ALLOW 5 -#define ACTION_ALLOW_REQUEST 6 -#define ACTION_ALLOW_PHASE 7 -#define ACTION_PAUSE 8 - -#define MODSEC_DISABLED 0 -#define MODSEC_DETECTION_ONLY 1 -#define MODSEC_ENABLED 2 - -#define HASH_DISABLED 0 -#define HASH_ENABLED 1 - -#define HASH_URL_HREF_HASH_RX 0 -#define HASH_URL_HREF_HASH_PM 1 -#define HASH_URL_FACTION_HASH_RX 2 -#define HASH_URL_FACTION_HASH_PM 3 -#define HASH_URL_LOCATION_HASH_RX 4 -#define HASH_URL_LOCATION_HASH_PM 5 -#define HASH_URL_IFRAMESRC_HASH_RX 6 -#define HASH_URL_IFRAMESRC_HASH_PM 7 -#define HASH_URL_FRAMESRC_HASH_RX 8 -#define HASH_URL_FRAMESRC_HASH_PM 9 - -#define HASH_KEYONLY 0 -#define HASH_SESSIONID 1 -#define HASH_REMOTEIP 2 - -#define MODSEC_CACHE_DISABLED 0 -#define MODSEC_CACHE_ENABLED 1 - -#define MODSEC_OFFLINE 0 -#define MODSEC_ONLINE 1 - -#define REGEX_CAPTURE_BUFLEN 1024 - -#define KEEP_FILES_OFF 0 -#define KEEP_FILES_ON 1 -#define KEEP_FILES_RELEVANT_ONLY 2 - -#define RULE_EXCEPTION_IMPORT_ID 1 -#define RULE_EXCEPTION_IMPORT_MSG 2 -#define RULE_EXCEPTION_REMOVE_ID 3 -#define RULE_EXCEPTION_REMOVE_MSG 4 -#define RULE_EXCEPTION_REMOVE_TAG 5 - -#define NBSP 160 - -struct rule_exception { - int type; - const char *param; - void *param_data; -}; - -struct modsec_rec { - apr_pool_t *mp; - msc_engine *modsecurity; - - request_rec *r_early; - request_rec *r; - directory_config *dcfg1; - directory_config *dcfg2; - directory_config *usercfg; - directory_config *txcfg; - - unsigned int reqbody_should_exist; - unsigned int reqbody_chunked; - - unsigned int phase; - unsigned int phase_request_headers_complete; - unsigned int phase_request_body_complete; - - apr_bucket_brigade *if_brigade; - unsigned int if_status; - unsigned int if_started_forwarding; - - apr_size_t reqbody_length; - - apr_bucket_brigade *of_brigade; - unsigned int of_status; - unsigned int of_done_reading; - unsigned int of_skipping; - unsigned int of_partial; - unsigned int of_is_error; - - unsigned int resbody_status; - apr_size_t resbody_length; - char *resbody_data; - unsigned int resbody_contains_html; - - apr_size_t stream_input_length; - char *stream_input_data; - apr_size_t stream_output_length; - char *stream_output_data; - unsigned int of_stream_changed; - unsigned int if_stream_changed; - - apr_array_header_t *error_messages; - apr_array_header_t *alerts; - - const char *txid; - const char *sessionid; - const char *userid; - - const char *server_software; - const char *local_addr; - unsigned int local_port; - const char *local_user; - - /* client */ - - const char *remote_addr; - unsigned int remote_port; - const char *remote_user; - - /* useragent */ - const char *useragent_ip; - - /* request */ - - const char *request_line; - const char *request_method; - const char *request_uri; - const char *query_string; - const char *request_protocol; - - const char *hostname; - - apr_table_t *request_headers; - - apr_off_t request_content_length; - const char *request_content_type; - - apr_table_t *arguments; - apr_table_t *arguments_to_sanitize; - apr_table_t *request_headers_to_sanitize; - apr_table_t *response_headers_to_sanitize; - apr_table_t *request_cookies; - apr_table_t *pattern_to_sanitize; - - unsigned int urlencoded_error; - unsigned int inbound_error; - unsigned int outbound_error; - - unsigned int is_relevant; - - apr_table_t *tx_vars; - - /* ENH: refactor to allow arbitrary var tables */ - apr_table_t *geo_vars; - - /* response */ - unsigned int response_status; - const char *status_line; - const char *response_protocol; - apr_table_t *response_headers; - unsigned int response_headers_sent; - apr_off_t bytes_sent; - - /* modsecurity request body processing stuff */ - - unsigned int msc_reqbody_storage; /* on disk or in memory */ - unsigned int msc_reqbody_spilltodisk; - unsigned int msc_reqbody_read; - - apr_pool_t *msc_reqbody_mp; /* this is where chunks are allocated from */ - apr_array_header_t *msc_reqbody_chunks; /* data chunks when stored in memory */ - unsigned int msc_reqbody_length; /* the amount of data received */ - int msc_reqbody_chunk_position; /* used when retrieving the body */ - unsigned int msc_reqbody_chunk_offset; /* offset of the chunk currently in use */ - msc_data_chunk *msc_reqbody_chunk_current; /* current chunk */ - char *msc_reqbody_buffer; - - const char *msc_reqbody_filename; /* when stored on disk */ - int msc_reqbody_fd; - msc_data_chunk *msc_reqbody_disk_chunk; - - const char *msc_reqbody_processor; - int msc_reqbody_error; - const char *msc_reqbody_error_msg; - - apr_size_t msc_reqbody_no_files_length; - - char *multipart_filename; - char *multipart_name; - multipart_data *mpd; /* MULTIPART processor data structure */ - - xml_data *xml; /* XML processor data structure */ - - /* audit logging */ - char *new_auditlog_boundary; - char *new_auditlog_filename; - apr_file_t *new_auditlog_fd; - unsigned int new_auditlog_size; - apr_md5_ctx_t new_auditlog_md5ctx; - - unsigned int was_intercepted; - unsigned int rule_was_intercepted; - unsigned int intercept_phase; - msre_actionset *intercept_actionset; - const char *intercept_message; - - /* performance measurement */ - apr_time_t request_time; - apr_time_t time_phase1; - apr_time_t time_phase2; - apr_time_t time_phase3; - apr_time_t time_phase4; - apr_time_t time_phase5; - apr_time_t time_storage_read; - apr_time_t time_storage_write; - apr_time_t time_logging; - apr_time_t time_gc; - apr_table_t *perf_rules; - - apr_array_header_t *matched_rules; - msc_string *matched_var; - int highest_severity; - - /* upload */ - int upload_extract_files; - int upload_remove_files; - int upload_files_count; - - /* other */ - apr_table_t *collections_original; - apr_table_t *collections; - apr_table_t *collections_dirty; - - /* rule processing temp pool */ - apr_pool_t *msc_rule_mptmp; - - /* content injection */ - const char *content_prepend; - apr_off_t content_prepend_len; - const char *content_append; - apr_off_t content_append_len; - - /* data cache */ - apr_hash_t *tcache; - apr_size_t tcache_items; - - /* removed rules */ - apr_array_header_t *removed_rules; - apr_array_header_t *removed_rules_tag; - apr_array_header_t *removed_rules_msg; - - /* removed targets */ - apr_table_t *removed_targets; - - /* When "allow" is executed the variable below is - * updated to contain the scope of the allow action. Set - * at 0 by default, it will have ACTION_ALLOW if we are - * to allow phases 1-4 and ACTION_ALLOW_REQUEST if we - * are to allow phases 1-2 only. - */ - unsigned int allow_scope; - - /* matched vars */ - apr_table_t *matched_vars; - - /* Generic request body processor context to be used by custom parsers. */ - void *reqbody_processor_ctx; - - htmlDocPtr crypto_html_tree; -#if defined(WITH_LUA) - #ifdef CACHE_LUA - lua_State *L; - #endif -#endif - - int msc_sdbm_delete_error; -}; - -struct directory_config { - apr_pool_t *mp; - - msre_ruleset *ruleset; - - int is_enabled; - int reqbody_access; - int reqintercept_oe; - int reqbody_buffering; - long int reqbody_inmemory_limit; - long int reqbody_limit; - long int reqbody_no_files_limit; - int resbody_access; - - long int of_limit; - apr_table_t *of_mime_types; - int of_mime_types_cleared; - int of_limit_action; - int if_limit_action; - - const char *debuglog_name; - int debuglog_level; - apr_file_t *debuglog_fd; - - int cookie_format; - int argument_separator; - const char *cookiev0_separator; - - int rule_inheritance; - apr_array_header_t *rule_exceptions; - - - /* -- Audit log -- */ - - /* Max rule time */ - int max_rule_time; - - /* Whether audit log should be enabled in the context or not */ - int auditlog_flag; - - /* AUDITLOG_SERIAL (single file) or AUDITLOG_CONCURRENT (multiple files) */ - int auditlog_type; - - /* Mode for audit log directories and files */ - apr_fileperms_t auditlog_dirperms; - apr_fileperms_t auditlog_fileperms; - - /* The name of the audit log file (for the old type), or the - * name of the index file (for the new audit log type) - */ - char *auditlog_name; - /* The name of the secondary index file */ - char *auditlog2_name; - - /* The file descriptors for the files above */ - apr_file_t *auditlog_fd; - apr_file_t *auditlog2_fd; - - /* For the new-style audit log only, the path where - * audit log entries will be stored - */ - char *auditlog_storage_dir; - - /* A list of parts to include in the new-style audit log - * entry. By default, it contains 'ABCFHZ'. Have a look at - * the AUDITLOG_PART_* constants above to decipher the - * meaning. - */ - char *auditlog_parts; - - /* A regular expression that determines if a response - * status is treated as relevant. - */ - msc_regex_t *auditlog_relevant_regex; - - /* Upload */ - const char *tmp_dir; - const char *upload_dir; - 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; - msre_actionset *tmp_default_actionset; - apr_table_t *tmp_rule_placeholders; - - /* Misc */ - const char *data_dir; - const char *webappid; - const char *sensor_id; - const char *httpBlkey; - - /* Content injection. */ - int content_injection_enabled; - - /* Stream Inspection */ - int stream_inbody_inspection; - int stream_outbody_inspection; - - /* Geo Lookup */ - geo_db *geo; - - /* Gsb Lookup */ - gsb_db *gsb; - - /* Unicode map */ - unicode_map *u_map; - - /* Cache */ - int cache_trans; - int cache_trans_incremental; - apr_size_t cache_trans_min; - apr_size_t cache_trans_max; - apr_size_t cache_trans_maxitems; - - /* Array to hold signatures of components, which will - * appear in the ModSecurity signature in the audit log. - */ - apr_array_header_t *component_signatures; - - /* Request character encoding. */ - const char *request_encoding; - - int disable_backend_compression; - - /* Collection timeout */ - int col_timeout; - - /* hash of ids */ - apr_hash_t *rule_id_htab; - - /* Hash */ - apr_array_header_t *hash_method; - const char *crypto_key; - int crypto_key_len; - const char *crypto_param_name; - int hash_is_enabled; - int hash_enforcement; - int crypto_key_add; - int crypto_hash_href_rx; - int crypto_hash_faction_rx; - int crypto_hash_location_rx; - int crypto_hash_iframesrc_rx; - int crypto_hash_framesrc_rx; - int crypto_hash_href_pm; - int crypto_hash_faction_pm; - int crypto_hash_location_pm; - int crypto_hash_iframesrc_pm; - int crypto_hash_framesrc_pm; - - /* xml */ - int xml_external_entity; -}; - -struct error_message_t { - const char *file; - int line; - int level; - apr_status_t status; - const char *message; -}; - -struct msc_engine { - apr_pool_t *mp; - apr_global_mutex_t *auditlog_lock; - apr_global_mutex_t *geo_lock; - msre_engine *msre; - unsigned int processing_mode; -}; - -struct msc_data_chunk { - char *data; - apr_size_t length; - unsigned int is_permanent; -}; - -struct msc_arg { - const char *name; - unsigned int name_len; - unsigned int name_origin_offset; - unsigned int name_origin_len; - const char *value; - unsigned int value_len; - unsigned int value_origin_offset; - unsigned int value_origin_len; - const char *origin; -}; - -struct msc_string { - char *name; - unsigned int name_len; - char *value; - unsigned int value_len; -}; - -struct msc_parm { - char *value; - int pad_1; - int pad_2; -}; - -/* Engine functions */ - -msc_engine DSOLOCAL *modsecurity_create(apr_pool_t *mp, int processing_mode); - -int DSOLOCAL modsecurity_init(msc_engine *msce, apr_pool_t *mp); - -void DSOLOCAL modsecurity_child_init(msc_engine *msce); - -void DSOLOCAL modsecurity_shutdown(msc_engine *msce); - -apr_status_t DSOLOCAL modsecurity_tx_init(modsec_rec *msr); - -apr_status_t DSOLOCAL modsecurity_process_phase(modsec_rec *msr, unsigned int phase); - - -/* Request body functions */ - -apr_status_t DSOLOCAL modsecurity_request_body_start(modsec_rec *msr, char **error_msg); - -apr_status_t DSOLOCAL modsecurity_request_body_store(modsec_rec *msr, - const char *data, apr_size_t length, char **error_msg); - -apr_status_t DSOLOCAL modsecurity_request_body_end(modsec_rec *msr, char **error_msg); - -apr_status_t DSOLOCAL modsecurity_request_body_to_stream(modsec_rec *msr, const char *buffer, int buflen, char **error_msg); - -apr_status_t DSOLOCAL modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg); - -apr_status_t DSOLOCAL modsecurity_request_body_retrieve_end(modsec_rec *msr); - -/* Retrieves up to nbytes bytes of the request body. Returns 1 on - * success, 0 when there is no more data, or -1 on error. On return - * nbytes will contain the number of bytes stored in the buffer. - */ -apr_status_t DSOLOCAL modsecurity_request_body_retrieve(modsec_rec *msr, msc_data_chunk **chunk, - long int nbytes, char **error_msg); - -void DSOLOCAL msc_add(modsec_rec *msr, int level, msre_actionset *actionset, - const char *action_message, const char *rule_message); - -const char DSOLOCAL *msc_alert_message(modsec_rec *msr, msre_actionset *actionset, const char *action_message, - const char *rule_message); - -void DSOLOCAL msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message, - const char *rule_message); - -apr_status_t DSOLOCAL modsecurity_request_body_clear(modsec_rec *msr, char **error_msg); - -#endif diff --git a/apache2/modsecurity_config.h b/apache2/modsecurity_config.h deleted file mode 100644 index 7de60afe71..0000000000 --- a/apache2/modsecurity_config.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Some APR files define PACKAGE* constants, which may conflict - * so this is here to prevent that by removing them. - */ - -#ifndef WIN32 - -/* Undefine all these so there are no conflicts */ -#undef PACKAGE -#undef PACKAGE_BUGREPORT -#undef PACKAGE_NAME -#undef PACKAGE_STRING -#undef PACKAGE_TARNAME -#undef PACKAGE_URL -#undef PACKAGE_VERSION - -/* Include the real autoconf header */ -#include "modsecurity_config_auto.h" - -/* Undefine all these (again) so there are no conflicts */ -#undef PACKAGE -#undef PACKAGE_BUGREPORT -#undef PACKAGE_NAME -#undef PACKAGE_STRING -#undef PACKAGE_TARNAME -#undef PACKAGE_URL -#undef PACKAGE_VERSION - -#endif diff --git a/apache2/modules.mk b/apache2/modules.mk deleted file mode 100644 index 3a8dd00adf..0000000000 --- a/apache2/modules.mk +++ /dev/null @@ -1,19 +0,0 @@ -MOD_SECURITY2 = mod_security2 apache2_config apache2_io apache2_util \ - re re_operators re_actions re_tfns re_variables \ - msc_logging msc_xml msc_multipart modsecurity msc_parsers msc_util msc_pcre \ - persist_dbm msc_reqbody pdf_protect msc_geo msc_gsb msc_crypt msc_tree msc_unicode acmp msc_lua - -H = re.h modsecurity.h msc_logging.h msc_multipart.h msc_parsers.h \ - msc_pcre.h msc_util.h msc_xml.h persist_dbm.h apache2.h pdf_protect.h \ - msc_geo.h msc_gsb.h msc_crypt.h msc_tree.h msc_unicode.h acmp.h utf8tables.h msc_lua.h - -${MOD_SECURITY2:=.slo}: ${H} -${MOD_SECURITY2:=.lo}: ${H} -${MOD_SECURITY2:=.o}: ${H} - -mod_security2.la: ${MOD_SECURITY2:=.slo} - $(SH_LINK) -rpath $(libexecdir) -module -avoid-version ${MOD_SECURITY2:=.lo} - -DISTCLEAN_TARGETS = modules.mk - -shared = mod_security2.la diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c deleted file mode 100644 index c7fd58e1a6..0000000000 --- a/apache2/msc_crypt.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* - * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) - * - * You may not use this file except in compliance with - * the License.  You may obtain a copy of the License at - * - *     http://www.apache.org/licenses/LICENSE-2.0 - * - * If any of the files related to licensing are missing or if you have any - * other questions related to licensing please contact Trustwave Holdings, Inc. - * directly using the email address security@modsecurity.org. - */ - -#include "msc_crypt.h" -#include "msc_util.h" -#include "apr_sha1.h" -#include "apr_uri.h" -#include "apr_base64.h" -#include "acmp.h" -#include "libxml/HTMLtree.h" -#include "libxml/uri.h" -#include - -/** - * \brief Normalize path in URI - * - * \param msr ModSecurity transaction resource - * \param input The URI to be normalized - * - * \retval input normalized input - * \retval NULL on fail - */ -char *normalize_path(modsec_rec *msr, char *input) { - xmlURI *uri = NULL; - char *parsed_content = NULL; - char *content = NULL; - - if(msr == NULL) return NULL; - - if(input == NULL) return NULL; - - uri = xmlParseURI(input); - - if(uri != NULL && uri->path) { - if(uri->scheme) { - content = apr_psprintf(msr->mp, "%s://", uri->scheme); - parsed_content = apr_pstrcat(msr->mp, content, NULL); - } - - if(uri->server) { - content = apr_psprintf(msr->mp, "%s", uri->server); - if(parsed_content) - parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL); - else - parsed_content = apr_pstrcat(msr->mp, content, NULL); - } - - if(uri->port) { - content = apr_psprintf(msr->mp, ":%d", uri->port); - if(parsed_content) - parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL); - else - parsed_content = apr_pstrcat(msr->mp, content, NULL); - } - - if(uri->path) { - char *Uri = NULL; - int bytes = 0; - int i; - char *relative_link = NULL; - char *filename = NULL; - char *relative_path = NULL; - char *relative_uri = NULL; - - filename = file_basename(msr->mp, msr->r->parsed_uri.path); - - if(filename == NULL || (strlen(msr->r->parsed_uri.path) - strlen(filename) < 0)) - return NULL; - - relative_path = apr_pstrndup(msr->mp, msr->r->parsed_uri.path, strlen(msr->r->parsed_uri.path) - strlen(filename)); - relative_uri = apr_pstrcat(msr->mp, relative_path, uri->path, NULL); - - relative_link = apr_pstrdup(msr->mp, relative_uri); - - xmlNormalizeURIPath(relative_link); - - Uri = apr_pstrdup(msr->mp, relative_link); - -/* - for(i = 0; i < (int)strlen(Uri); i++) { - if(Uri[i] != '.' && Uri[i] != '/') { - if (i - 1 < 0) - i = 0; - else - i--; - if(Uri[i] == '/') - --bytes; - break; - } else { - bytes++; - } - } - - if(bytes >= (int)strlen(uri->path)) - return NULL; -*/ - - content = apr_psprintf(msr->mp, "%s", Uri); - - if(parsed_content) - parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL); - else - parsed_content = apr_pstrcat(msr->mp, content, NULL); - - } - - if(uri->query_raw) { - content = apr_psprintf(msr->mp, "?%s", uri->query_raw); - if(parsed_content) - parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL); - else - parsed_content = apr_pstrcat(msr->mp, content, NULL); - } - - if(uri->fragment) { - content = apr_psprintf(msr->mp, "#%s", uri->fragment); - if(parsed_content) - parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL); - else - parsed_content = apr_pstrcat(msr->mp, content, NULL); - } - xmlFreeURI(uri); - return apr_pstrdup(msr->mp, parsed_content); - } - - if(uri != NULL) xmlFreeURI(uri); - return apr_pstrdup(msr->mp, input); -} - -/** - * \brief Create a random password - * - * \param mp ModSecurity transaction memory pool - * - * \retval key random key - */ -char *getkey(apr_pool_t *mp) { - unsigned char digest[APR_SHA1_DIGESTSIZE]; - char *sig, *key, *value; - apr_sha1_ctx_t ctx; - char salt[64]; - - apr_generate_random_bytes(salt, sizeof(salt)); - key = apr_psprintf(mp,"%s",salt); - - apr_sha1_init (&ctx); - apr_sha1_update (&ctx, (const char*)key, strlen(key)); - apr_sha1_update (&ctx, "\0", 1); - - apr_generate_random_bytes(salt, sizeof(salt)); - value = apr_psprintf(mp,"%s",salt); - - apr_sha1_update (&ctx, value, strlen (value)); - apr_sha1_final (digest, &ctx); - - sig = apr_pcalloc (mp, apr_base64_encode_len (sizeof (digest))); - apr_base64_encode (sig, (const char*)digest, sizeof (digest)); - - return sig; -} - - -/** - * \brief Generate the MAC for a given message - * - * \param msr ModSecurity transaction resource - * \param key The key used within HMAC - * \param key_len Key length - * \param msg The message to generate the MAC - * \param msglen The message length - * - * \retval hex_digest The MAC - */ -char *hmac(modsec_rec *msr, const char *key, int key_len, - unsigned char *msg, int msglen) { - apr_sha1_ctx_t ctx; - unsigned char digest[APR_SHA1_DIGESTSIZE]; - unsigned char hmac_ipad[HMAC_PAD_SIZE], hmac_opad[HMAC_PAD_SIZE]; - unsigned char nkey[APR_SHA1_DIGESTSIZE]; - unsigned char *hmac_key = (unsigned char *) key; - char hex_digest[APR_SHA1_DIGESTSIZE * 2], *hmac_digest; - const char hex[] = "0123456789abcdef"; - int i; - - if (key_len > HMAC_PAD_SIZE-1) { - hmac_key = nkey; - key_len = APR_SHA1_DIGESTSIZE; - } - - memset ((void *) hmac_ipad, 0, sizeof (hmac_ipad)); - memset ((void *) hmac_opad, 0, sizeof (hmac_opad)); - memmove (hmac_ipad, hmac_key, key_len); - memmove (hmac_opad, hmac_key, key_len); - - for (i = 0; i < HMAC_PAD_SIZE-1; i++) { - hmac_ipad[i] ^= 0x36; - hmac_opad[i] ^= 0x5c; - } - - apr_sha1_init (&ctx); - apr_sha1_update_binary (&ctx, hmac_ipad, HMAC_PAD_SIZE-1); - apr_sha1_update_binary (&ctx, (const unsigned char *) msg, msglen); - apr_sha1_final (digest, &ctx); - - apr_sha1_init (&ctx); - apr_sha1_update_binary (&ctx, hmac_opad, HMAC_PAD_SIZE-1); - apr_sha1_update_binary (&ctx, digest, sizeof (digest)); - apr_sha1_final (digest, &ctx); - - hmac_digest = hex_digest; - for (i = 0; i < sizeof (digest); i++) { - *hmac_digest++ = hex[digest[i] >> 4]; - *hmac_digest++ = hex[digest[i] & 0xF]; - } - - *hmac_digest = '\0'; - - return apr_pstrdup (msr->mp, hex_digest); -} - - -/** - * \brief Init html response body parser - * - * \param msr ModSecurity transaction resource - * - * \retval 1 on success - * \retval -1 on fail - */ -int init_response_body_html_parser(modsec_rec *msr) { - char *charset = NULL; - char *final_charset = NULL; - char sep; - - if(msr == NULL) return -1; - - if(msr->r == NULL) return -1; - - if(msr->r->content_type == NULL) return -1; - - if(msr->crypto_html_tree != NULL){ - xmlFreeDoc(msr->crypto_html_tree); - msr->crypto_html_tree = NULL; - } - - if((strncmp("text/html",msr->r->content_type, 9) != 0)){ - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "init_response_body_html_parser: skipping html_tree generation for Content[%s].", msr->r->content_type); - if(msr->crypto_html_tree != NULL){ - xmlFreeDoc(msr->crypto_html_tree); - msr->crypto_html_tree = NULL; - } - return -1; - } - - if (msr->resbody_length == 0) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "init_response_body_html_parser: skipping html_tree generation for zero length respomse body."); - msr->crypto_html_tree = NULL; - return 0; - } - - if((msr->r->content_encoding == NULL)||(apr_strnatcasecmp(msr->r->content_encoding,"(null)")==0)){ - charset=m_strcasestr(msr->r->content_type,"charset="); - if(charset == NULL){ - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "init_response_body_html_parser: assuming ISO-8859-1."); - msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL, - "ISO-8859-1", HTML_PARSE_RECOVER | HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *) "ISO-8859-1"); - } - else{ - charset+=8; - final_charset=strchr(charset,' '); - if(final_charset == NULL) final_charset = strchr(charset,';'); - if(final_charset != NULL) { - sep = *final_charset; - *final_charset = '\0'; - } - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "init_response_body_html_parser: Charset[%s]",charset); - msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL, - charset, HTML_PARSE_RECOVER| HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *)charset); - if(final_charset != NULL) *final_charset=sep; - } - - } - else{ - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4,"init_response_body_html_parser: Enconding[%s].",msr->r->content_encoding); - msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL, - msr->r->content_encoding, HTML_PARSE_RECOVER | HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *)msr->r->content_encoding); - } - if(msr->crypto_html_tree == NULL){ - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "init_response_body_html_parser: Failed to parse response body."); - return -1; - } - else { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "init_response_body_html_parser: Successfully html parser generated."); - return 1; - } - - return 1; -} - -/** - * \brief Execute all hash methods - * - * \param msr ModSecurity transaction resource - * \param link The html attr value to be checked - * \param type The hash method type - * - * \retval 1 Match - * \retval 0 No Match - * \retval -1 on fail - */ -int do_hash_method(modsec_rec *msr, char *link, int type) { - hash_method **em = NULL; - int i = 0; - char *error_msg = NULL; - char *my_error_msg = NULL; - int ovector[33]; - int rc; - - if(msr == NULL) return -1; - - em = (hash_method **)msr->txcfg->hash_method->elts; - - if(msr->txcfg->hash_method->nelts == 0) - return 1; - - for (i = 0; i < msr->txcfg->hash_method->nelts; i++) { - - if(em[i] != NULL && em[i]->param_data != NULL){ - - switch(type) { - case HASH_URL_HREF_HASH_PM: - if(em[i]->type == HASH_URL_HREF_HASH_PM) { - const char *match = NULL; - apr_status_t rc = 0; - ACMPT pt; - - pt.parser = (ACMP *)em[i]->param_data; - pt.ptr = NULL; - - rc = acmp_process_quick(&pt, &match, link, strlen(link)); - - if (rc) { - return 1; - } else { - return 0; - } - } - break; - case HASH_URL_HREF_HASH_RX: - if(em[i]->type == HASH_URL_HREF_HASH_RX) { - rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - error_msg = apr_psprintf(msr->mp, - "Execution error - " - "PCRE limits exceeded for Hash regex [%s] (%d): %s", - em[i]->param,rc, my_error_msg); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%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); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%s.", error_msg); - return -1; - } - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - return 1; - } - } - break; - case HASH_URL_FACTION_HASH_PM: - if(em[i]->type == HASH_URL_FACTION_HASH_PM) { - const char *match = NULL; - apr_status_t rc = 0; - ACMPT pt; - - pt.parser = (ACMP *)em[i]->param_data; - pt.ptr = NULL; - - rc = acmp_process_quick(&pt, &match, link, strlen(link)); - - if (rc) { - return 1; - } else { - return 0; - } - } - break; - case HASH_URL_FACTION_HASH_RX: - if(em[i]->type == HASH_URL_FACTION_HASH_RX) { - rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - error_msg = apr_psprintf(msr->mp, - "Execution error - " - "PCRE limits exceeded for Hash regex [%s] (%d): %s", - em[i]->param,rc, my_error_msg); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%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); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%s.", error_msg); - return -1; - } - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - return 1; - } - } - break; - case HASH_URL_LOCATION_HASH_PM: - if(em[i]->type == HASH_URL_LOCATION_HASH_PM) { - const char *match = NULL; - apr_status_t rc = 0; - ACMPT pt; - - pt.parser = (ACMP *)em[i]->param_data; - pt.ptr = NULL; - - rc = acmp_process_quick(&pt, &match, link, strlen(link)); - - if (rc) { - return 1; - } else { - return 0; - } - } - break; - case HASH_URL_LOCATION_HASH_RX: - if(em[i]->type == HASH_URL_LOCATION_HASH_RX) { - rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - error_msg = apr_psprintf(msr->mp, - "Execution error - " - "PCRE limits exceeded for Hash regex [%s] (%d): %s", - em[i]->param,rc, my_error_msg); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%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); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%s.", error_msg); - return -1; - } - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - return 1; - } - } - break; - case HASH_URL_IFRAMESRC_HASH_PM: - if(em[i]->type == HASH_URL_IFRAMESRC_HASH_PM) { - const char *match = NULL; - apr_status_t rc = 0; - ACMPT pt; - - pt.parser = (ACMP *)em[i]->param_data; - pt.ptr = NULL; - - rc = acmp_process_quick(&pt, &match, link, strlen(link)); - - if (rc) { - return 1; - } else { - return 0; - } - } - break; - case HASH_URL_IFRAMESRC_HASH_RX: - if(em[i]->type == HASH_URL_IFRAMESRC_HASH_RX) { - rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - error_msg = apr_psprintf(msr->mp, - "Execution error - " - "PCRE limits exceeded for Hash regex [%s] (%d): %s", - em[i]->param,rc, my_error_msg); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%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); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%s.", error_msg); - return -1; - } - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - return 1; - } - } - break; - case HASH_URL_FRAMESRC_HASH_PM: - if(em[i]->type == HASH_URL_FRAMESRC_HASH_PM) { - const char *match = NULL; - apr_status_t rc = 0; - ACMPT pt; - - pt.parser = (ACMP *)em[i]->param_data; - pt.ptr = NULL; - - rc = acmp_process_quick(&pt, &match, link, strlen(link)); - - if (rc) { - return 1; - } else { - return 0; - } - } - break; - case HASH_URL_FRAMESRC_HASH_RX: - if(em[i]->type == HASH_URL_FRAMESRC_HASH_RX) { - rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - error_msg = apr_psprintf(msr->mp, - "Execution error - " - "PCRE limits exceeded for Hash regex [%s] (%d): %s", - em[i]->param,rc, my_error_msg); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%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); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "%s.", error_msg); - return -1; - } - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - return 1; - } - } - break; - } - } - - } - return 0; -} - -/** - * \brief Hash the html elements - * - * \param msr ModSecurity transaction resource - * - * \retval 1 On success - * \retval 0 No element was changed - * \retval -1 On fail - */ -int hash_response_body_links(modsec_rec *msr) { - int lsize = 0, fsize = 0, lcount = 0, fcount = 0, i; - int isize = 0, icount = 0, frsize = 0, frcount = 0; - int bytes = 0; - xmlXPathContextPtr xpathCtx = NULL; - xmlXPathObjectPtr xpathObj = NULL; - xmlChar *content_option = NULL; - char *mac_link = NULL; - int rc, elts = 0; - - if(msr == NULL) - return -1; - - if (msr->crypto_html_tree == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "hash_response_body_links: Cannot parse NULL html tree"); - return -1; - } - - if(msr->txcfg->crypto_hash_href_rx == 0 && msr->txcfg->crypto_hash_href_pm == 0 - && msr->txcfg->crypto_hash_faction_rx == 0 && msr->txcfg->crypto_hash_faction_pm == 0 - && msr->txcfg->crypto_hash_iframesrc_rx == 0 && msr->txcfg->crypto_hash_iframesrc_pm == 0 - && msr->txcfg->crypto_hash_framesrc_rx == 0 && msr->txcfg->crypto_hash_framesrc_pm == 0) - return -1; - - xpathCtx = xmlXPathNewContext(msr->crypto_html_tree); - if(xpathCtx == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "hash_response_body_links: Unable to create Xpath context."); - goto ctx_error; - } - - lcount=fcount=0; - - if(msr->txcfg->crypto_hash_href_rx == 1 || msr->txcfg->crypto_hash_href_pm == 1) { - - xpathObj = xmlXPathEvalExpression((xmlChar*)"//*[@href]", xpathCtx); - if(xpathObj == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "hash_response_body_links: Unable to evaluate xpath expression."); - goto obj_error; - } - - lsize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0; - for(i = lsize - 1; i >=0; i--) { - register xmlNodePtr cur; - - cur = xpathObj->nodesetval->nodeTab[i]; - if(cur != NULL){ - xmlChar *href = xmlGetProp(cur, (const xmlChar *) "href"); - char *content_href = normalize_path(msr, (char *)href); - - if(content_href != NULL && strstr(content_href,msr->txcfg->crypto_param_name) == NULL) { - if(msr->txcfg->crypto_hash_href_rx == 1) { - rc = do_hash_method(msr, (char *)content_href, HASH_URL_HREF_HASH_RX); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_href, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "href", (const xmlChar *) mac_link); - lcount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(href != NULL) - xmlFree(href); - continue; - } - } - if(msr->txcfg->crypto_hash_href_pm == 1) { - rc = do_hash_method(msr, (char *)content_href, HASH_URL_HREF_HASH_PM); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_href, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "href", (const xmlChar *) mac_link); - lcount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(href != NULL) - xmlFree(href); - continue; - } - } - } - - if(href != NULL) { - xmlFree(href); - href = NULL; - } - } - } - - if(xpathObj != NULL) - xmlXPathFreeObject(xpathObj); - } - - if(msr->txcfg->crypto_hash_faction_rx == 1 || msr->txcfg->crypto_hash_faction_pm == 1) { - xpathObj = xmlXPathEvalExpression((xmlChar*)"//form", xpathCtx); - if(xpathObj == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "hash_response_body_links: Unable to evaluate xpath expression."); - goto obj_error; - } - - fsize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0; - for(i = fsize - 1; i >=0; i--) { - register xmlNodePtr cur; - - cur = xpathObj->nodesetval->nodeTab[i]; - if((cur != NULL)){ - xmlChar *action = NULL; - char *content_action = NULL; - - if(content_option) - xmlFree(content_option); - - action = xmlGetProp(cur, (const xmlChar *) "action"); - content_action = normalize_path(msr, (char *)action); - content_option = xmlGetProp(cur, (const xmlChar *) "option"); - - if(content_action != NULL && content_option == NULL && strstr(content_action,msr->txcfg->crypto_param_name) == NULL) { - if(msr->txcfg->crypto_hash_faction_rx == 1) { - rc = do_hash_method(msr, (char *)content_action, HASH_URL_FACTION_HASH_RX); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_action, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "action", (const xmlChar *) mac_link); - fcount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(action != NULL) - xmlFree(action); - continue; - } - } - if(msr->txcfg->crypto_hash_faction_pm == 1) { - rc = do_hash_method(msr, (char *)content_action, HASH_URL_FACTION_HASH_PM); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_action, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "action", (const xmlChar *) mac_link); - fcount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(action != NULL) - xmlFree(action); - continue; - } - } - } - - if(action != NULL) { - xmlFree(action); - action = NULL; - } - - if(content_option) { - xmlFree(content_option); - content_option = NULL; - } - } - } - - if(xpathObj != NULL) - xmlXPathFreeObject(xpathObj); - } - - if(msr->txcfg->crypto_hash_iframesrc_rx == 1 || msr->txcfg->crypto_hash_iframesrc_pm == 1) { - xpathObj = xmlXPathEvalExpression((xmlChar*)"//iframe", xpathCtx); - if(xpathObj == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "hash_response_body_links: Unable to evaluate xpath expression."); - goto obj_error; - } - - isize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0; - for(i = isize - 1; i >=0; i--) { - register xmlNodePtr cur; - - cur = xpathObj->nodesetval->nodeTab[i]; - if((cur != NULL)){ - - xmlChar *src = xmlGetProp(cur, (const xmlChar *) "src"); - char *content_src = normalize_path(msr, (char *)src); - - if(content_src != NULL && strstr(content_src,msr->txcfg->crypto_param_name) == NULL) { - if(msr->txcfg->crypto_hash_iframesrc_rx == 1) { - rc = do_hash_method(msr, (char *)content_src, HASH_URL_IFRAMESRC_HASH_RX); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link); - icount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(src != NULL) - xmlFree(src); - continue; - } - } - if(msr->txcfg->crypto_hash_iframesrc_pm == 1) { - rc = do_hash_method(msr, (char *)content_src, HASH_URL_IFRAMESRC_HASH_PM); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link); - icount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(src != NULL) - xmlFree(src); - continue; - } - } - } - - if(src != NULL) { - xmlFree(src); - src = NULL; - } - } - } - - if(xpathObj != NULL) - xmlXPathFreeObject(xpathObj); - } - - if(msr->txcfg->crypto_hash_framesrc_rx == 1 || msr->txcfg->crypto_hash_framesrc_pm == 1) { - xpathObj = xmlXPathEvalExpression((xmlChar*)"//frame", xpathCtx); - if(xpathObj == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, - "hash_response_body_links: Unable to evaluate xpath expression."); - goto obj_error; - } - - frsize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0; - for(i = frsize - 1; i >=0; i--) { - register xmlNodePtr cur; - - cur = xpathObj->nodesetval->nodeTab[i]; - if((cur != NULL)){ - - xmlChar *src = xmlGetProp(cur, (const xmlChar *) "src"); - char *content_src = normalize_path(msr, (char *)src); - - if(content_src != NULL && strstr(content_src,msr->txcfg->crypto_param_name) == NULL) { - if(msr->txcfg->crypto_hash_framesrc_rx == 1) { - rc = do_hash_method(msr, (char *)content_src, HASH_URL_FRAMESRC_HASH_RX); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link); - frcount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(src != NULL) - xmlFree(src); - continue; - } - } - if(msr->txcfg->crypto_hash_framesrc_pm == 1) { - rc = do_hash_method(msr, (char *)content_src, HASH_URL_FRAMESRC_HASH_PM); - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK); - if(mac_link != NULL) { - xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link); - frcount++; - bytes += strlen(mac_link); - msr->of_stream_changed = 1; - } - mac_link = NULL; - if(src != NULL) - xmlFree(src); - continue; - } - } - } - - if(src != NULL) { - xmlFree(src); - src = NULL; - } - } - } - - if(xpathObj != NULL) - xmlXPathFreeObject(xpathObj); - } - - if(xpathCtx != NULL) - xmlXPathFreeContext(xpathCtx); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "hash_response_body_links: Processed [%d] iframe src, [%d] hashed.",isize, icount); - msr_log(msr, 4, "hash_response_body_links: Processed [%d] frame src, [%d] hashed.",frsize, frcount); - msr_log(msr, 4, "hash_response_body_links: Processed [%d] form actions, [%d] hashed.",fsize, fcount); - msr_log(msr, 4, "hash_response_body_links: Processed [%d] links, [%d] hashed.",lsize, lcount); - } - - if(msr->of_stream_changed == 0) { - if(msr->crypto_html_tree != NULL) { - xmlFreeDoc(msr->crypto_html_tree); - msr->crypto_html_tree = NULL; - } - return 0; - } - - elts = (icount+frcount+fcount+lcount); - - if((elts >= INT32_MAX) || (elts < 0)) - return 0; - - return bytes; - -obj_error: - if(xpathCtx != NULL) - xmlXPathFreeContext(xpathCtx); -ctx_error: - return -1; -} - -/** - * \brief Inject the new response body - * - * \param msr ModSecurity transaction resource - * \param elts Number of hashed elements - * - * \retval 1 On success - * \retval -1 On fail - */ -int inject_hashed_response_body(modsec_rec *msr, int elts) { - xmlOutputBufferPtr output_buf = NULL; - xmlCharEncodingHandlerPtr handler = NULL; - char *p = NULL; - const char *ctype = NULL; - const char *encoding = NULL; - char *new_ct = NULL, *content_value = NULL; - - if(msr == NULL) return -1; - - if(msr->r == NULL) return -1; - - if (msr->crypto_html_tree == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Cannot parse NULL html tree"); - return -1; - } - - if(msr->r->content_type != NULL) - ctype = msr->r->content_type; - - encoding = (const char *) htmlGetMetaEncoding(msr->crypto_html_tree); - - if (ctype && encoding == NULL) { - if (ctype && (p = m_strcasestr(ctype, "charset=") , p != NULL)) { - p += 8 ; - if (encoding = apr_pstrndup(msr->mp, p, strcspn(p, " ;") ), encoding) { - xmlCharEncoding enc; - enc = xmlParseCharEncoding(encoding); - handler = xmlFindCharEncodingHandler(encoding); - } - } - } else { - if(encoding != NULL) { - xmlCharEncoding enc; - enc = xmlParseCharEncoding(encoding); - handler = xmlFindCharEncodingHandler(encoding); - } - } - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Detected encoding type [%s].", encoding); - - if (handler == NULL) - handler = xmlFindCharEncodingHandler("UTF-8"); - if (handler == NULL) - handler = xmlFindCharEncodingHandler("ISO-8859-1"); - if (handler == NULL) - handler = xmlFindCharEncodingHandler("HTML"); - if (handler == NULL) - handler = xmlFindCharEncodingHandler("ascii"); - - if(handler == NULL) { - xmlFreeDoc(msr->crypto_html_tree); - return -1; - } - - apr_table_unset(msr->r->headers_out,"Content-Type"); - new_ct = (char*)apr_psprintf(msr->mp, "text/html;%s",handler->name); - apr_table_set(msr->r->err_headers_out,"Content-Type",new_ct); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Using content-type [%s].", handler->name); - - output_buf = xmlAllocOutputBuffer(handler); - if (output_buf == NULL) { - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Unable to allocate memory buffer."); - xmlFreeDoc(msr->crypto_html_tree); - return -1; - } - - htmlDocContentDumpFormatOutput(output_buf, msr->crypto_html_tree, NULL, 0); - xmlOutputBufferFlush(output_buf); - -#ifdef LIBXML2_NEW_BUFFER - - if (output_buf->conv == NULL || (output_buf->conv && xmlOutputBufferGetSize(output_buf) == 0)) { - - if(output_buf->buffer == NULL || xmlOutputBufferGetSize(output_buf) == 0) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - msr->of_stream_changed = 0; - return -1; - } - - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - - msr->stream_output_length = xmlOutputBufferGetSize(output_buf); - msr->stream_output_data = (char *)malloc(msr->stream_output_length+1); - - if (msr->stream_output_data == NULL) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - return -1; - } - - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); - memcpy(msr->stream_output_data, xmlOutputBufferGetContent(output_buf), msr->stream_output_length); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONTENT to stream buffer [%d] bytes.", xmlOutputBufferGetSize(output_buf)); - - } else { - - if(output_buf->conv == NULL || xmlOutputBufferGetSize(output_buf) == 0) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - msr->of_stream_changed = 0; - return -1; - } - - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - - msr->stream_output_length = xmlOutputBufferGetSize(output_buf); - msr->stream_output_data = (char *)malloc(msr->stream_output_length+1); - - if (msr->stream_output_data == NULL) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - return -1; - } - - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); - memcpy(msr->stream_output_data, xmlOutputBufferGetContent(output_buf), msr->stream_output_length); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONV to stream buffer [%d] bytes.", xmlOutputBufferGetSize(output_buf)); - - } - -#else - - if (output_buf->conv == NULL || (output_buf->conv && output_buf->conv->use == 0)) { - - if(output_buf->buffer == NULL || output_buf->buffer->use == 0) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - msr->of_stream_changed = 0; - return -1; - } - - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - - msr->stream_output_length = output_buf->buffer->use; - msr->stream_output_data = (char *)malloc(msr->stream_output_length+1); - - if (msr->stream_output_data == NULL) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - return -1; - } - - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); - memcpy(msr->stream_output_data, (char *)xmlBufferContent(output_buf->buffer), msr->stream_output_length); - //memcpy(msr->stream_output_data, output_buf->buffer->content, msr->stream_output_length); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONTENT to stream buffer [%d] bytes.", msr->stream_output_length); - - } else { - - if(output_buf->conv == NULL || output_buf->conv->use == 0) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - msr->of_stream_changed = 0; - return -1; - } - - if(msr->stream_output_data != NULL) { - free(msr->stream_output_data); - msr->stream_output_data = NULL; - } - - msr->stream_output_length = output_buf->conv->use; - msr->stream_output_data = (char *)malloc(msr->stream_output_length+1); - - if (msr->stream_output_data == NULL) { - xmlOutputBufferClose(output_buf); - xmlFreeDoc(msr->crypto_html_tree); - return -1; - } - - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); - memcpy(msr->stream_output_data, (char *)xmlBufferContent(output_buf->conv), msr->stream_output_length); - //memcpy(msr->stream_output_data, output_buf->conv->content, msr->stream_output_length); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONV to stream buffer [%d] bytes.", msr->stream_output_length); - - } - -#endif - - xmlOutputBufferClose(output_buf); - - content_value = (char*)apr_psprintf(msr->mp, "%"APR_SIZE_T_FMT, msr->stream_output_length); - apr_table_unset(msr->r->headers_out,"Content-Length"); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Setting new content value %s", content_value); - apr_table_set(msr->r->headers_out, "Content-Length", content_value); - - xmlFreeDoc(msr->crypto_html_tree); - - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Stream buffer [%"APR_SIZE_T_FMT"]. Done",msr->stream_output_length); - - return 1; -} - -/** - * \brief Parse and MAC html elements - * - * \param msr ModSecurity transaction resource - * \param link The html attr value to be checked - * \param type The hash method type - * - * \retval mac_link MACed link - * \retval NULL on fail - */ -char *do_hash_link(modsec_rec *msr, char *link, int type) { - char *mac_link = NULL; - char *path_chunk = NULL; - char *hash_value = NULL; - char *qm = NULL; - - if(msr == NULL) return NULL; - - if(strlen(link) > 7 && strncmp("http:",(char*)link,5)==0){ - path_chunk = strchr(link+7,'/'); - if(path_chunk != NULL) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Signing data [%s]", path_chunk+1); - } - - if(msr->txcfg->crypto_key_add == HASH_KEYONLY) - hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - - if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { - if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - } else { - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Using session id [%s]", msr->sessionid); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - } - } - - if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - } - } else { - return NULL; - } - } else - if(strlen(link) > 8 && strncmp("https",(char*)link,5)==0){ - path_chunk = strchr(link+8,'/'); - if(path_chunk != NULL) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Signing data [%s]", path_chunk+1); - } - - if(msr->txcfg->crypto_key_add == HASH_KEYONLY) - hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - - if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { - if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - } else { - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Using session id [%s]", msr->sessionid); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - } - } - - if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - } - } else { - return NULL; - } - } - else if(*link=='/'){ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Signing data [%s]", link+1); - } - - if(msr->txcfg->crypto_key_add == HASH_KEYONLY) - hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); - - if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { - if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); - } else { - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Using session id [%s]", msr->sessionid); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); - } - } - - if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); - } - - } - else { - char *relative_link = NULL; - char *filename = NULL; - char *relative_path = NULL; - char *relative_uri = NULL; - - filename = file_basename(msr->mp, msr->r->parsed_uri.path); - - if(filename == NULL || (strlen(msr->r->parsed_uri.path) - strlen(filename) < 0)) - return NULL; - - relative_path = apr_pstrndup(msr->mp, msr->r->parsed_uri.path, strlen(msr->r->parsed_uri.path) - strlen(filename)); - relative_uri = apr_pstrcat(msr->mp, relative_path, link, NULL); - - relative_link = relative_uri+1; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Signing data [%s] size %d", relative_link, strlen(relative_link)); - } - - if(msr->txcfg->crypto_key_add == HASH_KEYONLY) - hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); - - if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { - if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); - } else { - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); - if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "Using session id [%s]", msr->sessionid); - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); - } - } - - if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip); -#else - const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); -#endif - msr->txcfg->crypto_key_len = strlen(new_pwd); - hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); - } - - link = relative_uri; - - } - - if(hash_value == NULL) return NULL; - - if(type == HASH_ONLY) - return hash_value; - - qm = strchr((char*)link,'?'); - if(qm == NULL){ - mac_link= (char*)apr_psprintf(msr->mp, "%s?%s=%s", link, msr->txcfg->crypto_param_name, (char *)hash_value); - } - else{ - mac_link= (char*)apr_psprintf(msr->mp, "%s&%s=%s", link, msr->txcfg->crypto_param_name, (char*)hash_value); - } - - return mac_link; -} - -/** - * \brief Modify Location in case of status 302 and 301 - * - * \param msr ModSecurity transaction resource - * - * \retval 1 On Success - * \retval 0 on fail - */ -int modify_response_header(modsec_rec *msr) { - char *mac_link = NULL; - const char *location = NULL; - int rc = 0; - - if(msr == NULL) return 0; - - if (msr->txcfg->debuglog_level >= 9) - msr_log(msr, 4, "HTTP status (%d)", msr->response_status); - - if(msr->response_status != HTTP_MOVED_TEMPORARILY && - msr->response_status != HTTP_MOVED_PERMANENTLY) { - if (msr->txcfg->debuglog_level >= 9) - msr_log(msr, 4, "Skipping status other than 302 an 301"); - return 0; - } - - location = apr_table_get(msr->r->headers_out, "Location"); - - if(location == NULL || strlen(location) == 0) - return 0; - - if (msr->txcfg->debuglog_level >= 9) - msr_log(msr, 4, "Processing reponse header location [%s]", location); - - if(msr->txcfg->crypto_hash_location_rx == 1) { - rc = do_hash_method(msr, (char *)location, HASH_URL_LOCATION_HASH_RX); - - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)location, FULL_LINK); - } else { - return 0; - } - - } else if(msr->txcfg->crypto_hash_location_pm == 1) { - rc = do_hash_method(msr, (char *)location, HASH_URL_LOCATION_HASH_PM); - - if(rc > 0) { - mac_link = NULL; - mac_link = do_hash_link(msr, (char *)location, FULL_LINK); - } else { - return 0; - } - - } - - if(mac_link == NULL) - return 0; - - if (msr->txcfg->debuglog_level >= 9) - msr_log(msr, 4, "Setting new reponse header location [%s]", mac_link); - - if(rc > 0) { - apr_table_unset(msr->r->headers_out,"Location"); - apr_table_set(msr->r->headers_out, "Location",(char*)apr_psprintf(msr->mp,"%s", mac_link)); - } - - return 1; -} diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h deleted file mode 100644 index 3b3aa6da44..0000000000 --- a/apache2/msc_crypt.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_CRYPT_H_ -#define _MSC_CRYPT_H_ - -#include "modsecurity.h" -#include -#include - -#define HMAC_PAD_SIZE 65 -#define HASH_ONLY 0 -#define FULL_LINK 1 - -#ifndef INT32_MAX -#define INT32_MAX (2147483647) -#endif - -char DSOLOCAL *hmac(modsec_rec *msr, const char *key, int key_len, - unsigned char *msg, int msglen); -char DSOLOCAL *do_hash_link(modsec_rec *msr, char *link, - int type); -char DSOLOCAL *getkey(apr_pool_t *mp); - -int DSOLOCAL init_response_body_html_parser(modsec_rec *msr); -int DSOLOCAL hash_response_body_links(modsec_rec *msr); -int DSOLOCAL inject_hashed_response_body(modsec_rec *msr, int elts); -int DSOLOCAL do_hash_method(modsec_rec *msr, char *link, int type); -int DSOLOCAL modify_response_header(modsec_rec *msr); -char DSOLOCAL *normalize_path(modsec_rec *msr, char *input); -#endif diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c deleted file mode 100644 index 49cf292c7d..0000000000 --- a/apache2/msc_geo.c +++ /dev/null @@ -1,506 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_geo.h" - - -/* -- Lookup Tables -- */ - -static const char geo_country_code[GEO_COUNTRY_LAST + 1][4] = { - "--", - "AP","EU","AD","AE","AF","AG","AI","AL","AM","AN", - "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB", - "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO", - "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD", - "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR", - "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO", - "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ", - "FK","FM","FO","FR","FX","GA","GB","GD","GE","GF", - "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT", - "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID", - "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO", - "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW", - "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT", - "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML", - "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV", - "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI", - "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF", - "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW", - "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD", - "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO", - "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH", - "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW", - "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE", - "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA", - "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE" -}; - -static const char geo_country_code3[GEO_COUNTRY_LAST + 1][4] = { - "--", - "AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT", - "AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB", - "BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL", - "BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD", - "CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI", - "CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM", - "DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI", - "FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF", - "GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM", - "GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN", - "IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR", - "JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT", - "CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU", - "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI", - "MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV", - "MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC", - "NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF", - "PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW", - "PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN", - "SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM", - "SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA", - "TJK","TKL","TKM","TUN","TON","TLS","TUR","TTO","TUV","TWN", - "TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN", - "VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","SRB","ZAF", - "ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY" -}; - -static const char *const geo_country_name[GEO_COUNTRY_LAST + 1] = { - "N/A", - "Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", - "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados", - "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia", - "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the", - "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica", - "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", - "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji", - "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana", - "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala", - "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia", - "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan", - "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", - "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", - "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali", - "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives", - "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", - "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", - "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau", - "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan", - "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname", - "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", - "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", - "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", - "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", - "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey" -}; - -static const char geo_country_continent[GEO_COUNTRY_LAST + 1][4] = { - "--", - "AS","EU","EU","AS","AS","SA","SA","EU","AS","SA", - "AF","AN","SA","OC","EU","OC","SA","AS","EU","SA", - "AS","EU","AF","EU","AS","AF","AF","SA","AS","SA", - "SA","SA","AS","AF","AF","EU","SA","NA","AS","AF", - "AF","AF","EU","AF","OC","SA","AF","AS","SA","SA", - "SA","AF","AS","AS","EU","EU","AF","EU","SA","SA", - "AF","SA","EU","AF","AF","AF","EU","AF","EU","OC", - "SA","OC","EU","EU","EU","AF","EU","SA","AS","SA", - "AF","EU","SA","AF","AF","SA","AF","EU","SA","SA", - "OC","AF","SA","AS","AF","SA","EU","SA","EU","AS", - "EU","AS","AS","AS","AS","AS","EU","EU","SA","AS", - "AS","AF","AS","AS","OC","AF","SA","AS","AS","AS", - "SA","AS","AS","AS","SA","EU","AS","AF","AF","EU", - "EU","EU","AF","AF","EU","EU","AF","OC","EU","AF", - "AS","AS","AS","OC","SA","AF","SA","EU","AF","AS", - "AF","NA","AS","AF","AF","OC","AF","OC","AF","SA", - "EU","EU","AS","OC","OC","OC","AS","SA","SA","OC", - "OC","AS","AS","EU","SA","OC","SA","AS","EU","OC", - "SA","AS","AF","EU","AS","AF","AS","OC","AF","AF", - "EU","AS","AF","EU","EU","EU","AF","EU","AF","AF", - "SA","AF","SA","AS","AF","SA","AF","AF","AF","AS", - "AS","OC","AS","AF","OC","AS","AS","SA","OC","AS", - "AF","EU","AF","OC","NA","SA","AS","EU","SA","SA", - "SA","SA","AS","OC","OC","OC","AS","AF","EU","AF", - "AF","EU","AF","--","--","--","EU","EU","EU","EU" -}; - -typedef enum { -GEOIP_COUNTRY_EDITION = 1, -GEOIP_REGION_EDITION_REV0 = 7, -GEOIP_CITY_EDITION_REV0 = 6, -GEOIP_ORG_EDITION = 5, -GEOIP_ISP_EDITION = 4, -GEOIP_CITY_EDITION_REV1 = 2, -GEOIP_REGION_EDITION_REV1 = 3, -GEOIP_PROXY_EDITION = 8, -GEOIP_ASNUM_EDITION = 9, -GEOIP_NETSPEED_EDITION = 10, -GEOIP_DOMAIN_EDITION = 11 -} GeoIPDBTypes; - -static void create_segments(geo_db *geo) { - int i, j; - unsigned char delim[3]; - unsigned char buf[GEO_SEGMENT_RECORD_LENGTH]; - apr_size_t nbytes; - apr_off_t offset; - - geo->ctry_offset = 0; - - geo->dbtype = GEOIP_COUNTRY_EDITION; - offset = -3l; - apr_file_seek(geo->db, APR_END, &offset); - - for (i = 0; i < GEO_STRUCT_INFO_MAX_SIZE; i++) { - - apr_file_read_full(geo->db, &delim, 3, &nbytes); - - if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) { - apr_file_read_full(geo->db, &geo->dbtype, 1, &nbytes); - if (geo->dbtype >= 106) { - geo->dbtype -= 105; - } - - if (geo->dbtype == GEOIP_REGION_EDITION_REV0) { - geo->ctry_offset = GEO_STATE_BEGIN_REV0; - } else if (geo->dbtype == GEOIP_REGION_EDITION_REV1) { - geo->ctry_offset = GEO_STATE_BEGIN_REV1; - } else if (geo->dbtype == GEOIP_CITY_EDITION_REV0 || - geo->dbtype == GEOIP_CITY_EDITION_REV1 || - geo->dbtype == GEOIP_ORG_EDITION || - geo->dbtype == GEOIP_ISP_EDITION || - geo->dbtype == GEOIP_ASNUM_EDITION) { - geo->ctry_offset = 0; - apr_file_read_full(geo->db, &buf, GEO_SEGMENT_RECORD_LENGTH, &nbytes); - for (j = 0; j < GEO_SEGMENT_RECORD_LENGTH; j++) { - geo->ctry_offset += (buf[j] << (j * 8)); - } - } - break; - } else { - offset = -4l; - apr_file_seek(geo->db, APR_CUR, &offset); - } - } - if (geo->dbtype == GEOIP_COUNTRY_EDITION || - geo->dbtype == GEOIP_PROXY_EDITION || - geo->dbtype == GEOIP_NETSPEED_EDITION) { - geo->ctry_offset = GEO_COUNTRY_BEGIN; - } -} - -static int db_open(directory_config *dcfg, char **error_msg) -{ - char errstr[1024]; - apr_pool_t *mp = dcfg->mp; - geo_db *geo = dcfg->geo; - apr_status_t rc; - - #ifdef DEBUG_CONF - fprintf(stderr, "GEO: Initializing geo DB \"%s\".\n", geo->dbfn); - #endif - - if ((rc = apr_file_open(&geo->db, geo->dbfn, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) { - *error_msg = apr_psprintf(mp, "Could not open geo database \"%s\": %s", geo->dbfn, apr_strerror(rc, errstr, 1024)); - return 0; - } - - create_segments(geo); - return 1; -} - -static int field_length(const char *field, int maxlen) -{ - int i; - - if (field == NULL) { - return 0; - } - - for (i = 0; i < maxlen; i++) { - if (field[i] == '\0') { - break; - } - } - - return i; -} - -/** - * Initialise Geo data structure - */ -int geo_init(directory_config *dcfg, const char *dbfn, char **error_msg) -{ - *error_msg = NULL; - - if ((dcfg->geo == NULL) || (dcfg->geo == NOT_SET_P)) { - dcfg->geo = apr_pcalloc(dcfg->mp, sizeof(geo_db)); - } - - dcfg->geo->db = NULL; - dcfg->geo->dbfn = apr_pstrdup(dcfg->mp, dbfn); - dcfg->geo->dbtype = 0; - dcfg->geo->ctry_offset = 0; - - return db_open(dcfg, error_msg); -} - -/** - * Perform geographical lookup on target. - */ -int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **error_msg) -{ - apr_sockaddr_t *addr; - long ipnum = 0; - char *targetip = NULL; - geo_db *geo = msr->txcfg->geo; - char errstr[1024]; - unsigned char buf[2* GEO_MAX_RECORD_LEN]; - const int reclen = 3; /* Algorithm needs changed if this changes */ - apr_size_t nbytes; - unsigned int rec_val = 0; - apr_off_t seekto = 0; - apr_status_t ret; - int rc; - int country = 0; - int level; - double dtmp; - int itmp; - - *error_msg = NULL; - - /* init */ - georec->country_code = geo_country_code[0]; - georec->country_code3 = geo_country_code3[0]; - georec->country_name = geo_country_name[0]; - georec->country_continent = geo_country_continent[0]; - georec->region = ""; - georec->city = ""; - georec->postal_code = ""; - georec->latitude = 0; - georec->longitude = 0; - georec->dma_code = 0; - georec->area_code = 0; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: Looking up \"%s\".", log_escape(msr->mp, target)); - } - - /* NOTE: This only works with ipv4 */ - if ((rc = apr_sockaddr_info_get(&addr, target, APR_INET, 0, 0, msr->mp)) != APR_SUCCESS) { - - *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024)); - msr_log(msr, 4, "%s", *error_msg); - return 0; - } - if ((rc = apr_sockaddr_ip_get(&targetip, addr)) != APR_SUCCESS) { - *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024)); - msr_log(msr, 4, "%s", *error_msg); - return 0; - }; - - /* Why is this in host byte order? */ - ipnum = ntohl(addr->sa.sin.sin_addr.s_addr); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08lx). %lu", targetip, ipnum, ipnum); - } - - ret = apr_global_mutex_lock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - - for (level = 31; level >= 0; level--) { - /* Read the record */ - seekto = 2 * reclen * rec_val; - apr_file_seek(geo->db, APR_SET, &seekto); - /* TODO: check rc */ - rc = apr_file_read_full(geo->db, &buf, (2 * reclen), &nbytes); - - /* NOTE: This is hard-coded for size 3 records */ - /* Left */ - if ((ipnum & (1 << level)) == 0) { - rec_val = (buf[3*0 + 0] << (0*8)) + - (buf[3*0 + 1] << (1*8)) + - (buf[3*0 + 2] << (2*8)); - } - /* Right */ - else { - rec_val = (buf[3*1 + 0] << (0*8)) + - (buf[3*1 + 1] << (1*8)) + - (buf[3*1 + 2] << (2*8)); - } - - /* If we are past the country offset, then we are done */ - if (rec_val >= geo->ctry_offset) { - break; - } - } - - if (rec_val == geo->ctry_offset) { - *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\").", log_escape(msr->mp, target)); - msr_log(msr, 4, "%s", *error_msg); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - - return 0; - } - - if (geo->dbtype == GEO_COUNTRY_DATABASE) { - country = rec_val; - country -= geo->ctry_offset; - if ((country <= 0) || (country > GEO_COUNTRY_LAST)) { - *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country); - msr_log(msr, 4, "%s", *error_msg); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - - return 0; - } - - /* Country */ - georec->country_code = geo_country_code[country]; - georec->country_code3 = geo_country_code3[country]; - georec->country_name = geo_country_name[country]; - georec->country_continent = geo_country_continent[country]; - } - else { - int field_len = 0; - int rec_offset = 0; - int remaining = GEO_CITY_RECORD_LEN; - unsigned char cbuf[GEO_CITY_RECORD_LEN]; - - seekto = rec_val + (2 * reclen - 1) * geo->ctry_offset; - apr_file_seek(geo->db, APR_SET, &seekto); - /* TODO: check rc */ - rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes); - - country = cbuf[0]; - if ((country <= 0) || (country > GEO_COUNTRY_LAST)) { - *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country); - msr_log(msr, 4, "%s", *error_msg); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - - return 0; - } - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: rec=\"%s\"", log_escape_raw(msr->mp, cbuf, sizeof(cbuf))); - } - - /* Country */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: country=\"%.*s\"", (1*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))); - } - georec->country_code = geo_country_code[country]; - georec->country_code3 = geo_country_code3[country]; - georec->country_name = geo_country_name[country]; - georec->country_continent = geo_country_continent[country]; - rec_offset++; - remaining -= rec_offset; - - /* Region */ - field_len = field_length((const char *)cbuf+rec_offset, remaining); - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: region=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); - } - georec->region = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); - rec_offset += field_len + 1; - remaining -= field_len + 1; - - /* City */ - field_len = field_length((const char *)cbuf+rec_offset, remaining); - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: city=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); - } - georec->city = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); - rec_offset += field_len + 1; - remaining -= field_len + 1; - - /* Postal Code */ - field_len = field_length((const char *)cbuf+rec_offset, remaining); - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: postal_code=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); - } - georec->postal_code = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); - rec_offset += field_len + 1; - remaining -= field_len + 1; - - /* Latitude */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: latitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); - } - dtmp = cbuf[rec_offset] + - (cbuf[rec_offset+1] << 8) + - (cbuf[rec_offset+2] << 16); - georec->latitude = dtmp/10000 - 180; - rec_offset += 3; - remaining -= 3; - - - /* Longitude */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: longitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); - } - dtmp = cbuf[rec_offset] + - (cbuf[rec_offset+1] << 8) + - (cbuf[rec_offset+2] << 16); - georec->longitude = dtmp/10000 - 180; - rec_offset += 3; - remaining -= 3; - - /* dma/area codes are in city rev1 and US only */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: dma/area=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); - } - if (geo->dbtype == GEO_CITY_DATABASE_1 - && georec->country_code[0] == 'U' - && georec->country_code[1] == 'S') - { - /* DMA Code */ - itmp = cbuf[rec_offset] + - (cbuf[rec_offset+1] << 8) + - (cbuf[rec_offset+2] << 16); - georec->dma_code = itmp / 1000; - georec->area_code = itmp % 1000; - rec_offset += 6; - remaining -= 6; - } - - } - - *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded.", log_escape(msr->mp, target)); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - - return 1; -} - - diff --git a/apache2/msc_geo.h b/apache2/msc_geo.h deleted file mode 100644 index 1293614608..0000000000 --- a/apache2/msc_geo.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_GEO_H_ -#define _MSC_GEO_H_ - -#define GEO_STRUCT_INFO_MAX_SIZE 20 -#define GEO_DB_INFO_MAX_SIZE 100 -#define GEO_COUNTRY_OFFSET 0xffff00 -#define GEO_MAX_RECORD_LEN 4 -#define GEO_COUNTRY_UNKNOWN "Unknown" -#define GEO_CITY_UNKNOWN "Unknown" -#define GEO_CITY_RECORD_LEN 50 -#define GEO_COUNTRY_DATABASE 1 -#define GEO_CITY_DATABASE_0 6 -#define GEO_CITY_DATABASE_1 2 -#define GEO_COUNTRY_LAST 250 -#define GEO_SEGMENT_RECORD_LENGTH 3 -#define GEO_STATE_BEGIN_REV0 16700000 -#define GEO_STATE_BEGIN_REV1 16000000 -#define GEO_COUNTRY_BEGIN 16776960 - - -typedef struct geo_rec geo_rec; -typedef struct geo_db geo_db; - -#include -#include "modsecurity.h" - -/* Structures */ - -struct geo_rec { - const char *country_code; - const char *country_code3; - const char *country_name; - const char *country_continent; - const char *region; - const char *city; - const char *postal_code; - float latitude; - float longitude; - int dma_code; - int area_code; -}; - -struct geo_db { - apr_file_t *db; - const char *dbfn; - int dbtype; - unsigned int ctry_offset; -}; - -/* Functions */ - -int DSOLOCAL geo_init(directory_config *dcfg, const char *dbfn, char **error_msg); - -int DSOLOCAL geo_lookup(modsec_rec *msr, geo_rec *rec, const char *target, char **error_msg); - -#endif diff --git a/apache2/msc_gsb.c b/apache2/msc_gsb.c deleted file mode 100644 index 49b1ff4e5d..0000000000 --- a/apache2/msc_gsb.c +++ /dev/null @@ -1,126 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_gsb.h" - -/** \brief Load GSB database - * - * \param dcfg Pointer to directory configuration - * \param error_msg Error message - * - * \retval 1 On Success - * \retval 0 On Fail - */ -static int gsb_db_create(directory_config *dcfg, char **error_msg) -{ - char errstr[1024]; - apr_pool_t *mp = dcfg->mp; - gsb_db *gsb = dcfg->gsb; - apr_int32_t wanted = APR_FINFO_SIZE; - apr_finfo_t finfo; - apr_status_t rc; - apr_size_t nbytes; - char *buf = NULL, *p = NULL, *savedptr = NULL; - char *op = NULL; - - if ((rc = apr_file_open(&gsb->db, gsb->dbfn, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) { - *error_msg = apr_psprintf(mp, "Could not open gsb database \"%s\": %s", gsb->dbfn, apr_strerror(rc, errstr, 1024)); - return 0; - } - - if ((rc = apr_file_info_get(&finfo, wanted, gsb->db)) != APR_SUCCESS) { - *error_msg = apr_psprintf(mp, "Could not cannot get gsb malware file information \"%s\": %s", gsb->dbfn, apr_strerror(rc, errstr, 1024)); - apr_file_close(gsb->db); - return 0; - } - - buf = (char *)malloc(finfo.size+1); - - if (buf == NULL) { - *error_msg = apr_psprintf(mp, "Could not alloc memory for gsb data"); - apr_file_close(gsb->db); - return 0; - } - - rc = apr_file_read_full(gsb->db, buf, finfo.size, &nbytes); - - gsb->gsb_table = apr_hash_make(dcfg->mp); - - if (gsb->gsb_table == NULL) { - *error_msg = apr_psprintf(mp, "Could not alloc memory for gsb table"); - free(buf); - buf = NULL; - apr_file_close(gsb->db); - return 0; - } - - p = apr_strtok(buf,"\t",&savedptr); - - while (p != NULL) { - - op = strchr(p,'+'); - - if(op != NULL) { - char *hash = ++op; - if(strlen(hash) == 32) - apr_hash_set(gsb->gsb_table, hash, APR_HASH_KEY_STRING, "malware"); - } - - op = strchr(p,'-'); - - if(op != NULL) { - char *hash = ++op; - if(strlen(hash) == 32) - apr_hash_set(gsb->gsb_table, hash, APR_HASH_KEY_STRING, NULL); - } - - p = apr_strtok(NULL,"\t",&savedptr); - } - - apr_file_close(gsb->db); - - free(buf); - buf = NULL; - - return 1; -} - - -/** \brief Init GSB database - * - * \param dcfg Pointer to directory configuration - * \param dbfn Database filename - * \param error_msg Error message - * - * \retval gsb_db_create On Success - * \retval -1 On Fail - */ -int gsb_db_init(directory_config *dcfg, const char *dbfn, char **error_msg) -{ - - *error_msg = NULL; - - if ((dcfg->gsb == NULL) || (dcfg->gsb == NOT_SET_P)) { - dcfg->gsb = apr_pcalloc(dcfg->mp, sizeof(gsb_db)); - if (dcfg->gsb == NULL) { - return -1; - } - } - - dcfg->gsb->db = NULL; - dcfg->gsb->dbfn = apr_pstrdup(dcfg->mp, dbfn); - - return gsb_db_create(dcfg, error_msg); -} - diff --git a/apache2/msc_gsb.h b/apache2/msc_gsb.h deleted file mode 100644 index 87399d16d5..0000000000 --- a/apache2/msc_gsb.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_GSB_H_ -#define _MSC_GSB_H_ - -typedef struct gsb_db gsb_db; - -#include -#include "modsecurity.h" -#include "apr_hash.h" - -struct gsb_db { - apr_file_t *db; - const char *dbfn; - apr_hash_t *gsb_table; -}; - -int DSOLOCAL gsb_db_init(directory_config *dcfg, const char *dbfn, char **error_msg); - -#endif diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c deleted file mode 100644 index 66d204f7b5..0000000000 --- a/apache2/msc_logging.c +++ /dev/null @@ -1,1238 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "modsecurity.h" -#include - -#include "re.h" -#include "msc_logging.h" -#include "httpd.h" -#include "apr_strings.h" -#include "apr_global_mutex.h" -#include "msc_util.h" - -#include "apr_version.h" -#include - -/** - * Write the supplied data to the audit log (if the FD is ready), update - * the size counters, update the hash context. - */ -static int sec_auditlog_write(modsec_rec *msr, const char *data, unsigned int len) { - apr_size_t nbytes_written, nbytes = len; - apr_status_t rc; - - /* Do nothing if there's no data. */ - if (data == NULL) return -1; - - /* Update size counters and the hash calculation. We always do this, - * even in cases where write fails. That will make it easier to detect - * problems with partial writes. - */ - msr->new_auditlog_size += len; - apr_md5_update(&msr->new_auditlog_md5ctx, data, len); - - /* Do not write if we do not have a file descriptor. */ - if (msr->new_auditlog_fd == NULL) return -1; - - /* Write data to file. */ - rc = apr_file_write_full(msr->new_auditlog_fd, data, nbytes, &nbytes_written); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed writing (requested %" APR_SIZE_T_FMT - " bytes, written %" APR_SIZE_T_FMT ")", nbytes, nbytes_written); - - /* Set to NULL to prevent more than one error message on - * out-of-disk-space events and to prevent further attempts - * to write to the same file in this request. - * - * Note that, as we opened the file through the pool mechanism of - * the APR, we do not need to close the file here. It will be closed - * automatically at the end of the request. - */ - msr->new_auditlog_fd = NULL; - - return -1; - } - - return 1; -} - -/** - * Constructs a log line in vcombined log format trying to truncate - * some of the fields to make the log line shorter than _limit bytes. - */ -char *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_limited) { - char *hostname; - char *local_user, *remote_user; - char *referer, *user_agent, *uniqueid; - const char *sessionid; - char *the_request, *bytes_sent; - int limit = _limit; - - /* hostname */ - hostname = (msr->hostname == NULL ? "-" : log_escape_nq(msr->mp, msr->hostname)); - - /* remote log name */ - if (msr->remote_user == NULL) remote_user = "-"; - else remote_user = log_escape_nq(msr->mp, msr->remote_user); - - /* authenticated user */ - if (msr->local_user == NULL) local_user = "-"; - else local_user = log_escape_nq(msr->mp, msr->local_user); - - /* unique id */ - if (msr->txid == NULL) uniqueid = "-"; - else uniqueid = log_escape(msr->mp, msr->txid); - - /* referer */ - referer = "-"; - /* - referer = (char *)apr_table_get(msr->request_headers, "Referer"); - if (referer == NULL) referer = "-"; - else referer = log_escape(msr->mp, referer); - */ - - /* user agent */ - user_agent = "-"; - /* - user_agent = (char *)apr_table_get(msr->request_headers, "User-Agent"); - if (user_agent == NULL) user_agent = "-"; - else user_agent = log_escape(msr->mp, user_agent); - */ - - /* sessionid */ - sessionid = (msr->sessionid == NULL) ? "-" : log_escape(msr->mp, msr->sessionid); - - the_request = (msr->request_line == NULL) ? "" : log_escape(msr->mp, msr->request_line); - - bytes_sent = apr_psprintf(msr->mp, "%" APR_OFF_T_FMT, msr->bytes_sent); - - /* first take away the size of the - * information we must log - */ - limit -= 22; /* spaces and double quotes */ - limit -= strlen(hostname); /* server name or IP */ - limit -= strlen(msr->remote_addr); /* remote IP */ - limit -= 28; /* current_logtime */ - limit -= 3; /* status */ - limit -= strlen(bytes_sent); /* bytes sent */ - limit -= strlen(uniqueid); /* unique id */ - limit -= strlen(sessionid); /* session id */ - - if (limit <= 0) { - msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %d", PIPE_BUF); - return NULL; - } - - /* we hope to be able to squeeze everything in */ - if (limit < (int)(strlen(remote_user) + strlen(local_user) + strlen(referer) - + strlen(user_agent) + strlen(the_request))) - { - /* Boo hoo hoo, there's not enough space available. */ - *was_limited = 1; - - /* Let's see if we can reduce the size of something. This - * is a very crude approach but it seems to work for our - * needs. - */ - if (strlen(remote_user) > 32) { - msr_log(msr, 9, "GuardianLog: Reduced remote_user to 32."); - remote_user[32] = '\0'; - } - limit -= strlen(remote_user); - - if (strlen(local_user) > 32) { - msr_log(msr, 9, "GuardianLog: Reduced local_user to 32."); - local_user[32] = '\0'; - } - limit -= strlen(local_user); - - if (strlen(referer) > 64) { - msr_log(msr, 9, "GuardianLog: Reduced referer to 64."); - referer[64] = '\0'; - } - limit -= strlen(referer); - - if (strlen(user_agent) > 64) { - msr_log(msr, 9, "GuardianLog: Reduced user_agent to 64."); - user_agent[64] = '\0'; - } - limit -= strlen(user_agent); - - if (limit <= 0) { - msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %d.", PIPE_BUF); - return NULL; - } - - /* use what's left for the request line */ - if ((int)strlen(the_request) > limit) { - the_request[limit] = '\0'; - msr_log(msr, 9, "GuardianLog: Reduced the_request to %d bytes.", limit); - } - } else { - /* Yay! We have enough space! */ - *was_limited = 0; - } - - return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %u %s \"%s\" \"%s\" %s \"%s\"", - hostname, msr->remote_addr, remote_user, - local_user, current_logtime(msr->mp), the_request, - msr->response_status, bytes_sent, referer, user_agent, - uniqueid, sessionid - ); -} - -/** - * Checks if the provided string is a valid audit log parts specification. - */ -int is_valid_parts_specification(char *p) { - char c, *t = p; - - while((c = *(t++)) != '\0') { - if ((c != AUDITLOG_PART_ENDMARKER)&&((c < AUDITLOG_PART_FIRST)||(c > AUDITLOG_PART_LAST))) { - return 0; - } - } - - return 1; -} - -/** - * Constructs a filename that will be used to store an - * audit log entry. - */ -static char *construct_auditlog_filename(apr_pool_t *mp, const char *uniqueid) { - apr_time_exp_t t; - char tstr[300]; - apr_size_t len; - - apr_time_exp_lt(&t, apr_time_now()); - - apr_strftime(tstr, &len, 299, "/%Y%m%d/%Y%m%d-%H%M/%Y%m%d-%H%M%S", &t); - return apr_psprintf(mp, "%s-%s", tstr, uniqueid); -} - -/** - * Creates a random 8-character string that - * consists of hexadecimal numbers, to be used - * as an audit log boundary. - */ -static char *create_auditlog_boundary(request_rec *r) { -#ifdef LINUX_S390 - int data = swap_int32(rand()); -#else - unsigned long data = rand(); -#endif - /* Do note that I tried using apr_generate_random_bytes but it turned - * out to be terribly slow for some reason. Needs further investigation. - */ - return bytes2hex(r->pool, (void *)&data, 4); -} - -/** - * Sanitises the request line by removing the parameters - * that have been marked as sensitive. - */ -static void sanitize_request_line(modsec_rec *msr) { - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - const apr_array_header_t *tarr_pattern = NULL; - const apr_table_entry_t *telts_pattern = NULL; - msc_parm *mparm = NULL; - int i, k; - char *qspos; - char *buf = NULL; - int sanitized_partial = 0; - int sanitize_matched = 0; - - /* Locate the query string. */ - qspos = strstr(msr->request_line, "?"); - if (qspos == NULL) return; - qspos++; - - /* Loop through the list of sensitive parameters. */ - tarr = apr_table_elts(msr->arguments_to_sanitize); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msc_arg *arg = (msc_arg *)telts[i].val; - /* Only look at the parameters that appeared in the query string. */ - if (strcmp(arg->origin, "QUERY_STRING") == 0) { - char *pat = NULL; - char *p; - int j, arg_min, arg_max; - - /* Go to the beginning of the parameter. */ - p = qspos; - j = arg->value_origin_offset; - while((*p != '\0')&&(j--)) p++; - if (*p == '\0') { - msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u of QUERY_STRING" - "because the request line is too short.", - log_escape_ex(msr->mp, arg->name, arg->name_len), - arg->value_origin_offset); - continue; - } - - tarr_pattern = apr_table_elts(msr->pattern_to_sanitize); - telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts; - - sanitized_partial = 0; - sanitize_matched = 0; - buf = apr_psprintf(msr->mp, "%s",p); - for ( k = 0; k < tarr_pattern->nelts; k++) { - if(strncmp(telts_pattern[k].key,arg->name,strlen(arg->name)) ==0 ) { - mparm = (msc_parm *)telts_pattern[k].val; - pat = strstr(buf,mparm->value); - if(mparm->pad_1 == -1) - sanitize_matched = 1; - - if (pat != NULL) { - j = strlen(mparm->value); - arg_min = j; - arg_max = 1; - while((*pat != '\0')&&(j--)) { - if(arg_max > mparm->pad_2) { - int off = (strlen(mparm->value) - arg_max); - int pos = (mparm->pad_1-1); - if(off > pos) { - *pat = '*'; - } - } - arg_max++; - arg_min--; - pat++; - } - } - sanitized_partial = 1; - } - } - - if(sanitized_partial == 1 && sanitize_matched == 0) { - while(*buf != '\0') { - *p++ = *buf++; - } - continue; - } else { - /* Write over the value. */ - j = arg->value_origin_len; - while((*p != '\0')&&(j--)) { - *p++ = '*'; - } - - if (*p == '\0') { - msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u (size %d) " - "of QUERY_STRING because the request line is too short.", - log_escape_ex(msr->mp, arg->name, arg->name_len), - arg->value_origin_offset, arg->value_origin_len); - continue; - } - } - } - } -} - -/** - * Output the Producer header. - */ -static void sec_auditlog_write_producer_header(modsec_rec *msr) { - char **signatures = NULL; - char *text = NULL; - int i; - - /* Try to write everything in one go. */ - if (msr->txcfg->component_signatures->nelts == 0) { - text = apr_psprintf(msr->mp, "Producer: %s.\n", MODSEC_MODULE_NAME_FULL); - sec_auditlog_write(msr, text, strlen(text)); - - return; - } - - /* Start with the ModSecurity signature. */ - text = apr_psprintf(msr->mp, "Producer: %s", MODSEC_MODULE_NAME_FULL); - sec_auditlog_write(msr, text, strlen(text)); - - - /* Then loop through the components and output individual signatures. */ - signatures = (char **)msr->txcfg->component_signatures->elts; - for(i = 0; i < msr->txcfg->component_signatures->nelts; i++) { - text = apr_psprintf(msr->mp, "; %s", (char *)signatures[i]); - sec_auditlog_write(msr, text, strlen(text)); - } - - sec_auditlog_write(msr, ".\n", 2); -} - -/* -* \brief This function will returns the next chain node -* -* \param current Pointer to current chined rule -* \param msr Pointer to modsec_rec -* -* \retval NULL On failure -* \retval next_rule On Success -*/ -static msre_rule *return_chained_rule(const msre_rule *current, modsec_rec *msr) { - apr_array_header_t *arr = NULL; - msre_rule **rules = NULL; - msre_rule *rule = NULL, *next_rule = NULL; - int i; - int phase; - - if (current == NULL || current->actionset == NULL || current->ruleset == NULL) - return NULL; - - phase = current->actionset->phase; - - switch (phase) { - case PHASE_REQUEST_HEADERS : - arr = current->ruleset->phase_request_headers; - break; - case PHASE_REQUEST_BODY : - arr = current->ruleset->phase_request_body; - break; - case PHASE_RESPONSE_HEADERS : - arr = current->ruleset->phase_response_headers; - break; - case PHASE_RESPONSE_BODY : - arr = current->ruleset->phase_response_body; - break; - case PHASE_LOGGING : - arr = current->ruleset->phase_logging; - break; - default : - msr_log(msr, 1, "Logging: Invalid phase %d",phase); - return NULL; - } - - rules = (msre_rule **)arr->elts; - for (i = 0; i < arr->nelts; i++) { - rule = rules[i]; - if (rule != NULL) { - if (strncmp(current->unparsed,rule->unparsed,strlen(current->unparsed)) == 0) { - - if (i < arr->nelts -1) { - next_rule = rules[i+1]; - } else { - next_rule = rules[i]; - } - - if (next_rule == NULL || next_rule->chain_starter == NULL) - return NULL; - - if(current->chain_starter == NULL && next_rule->chain_starter != NULL) { - if (strncmp(current->unparsed, next_rule->chain_starter->unparsed, strlen(current->unparsed)) != 0) - return NULL; - } - - if(current->chain_starter != NULL && next_rule->chain_starter != NULL) { - if (strncmp(current->chain_starter->unparsed, rule->chain_starter->unparsed, strlen(current->chain_starter->unparsed)) != 0) - return NULL; - } - - return next_rule; - } - } - } - - return NULL; -} - -/* -* \brief This function will check if a chained rule -* appears into matched array. -* -* \param msr Pointer to modsec_rec -* \param next_rule Pointer to chained rule -* -* \retval 0 On failure -* \retval 1 On Success -*/ -static int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) { - int i = 0; - const msre_rule *rule = NULL; - - for (i = 0; i < msr->matched_rules->nelts; i++) { - rule = ((msre_rule **)msr->matched_rules->elts)[i]; - if (rule != NULL && (strncmp(rule->unparsed,next_rule->unparsed,strlen(rule->unparsed)) == 0)) { - return 1; - } - } - - return 0; -} - -/** - * Produce an audit log entry. - */ -void sec_audit_logger(modsec_rec *msr) { - const apr_array_header_t *arr = NULL; - apr_table_entry_t *te = NULL; - const apr_array_header_t *tarr_pattern = NULL; - const apr_table_entry_t *telts_pattern = NULL; - char *str1 = NULL, *str2 = NULL, *text = NULL; - const msre_rule *rule = NULL, *next_rule = NULL; - apr_size_t nbytes, nbytes_written; - unsigned char md5hash[APR_MD5_DIGESTSIZE]; - int was_limited = 0; - int present = 0; - int wrote_response_body = 0; - char *entry_filename, *entry_basename; - apr_status_t rc; - int i, limit, k, sanitized_partial, j; - char *buf = NULL, *pat = NULL; - msc_parm *mparm = NULL; - int arg_min, arg_max, sanitize_matched; - - /* the boundary is used by both audit log types */ - msr->new_auditlog_boundary = create_auditlog_boundary(msr->r); - - /* Return silently if we don't have a request line. This - * means we will not be logging request timeouts. - */ - if (msr->request_line == NULL) { - msr_log(msr, 4, "Audit log: Skipping request whose request_line is null."); - return; - } - - /* Also return silently if we don't have a file descriptor. */ - if (msr->txcfg->auditlog_fd == NULL) { - msr_log(msr, 4, "Audit log: Skipping request since there is nowhere to write to."); - return; - } - - if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { - /* Serial logging - we already have an open file - * descriptor to write to. - */ - msr->new_auditlog_fd = msr->txcfg->auditlog_fd; - } else { - /* Concurrent logging - we need to create a brand - * new file for this request. - */ - apr_md5_init(&msr->new_auditlog_md5ctx); - - msr->new_auditlog_filename = construct_auditlog_filename(msr->mp, msr->txid); - if (msr->new_auditlog_filename == NULL) return; - - /* The audit log storage directory should be explicitly - * defined. But if it isn't try to write to the same - * directory where the index file is placed. Of course, - * it is *very* bad practice to allow the Apache user - * to write to the same directory where a root user is - * writing to but it's not us that's causing the problem - * and there isn't anything we can do about that. - * - * ENH Actually there is something we can do! We will make - * SecAuditStorageDir mandatory, ask the user to explicitly - * define the storage location *and* refuse to work if the - * index and the storage location are in the same folder. - */ - if (msr->txcfg->auditlog_storage_dir == NULL) { - entry_filename = file_dirname(msr->mp, msr->txcfg->auditlog_name); - } - else { - entry_filename = msr->txcfg->auditlog_storage_dir; - } - if (entry_filename == NULL) return; - - entry_filename = apr_psprintf(msr->mp, "%s%s", entry_filename, msr->new_auditlog_filename); - if (entry_filename == NULL) return; - entry_basename = file_dirname(msr->mp, entry_filename); - if (entry_basename == NULL) return; - - /* IMP1 Surely it would be more efficient to check the folders for - * the audit log repository base path in the configuration phase, to reduce - * the work we do on every request. Also, since our path depends on time, - * we could cache the time we last checked and don't check if we know - * the folder is there. - */ - rc = apr_dir_make_recursive(entry_basename, msr->txcfg->auditlog_dirperms, msr->mp); - if ((rc != APR_SUCCESS) && (rc != APR_EEXIST)) { - msr_log(msr, 1, "Audit log: Failed to create subdirectories: %s (%s)", - entry_basename, get_apr_error(msr->mp, rc)); - return; - } - - rc = apr_file_open(&msr->new_auditlog_fd, entry_filename, - APR_WRITE | APR_TRUNCATE | APR_CREATE | APR_BINARY | APR_FILE_NOCLEANUP, - msr->txcfg->auditlog_fileperms, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to create file: %s (%s)", - entry_filename, get_apr_error(msr->mp, rc)); - return; - } - } - - /* Lock the mutex, but only if we are using serial format. */ - if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { - rc = apr_global_mutex_lock(msr->modsecurity->auditlog_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to lock global mutex: %s", - get_apr_error(msr->mp, rc)); - } - } - - /* AUDITLOG_PART_HEADER */ - - text = apr_psprintf(msr->mp, "--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_HEADER); - sec_auditlog_write(msr, text, strlen(text)); - - /* Format: time transaction_id remote_addr remote_port local_addr local_port */ - - text = apr_psprintf(msr->mp, "[%s] %s %s %u %s %u", - current_logtime(msr->mp), msr->txid, msr->remote_addr, msr->remote_port, - msr->local_addr, msr->local_port); - sec_auditlog_write(msr, text, strlen(text)); - - - /* AUDITLOG_PART_REQUEST_HEADERS */ - - if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_HEADERS) != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_REQUEST_HEADERS); - sec_auditlog_write(msr, text, strlen(text)); - - sanitize_request_line(msr); - - sec_auditlog_write(msr, msr->request_line, strlen(msr->request_line)); - sec_auditlog_write(msr, "\n", 1); - - arr = apr_table_elts(msr->request_headers); - te = (apr_table_entry_t *)arr->elts; - - tarr_pattern = apr_table_elts(msr->pattern_to_sanitize); - telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts; - - for (i = 0; i < arr->nelts; i++) { - sanitized_partial = 0; - sanitize_matched = 0; - text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val); - if (apr_table_get(msr->request_headers_to_sanitize, te[i].key) != NULL) { - buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2); - - for ( k = 0; k < tarr_pattern->nelts; k++) { - if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) { - mparm = (msc_parm *)telts_pattern[k].val; - if(mparm->pad_1 == -1) - sanitize_matched = 1; - pat = strstr(buf,mparm->value); - if (pat != NULL) { - j = strlen(mparm->value); - arg_min = j; - arg_max = 1; - while((*pat != '\0')&&(j--)) { - if(arg_max > mparm->pad_2) { - int off = strlen(mparm->value) - arg_max; - int pos = mparm->pad_1-1; - if(off > pos) { - *pat = '*'; - } - } - arg_max++; - arg_min--; - pat++; - } - sanitized_partial = 1; - } - } - } - - if(sanitized_partial == 1 && sanitize_matched == 0) { - text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf); - } else { - memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val)); - } - } - sec_auditlog_write(msr, text, strlen(text)); - } - } - - /* AUDITLOG_PART_REQUEST_BODY */ - - /* Output this part of it was explicitly requested (C) or if it was the faked - * request body that was requested (I) but we have no reason to fake it (it's - * already in the correct format). - */ - if ( (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_BODY) != NULL) - || ( (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_FAKE_REQUEST_BODY) != NULL) - && (msr->mpd == NULL) ) ) - { - if (msr->msc_reqbody_read) { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - apr_array_header_t *sorted_args; - unsigned int offset = 0, last_offset = 0; - msc_arg *nextarg = NULL; - int sanitize = 0; /* IMP1 Use constants for "sanitize" values. */ - char *my_error_msg = NULL; - - sorted_args = apr_array_make(msr->mp, 25, sizeof(const msc_arg *)); - - /* First we need to sort the arguments that need to be - * sanitized in descending order (we are using a stack structure - * to store then so the order will be ascending when we start - * popping them out). This is because we will - * be reading the request body sequentially and must - * sanitize it as we go. - */ - - for(;;) { - nextarg = NULL; - - /* Find the next largest offset (excluding - * the ones we've used up already). - */ - tarr = apr_table_elts(msr->arguments_to_sanitize); - telts = (const apr_table_entry_t*)tarr->elts; - for(i = 0; i < tarr->nelts; i++) { - msc_arg *arg = (msc_arg *)telts[i].val; - if (strcmp(arg->origin, "BODY") != 0) continue; - - if (last_offset == 0) { /* The first time we're here. */ - if (arg->value_origin_offset > offset) { - offset = arg->value_origin_offset; - nextarg = arg; - } - } else { /* Not the first time. */ - if ((arg->value_origin_offset > offset) - &&(arg->value_origin_offset < last_offset)) - { - offset = arg->value_origin_offset; - nextarg = arg; - } - } - } - - /* If we don't have the next argument that means - * we're done here. - */ - if (nextarg == NULL) break; - - sanitize = 2; /* Means time to pop the next argument out. */ - last_offset = offset; - offset = 0; - { /* IMP1 Fix this ugly bit here. */ - msc_arg **x = apr_array_push(sorted_args); - *x = nextarg; - } - } - - /* Now start retrieving the body chunk by chunk and - * sanitize data in pieces. - */ - - rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg); - if (rc < 0) { - msr_log(msr, 1, "Audit log: %s", my_error_msg); - } else { - msc_data_chunk *chunk = NULL; - unsigned int chunk_offset = 0; - unsigned int sanitize_offset = 0; - unsigned int sanitize_length = 0; - - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_REQUEST_BODY); - sec_auditlog_write(msr, text, strlen(text)); - - for(;;) { - rc = modsecurity_request_body_retrieve(msr, &chunk, -1, &my_error_msg); - if (chunk != NULL) { - /* Anything greater than 1 means we have more data to sanitize. */ - while (sanitize > 1) { - msc_arg **arg = NULL; - - if (sanitize == 2) { - /* Get the next argument from the stack. */ - arg = (msc_arg **)apr_array_pop(sorted_args); - if (arg == NULL) sanitize = 0; /* We're done sanitising. */ - else { - /* Continue with sanitation to process the - * retrieved argument. - */ - sanitize = 1; - sanitize_offset = (*arg)->value_origin_offset; - sanitize_length = (*arg)->value_origin_len; - } - } - - if (sanitize) { - /* Check if the data we want to sanitize is - * stored in the current chunk. - */ - if (chunk_offset + chunk->length > sanitize_offset) { - unsigned int soff; /* data offset within chunk */ - unsigned int len; /* amount in this chunk to sanitize */ - - soff = sanitize_offset - chunk_offset; - - if (soff + sanitize_length <= chunk->length) { - /* The entire argument resides in the current chunk. */ - len = sanitize_length; - sanitize = 2; /* Get another parameter to sanitize. */ - } else { - /* Some work to do here but we'll need to seek - * another chunk. - */ - len = chunk->length - soff; - sanitize_offset += len; - sanitize_length -= len; - sanitize = 1; /* It's OK to go to the next chunk. */ - } - - /* Yes, we actually write over the original data. - * We shouldn't be needing it any more. - */ - if (soff + len <= chunk->length) { /* double check */ - memset((char *)chunk->data + soff, '*', len); - } - } - } - } - - /* Write the sanitized chunk to the log - * and advance to the next chunk. */ - sec_auditlog_write(msr, chunk->data, chunk->length); - chunk_offset += chunk->length; - } - - if (rc <= 0) { - break; - } - } - - if (rc < 0) { - msr_log(msr, 1, "Audit log: %s", my_error_msg); - } - - modsecurity_request_body_retrieve_end(msr); - } - } - } - - /* AUDITLOG_PART_FAKE_REQUEST_BODY */ - - if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_FAKE_REQUEST_BODY) != NULL) { - if ((msr->msc_reqbody_read)&&(msr->mpd != NULL)) { - char *buffer = NULL; - - buffer = multipart_reconstruct_urlencoded_body_sanitize(msr); - if (buffer == NULL) { - msr_log(msr, 1, "Audit log: Failed to reconstruct request body."); - } else { - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_FAKE_REQUEST_BODY); - sec_auditlog_write(msr, text, strlen(text)); - sec_auditlog_write(msr, buffer, strlen(buffer)); - } - } - } - - /* AUDITLOG_PART_A_RESPONSE_HEADERS */ - - if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_A_RESPONSE_HEADERS) != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_A_RESPONSE_HEADERS); - sec_auditlog_write(msr, text, strlen(text)); - - /* There are no response headers (or the status line) in HTTP 0.9 */ - if (msr->response_headers_sent) { - if (msr->status_line != NULL) { - text = apr_psprintf(msr->mp, "%s %s\n", msr->response_protocol, - msr->status_line); - } else { - text = apr_psprintf(msr->mp, "%s %u\n", msr->response_protocol, - msr->response_status); - } - sec_auditlog_write(msr, text, strlen(text)); - - /* Output headers */ - - arr = apr_table_elts(msr->response_headers); - te = (apr_table_entry_t *)arr->elts; - - tarr_pattern = apr_table_elts(msr->pattern_to_sanitize); - telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts; - - for (i = 0; i < arr->nelts; i++) { - sanitized_partial = 0; - sanitize_matched = 0; - text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val); - if (apr_table_get(msr->response_headers_to_sanitize, te[i].key) != NULL) { - buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2); - - for ( k = 0; k < tarr_pattern->nelts; k++) { - if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) { - mparm = (msc_parm *)telts_pattern[k].val; - if(mparm->pad_1 == -1) - sanitize_matched = 1; - pat = strstr(buf,mparm->value); - if (pat != NULL) { - j = strlen(mparm->value); - arg_min = j; - arg_max = 1; - while((*pat != '\0')&&(j--)) { - if(arg_max > mparm->pad_2) { - int off = strlen(mparm->value) - arg_max; - int pos = mparm->pad_1-1; - if(off > pos) { - *pat = '*'; - } - } - arg_max++; - arg_min--; - pat++; - } - sanitized_partial = 1; - } - } - } - - if(sanitized_partial == 1 && sanitize_matched == 0) { - text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf); - } else { - memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val)); - } - } - sec_auditlog_write(msr, text, strlen(text)); - } - } - } - - apr_table_clear(msr->pattern_to_sanitize); - - /* AUDITLOG_PART_RESPONSE_BODY */ - - if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_RESPONSE_BODY) != NULL) { - if (msr->resbody_data != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_RESPONSE_BODY); - sec_auditlog_write(msr, text, strlen(text)); - sec_auditlog_write(msr, msr->resbody_data, msr->resbody_length); - wrote_response_body = 1; - } - } - - /* AUDITLOG_PART_TRAILER */ - - if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_TRAILER) != NULL) { - apr_time_t now = apr_time_now(); - - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_TRAILER); - sec_auditlog_write(msr, text, strlen(text)); - - /* Messages */ - for(i = 0; i < msr->alerts->nelts; i++) { - text = apr_psprintf(msr->mp, "Message: %s\n", ((char **)msr->alerts->elts)[i]); - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Apache error messages */ - for(i = 0; i < msr->error_messages->nelts; i++) { - error_message_t *em = (((error_message_t **)msr->error_messages->elts)[i]); - text = apr_psprintf(msr->mp, "Apache-Error: %s\n", - format_error_log_message(msr->mp, em)); - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Action */ - if (msr->was_intercepted) { - text = apr_psprintf(msr->mp, "Action: Intercepted (phase %d)\n", msr->intercept_phase); - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Apache-Handler */ - if (msr->r->handler != NULL) { - text = apr_psprintf(msr->mp, "Apache-Handler: %s\n", msr->r->handler); - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Stopwatch; left in for compatibility reasons */ - text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n", - msr->request_time, (now - msr->request_time)); - sec_auditlog_write(msr, text, strlen(text)); - - /* Stopwatch2 */ - { - char *perf_all = format_all_performance_variables(msr, msr->mp); - - text = apr_psprintf(msr->mp, "Stopwatch2: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT - "; %s\n", msr->request_time, (now - msr->request_time), perf_all); - - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Our response body does not contain chunks */ - /* ENH Only write this when the output was chunked. */ - /* ENH Add info when request body was decompressed, dechunked too. */ - if (wrote_response_body) { - text = apr_psprintf(msr->mp, "Response-Body-Transformed: Dechunked\n"); - sec_auditlog_write(msr, text, strlen(text)); - } - - sec_auditlog_write_producer_header(msr); - - /* Server */ - if (msr->server_software != NULL) { - text = apr_psprintf(msr->mp, "Server: %s\n", msr->server_software); - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Sanitised arguments */ - { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - - tarr = apr_table_elts(msr->arguments_to_sanitize); - telts = (const apr_table_entry_t*)tarr->elts; - - if (tarr->nelts > 0) { - text = apr_psprintf(msr->mp, "Sanitised-Args: "); - sec_auditlog_write(msr, text, strlen(text)); - } - - for(i = 0; i < tarr->nelts; i++) { - msc_arg *arg = (msc_arg *)telts[i].val; - text = apr_psprintf(msr->mp, "%s\"%s\"%s", ((i == 0) ? "" : ", "), - log_escape(msr->mp, arg->name), ((i == (tarr->nelts - 1)) ? ".\n" : "")); - sec_auditlog_write(msr, text, strlen(text)); - } - } - - /* Sanitised request headers */ - { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - - tarr = apr_table_elts(msr->request_headers_to_sanitize); - telts = (const apr_table_entry_t*)tarr->elts; - - if (tarr->nelts > 0) { - text = apr_psprintf(msr->mp, "Sanitised-Request-Headers: "); - sec_auditlog_write(msr, text, strlen(text)); - } - - for(i = 0; i < tarr->nelts; i++) { - text = apr_psprintf(msr->mp, "%s\"%s\"%s", ((i == 0) ? "" : ", "), - log_escape(msr->mp, telts[i].key), ((i == (tarr->nelts - 1)) ? ".\n" : "")); - sec_auditlog_write(msr, text, strlen(text)); - } - } - - /* Sanitised response headers */ - { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - - tarr = apr_table_elts(msr->response_headers_to_sanitize); - telts = (const apr_table_entry_t*)tarr->elts; - - if (tarr->nelts > 0) { - text = apr_psprintf(msr->mp, "Sanitised-Response-Headers: "); - sec_auditlog_write(msr, text, strlen(text)); - } - - for(i = 0; i < tarr->nelts; i++) { - text = apr_psprintf(msr->mp, "%s\"%s\"%s", ((i == 0) ? "" : ", "), - log_escape(msr->mp, telts[i].key), ((i == (tarr->nelts - 1)) ? ".\n" : "")); - sec_auditlog_write(msr, text, strlen(text)); - } - } - - /* Web application info. */ - if ( ((msr->txcfg->webappid != NULL)&&(strcmp(msr->txcfg->webappid, "default") != 0)) - || (msr->sessionid != NULL) || (msr->userid != NULL)) - { - text = apr_psprintf(msr->mp, "WebApp-Info: \"%s\" \"%s\" \"%s\"\n", - msr->txcfg->webappid == NULL ? "-" : log_escape(msr->mp, msr->txcfg->webappid), - msr->sessionid == NULL ? "-" : log_escape(msr->mp, msr->sessionid), - msr->userid == NULL ? "-" : log_escape(msr->mp, msr->userid)); - sec_auditlog_write(msr, text, strlen(text)); - } - - if ( ((msr->txcfg->sensor_id != NULL)&&(strcmp(msr->txcfg->sensor_id, "default") != 0))) - { - text = apr_psprintf(msr->mp, "Sensor-Id: \"%s\"\n", - msr->txcfg->sensor_id == NULL ? "-" : log_escape(msr->mp, msr->txcfg->sensor_id)), - sec_auditlog_write(msr, text, strlen(text)); - } - - - if (msr->txcfg->is_enabled > 0) { - text = apr_psprintf(msr->mp, "Engine-Mode: \"%s\"\n", - msr->txcfg->is_enabled == 1 ? "DETECTION_ONLY" : "ENABLED"), - sec_auditlog_write(msr, text, strlen(text)); - } - - /* Rule performance time */ - if(msr->txcfg->max_rule_time > 0) { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - - tarr = apr_table_elts(msr->perf_rules); - telts = (const apr_table_entry_t*)tarr->elts; - - if (tarr->nelts > 0) { - text = apr_psprintf(msr->mp, "Rules-Performance-Info: "); - sec_auditlog_write(msr, text, strlen(text)); - } - - for(i = 0; i < tarr->nelts; i++) { - text = apr_psprintf(msr->mp, "%s\"%s=%s\"%s", ((i == 0) ? "" : ", "), - log_escape(msr->mp, telts[i].key), log_escape(msr->mp, telts[i].val), ((i == (tarr->nelts - 1)) ? ".\n" : "")); - sec_auditlog_write(msr, text, strlen(text)); - } - } - - } - - /* AUDITLOG_PART_UPLOADS */ - if ((strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_UPLOADS) != NULL) && (msr->mpd != NULL)) { - multipart_part **parts = NULL; - unsigned int total_size = 0; - int cfiles = 0; - - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_UPLOADS); - sec_auditlog_write(msr, text, strlen(text)); - - parts = (multipart_part **)msr->mpd->parts->elts; - for(cfiles = 0; cfiles < msr->mpd->parts->nelts; cfiles++) { - if (parts[cfiles]->type == MULTIPART_FILE) { - if(parts[cfiles]->filename != NULL) { - text = apr_psprintf(msr->mp, "%d,%u,\"%s\",\"%s\"\n", cfiles+1, parts[cfiles]->tmp_file_size, log_escape(msr->mp, parts[cfiles]->filename), log_escape(msr->mp, parts[cfiles]->content_type ? parts[cfiles]->content_type : "")); - sec_auditlog_write(msr, text, strlen(text)); - total_size += parts[cfiles]->tmp_file_size; - } - } - } - text = apr_psprintf(msr->mp, "Total,%u\n", total_size); - sec_auditlog_write(msr, text, strlen(text)); - } - - /* AUDITLOG_PART_MATCHEDRULES */ - - if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_MATCHEDRULES) != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_MATCHEDRULES); - sec_auditlog_write(msr, text, strlen(text)); - - /* Matched Rules */ - for(i = 0; i < msr->matched_rules->nelts; i++) { - rule = ((msre_rule **)msr->matched_rules->elts)[i]; - if ((rule != NULL) && (rule->actionset != NULL) && rule->actionset->is_chained && (rule->chain_starter == NULL)) { - text = apr_psprintf(msr->mp, "%s\n", rule->unparsed); - sec_auditlog_write(msr, text, strlen(text)); - do { - if (rule->ruleset != NULL) { - - next_rule = return_chained_rule(rule,msr); - - if (next_rule != NULL) { - - present = chained_is_matched(msr,next_rule); - - if (present == 0) { - text = apr_psprintf(msr->mp, "#%s\n",next_rule->unparsed); - } else { - text = apr_psprintf(msr->mp, "%s\n",next_rule->unparsed); - i++; - } - - sec_auditlog_write(msr, text, strlen(text)); - } - } - rule = next_rule; - } while (rule != NULL && rule->actionset != NULL && rule->actionset->is_chained); - text = apr_psprintf(msr->mp, "\n"); - sec_auditlog_write(msr, text, strlen(text)); - } else { - if ((rule != NULL) && (rule->actionset != NULL) && !rule->actionset->is_chained && (rule->chain_starter == NULL)) { - text = apr_psprintf(msr->mp, "%s\n\n", rule->unparsed); - sec_auditlog_write(msr, text, strlen(text)); - } - } - } - } - - /* AUDITLOG_PART_ENDMARKER */ - - text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_ENDMARKER); - sec_auditlog_write(msr, text, strlen(text)); - - /* Return here if we were writing to a serial log - * as it does not need an index file. - */ - if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { - sec_auditlog_write(msr, "\n", 1); - - /* Unlock the mutex we used to serialise access to the audit log file. */ - rc = apr_global_mutex_unlock(msr->modsecurity->auditlog_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to unlock global mutex: %s", - get_apr_error(msr->mp, rc)); - } - - return; - } - - /* From here on only concurrent-style processing. */ - - apr_file_close(msr->new_auditlog_fd); - - /* Write an entry to the index file */ - - /* Calculate hash of the entry. */ - apr_md5_final(md5hash, &msr->new_auditlog_md5ctx); - - str2 = apr_psprintf(msr->mp, "%s %d %d md5:%s", msr->new_auditlog_filename, 0, - msr->new_auditlog_size, bytes2hex(msr->mp, md5hash, 16)); - if (str2 == NULL) return; - - /* We do not want the index line to be longer than 3980 bytes. */ - limit = 3980; - was_limited = 0; - - /* If we are logging to a pipe we need to observe and - * obey the pipe atomic write limit - PIPE_BUF. For - * more details see the discussion in sec_guardian_logger code. - */ - if (msr->txcfg->auditlog_name[0] == '|') { - if (PIPE_BUF < limit) { - limit = PIPE_BUF; - } - } - - limit = limit - strlen(str2) - 5; - if (limit <= 0) { - msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF); - return; - } - - str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited); - if (str1 == NULL) return; - - if (was_limited == 0) { - text = apr_psprintf(msr->mp, "%s %s \n", str1, str2); - } else { - text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2); - } - if (text == NULL) return; - - nbytes = strlen(text); - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to primary concurrent index", nbytes); - } - apr_file_write_full(msr->txcfg->auditlog_fd, text, nbytes, &nbytes_written); - - /* Write to the secondary audit log if we have one */ - if (msr->txcfg->auditlog2_fd != NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to secondary concurrent index", nbytes); - } - apr_file_write_full(msr->txcfg->auditlog2_fd, text, nbytes, &nbytes_written); - } -} diff --git a/apache2/msc_logging.h b/apache2/msc_logging.h deleted file mode 100644 index 75af9da7ae..0000000000 --- a/apache2/msc_logging.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_LOGGING_H_ -#define _MSC_LOGGING_H_ - -#define AUDITLOG_OFF 0 -#define AUDITLOG_ON 1 -#define AUDITLOG_RELEVANT 2 - -#define AUDITLOG_SERIAL 0 -#define AUDITLOG_CONCURRENT 1 - -#define AUDITLOG_PART_FIRST 'A' -#define AUDITLOG_PART_HEADER 'A' -#define AUDITLOG_PART_REQUEST_HEADERS 'B' -#define AUDITLOG_PART_REQUEST_BODY 'C' -#define AUDITLOG_PART_RESPONSE_HEADERS 'D' -#define AUDITLOG_PART_RESPONSE_BODY 'E' -#define AUDITLOG_PART_A_RESPONSE_HEADERS 'F' -#define AUDITLOG_PART_A_RESPONSE_BODY 'G' -#define AUDITLOG_PART_TRAILER 'H' -#define AUDITLOG_PART_FAKE_REQUEST_BODY 'I' -#define AUDITLOG_PART_UPLOADS 'J' -#define AUDITLOG_PART_MATCHEDRULES 'K' -#define AUDITLOG_PART_LAST 'K' -#define AUDITLOG_PART_ENDMARKER 'Z' - -#include "modsecurity.h" -#include "apr_pools.h" - -int DSOLOCAL is_valid_parts_specification(char *p); - -char DSOLOCAL *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_limited); - -void DSOLOCAL sec_audit_logger(modsec_rec *msr); - -#endif diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c deleted file mode 100644 index f4482ae7e5..0000000000 --- a/apache2/msc_lua.c +++ /dev/null @@ -1,503 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#if defined(WITH_LUA) - -#include "msc_lua.h" - -#include "apr_strings.h" - -typedef struct { - apr_array_header_t *parts; - apr_pool_t *pool; -} msc_lua_dumpw_t; - -typedef struct { - msc_script *script; - int index; -} msc_lua_dumpr_t; - -/** - * - */ -static const char* dump_reader(lua_State* L, void* user_data, size_t* size) { - msc_lua_dumpr_t *dumpr = (msc_lua_dumpr_t *)user_data; - msc_script_part *part; - - /* Do we have more chunks to return? */ - if (dumpr->index == dumpr->script->parts->nelts) { - return NULL; - } - - /* Get one chunk. */ - part = ((msc_script_part **)dumpr->script->parts->elts)[dumpr->index]; - *size = part->len; - - dumpr->index++; - - return part->data; -} - -/** - * - */ -static int dump_writer(lua_State *L, const void* data, size_t len, void* user_data) { - msc_lua_dumpw_t *dump = (msc_lua_dumpw_t *)user_data; - msc_script_part *part; - void *part_data; - - /* Allocate new part, copy the data into it. */ - part_data = apr_palloc(dump->pool, len); - memcpy(part_data, data, len); - part = apr_palloc(dump->pool, sizeof(msc_script_part)); - part->data = part_data; - part->len = len; - - /* Then add it to the list of parsts. */ - *(const msc_script_part **)apr_array_push(dump->parts) = part; - - return 0; -} - -/** - * - */ -static int lua_restore(lua_State *L, msc_script *script) { - msc_lua_dumpr_t dumpr; - - dumpr.script = script; - dumpr.index = 0; - -#if LUA_VERSION_NUM > 501 - return lua_load(L, dump_reader, &dumpr, script->name, NULL); -#else - return lua_load(L, dump_reader, &dumpr, script->name); -#endif -} - -/** - * - */ -char *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool) { - lua_State *L = NULL; - msc_lua_dumpw_t dump; - - /* Initialise state. */ -#if LUA_VERSION_NUM > 501 - L = luaL_newstate(); -#else - L = lua_open(); -#endif - luaL_openlibs(L); - - /* Find script. */ - if (luaL_loadfile(L, filename)) { - return apr_psprintf(pool, "ModSecurity: Failed to compile script %s: %s", - filename, lua_tostring(L, -1)); - } - - /* Dump the script into binary form. */ - dump.pool = pool; - dump.parts = apr_array_make(pool, 128, sizeof(msc_script_part *)); - - lua_dump(L, dump_writer, &dump); - - (*script) = apr_pcalloc(pool, sizeof(msc_script)); - (*script)->name = filename; - (*script)->parts = dump.parts; - - /* Destroy state. */ - lua_close(L); - - return NULL; -} - -/** - * - */ -static int l_log(lua_State *L) { - modsec_rec *msr = NULL; - const char *text; - int level; - - /* Retrieve parameters. */ - level = luaL_checknumber(L, 1); - text = luaL_checkstring(L, 2); - - /* Retrieve msr. */ - lua_getglobal(L, "__msr"); - msr = (modsec_rec *)lua_topointer(L, -1); - - /* Log message. */ - if (msr != NULL) { - msr_log(msr, level, "%s", text); - } - - return 0; -} - -/** - * - */ -static apr_array_header_t *resolve_tfns(lua_State *L, int idx, modsec_rec *msr, apr_pool_t *mp) { - apr_array_header_t *tfn_arr = NULL; - msre_tfn_metadata *tfn = NULL; - char *name = NULL; - - tfn_arr = apr_array_make(mp, 25, sizeof(msre_tfn_metadata *)); - if (tfn_arr == NULL) return NULL; - - /* ENH: Why is this userdata and not none/nil when parameter not given? */ - if (lua_isuserdata(L, idx) || lua_isnoneornil(L, idx)) { /* No second parameter */ - return tfn_arr; - } else if (lua_istable(L, idx)) { /* Is the second parameter an array? */ -#if LUA_VERSION_NUM > 501 - int i, n = lua_rawlen(L, idx); -#else - int i, n = lua_objlen(L, idx); -#endif - - for(i = 1; i <= n; i++) { - lua_rawgeti(L, idx, i); - name = (char *)luaL_checkstring(L, -1); - - /* A "none" means start over */ - if (strcmp("none", name) == 0) { - tfn_arr->nelts = 0; - continue; - } - - tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); - if (tfn == NULL) { - msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name); - } else { - *(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn; - } - } - } else if (lua_isstring(L, idx)) { /* The second parameter may be a simple string? */ - name = (char *)luaL_checkstring(L, idx); - - /* A "none" means start over */ - if (strcmp("none", name) == 0) { - tfn_arr->nelts = 0; - } - else { - tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); - if (tfn == NULL) { - msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name); - } else { - *(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn; - } - } - } else { - msr_log(msr, 1, "SecRuleScript: Transformation parameter must be a transformation name or array of transformation names, but found \"%s\" (type %d).", lua_typename(L, idx), lua_type(L, idx)); - return NULL; - } - - return tfn_arr; -} - -/** - * - */ -static int l_getvar(lua_State *L) { - char *varname = NULL, *param = NULL; - modsec_rec *msr = NULL; - msre_rule *rule = NULL; - char *my_error_msg = NULL; - char *p1 = NULL; - apr_array_header_t *tfn_arr = NULL; - msre_var *vx = NULL; - msre_var *var; - - /* Retrieve parameters. */ - p1 = (char *)luaL_checkstring(L, 1); - - /* Retrieve msr. */ - lua_getglobal(L, "__msr"); - msr = (modsec_rec *)lua_topointer(L, -1); - - /* Retrieve rule. */ - lua_getglobal(L, "__rule"); - rule = (msre_rule *)lua_topointer(L, -1); - - /* Extract the variable name and its parameter from the script. */ - varname = apr_pstrdup(msr->msc_rule_mptmp, p1); - param = strchr(varname, '.'); - if (param != NULL) { - *param = '\0'; - param++; - } - - /* Resolve variable. */ - var = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre, - varname, param, msr, &my_error_msg); - - if (var == NULL) { - msr_log(msr, 1, "%s", my_error_msg); - - lua_pushnil(L); - - return 0; - } - - /* Resolve transformation functions. */ - tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp); - - /* Generate variable. */ - vx = generate_single_var(msr, var, tfn_arr, rule, msr->msc_rule_mptmp); - if (vx == NULL) { - lua_pushnil(L); - - return 0; - } - - /* Return variable value. */ - lua_pushlstring(L, vx->value, vx->value_len); - - return 1; -} - -/** - * - */ -static int l_getvars(lua_State *L) { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - apr_table_t *vartable = NULL; - apr_array_header_t *tfn_arr = NULL; - char *varname = NULL, *param = NULL; - modsec_rec *msr = NULL; - msre_rule *rule = NULL; - msre_var *vartemplate = NULL; - char *my_error_msg = NULL; - char *p1 = NULL; - int i; - - /* Retrieve parameters. */ - p1 = (char *)luaL_checkstring(L, 1); - - /* Retrieve msr. */ - lua_getglobal(L, "__msr"); - msr = (modsec_rec *)lua_topointer(L, -1); - - /* Retrieve rule. */ - lua_getglobal(L, "__rule"); - rule = (msre_rule *)lua_topointer(L, -1); - - /* Extract the variable name and its parameter from the script. */ - varname = apr_pstrdup(msr->msc_rule_mptmp, p1); - param = strchr(varname, '.'); - if (param != NULL) { - *param = '\0'; - param++; - } - - /* Resolve transformation functions. */ - tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp); - - lua_newtable(L); - - /* Resolve variable. */ - vartemplate = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre, - varname, param, msr, &my_error_msg); - - if (vartemplate == NULL) { - msr_log(msr, 1, "%s", my_error_msg); - - /* Returning empty table. */ - return 1; - } - - vartable = generate_multi_var(msr, vartemplate, tfn_arr, rule, msr->msc_rule_mptmp); - - tarr = apr_table_elts(vartable); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_var *var = (msre_var *)telts[i].val; - - lua_pushnumber(L, i + 1); /* Index is not zero-based. */ - - lua_newtable(L); /* Per-parameter table. */ - - lua_pushstring(L, "name"); - lua_pushlstring(L, var->name, strlen(var->name)); - lua_settable(L, -3); - - lua_pushstring(L, "value"); - lua_pushlstring(L, var->value, var->value_len); - lua_settable(L, -3); - - lua_settable(L, -3); /* Push one parameter into the results table. */ - } - - return 1; -} - -/* -* \brief New setvar function for Lua API. Users can put back -* data in modsecurity core via new variables -* -* \param L Pointer to Lua state -* -* \retval -1 On failure -* \retval 0 On Collection failure -* \retval 1 On Success -*/ -static int l_setvar(lua_State *L) { - modsec_rec *msr = NULL; - msre_rule *rule = NULL; - const char *var_value = NULL; - const char *var_name = NULL; - int nargs = lua_gettop(L); - char *chr = NULL; - - lua_getglobal(L, "__msr"); - msr = (modsec_rec *)lua_topointer(L, -1); - - lua_getglobal(L, "__rule"); - rule = (msre_rule *)lua_topointer(L, -1); - - if(nargs != 2) { - msr_log(msr, 8, "m.setvar: Failed m.setvar funtion must has 2 arguments"); - return -1; - } - var_value = luaL_checkstring (L, 2); - var_name = luaL_checkstring (L, 1); - - lua_pop(L,2); - - if(var_value == NULL || var_name == NULL) - return -1; - - chr = strchr((char *)var_name,0x2e); - - if(chr == NULL) { - msr_log(msr, 8, "m.setvar: Must specify a collection using dot character - ie m.setvar(tx.myvar,mydata)"); - return -1; - } - - return msre_action_setvar_execute(msr,msr->msc_rule_mptmp,rule,(char *)var_name,(char *)var_value); -} - -static const struct luaL_Reg mylib[] = { - { "log", l_log }, - { "getvar", l_getvar }, - { "getvars", l_getvars }, - { "setvar", l_setvar }, - { NULL, NULL } -}; - -/** - * - */ -int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg) { - apr_time_t time_before; - lua_State *L = NULL; - int rc = 0; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (msr->txcfg->debuglog_level >= 8) { - msr_log(msr, 8, "Lua: Executing script: %s", script->name); - } - - time_before = apr_time_now(); - -#ifdef CACHE_LUA - L = msr->L; - rc = lua_gettop(L); - if(rc) - lua_pop(L, rc); -#else - /* Create new state. */ -#if LUA_VERSION_NUM > 501 - L = luaL_newstate(); -#else - L = lua_open(); -#endif - luaL_openlibs(L); -#endif - - if(L == NULL) - return -1; - - /* Associate msr with the state. */ - lua_pushlightuserdata(L, (void *)msr); - lua_setglobal(L, "__msr"); - - /* Associate rule with the state. */ - if (rule != NULL) { - lua_pushlightuserdata(L, (void *)rule); - lua_setglobal(L, "__rule"); - } - - /* Register functions. */ -#if LUA_VERSION_NUM > 501 - luaL_setfuncs(L,mylib,0); - lua_setglobal(L,"m"); -#else - luaL_register(L, "m", mylib); -#endif - - rc = lua_restore(L, script); - if (rc) { - *error_msg = apr_psprintf(msr->mp, "Lua: Failed to restore script with %i.", rc); - return -1; - } - - /* Execute the chunk so that the functions are defined. */ - lua_pcall(L, 0, 0, 0); - - /* Execute main() */ - lua_getglobal(L, "main"); - - /* Put the parameter on the stack. */ - if (param != NULL) { - lua_pushlstring(L, param, strlen(param)); - } - - if (lua_pcall(L, ((param != NULL) ? 1 : 0), 1, 0)) { - *error_msg = apr_psprintf(msr->mp, "Lua: Script execution failed: %s", lua_tostring(L, -1)); - - if (msr->txcfg->debuglog_level >= 8) { - msr_log(msr, 8, "Lua: Script execution failed: %s", lua_tostring(L, -1)); - } - - return -1; - } - - /* Get the response from the script. */ - *error_msg = (char *)lua_tostring(L, -1); - if (*error_msg != NULL) { - *error_msg = apr_pstrdup(msr->mp, *error_msg); - } - - /* Destroy state. */ - lua_pop(L, 1); -#ifndef CACHE_LUA - lua_close(L); -#endif - - /* Returns status code to caller. */ - if (msr->txcfg->debuglog_level >= 8) { - msr_log(msr, 8, "Lua: Script completed in %" APR_TIME_T_FMT " usec, returning: %s.", - (apr_time_now() - time_before), *error_msg); - } - - return ((*error_msg != NULL) ? RULE_MATCH : RULE_NO_MATCH); -} - -#endif /* WITH_LUA */ diff --git a/apache2/msc_lua.h b/apache2/msc_lua.h deleted file mode 100644 index a60697ce90..0000000000 --- a/apache2/msc_lua.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#if defined(WITH_LUA) - -#ifndef _MSC_LUA_H_ -#define _MSC_LUA_H_ - -typedef struct msc_script msc_script; -typedef struct msc_script_part msc_script_part; - -#include -#include -#include - -#include "apr_general.h" -#include "apr_tables.h" -#include "modsecurity.h" - -struct msc_script { - const char *name; - apr_array_header_t *parts; -}; - -struct msc_script_part { - const void *data; - size_t len; -}; - -char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool); - -int DSOLOCAL lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg); - -apr_status_t DSOLOCAL msre_action_setvar_execute(modsec_rec *r, apr_pool_t *, msre_rule *, char *, char *); - -#endif - -#endif /* WITH_LUA */ diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c deleted file mode 100644 index 4afd5c757b..0000000000 --- a/apache2/msc_multipart.c +++ /dev/null @@ -1,1445 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "modsecurity.h" -#include -#include - -#include "msc_multipart.h" -#include "msc_util.h" -#include "msc_parsers.h" - -void validate_quotes(modsec_rec *msr, char *data) { - int i, len; - - if(msr == NULL) - return; - - if(msr->mpd == NULL) - return; - - if(data == NULL) - return; - - len = strlen(data); - - for(i = 0; i < len; i++) { - - if(data[i] == '\'') { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %d bytes", - log_escape_nq(msr->mp, data), len); - } - msr->mpd->flag_invalid_quoting = 1; - } - } -} - -#if 0 -static char *multipart_construct_filename(modsec_rec *msr) { - char c, *p, *q = msr->mpd->mpp->filename; - char *filename; - - /* find the last backward slash and consider the - * filename to be only what's right from it - */ - p = strrchr(q, '\\'); - if (p != NULL) q = p + 1; - - /* do the same for the forward slash */ - p = strrchr(q, '/'); - if (p != NULL) q = p + 1; - - /* allow letters, digits and dots, replace - * everything else with underscores - */ - p = filename = apr_pstrdup(msr->mp, q); - while((c = *p) != 0) { - if (!( isalnum(c) || (c == '.') )) *p = '_'; - p++; - } - - return filename; -} -#endif - -/** - * - */ -static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) { - char *p = NULL, *t = NULL; - - /* accept only what we understand */ - if (strncmp(c_d_value, "form-data", 9) != 0) { - return -1; - } - - /* see if there are any other parts to parse */ - - p = c_d_value + 9; - while((*p == '\t') || (*p == ' ')) p++; - if (*p == '\0') return 1; /* this is OK */ - - if (*p != ';') return -2; - p++; - - /* parse the appended parts */ - - while(*p != '\0') { - char *name = NULL, *value = NULL, *start = NULL; - - /* go over the whitespace */ - while((*p == '\t') || (*p == ' ')) p++; - if (*p == '\0') return -3; - - start = p; - while((*p != '\0') && (*p != '=') && (*p != '\t') && (*p != ' ')) p++; - if (*p == '\0') return -4; - - name = apr_pstrmemdup(msr->mp, start, (p - start)); - - while((*p == '\t') || (*p == ' ')) p++; - if (*p == '\0') return -5; - - if (*p != '=') return -13; - p++; - - while((*p == '\t') || (*p == ' ')) p++; - if (*p == '\0') return -6; - - /* Accept both quotes as some backends will accept them, but - * technically "'" is invalid and so flag_invalid_quoting is - * set so the user can deal with it in the rules if they so wish. - */ - - if ((*p == '"') || (*p == '\'')) { - /* quoted */ - char quote = *p; - - if (quote == '\'') { - msr->mpd->flag_invalid_quoting = 1; - } - - p++; - if (*p == '\0') return -7; - - start = p; - value = apr_pstrdup(msr->mp, p); - t = value; - - while(*p != '\0') { - if (*p == '\\') { - if (*(p + 1) == '\0') { - /* improper escaping */ - return -8; - } - /* only quote and \ can be escaped */ - if ((*(p + 1) == quote) || (*(p + 1) == '\\')) { - p++; - } - else { - /* improper escaping */ - - /* We allow for now because IE sends - * improperly escaped content and there's - * nothing we can do about it. - * - * return -9; - */ - } - } - else if (*p == quote) { - *t = '\0'; - break; - } - - *(t++) = *(p++); - } - if (*p == '\0') return -10; - - p++; /* go over the quote at the end */ - - } else { - /* not quoted */ - - start = p; - while((*p != '\0') && (is_token_char(*p))) p++; - value = apr_pstrmemdup(msr->mp, start, (p - start)); - } - - /* evaluate part */ - - if (strcmp(name, "name") == 0) { - - validate_quotes(msr, value); - - msr->multipart_name = apr_pstrdup(msr->mp, value); - - if (msr->mpd->mpp->name != NULL) { - msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition name: %s", - log_escape_nq(msr->mp, value)); - return -14; - } - msr->mpd->mpp->name = value; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Content-Disposition name: %s", - log_escape_nq(msr->mp, value)); - } - } - else - if (strcmp(name, "filename") == 0) { - - validate_quotes(msr, value); - - msr->multipart_filename = apr_pstrdup(msr->mp, value); - - if (msr->mpd->mpp->filename != NULL) { - msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename: %s", - log_escape_nq(msr->mp, value)); - return -15; - } - msr->mpd->mpp->filename = value; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Content-Disposition filename: %s", - log_escape_nq(msr->mp, value)); - } - } - else return -11; - - if (*p != '\0') { - while((*p == '\t') || (*p == ' ')) p++; - /* the next character must be a zero or a semi-colon */ - if (*p == '\0') return 1; /* this is OK */ - if (*p != ';') { - p--; - if(*p == '\'' || *p == '\"') { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %d bytes", - log_escape_nq(msr->mp, p), strlen(p)); - } - msr->mpd->flag_invalid_quoting = 1; - } - p++; - return -12; - } - p++; /* move over the semi-colon */ - } - - /* loop will stop when (*p == '\0') */ - } - - return 1; -} - -/** - * - */ -static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { - int i, len, rc; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Check for nul bytes. */ - len = MULTIPART_BUF_SIZE - msr->mpd->bufleft; - for(i = 0; i < len; i++) { - if (msr->mpd->buf[i] == '\0') { - *error_msg = apr_psprintf(msr->mp, "Multipart: Nul byte in part headers."); - return -1; - } - } - - /* The buffer is data so increase the data length counter. */ - msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - - if (len > 1) { - if (msr->mpd->buf[len - 2] == '\r') { - msr->mpd->flag_crlf_line = 1; - } else { - msr->mpd->flag_lf_line = 1; - } - } else { - msr->mpd->flag_lf_line = 1; - } - - /* Is this an empty line? */ - if ( ((msr->mpd->buf[0] == '\r') - &&(msr->mpd->buf[1] == '\n') - &&(msr->mpd->buf[2] == '\0') ) - || ((msr->mpd->buf[0] == '\n') - &&(msr->mpd->buf[1] == '\0') ) ) - { /* Empty line. */ - char *header_value = NULL; - - header_value = (char *)apr_table_get(msr->mpd->mpp->headers, "Content-Disposition"); - if (header_value == NULL) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Part missing Content-Disposition header."); - return -1; - } - - rc = multipart_parse_content_disposition(msr, header_value); - if (rc < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%d): %s.", - rc, log_escape_nq(msr->mp, header_value)); - return -1; - } - - if (msr->mpd->mpp->name == NULL) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Content-Disposition header missing name field."); - return -1; - } - - if (msr->mpd->mpp->filename != NULL) { - /* Some parsers use crude methods to extract the name and filename - * values from the C-D header. We need to check for the case where they - * didn't understand C-D but we did. - */ - if (strstr(header_value, "filename=") == NULL) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (filename)."); - return -1; - } - - msr->mpd->mpp->type = MULTIPART_FILE; - } else { - msr->mpd->mpp->type = MULTIPART_FORMDATA; - } - - msr->mpd->mpp_state = 1; - msr->mpd->mpp->last_header_name = NULL; - } else { - /* Header line. */ - - 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)."); - return -1; - } - - /* locate the beginning of data */ - data = msr->mpd->buf; - 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); - - /* update the header value in the table */ - header_value = (char *)apr_table_get(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name); - new_value = apr_pstrcat(msr->mp, header_value, " ", new_value, NULL); - apr_table_set(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name, new_value); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Continued folder header \"%s\" with \"%s\"", - log_escape(msr->mp, msr->mpd->mpp->last_header_name), - log_escape(msr->mp, data)); - } - - if (strlen(new_value) > MULTIPART_BUF_SIZE) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long."); - return -1; - } - } else { - char *header_name, *header_value, *data; - - /* new header */ - - data = msr->mpd->buf; - while((*data != ':') && (*data != '\0')) data++; - if (*data == '\0') { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (colon missing): %s.", - log_escape_nq(msr->mp, msr->mpd->buf)); - return -1; - } - - /* extract header name */ - header_name = apr_pstrmemdup(msr->mp, msr->mpd->buf, (data - msr->mpd->buf)); - if (data == msr->mpd->buf) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (header name missing)."); - - return -1; - } - - /* extract the value value */ - data++; - while((*data == '\t') || (*data == ' ')) data++; - header_value = apr_pstrdup(msr->mp, data); - remove_lf_crlf_inplace(header_value); - - /* error if the name already exists */ - if (apr_table_get(msr->mpd->mpp->headers, header_name) != NULL) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Duplicate part header: %s.", - log_escape_nq(msr->mp, header_name)); - return -1; - } - - apr_table_setn(msr->mpd->mpp->headers, header_name, header_value); - msr->mpd->mpp->last_header_name = header_name; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added part header \"%s\" \"%s\"", - log_escape(msr->mp, header_name), - log_escape(msr->mp, header_value)); - } - } - } - - return 1; -} - -/** - * - */ -static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { - char *p = msr->mpd->buf + (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - char localreserve[2] = { '\0', '\0' }; /* initialized to quiet warning */ - int bytes_reserved = 0; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Preserve some bytes for later. */ - if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 1) - && (*(p - 1) == '\n') ) - { - if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 2) - && (*(p - 2) == '\r') ) - { - /* Two bytes. */ - bytes_reserved = 2; - localreserve[0] = *(p - 2); - localreserve[1] = *(p - 1); - msr->mpd->bufleft += 2; - *(p - 2) = 0; - } else { - /* Only one byte. */ - bytes_reserved = 1; - localreserve[0] = *(p - 1); - localreserve[1] = 0; - msr->mpd->bufleft += 1; - *(p - 1) = 0; - } - } - - /* 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 (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 */ - msr->mpd->mpp->tmp_file_name = apr_psprintf(msr->mp, "%s/%s-%s-file-XXXXXX", - msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid); - msr->mpd->mpp->tmp_file_fd = msc_mkstemp_ex(msr->mpd->mpp->tmp_file_name, msr->txcfg->upload_filemode); - - /* do we have an opened file? */ - if (msr->mpd->mpp->tmp_file_fd < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Failed to create file: %s", - log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); - 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 %d (mode %04o): %s", - msr->mpd->nfiles, - (unsigned int)msr->txcfg->upload_filemode, - log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); - } - } - - /* write the reserve first */ - if (msr->mpd->reserve[0] != 0) { - if (write(msr->mpd->mpp->tmp_file_fd, &msr->mpd->reserve[1], msr->mpd->reserve[0]) != msr->mpd->reserve[0]) { - *error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed", - log_escape(msr->mp, msr->mpd->mpp->tmp_file_name)); - return -1; - } - - msr->mpd->mpp->tmp_file_size += msr->mpd->reserve[0]; - msr->mpd->mpp->length += msr->mpd->reserve[0]; - } - - /* write data to the file */ - if (write(msr->mpd->mpp->tmp_file_fd, msr->mpd->buf, MULTIPART_BUF_SIZE - msr->mpd->bufleft) - != (MULTIPART_BUF_SIZE - msr->mpd->bufleft)) - { - *error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed", - log_escape(msr->mp, msr->mpd->mpp->tmp_file_name)); - return -1; - } - - msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - } else { - /* just keep track of the file size */ - msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; - msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; - } - } - else if (msr->mpd->mpp->type == MULTIPART_FORMDATA) { - value_part_t *value_part = apr_pcalloc(msr->mp, sizeof(value_part_t)); - - /* The buffer contains data so increase the data length counter. */ - msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; - - /* add this part to the list of parts */ - - /* remember where we started */ - if (msr->mpd->mpp->length == 0) { - msr->mpd->mpp->offset = msr->mpd->buf_offset; - } - - if (msr->mpd->reserve[0] != 0) { - value_part->data = apr_palloc(msr->mp, (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]); - memcpy(value_part->data, &(msr->mpd->reserve[1]), msr->mpd->reserve[0]); - memcpy(value_part->data + msr->mpd->reserve[0], msr->mpd->buf, (MULTIPART_BUF_SIZE - msr->mpd->bufleft)); - - value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; - msr->mpd->mpp->length += value_part->length; - } else { - value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - value_part->data = apr_pstrmemdup(msr->mp, msr->mpd->buf, value_part->length); - msr->mpd->mpp->length += value_part->length; - } - - *(value_part_t **)apr_array_push(msr->mpd->mpp->value_parts) = value_part; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added data to variable: %s", - log_escape_nq_ex(msr->mp, value_part->data, value_part->length)); - } - } - else { - *error_msg = apr_psprintf(msr->mp, "Multipart: unknown part type %d", msr->mpd->mpp->type); - return -1; - } - - /* store the reserved bytes to the multipart - * context so that they don't get lost - */ - if (bytes_reserved) { - msr->mpd->reserve[0] = bytes_reserved; - msr->mpd->reserve[1] = localreserve[0]; - msr->mpd->reserve[2] = localreserve[1]; - msr->mpd->buf_offset += bytes_reserved; - } - else { - msr->mpd->buf_offset -= msr->mpd->reserve[0]; - msr->mpd->reserve[0] = 0; - } - - return 1; -} - -/** - * - */ -static char *multipart_combine_value_parts(modsec_rec *msr, apr_array_header_t *value_parts) { - value_part_t **parts = NULL; - char *rval = apr_palloc(msr->mp, msr->mpd->mpp->length + 1); - unsigned long int offset; - int i; - - if (rval == NULL) return NULL; - - offset = 0; - parts = (value_part_t **)value_parts->elts; - for(i = 0; i < value_parts->nelts; i++) { - if (offset + parts[i]->length <= msr->mpd->mpp->length) { - memcpy(rval + offset, parts[i]->data, parts[i]->length); - offset += parts[i]->length; - } - } - rval[offset] = '\0'; - - return rval; -} - -/** - * - */ -static int multipart_process_boundary(modsec_rec *msr, int last_part, char **error_log) { - /* if there was a part being built finish it */ - if (msr->mpd->mpp != NULL) { - /* close the temp file */ - if ((msr->mpd->mpp->type == MULTIPART_FILE) - &&(msr->mpd->mpp->tmp_file_name != NULL) - &&(msr->mpd->mpp->tmp_file_fd != 0)) - { - close(msr->mpd->mpp->tmp_file_fd); - msr->mpd->mpp->tmp_file_fd = -1; - } - - if (msr->mpd->mpp->type != MULTIPART_FILE) { - /* now construct a single string out of the parts */ - msr->mpd->mpp->value = multipart_combine_value_parts(msr, msr->mpd->mpp->value_parts); - if (msr->mpd->mpp->value == NULL) return -1; - } - - if (msr->mpd->mpp->name) { - /* add the part to the list of parts */ - *(multipart_part **)apr_array_push(msr->mpd->parts) = msr->mpd->mpp; - if (msr->mpd->mpp->type == MULTIPART_FILE) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added file part %pp to the list: name \"%s\" " - "file name \"%s\" (offset %u, length %u)", - msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), - log_escape(msr->mp, msr->mpd->mpp->filename), - msr->mpd->mpp->offset, msr->mpd->mpp->length); - } - } - else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added part %pp to the list: name \"%s\" " - "(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), - msr->mpd->mpp->offset, msr->mpd->mpp->length); - } - } - } - else { - msr->mpd->flag_invalid_part = 1; - msr_log(msr, 3, "Multipart: Skipping invalid part %pp (part name missing): " - "(offset %u, length %u)", msr->mpd->mpp, - msr->mpd->mpp->offset, msr->mpd->mpp->length); - } - - msr->mpd->mpp = NULL; - } - - if (last_part == 0) { - /* start building a new part */ - msr->mpd->mpp = (multipart_part *)apr_pcalloc(msr->mp, sizeof(multipart_part)); - if (msr->mpd->mpp == NULL) return -1; - msr->mpd->mpp->type = MULTIPART_FORMDATA; - msr->mpd->mpp_state = 0; - - msr->mpd->mpp->headers = apr_table_make(msr->mp, 10); - if (msr->mpd->mpp->headers == NULL) return -1; - msr->mpd->mpp->last_header_name = NULL; - - msr->mpd->reserve[0] = 0; - msr->mpd->reserve[1] = 0; - msr->mpd->reserve[2] = 0; - msr->mpd->reserve[3] = 0; - - msr->mpd->mpp->value_parts = apr_array_make(msr->mp, 10, sizeof(value_part_t *)); - } - - return 1; -} - -static int multipart_boundary_characters_valid(char *boundary) { - unsigned char *p = (unsigned char *)boundary; - unsigned char c; - - if (p == NULL) return -1; - - while((c = *p) != '\0') { - /* Control characters and space not allowed. */ - if (c < 32) { - return 0; - } - - /* Non-ASCII characters not allowed. */ - if (c > 126) { - return 0; - } - - switch(c) { - /* Special characters not allowed. */ - case '(' : - case ')' : - case '<' : - case '>' : - case '@' : - case ',' : - case ';' : - case ':' : - case '\\' : - case '"' : - case '/' : - case '[' : - case ']' : - case '?' : - case '=' : - return 0; - break; - - default : - /* Do nothing. */ - break; - } - - p++; - } - - return 1; -} - -static int multipart_count_boundary_params(apr_pool_t *mp, const char *header_value) { - char *duplicate = NULL; - char *s = NULL; - int count = 0; - - if (header_value == NULL) return -1; - duplicate = apr_pstrdup(mp, header_value); - if (duplicate == NULL) return -1; - - /* Performing a case-insensitive search. */ - strtolower_inplace((unsigned char *)duplicate); - - s = duplicate; - while((s = strstr(s, "boundary")) != NULL) { - s += 8; - - if (strchr(s, '=') != NULL) { - count++; - } - } - - return count; -} - -/** - * - */ -int multipart_init(modsec_rec *msr, char **error_msg) { - if (error_msg == NULL) return -1; - *error_msg = NULL; - - msr->mpd = (multipart_data *)apr_pcalloc(msr->mp, sizeof(multipart_data)); - if (msr->mpd == NULL) return -1; - - msr->mpd->parts = apr_array_make(msr->mp, 10, sizeof(multipart_part *)); - msr->mpd->bufleft = MULTIPART_BUF_SIZE; - msr->mpd->bufptr = msr->mpd->buf; - msr->mpd->buf_contains_line = 1; - msr->mpd->mpp = NULL; - - if (msr->request_content_type == NULL) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Content-Type header not available."); - return -1; - } - - if (strlen(msr->request_content_type) > 1024) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (length)."); - return -1; - } - - if (strncasecmp(msr->request_content_type, "multipart/form-data", 19) != 0) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid MIME type."); - return -1; - } - - /* Count how many times the word "boundary" appears in the C-T header. */ - if (multipart_count_boundary_params(msr->mp, msr->request_content_type) > 1) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Multiple boundary parameters in C-T."); - return -1; - } - - msr->mpd->boundary = strstr(msr->request_content_type, "boundary"); - if (msr->mpd->boundary != NULL) { - char *p = NULL; - char *b = NULL; - int seen_semicolon = 0; - int len = 0; - - /* Check for extra characters before the boundary. */ - for (p = (char *)(msr->request_content_type + 19); p < msr->mpd->boundary; p++) { - if (!isspace(*p)) { - if ((seen_semicolon == 0) && (*p == ';')) { - seen_semicolon = 1; /* It is OK to have one semicolon. */ - } else { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (malformed)."); - return -1; - } - } - } - - /* Have we seen the semicolon in the header? */ - if (seen_semicolon == 0) { - msr->mpd->flag_missing_semicolon = 1; - } - - b = strchr(msr->mpd->boundary + 8, '='); - if (b == NULL) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (malformed)."); - return -1; - } - - /* Check parameter name ends well. */ - if (b != (msr->mpd->boundary + 8)) { - /* Check all characters between the end of the boundary - * and the = character. - */ - for (p = msr->mpd->boundary + 8; p < b; p++) { - if (isspace(*p)) { - /* Flag for whitespace after parameter name. */ - msr->mpd->flag_boundary_whitespace = 1; - } else { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (parameter name)."); - return -1; - } - } - } - - b++; /* Go over the = character. */ - len = strlen(b); - - /* Flag for whitespace before parameter value. */ - if (isspace(*b)) { - msr->mpd->flag_boundary_whitespace = 1; - } - - /* Is the boundary quoted? */ - if ((len >= 2) && (*b == '"') && (*(b + len - 1) == '"')) { - /* Quoted. */ - msr->mpd->boundary = apr_pstrndup(msr->mp, b + 1, len - 2); - if (msr->mpd->boundary == NULL) return -1; - msr->mpd->flag_boundary_quoted = 1; - } else { - /* Not quoted. */ - - /* Test for partial quoting. */ - if ( (*b == '"') - || ((len >= 2) && (*(b + len - 1) == '"')) ) - { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (quote)."); - return -1; - } - - msr->mpd->boundary = apr_pstrdup(msr->mp, b); - if (msr->mpd->boundary == NULL) return -1; - msr->mpd->flag_boundary_quoted = 0; - } - - /* Case-insensitive test for the string "boundary" in the boundary. */ - if (multipart_count_boundary_params(msr->mp, msr->mpd->boundary) != 0) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (content)."); - return -1; - } - - /* Validate the characters used in the boundary. */ - if (multipart_boundary_characters_valid(msr->mpd->boundary) != 1) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (characters)."); - return -1; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Boundary%s: %s", - (msr->mpd->flag_boundary_quoted ? " (quoted)" : ""), - log_escape_nq(msr->mp, msr->mpd->boundary)); - } - - if (strlen(msr->mpd->boundary) == 0) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (empty)."); - return -1; - } - } - else { /* Could not find boundary in the C-T header. */ - msr->mpd->flag_error = 1; - - /* Test for case-insensitive boundary. Allowed by the RFC but highly unusual. */ - if (multipart_count_boundary_params(msr->mp, msr->request_content_type) > 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (case sensitivity)."); - return -1; - } - - *error_msg = apr_psprintf(msr->mp, "Multipart: Boundary not found in C-T."); - return -1; - } - - return 1; -} - -/** - * Finalise multipart processing. This method is invoked at the end, when it - * is clear that there is no more data to be processed. - */ -int multipart_complete(modsec_rec *msr, char **error_msg) { - if (msr->mpd == NULL) return 1; - - if (msr->txcfg->debuglog_level >= 4) { - if (msr->mpd->flag_data_before) { - msr_log(msr, 4, "Multipart: Warning: seen data before first boundary."); - } - - if (msr->mpd->flag_data_after) { - msr_log(msr, 4, "Multipart: Warning: seen data after last boundary."); - } - - if (msr->mpd->flag_boundary_quoted) { - msr_log(msr, 4, "Multipart: Warning: boundary was quoted."); - } - - if (msr->mpd->flag_boundary_whitespace) { - msr_log(msr, 4, "Multipart: Warning: boundary whitespace in C-T header."); - } - - if (msr->mpd->flag_header_folding) { - msr_log(msr, 4, "Multipart: Warning: header folding used."); - } - - if (msr->mpd->flag_crlf_line && msr->mpd->flag_lf_line) { - msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); - } - else if (msr->mpd->flag_lf_line) { - msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF)."); - } - - 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_part) { - msr_log(msr, 4, "Multipart: Warning: invalid part parsing."); - } - - 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)) { - if (msr->mpd->boundary_count > 0) { - /* Check if we have the final boundary (that we haven't - * processed yet) in the buffer. - */ - if (msr->mpd->buf_contains_line) { - if ( ((unsigned int)(MULTIPART_BUF_SIZE - msr->mpd->bufleft) == (4 + strlen(msr->mpd->boundary))) - && (*(msr->mpd->buf) == '-') - && (*(msr->mpd->buf + 1) == '-') - && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) - && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary)) == '-') - && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary) + 1) == '-') ) - { - /* Looks like the final boundary - process it. */ - if (multipart_process_boundary(msr, 1 /* final */, error_msg) < 0) { - msr->mpd->flag_error = 1; - return -1; - } - - /* The payload is complete after all. */ - msr->mpd->is_complete = 1; - } - } - - if (msr->mpd->is_complete == 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Final boundary missing."); - return -1; - } - } else { - *error_msg = apr_psprintf(msr->mp, "Multipart: No boundaries found in payload."); - return -1; - } - } - - return 1; -} - -/** - * - */ -int multipart_process_chunk(modsec_rec *msr, const char *buf, - unsigned int size, char **error_msg) -{ - char *inptr = (char *)buf; - unsigned int inleft = size; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (size == 0) return 1; - - msr->mpd->seen_data = 1; - - if (msr->mpd->is_complete) { - msr->mpd->flag_data_before = 1; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %u bytes)", size); - } - - return 1; - } - - if (msr->mpd->bufleft == 0) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, - "Multipart: Internal error in process_chunk: no space left in the buffer"); - return -1; - } - - /* here we loop through the available data, one byte at a time */ - while(inleft > 0) { - char c = *inptr; - int process_buffer = 0; - - if ((c == '\r') && (msr->mpd->bufleft == 1)) { - /* we don't want to take \r as the last byte in the buffer */ - process_buffer = 1; - } else { - inptr++; - inleft = inleft - 1; - - *(msr->mpd->bufptr) = c; - msr->mpd->bufptr++; - msr->mpd->bufleft--; - } - - /* until we either reach the end of the line - * or the end of our internal buffer - */ - if ((c == '\n') || (msr->mpd->bufleft == 0) || (process_buffer)) { - int processed_as_boundary = 0; - - *(msr->mpd->bufptr) = 0; - - /* Do we have something that looks like a boundary? */ - if ( msr->mpd->buf_contains_line - && (strlen(msr->mpd->buf) > 3) - && (*(msr->mpd->buf) == '-') - && (*(msr->mpd->buf + 1) == '-') ) - { - /* Does it match our boundary? */ - if ( (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2) - && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) ) - { - char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary); - int is_final = 0; - - /* Is this the final boundary? */ - if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) { - is_final = 1; - boundary_end += 2; - - if (msr->mpd->is_complete != 0) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, - "Multipart: Invalid boundary (final duplicate)."); - return -1; - } - } - - /* Allow for CRLF and LF line endings. */ - if ( ( (*boundary_end == '\r') - && (*(boundary_end + 1) == '\n') - && (*(boundary_end + 2) == '\0') ) - || ( (*boundary_end == '\n') - && (*(boundary_end + 1) == '\0') ) ) - { - if (*boundary_end == '\n') { - msr->mpd->flag_lf_line = 1; - } else { - msr->mpd->flag_crlf_line = 1; - } - - if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) { - msr->mpd->flag_error = 1; - return -1; - } - - if (is_final) { - msr->mpd->is_complete = 1; - } - - processed_as_boundary = 1; - msr->mpd->boundary_count++; - } - else { - /* error */ - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, - "Multipart: Invalid boundary: %s", - log_escape_nq(msr->mp, msr->mpd->buf)); - return -1; - } - } else { /* It looks like a boundary but we couldn't match it. */ - char *p = NULL; - - /* Check if an attempt to use quotes around the boundary was made. */ - if ( (msr->mpd->flag_boundary_quoted) - && (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 3) - && (*(msr->mpd->buf + 2) == '"') - && (strncmp(msr->mpd->buf + 3, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) - ) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (quotes)."); - return -1; - } - - /* Check the beginning of the boundary for whitespace. */ - p = msr->mpd->buf + 2; - while(isspace(*p)) { - p++; - } - - if ( (p != msr->mpd->buf + 2) - && (strncmp(p, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) - ) { - /* Found whitespace in front of a boundary. */ - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (whitespace)."); - return -1; - } - - msr->mpd->flag_unmatched_boundary = 1; - } - } else { /* We do not think the buffer contains a boundary. */ - /* Look into the buffer to see if there's anything - * there that resembles a boundary. - */ - if (msr->mpd->buf_contains_line) { - int i, len = (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - char *p = msr->mpd->buf; - - for(i = 0; i < len; i++) { - if ((p[i] == '-') && (i + 1 < len) && (p[i + 1] == '-')) - { - if (strncmp(p + i + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) { - msr->mpd->flag_unmatched_boundary = 1; - break; - } - } - } - } - } - - /* Process as data if it was not a boundary. */ - if (processed_as_boundary == 0) { - if (msr->mpd->mpp == NULL) { - msr->mpd->flag_data_before = 1; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data before first boundary."); - } - } else { - if (msr->mpd->mpp_state == 0) { - if ((msr->mpd->bufleft == 0) || (process_buffer)) { - /* part header lines must be shorter than - * MULTIPART_BUF_SIZE bytes - */ - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, - "Multipart: Part header line over %d bytes long", - MULTIPART_BUF_SIZE); - return -1; - } - - if (multipart_process_part_header(msr, error_msg) < 0) { - msr->mpd->flag_error = 1; - return -1; - } - } else { - if (multipart_process_part_data(msr, error_msg) < 0) { - msr->mpd->flag_error = 1; - return -1; - } - } - } - } - - /* Update the offset of the data we are about - * to process. This is to allow us to know the - * offsets of individual files and variables. - */ - msr->mpd->buf_offset += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); - - /* reset the pointer to the beginning of the buffer - * and continue to accept input data - */ - msr->mpd->bufptr = msr->mpd->buf; - msr->mpd->bufleft = MULTIPART_BUF_SIZE; - msr->mpd->buf_contains_line = (c == 0x0a) ? 1 : 0; - } - - if ((msr->mpd->is_complete) && (inleft != 0)) { - msr->mpd->flag_data_after = 1; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%u bytes left)", inleft); - } - - return 1; - } - } - - return 1; -} - -/** - * - */ -apr_status_t multipart_cleanup(modsec_rec *msr) { - int keep_files = 0; - - if (msr->mpd == NULL) return -1; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Cleanup started (remove files %d).", msr->upload_remove_files); - } - - if (msr->upload_remove_files == 0) { - if (msr->txcfg->upload_dir == NULL) { - msr_log(msr, 1, "Input filter: SecUploadDir is undefined, unable to store " - "multipart files."); - } else { - keep_files = 1; - } - } - - /* Loop through the list of parts - * and delete the temporary files, but only if - * file storage was not requested, or if storage - * of relevant files was requested and this isn't - * such a request. - */ - if (keep_files == 0) { - multipart_part **parts; - int i; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FILE) { - if (parts[i]->tmp_file_name != NULL) { - /* make sure it is closed first */ - if (parts[i]->tmp_file_fd > 0) { - close(parts[i]->tmp_file_fd); - parts[i]->tmp_file_fd = -1; - } - - if (unlink(parts[i]->tmp_file_name) < 0) { - msr_log(msr, 1, "Multipart: Failed to delete file (part) \"%s\" because %d(%s)", - log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno)); - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Deleted file (part) \"%s\"", - log_escape(msr->mp, parts[i]->tmp_file_name)); - } - } - } - } - } - } else { - /* delete empty files, move the others to the upload dir */ - multipart_part **parts; - int i; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if ( (parts[i]->type == MULTIPART_FILE) - && (parts[i]->tmp_file_size == 0)) - { - /* Delete empty file. */ - if (parts[i]->tmp_file_name != NULL) { - /* make sure it is closed first */ - if (parts[i]->tmp_file_fd > 0) { - close(parts[i]->tmp_file_fd); - parts[i]->tmp_file_fd = -1; - } - - if (unlink(parts[i]->tmp_file_name) < 0) { - msr_log(msr, 1, "Multipart: Failed to delete empty file (part) \"%s\" because %d(%s)", - log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno)); - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Deleted empty file (part) \"%s\"", - log_escape(msr->mp, parts[i]->tmp_file_name)); - } - } - } - } else { - /* Move file to the upload dir. */ - if (parts[i]->tmp_file_name != NULL) { - const char *new_filename = NULL; - const char *new_basename = NULL; - - /* make sure it is closed first */ - if (parts[i]->tmp_file_fd > 0) { - close(parts[i]->tmp_file_fd); - parts[i]->tmp_file_fd = -1; - } - - new_basename = file_basename(msr->mp, parts[i]->tmp_file_name); - if (new_basename == NULL) return -1; - new_filename = apr_psprintf(msr->mp, "%s/%s", msr->txcfg->upload_dir, - new_basename); - if (new_filename == NULL) return -1; - - if (apr_file_rename(parts[i]->tmp_file_name, new_filename, - msr->msc_reqbody_mp) != APR_SUCCESS) - { - msr_log(msr, 1, "Input filter: Failed to rename file from \"%s\" to \"%s\".", - log_escape(msr->mp, parts[i]->tmp_file_name), - log_escape(msr->mp, new_filename)); - return -1; - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".", - log_escape(msr->mp, parts[i]->tmp_file_name), - log_escape(msr->mp, new_filename)); - } - } - } - } - } - } - - return 1; -} - -/** - * - */ -int multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *arguments) { - multipart_part **parts; - int i; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FORMDATA) { - msc_arg *arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); - if (arg == NULL) return -1; - - arg->name = parts[i]->name; - arg->name_len = strlen(parts[i]->name); - arg->value = parts[i]->value; - arg->value_len = parts[i]->length; - arg->value_origin_offset = parts[i]->offset; - arg->value_origin_len = parts[i]->length; - arg->origin = origin; - - add_argument(msr, arguments, arg); - } - } - - return 1; -} - -/** - * - */ -char *multipart_reconstruct_urlencoded_body_sanitize(modsec_rec *msr) { - multipart_part **parts; - char *body; - unsigned int body_len; - int i; - - if (msr->mpd == NULL) return NULL; - - /* calculate the size of the buffer */ - body_len = 1; - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FORMDATA) { - body_len += 4; - body_len += strlen(parts[i]->name) * 3; - body_len += strlen(parts[i]->value) * 3; - } - } - - /* allocate the buffer */ - body = apr_palloc(msr->mp, body_len + 1); - if ((body == NULL) || (body_len + 1 == 0)) return NULL; - *body = 0; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FORMDATA) { - if (*body != 0) { - strncat(body, "&", body_len - strlen(body)); - } - strnurlencat(body, parts[i]->name, body_len - strlen(body)); - strncat(body, "=", body_len - strlen(body)); - - /* Sanitise the variable. Since we are only doing this for - * the logging we will actually write over the data we keep - * in the memory. - */ - if (msr->phase >= PHASE_LOGGING) { - if (apr_table_get(msr->arguments_to_sanitize, parts[i]->name) != NULL) { - memset(parts[i]->value, '*', strlen(parts[i]->value)); - } - } - strnurlencat(body, parts[i]->value, body_len - strlen(body)); - } - } - - return body; -} diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h deleted file mode 100644 index a0f6a08ddf..0000000000 --- a/apache2/msc_multipart.h +++ /dev/null @@ -1,141 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_MULTIPART_H_ -#define _MSC_MULTIPART_H_ - -#define MULTIPART_BUF_SIZE 4096 - -#define MULTIPART_FORMDATA 1 -#define MULTIPART_FILE 2 - -typedef struct multipart_part multipart_part; -typedef struct multipart_data multipart_data; - -#include "apr_general.h" -#include "apr_tables.h" -#include "modsecurity.h" - -typedef struct value_part_t value_part_t; -struct value_part_t { - char *data; - long int length; -}; - -struct multipart_part { - /* part type, can be MULTIPART_FORMDATA or MULTIPART_FILE */ - int type; - /* the name */ - char *name; - - /* variables only, variable value */ - char *value; - apr_array_header_t *value_parts; - - /* files only, the content type (where available) */ - char *content_type; - - /* files only, the name of the temporary file holding data */ - char *tmp_file_name; - int tmp_file_fd; - unsigned int tmp_file_size; - /* files only, filename as supplied by the browser */ - char *filename; - - char *last_header_name; - apr_table_t *headers; - - unsigned int offset; - unsigned int length; -}; - -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 - */ - char *boundary; - int boundary_count; - - /* internal buffer and other variables - * used while parsing - */ - char buf[MULTIPART_BUF_SIZE + 2]; - int buf_contains_line; - char *bufptr; - int bufleft; - - unsigned int buf_offset; - - /* pointer that keeps track of a part while - * it is being built - */ - multipart_part *mpp; - - - /* part parsing state; 0 means we are reading - * headers, 1 means we are collecting data - */ - int mpp_state; - - /* because of the way this parsing algorithm - * works we hold back the last two bytes of - * each data chunk so that we can discard it - * later if the next data chunk proves to be - * a boundary; the first byte is an indicator - * 0 - no content, 1 - two data bytes available - */ - char reserve[4]; - - int seen_data; - int is_complete; - - int flag_error; - int flag_data_before; - int flag_data_after; - int flag_header_folding; - int flag_boundary_quoted; - int flag_lf_line; - int flag_crlf_line; - int flag_unmatched_boundary; - int flag_boundary_whitespace; - int flag_missing_semicolon; - int flag_invalid_quoting; - int flag_invalid_part; - int flag_invalid_header_folding; - int flag_file_limit_exceeded; -}; - - -/* Functions */ - -int DSOLOCAL multipart_init(modsec_rec *msr, char **error_msg); - -int DSOLOCAL multipart_complete(modsec_rec *msr, char **error_msg); - -int DSOLOCAL multipart_process_chunk(modsec_rec *msr, const char *buf, - unsigned int size, char **error_msg); - -apr_status_t DSOLOCAL multipart_cleanup(modsec_rec *msr); - -int DSOLOCAL multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *arguments); - -char DSOLOCAL *multipart_reconstruct_urlencoded_body_sanitize(modsec_rec *msr); - -#endif diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c deleted file mode 100644 index 57f291ec2a..0000000000 --- a/apache2/msc_parsers.c +++ /dev/null @@ -1,351 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_parsers.h" -#include - -/** - * - */ -int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, - apr_table_t *cookies, const char *delim) -{ - char *attr_name = NULL, *attr_value = NULL; - char *cookie_header; - char *saveptr = NULL; - int cookie_count = 0; - char *p = NULL; - - 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; - - if(msr->txcfg->cookiev0_separator == NULL) { - p = apr_strtok(cookie_header, delim, &saveptr); - } else { - p = apr_strtok(cookie_header, msr->txcfg->cookiev0_separator, &saveptr); - } - - while(p != NULL) { - attr_name = NULL; - attr_value = NULL; - - /* ignore whitespace at the beginning of cookie name */ - while(isspace(*p)) p++; - attr_name = p; - - attr_value = strstr(p, "="); - if (attr_value != NULL) { - /* terminate cookie name */ - *attr_value = 0; - /* move over to the beginning of the value */ - attr_value++; - } - - /* we ignore cookies with empty names */ - if ((attr_name != NULL)&&(strlen(attr_name) != 0)) { - if (attr_value != NULL) { - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"", - log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value)); - } - - apr_table_add(cookies, attr_name, attr_value); - } else { - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty", - log_escape(msr->mp, attr_name)); - } - - apr_table_add(cookies, attr_name, ""); - } - - cookie_count++; - } - - if(msr->txcfg->cookiev0_separator == NULL) { - p = apr_strtok(NULL, delim, &saveptr); - } else { - p = apr_strtok(NULL, msr->txcfg->cookiev0_separator, &saveptr); - } - } - - free(cookie_header); - return cookie_count; -} - -/** - * - */ -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; - - p = cookie_header; - while(*p != 0) { - attr_name = NULL; - attr_value = NULL; - - /* attribute name */ - - /* remove space from the beginning */ - while((isspace(*p))&&(*p != 0)) p++; - attr_name = p; - while((*p != 0)&&(*p != '=')&&(*p != ';')&&(*p != ',')) p++; - - /* if we've reached the end of string */ - if (*p == 0) goto add_cookie; - - /* if there is no cookie value supplied */ - if ((*p == ';')||(*p == ',')) { - *p++ = 0; /* terminate the name */ - goto add_cookie; - } - - /* terminate the attribute name, - * writing over the = character - */ - *p++ = 0; - - /* attribute value */ - - /* skip over the whitespace at the beginning */ - while((isspace(*p))&&(*p != 0)) p++; - - /* no value supplied */ - if (*p == 0) goto add_cookie; - - if (*p == '"') { - if (*++p == 0) goto add_cookie; - attr_value = p; - while((*p != 0)&&(*p != '"')) p++; - if (*p != 0) *p++ = 0; - else { - /* Do nothing about this. */ - } - } else { - attr_value = p; - while((*p != 0)&&(*p != ',')&&(*p != ';')) p++; - if (*p != 0) *p++ = 0; - - /* remove the whitespace from the end of cookie value */ - if (attr_value != NULL) { - char *t = attr_value; - int i = 0; - - while(*t != 0) { - t++; - i++; - } - - while((i-- > 0)&&(isspace(*(--t)))) *t = 0; - } - } - -add_cookie: - - /* remove the whitespace from the end of cookie name */ - if (attr_name != NULL) { - char *t = attr_name; - int i = 0; - - while(*t != 0) { - t++; - i++; - } - - while((i-- > 0)&&(isspace(*(--t)))) *t = 0; - } - - /* add the cookie to the list now */ - if ((attr_name != NULL)&&(strlen(attr_name) != 0)) { - - /* handle special attribute names */ - if (attr_name[0] == '$') { - if (prev_attr_name != NULL) { - /* cookie keyword, we change the name we use - * so they can have a unique name in the cookie table - */ - attr_name = apr_psprintf(msr->mp, "$%s_%s", prev_attr_name, attr_name + 1); - } - } - - if (attr_value != NULL) { - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"", - log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value)); - } - - apr_table_add(cookies, attr_name, attr_value); - } else { - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty", - log_escape(msr->mp, attr_name)); - } - - apr_table_add(cookies, attr_name, ""); - } - - cookie_count++; - - /* only keep the cookie names for later */ - if (attr_name[0] != '$') prev_attr_name = attr_name; - } - - /* at this point the *p is either 0 (in which case we exit), or - * right after the current cookie ended - we need to look for - * the next cookie - */ - while( (*p != 0)&&( (*p == ',')||(*p == ';')||(isspace(*p)) ) ) p++; - } - - free(cookie_header); - return cookie_count; -} - -/** - * - */ -int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, - int argument_separator, const char *origin, - apr_table_t *arguments, int *invalid_count) -{ - msc_arg *arg; - apr_size_t i, j; - char *value = NULL; - char *buf; - int status; - int changed; - - if (s == NULL) return -1; - if (inputlength == 0) return 1; - - /* Check that adding one will not overflow */ - if (inputlength + 1 <= 0) return -1; - - buf = (char *)malloc(inputlength + 1); - if (buf == NULL) return -1; - - arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); - arg->origin = origin; - - i = 0; - j = 0; - status = 0; - *invalid_count = 0; - while (i < inputlength) { - if (status == 0) { - /* parameter name */ - arg->name_origin_offset = i; - while ((s[i] != '=') && (s[i] != argument_separator) && (i < inputlength)) { - buf[j] = s[i]; - j++; - i++; - } - buf[j++] = '\0'; - arg->name_origin_len = i - arg->name_origin_offset; - } else { - /* parameter value */ - arg->value_origin_offset = i; - while ((s[i] != argument_separator) && (i < inputlength)) { - buf[j] = s[i]; - j++; - i++; - } - buf[j++] = '\0'; - arg->value_origin_len = i - arg->value_origin_offset; - } - - if (status == 0) { - arg->name_len = urldecode_nonstrict_inplace_ex((unsigned char *)buf, arg->name_origin_len, invalid_count, &changed); - arg->name = apr_pstrmemdup(msr->mp, buf, arg->name_len); - - if (s[i] == argument_separator) { - /* Empty parameter */ - arg->value_len = 0; - arg->value = ""; - - add_argument(msr, arguments, arg); - - arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); - arg->origin = origin; - - status = 0; /* unchanged */ - j = 0; - } else { - status = 1; - value = &buf[j]; - } - } - else { - arg->value_len = urldecode_nonstrict_inplace_ex((unsigned char *)value, arg->value_origin_len, invalid_count, &changed); - arg->value = apr_pstrmemdup(msr->mp, value, arg->value_len); - - add_argument(msr, arguments, arg); - - arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); - arg->origin = origin; - - status = 0; - j = 0; - } - - i++; /* skip over the separator */ - } - - /* the last parameter was empty */ - if (status == 1) { - arg->value_len = 0; - arg->value = ""; - - add_argument(msr, arguments, arg); - } - - free(buf); - - return 1; -} - -/** - * - */ -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), - log_escape_ex(msr->mp, arg->value, arg->value_len)); - } - - apr_table_addn(arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *)arg); -} - diff --git a/apache2/msc_parsers.h b/apache2/msc_parsers.h deleted file mode 100644 index 2be96dc666..0000000000 --- a/apache2/msc_parsers.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_PARSERS_H_ -#define _MSC_PARSERS_H_ - -#include "modsecurity.h" - -int DSOLOCAL parse_cookies_v0(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies, - const char *delim); - -int DSOLOCAL parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies); - -int DSOLOCAL parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, - int argument_separator, const char *origin, apr_table_t *arguments, int *invalid_count); - -void DSOLOCAL add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg); - -#endif diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c deleted file mode 100644 index 8534a20914..0000000000 --- a/apache2/msc_pcre.c +++ /dev/null @@ -1,193 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_pcre.h" -#include "apr_strings.h" - -/** - * Releases the resources used by a single regular expression pattern. - */ -static apr_status_t msc_pcre_cleanup(msc_regex_t *regex) { - if (regex != NULL) { - if (regex->pe != NULL) { -#if defined(VERSION_NGINX) - pcre_free(regex->pe); -#else - free(regex->pe); -#endif - regex->pe = NULL; - } - if (regex->re != NULL) { - pcre_free(regex->re); - regex->re = NULL; - } - } - - return APR_SUCCESS; -} - -/** - * 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 - * match_limit* parameters are optional and if >0, then will set - * match limits. - */ -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; - regex->pattern = pattern; - - if ((_errptr == NULL)||(_erroffset == NULL)) { - regex->re = pcre_compile(pattern, options, &errptr, &erroffset, NULL); - } else { - regex->re = pcre_compile(pattern, options, _errptr, _erroffset, NULL); - } - if (regex->re == NULL) return NULL; - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - pe = pcre_study(regex->re, PCRE_STUDY_JIT_COMPILE, &errptr); - #else - pe = pcre_study(regex->re, 0, &errptr); - #endif - #endif - - /* Setup the pcre_extra record if pcre_study did not already do it */ - if (pe == NULL) { -#if defined(VERSION_NGINX) - pe = pcre_malloc(sizeof(pcre_extra)); -#else - pe = malloc(sizeof(pcre_extra)); -#endif - 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 -#pragma message ( "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 -#pragma message ( "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 - * on errors, and a value > 0 when there is a match. - */ -int msc_regexec_ex(msc_regex_t *regex, const char *s, unsigned int slen, - int startoffset, int options, int *ovector, int ovecsize, char **error_msg) -{ - if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ - *error_msg = NULL; - - return pcre_exec(regex->re, regex->pe, s, slen, startoffset, options, ovector, ovecsize); -} - -/** - * Executes regular expression, capturing subexpressions in the given - * vector. Returns PCRE_ERROR_NOMATCH when there is no match, error code < -1 - * on errors, and a value > 0 when there is a match. - */ -int msc_regexec_capture(msc_regex_t *regex, const char *s, unsigned int slen, - int *ovector, int ovecsize, char **error_msg) -{ - if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ - *error_msg = NULL; - - return msc_regexec_ex(regex, s, slen, 0, 0, ovector, ovecsize, error_msg); -} - -/** - * Executes regular expression but ignores any of the subexpression - * captures. See above for the return codes. - */ -int msc_regexec(msc_regex_t *regex, const char *s, unsigned int slen, - char **error_msg) -{ - if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ - *error_msg = NULL; - - return msc_regexec_ex(regex, s, slen, 0, 0, NULL, 0, error_msg); -} - -/** - * Gets info on a compiled regex. - */ -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 deleted file mode 100644 index bbaa818b29..0000000000 --- a/apache2/msc_pcre.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_PCRE_H_ -#define _MSC_PCRE_H_ - -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" - -struct msc_regex_t { - void *re; - void *pe; - const char *pattern; -}; - -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); - -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); - -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); - -#endif diff --git a/apache2/msc_release.c b/apache2/msc_release.c deleted file mode 100644 index 4c06a8cef0..0000000000 --- a/apache2/msc_release.c +++ /dev/null @@ -1,39 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_release.h" - -static const struct modsec_build_type_rec { - char name[12]; /* pads at 16 bytes with val */ - int val; -} modsec_build_type[] = { - { "-dev", 1 }, /* Development build */ - { "-rc", 3 }, /* Release Candidate build */ - { "", 9 }, /* Production build */ - { "-tw", 9 }, /* Truswave Holdings build */ - { "-trunk", 9 } /* Trunk build */ -}; - -int get_modsec_build_type(const char *name) -{ - size_t i; - - for (i = 0; i < sizeof(modsec_build_type)/sizeof(modsec_build_type[0]); i++) { - if (strcmp(((name == NULL) ? MODSEC_VERSION_TYPE : name), modsec_build_type[i].name) == 0) { - return modsec_build_type[i].val; - } - } - - return 9; /* so no warning */ -} diff --git a/apache2/msc_release.h b/apache2/msc_release.h deleted file mode 100644 index e34237125e..0000000000 --- a/apache2/msc_release.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_RELEASE_H_ -#define _MSC_RELEASE_H_ - -#include -#include - -/* ENH: Clean this mess up by detecting this is possible */ -#if !(defined(_AIX) || defined(WIN32) || defined(CYGWIN) || defined(NETWARE) || defined(SOLARIS2) || defined(OSF1)) -#define DSOLOCAL __attribute__((visibility("hidden"))) -#else -#define DSOLOCAL -#endif - -#if defined(DEBUG_MEM) -/* Nothing Yet */ -#endif - -/* For GNU C, tell the compiler to check printf like formatters */ -#if (defined(__GNUC__) && !defined(SOLARIS2)) -#define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) -#else -#define PRINTF_ATTRIBUTE(a,b) -#endif - -#define MODSEC_VERSION_MAJOR "2" -#define MODSEC_VERSION_MINOR "7" -#define MODSEC_VERSION_MAINT "5" -#define MODSEC_VERSION_TYPE "" -#define MODSEC_VERSION_RELEASE "" - -#define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE - -#define MODSEC_VERSION \ - MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \ - MODSEC_VERSION_SUFFIX - -/* Apache Module Defines */ -#ifdef VERSION_IIS -#define MODSEC_MODULE_NAME "ModSecurity for IIS (STABLE)" -#else -#ifdef VERSION_NGINX -#define MODSEC_MODULE_NAME "ModSecurity for nginx (STABLE)" -#else -#ifdef VERSION_STANDALONE -#define MODSEC_MODULE_NAME "ModSecurity Standalone (STABLE)" -#else -#define MODSEC_MODULE_NAME "ModSecurity for Apache" -#endif -#endif -#endif -#define MODSEC_MODULE_VERSION MODSEC_VERSION -#define MODSEC_MODULE_NAME_FULL MODSEC_MODULE_NAME "/" MODSEC_MODULE_VERSION " (http://www.modsecurity.org/)" - -int DSOLOCAL get_modsec_build_type(const char *name); - -#endif /* _MSC_RELEASE_H_ */ diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c deleted file mode 100644 index 384e4578a8..0000000000 --- a/apache2/msc_reqbody.c +++ /dev/null @@ -1,894 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "modsecurity.h" -#include "re.h" -#include "msc_parsers.h" - -#define CHUNK_CAPACITY 8192 - - -/** - * - */ -void msre_engine_reqbody_processor_register(msre_engine *engine, - const char *name, void *fn_init, void *fn_process, void *fn_complete) -{ - msre_reqbody_processor_metadata *metadata = - (msre_reqbody_processor_metadata *)apr_pcalloc(engine->mp, - sizeof(msre_reqbody_processor_metadata)); - if (metadata == NULL) return; - - metadata->name = name; - metadata->init = fn_init; - metadata->process = fn_process; - metadata->complete = fn_complete; - apr_table_setn(engine->reqbody_processors, name, (void *)metadata); -} - -/** - * Prepare to accept the request body (part 2). - */ -static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **error_msg) { - *error_msg = NULL; - - if(msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { - /* Prepare to store request body in memory. */ - - msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, - 32, sizeof(msc_data_chunk *)); - if (msr->msc_reqbody_chunks == NULL) { - *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage."); - return -1; - } - } else { - /* Prepare to store request body on disk. */ - - msr->msc_reqbody_filename = apr_psprintf(msr->mp, "%s/%s-%s-request_body-XXXXXX", - msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid); - if (msr->msc_reqbody_filename == NULL) { - *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to generate an on-disk filename."); - return -1; - } - - msr->msc_reqbody_fd = msc_mkstemp((char *)msr->msc_reqbody_filename); - if (msr->msc_reqbody_fd < 0) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to create temporary file: %s", - msr->msc_reqbody_filename); - return -1; - } - - msr_log(msr, 4, "Input filter: Created temporary file to store request body: %s", - msr->msc_reqbody_filename); - } - - return 1; -} - -/** - * Prepare to accept the request body (part 1). - */ -apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { - *error_msg = NULL; - msr->msc_reqbody_length = 0; - msr->stream_input_length = 0; - - /* Create a separate memory pool that will be used - * to allocate structures from (not data, which is allocated - * via malloc). - */ - apr_pool_create(&msr->msc_reqbody_mp, NULL); - - /* Initialise request body processors, if any. */ - - if (msr->msc_reqbody_processor != NULL) { - char *my_error_msg = NULL; - msre_reqbody_processor_metadata *metadata = - (msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor); - - if (metadata != NULL) { - if ( (metadata->init != NULL) - && (metadata->init(msr, &my_error_msg) < 0)) - { - *error_msg = apr_psprintf(msr->mp, - "%s parsing error (init): %s", - msr->msc_reqbody_processor, - my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - // TODO: All these below need to be registered in the same way as above - else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { - if (multipart_init(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart parsing error (init): %s", my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { - if (xml_init(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "XML parsing error (init): %s", my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) { - /* Do nothing, URLENCODED processor does not support streaming yet. */ - } - else { - *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s", - msr->msc_reqbody_processor); - return -1; - } - } - - return modsecurity_request_body_start_init(msr, error_msg); -} - -/** - * Stores a chunk of request body data to disk. - */ -static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, - const char *data, apr_size_t length, char **error_msg) -{ - apr_size_t i; - - *error_msg = NULL; - - i = write(msr->msc_reqbody_fd, data, length); - if (i != length) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed writing %" APR_SIZE_T_FMT - " bytes to temporary file (rc %" APR_SIZE_T_FMT ").", length, i); - return -1; - } - - return 1; -} - -/** - * Stores one chunk of request body data in memory. - */ -static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, - const char *data, apr_size_t length, char **error_msg) -{ - *error_msg = NULL; - - /* Would storing this chunk mean going over the limit? */ - if ((msr->msc_reqbody_spilltodisk) - && (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_ON) - && (msr->msc_reqbody_length + length > (apr_size_t)msr->txcfg->reqbody_inmemory_limit)) - { - msc_data_chunk **chunks; - unsigned int disklen = 0; - int i; - - msr_log(msr, 4, "Input filter: Request too large to store in memory, switching to disk."); - - /* NOTE Must use modsecurity_request_body_store_disk() here - * to prevent data to be sent to the streaming - * processors again. - */ - - /* Initialise disk storage */ - msr->msc_reqbody_storage = MSC_REQBODY_DISK; - if (modsecurity_request_body_start_init(msr, error_msg) < 0) return -1; - - /* Write the data we keep in memory */ - chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; - for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) { - disklen += chunks[i]->length; - - if (modsecurity_request_body_store_disk(msr, chunks[i]->data, chunks[i]->length, error_msg) < 0) { - return -1; - } - - free(chunks[i]->data); - chunks[i]->data = NULL; - } - - /* Clear the memory pool as we no longer need the bits. */ - - /* IMP1 But since we only used apr_pool_clear memory might - * not be released back to the OS straight away? - */ - msr->msc_reqbody_chunks = NULL; - apr_pool_clear(msr->msc_reqbody_mp); - - msr_log(msr, 4, "Input filter: Wrote %u bytes from memory to disk.", disklen); - - /* Continue with disk storage from now on */ - return modsecurity_request_body_store_disk(msr, data, length, error_msg); - } - - /* If we're here that means we are not over the - * request body in-memory limit yet. - */ - { - unsigned long int bucket_offset, bucket_left; - - bucket_offset = 0; - bucket_left = length; - - /* Although we store the request body in chunks we don't - * want to use the same chunk sizes as the incoming memory - * buffers. They are often of very small sizes and that - * would make us waste a lot of memory. That's why we - * use our own chunks of CHUNK_CAPACITY sizes. - */ - - /* Loop until we empty this bucket into our chunks. */ - while(bucket_left > 0) { - /* Allocate a new chunk if we have to. */ - if (msr->msc_reqbody_chunk_current == NULL) { - msr->msc_reqbody_chunk_current = (msc_data_chunk *) - apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); - if (msr->msc_reqbody_chunk_current == NULL) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes " - "for request body chunk.", (unsigned long)sizeof(msc_data_chunk)); - return -1; - } - - msr->msc_reqbody_chunk_current->data = malloc(CHUNK_CAPACITY); - if (msr->msc_reqbody_chunk_current->data == NULL) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes " - "for request body chunk data.", CHUNK_CAPACITY); - return -1; - } - - msr->msc_reqbody_chunk_current->length = 0; - msr->msc_reqbody_chunk_current->is_permanent = 1; - - *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) - = msr->msc_reqbody_chunk_current; - } - - if (bucket_left < (CHUNK_CAPACITY - msr->msc_reqbody_chunk_current->length)) { - /* There's enough space in the current chunk. */ - memcpy(msr->msc_reqbody_chunk_current->data + - msr->msc_reqbody_chunk_current->length, data + bucket_offset, bucket_left); - msr->msc_reqbody_chunk_current->length += bucket_left; - bucket_left = 0; - } else { - /* Fill the existing chunk. */ - unsigned long int copy_length = CHUNK_CAPACITY - - msr->msc_reqbody_chunk_current->length; - - memcpy(msr->msc_reqbody_chunk_current->data + - msr->msc_reqbody_chunk_current->length, data + bucket_offset, copy_length); - bucket_offset += copy_length; - bucket_left -= copy_length; - msr->msc_reqbody_chunk_current->length += copy_length; - - /* We're done with this chunk. Setting the pointer - * to NULL is going to force a new chunk to be allocated - * on the next go. - */ - msr->msc_reqbody_chunk_current = NULL; - } - } - - msr->msc_reqbody_length += length; - } - - return 1; -} - -/** - * Stores one chunk of request body data. Returns -1 on error. - */ -apr_status_t modsecurity_request_body_store(modsec_rec *msr, - const char *data, apr_size_t length, char **error_msg) -{ - *error_msg = NULL; - - /* If we have a processor for this request body send - * data to it first (but only if it did not report an - * error on previous invocations). - */ - if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) { - char *my_error_msg = NULL; - msre_reqbody_processor_metadata *metadata = - (msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor); - - if (metadata != NULL) { - if ( (metadata->process != NULL) - && (metadata->process(msr, data, length, &my_error_msg) < 0)) - { - *error_msg = apr_psprintf(msr->mp, - "%s parsing error: %s", - msr->msc_reqbody_processor, - my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - // TODO: All these below need to be registered in the same way as above - else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { - /* The per-request data length counter will - * be updated by the multipart parser. - */ - - /* Process data as multipart/form-data. */ - if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = *error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { - /* Increase per-request data length counter. */ - msr->msc_reqbody_no_files_length += length; - - /* Process data as XML. */ - if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "XML parsing error: %s", my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = *error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) { - /* Increase per-request data length counter. */ - msr->msc_reqbody_no_files_length += length; - - /* Do nothing else, URLENCODED processor does not support streaming. */ - } - else { - *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s", - msr->msc_reqbody_processor); - return -1; - } - } else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) { - /* Increase per-request data length counter if forcing buffering. */ - msr->msc_reqbody_no_files_length += length; - } - - /* Check that we are not over the request body no files limit. */ - if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) { - - *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - if (msr->txcfg->debuglog_level >= 1) { - msr_log(msr, 1, "%s", *error_msg); - } - - if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { - return -5; - } else if (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL) { - if(msr->txcfg->is_enabled == MODSEC_ENABLED) - return -5; - } - } - - - /* Store data. */ - if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { - return modsecurity_request_body_store_memory(msr, data, length, error_msg); - } - else - if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - return modsecurity_request_body_store_disk(msr, data, length, error_msg); - } - - /* Should never happen. */ - *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %u", - msr->msc_reqbody_storage); - return -1; -} - -apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, const char *buffer, int buflen, char **error_msg) { - char *stream_input_body = NULL; - char *data = NULL; - int first_pkt = 0; - - if(msr->stream_input_data == NULL) { - msr->stream_input_data = (char *)calloc(sizeof(char), msr->stream_input_length + 1); - first_pkt = 1; - } - else { - - data = (char *)malloc(msr->stream_input_length + 1 - buflen); - - if(data == NULL) - return -1; - - memset(data, 0, msr->stream_input_length + 1 - buflen); - memcpy(data, msr->stream_input_data, msr->stream_input_length - buflen); - - stream_input_body = (char *)realloc(msr->stream_input_data, msr->stream_input_length + 1); - - msr->stream_input_data = (char *)stream_input_body; - } - - if (msr->stream_input_data == NULL) { - if(data) { - free(data); - data = NULL; - } - *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.", - msr->stream_input_length + 1); - return -1; - } - - memset(msr->stream_input_data, 0, msr->stream_input_length+1); - - if(first_pkt) { - memcpy(msr->stream_input_data, buffer, msr->stream_input_length); - } else { - memcpy(msr->stream_input_data, data, msr->stream_input_length - buflen); - memcpy(msr->stream_input_data+(msr->stream_input_length - buflen), buffer, buflen); - } - - if(data) { - free(data); - data = NULL; - } - - return 1; -} -/** - * Replace a bunch of chunks holding a request body with a single large chunk. - */ -static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **error_msg) { - msc_data_chunk **chunks, *one_chunk; - char *d; - int i, sofar; - - *error_msg = NULL; - - /* Allocate a buffer large enough to hold the request body. */ - - if (msr->msc_reqbody_length + 1 == 0) { - *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u", - msr->msc_reqbody_length); - return -1; - } - - msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1); - if (msr->msc_reqbody_buffer == NULL) { - *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %u bytes.", - msr->msc_reqbody_length + 1); - return -1; - } - - msr->msc_reqbody_buffer[msr->msc_reqbody_length] = '\0'; - - /* Copy the data we keep in chunks into the new buffer. */ - - sofar = 0; - d = msr->msc_reqbody_buffer; - chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; - for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) { - if (sofar + chunks[i]->length <= msr->msc_reqbody_length) { - memcpy(d, chunks[i]->data, chunks[i]->length); - d += chunks[i]->length; - sofar += chunks[i]->length; - } else { - *error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow."); - return -1; - } - } - - - /* Now free the memory used by the chunks. */ - - chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; - for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) { - free(chunks[i]->data); - chunks[i]->data = NULL; - } - - /* Create a new array with only one chunk in it. */ - - msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 2, sizeof(msc_data_chunk *)); - if (msr->msc_reqbody_chunks == NULL) { - *error_msg = apr_pstrdup(msr->mp, "Failed to create structure to hold request body."); - return -1; - } - - one_chunk = (msc_data_chunk *)apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); - one_chunk->data = msr->msc_reqbody_buffer; - one_chunk->length = msr->msc_reqbody_length; - one_chunk->is_permanent = 1; - *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) = one_chunk; - - if(msr->txcfg->reqbody_limit > 0 && msr->txcfg->reqbody_limit < msr->msc_reqbody_length) { - msr->msc_reqbody_length = msr->txcfg->reqbody_limit; - } - - return 1; -} - -/** - * - */ -static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) { - int invalid_count = 0; - - *error_msg = NULL; - - /* Create the raw buffer */ - if (modsecurity_request_body_end_raw(msr, error_msg) != 1) { - return -1; - } - - /* Parse URL-encoded arguments in the request body. */ - - if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length, - msr->txcfg->argument_separator, "BODY", msr->arguments, &invalid_count) < 0) - { - *error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments."); - return -1; - } - - if (invalid_count) { - msr->urlencoded_error = 1; - } - - return 1; -} - -/** - * Stops receiving the request body. - */ -apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { - *error_msg = NULL; - - /* Close open file descriptors, if any. */ - if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - if (msr->msc_reqbody_fd > 0) { - close(msr->msc_reqbody_fd); - msr->msc_reqbody_fd = -1; - } - } - - /* Note that we've read the body. */ - msr->msc_reqbody_read = 1; - - /* Finalise body processing. */ - if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) { - char *my_error_msg = NULL; - msre_reqbody_processor_metadata *metadata = - (msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor); - - if (metadata != NULL) { - if ( (metadata->complete != NULL) - && (metadata->complete(msr, &my_error_msg) < 0)) - { - *error_msg = apr_psprintf(msr->mp, - "%s parsing error (complete): %s", - msr->msc_reqbody_processor, - my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "%s", *error_msg); - } - } - // TODO: All these below need to be registered in the same way as above - else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { - if (multipart_complete(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = *error_msg; - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "%s", *error_msg); - } - return -1; - } - - if (multipart_get_arguments(msr, "BODY", msr->arguments) < 0) { - *error_msg = "Multipart parsing error: Failed to retrieve arguments."; - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = *error_msg; - msr_log(msr, 2, "%s", *error_msg); - return -1; - } - } - else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) { - return modsecurity_request_body_end_urlencoded(msr, error_msg); - } - else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { - if (xml_complete(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "XML parser error: %s", my_error_msg); - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = *error_msg; - msr_log(msr, 2, "%s", *error_msg); - return -1; - } - } - } else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) { - /* Convert to a single continous buffer, but don't do anything else. */ - return modsecurity_request_body_end_raw(msr, error_msg); - } - - /* Note the request body no files length. */ - msr_log(msr, 4, "Request body no files length: %" APR_SIZE_T_FMT, msr->msc_reqbody_no_files_length); - - return 1; -} - -/** - * Prepares to forward the request body. - */ -apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) { - *error_msg = NULL; - - if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { - msr->msc_reqbody_chunk_position = 0; - msr->msc_reqbody_chunk_offset = 0; - - msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); - if (msr->msc_reqbody_disk_chunk == NULL) { - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", - (unsigned long)sizeof(msc_data_chunk)); - return -1; - } - msr->msc_reqbody_disk_chunk->is_permanent = 1; - } - else - if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); - if (msr->msc_reqbody_disk_chunk == NULL) { - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", - (unsigned long)sizeof(msc_data_chunk)); - return -1; - } - - msr->msc_reqbody_disk_chunk->is_permanent = 0; - msr->msc_reqbody_disk_chunk->data = apr_palloc(msr->msc_reqbody_mp, CHUNK_CAPACITY); - if (msr->msc_reqbody_disk_chunk->data == NULL) { - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.", - CHUNK_CAPACITY); - return -1; - } - - msr->msc_reqbody_fd = open(msr->msc_reqbody_filename, O_RDONLY | O_BINARY); - if (msr->msc_reqbody_fd < 0) { - *error_msg = apr_psprintf(msr->mp, "Failed to open temporary file for reading: %s", - msr->msc_reqbody_filename); - return -1; - } - } - - return 1; -} - -/** - * - */ -apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) { - if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - if (msr->msc_reqbody_fd > 0) { - close(msr->msc_reqbody_fd); - msr->msc_reqbody_fd = -1; - } - } - - return 1; -} - -/** - * Returns one chunk of request body data. It stores a NULL - * in the chunk pointer when there is no data to return. The - * return code is 1 if more calls can be made to retrieve more - * data, 0 if there is no more data to retrieve, or -1 on error. - * - * The caller can limit the amount of data returned by providing - * a non-negative value in nbytes. - */ -apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, - msc_data_chunk **chunk, long int nbytes, char **error_msg) -{ - msc_data_chunk **chunks; - - *error_msg = NULL; - - if (chunk == NULL) { - *error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk."); - return -1; - } - *chunk = NULL; - - if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { - /* Are there any chunks left? */ - if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) { - /* No more chunks. */ - return 0; - } - - /* We always respond with the same chunk, just different information in it. */ - *chunk = msr->msc_reqbody_disk_chunk; - - /* Advance to the current chunk and position on the - * next byte we need to send. - */ - chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; - msr->msc_reqbody_disk_chunk->data = chunks[msr->msc_reqbody_chunk_position]->data - + msr->msc_reqbody_chunk_offset; - - if (nbytes < 0) { - /* Send what's left in this chunk as there is no limit on the size. */ - msr->msc_reqbody_disk_chunk->length = chunks[msr->msc_reqbody_chunk_position]->length; - msr->msc_reqbody_chunk_position++; - msr->msc_reqbody_chunk_offset = 0; - } else { - /* We have a limit we must obey. */ - - if (chunks[msr->msc_reqbody_chunk_position]->length - - msr->msc_reqbody_chunk_offset <= (unsigned int)nbytes) - { - /* If what's left in our chunk is less than the limit - * then send it all back. - */ - msr->msc_reqbody_disk_chunk->length = - chunks[msr->msc_reqbody_chunk_position]->length - - msr->msc_reqbody_chunk_offset; - msr->msc_reqbody_chunk_position++; - msr->msc_reqbody_chunk_offset = 0; - } else { - /* If we have more data in our chunk, send the - * maximum bytes we can (nbytes). - */ - msr->msc_reqbody_disk_chunk->length = nbytes; - msr->msc_reqbody_chunk_offset += nbytes; - } - } - - /* If we've advanced beyond our last chunk then - * we have no more data to send. - */ - if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) { - return 0; /* No more chunks. */ - } - - /* More data available. */ - return 1; - } - - if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - long int my_nbytes = CHUNK_CAPACITY; - int i; - - /* Send CHUNK_CAPACITY bytes at a time unless a lower limit was requested. */ - if ((nbytes != -1)&&(my_nbytes > nbytes)) { - my_nbytes = nbytes; - } - - i = read(msr->msc_reqbody_fd, msr->msc_reqbody_disk_chunk->data, my_nbytes); - if (i < 0) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Error reading from temporary file: %s", - strerror(errno)); - return -1; - } - - *chunk = msr->msc_reqbody_disk_chunk; - msr->msc_reqbody_disk_chunk->length = i; - - if (i == 0) return 0; /* No more data available. */ - - return 1; /* More data available. */ - } - - /* Should never happen. */ - *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %u", - msr->msc_reqbody_storage); - - return -1; -} - -/** - * - */ -apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) { - *error_msg = NULL; - - /* Release memory we used to store request body data. */ - if (msr->msc_reqbody_chunks != NULL) { - msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; - int i; - - for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) { - if (chunks[i]->data != NULL) { - free(chunks[i]->data); - chunks[i]->data = NULL; - } - } - } - - if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - int keep_body = 0; - - /* Should we keep the body? This normally - * happens when a PUT method was used, which - * means the body is actually a file. - */ - if ((msr->upload_remove_files == 0)&&(strcasecmp(msr->request_method, "PUT") == 0)) { - if (msr->txcfg->upload_dir != NULL) { - keep_body = 1; - } else { - *error_msg = apr_psprintf(msr->mp, "Input filter: SecUploadDir is undefined, " - "unable to store PUT file."); - } - } - - /* Deal with a request body stored in a file. */ - - if (msr->msc_reqbody_filename != NULL) { - if (keep_body) { - /* Move request body (which is a file) to the storage area. */ - const char *put_filename = NULL; - const char *put_basename = NULL; - - /* Construct the new filename. */ - put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename); - if (put_basename == NULL) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate basename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename)); - return -1; - } - put_filename = apr_psprintf(msr->msc_reqbody_mp, "%s/%s", - msr->txcfg->upload_dir, put_basename); - if (put_filename == NULL) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate filename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename)); - return -1; - } - - if (apr_file_rename(msr->msc_reqbody_filename, put_filename, - msr->msc_reqbody_mp) != APR_SUCCESS) - { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to rename file from \"%s\" to \"%s\".", - log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename), - log_escape(msr->msc_reqbody_mp, put_filename)); - return -1; - } else { - msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".", - log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename), - log_escape(msr->msc_reqbody_mp, put_filename)); - } - } else { - /* make sure it is closed first */ - if (msr->msc_reqbody_fd > 0) { - close(msr->msc_reqbody_fd); - msr->msc_reqbody_fd = -1; - } - - /* We do not want to keep the request body. */ - if (apr_file_remove(msr->msc_reqbody_filename, - msr->msc_reqbody_mp) != APR_SUCCESS) - { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to delete temporary file: %s", - log_escape(msr->mp, msr->msc_reqbody_filename)); - return -1; - } - - msr_log(msr, 4, "Input filter: Removed temporary file: %s", - msr->msc_reqbody_filename); - } - - msr->msc_reqbody_filename = NULL; - } - } - - if (msr->msc_reqbody_mp != NULL) { - apr_pool_destroy(msr->msc_reqbody_mp); - msr->msc_reqbody_mp = NULL; - } - - return 1; -} diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c deleted file mode 100644 index ce0e70afc8..0000000000 --- a/apache2/msc_tree.c +++ /dev/null @@ -1,895 +0,0 @@ -/* - * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) - * - * You may not use this file except in compliance with - * the License.  You may obtain a copy of the License at - * - *     http://www.apache.org/licenses/LICENSE-2.0 - * - * If any of the files related to licensing are missing or if you have any - * other questions related to licensing please contact Trustwave Holdings, Inc. - * directly using the email address security@modsecurity.org. - */ - -#include -#include -#if APR_HAVE_STDINT_H -#include -#endif -#include -#if APR_HAVE_NETINET_IN_H -#include -#endif -#if APR_HAVE_ARPA_INET_H -#include -#endif -#include "apr_lib.h" -#include "msc_util.h" -#include "msc_tree.h" - -CPTTree *CPTCreateRadixTree(apr_pool_t *pool) { - CPTTree *tree = NULL; - - tree = apr_palloc(pool, sizeof(CPTTree)); - - if(tree == NULL) - return NULL; - - memset(tree, 0, sizeof(CPTTree)); - tree->pool = pool; - - return tree; -} - -void ConvertIPNetmask(unsigned char *buffer, unsigned char netmask, unsigned int ip_bitmask) { - int aux = 0, bytes = 0; - int mask = 0, mask_bit = 0; - - bytes = ip_bitmask/8; - - while(aux < bytes) { - mask_bit = ((1+aux) * 8); - - if (mask_bit > netmask) { - mask = 0; - if ((mask_bit - netmask) < 8) mask = SHIFT_LEFT_MASK(mask_bit - netmask); - } else { - mask = -1; - } - - buffer[aux] &= mask; - aux++; - } - - return; -} - -TreeNode *CPTCreateNode(apr_pool_t *pool) { - TreeNode *node = NULL; - - node = apr_palloc(pool, sizeof(TreeNode)); - - if(node == NULL) - return NULL; - - memset(node, 0, sizeof(TreeNode)); - return node; -} - -CPTData *CPTCreateCPTData(unsigned char netmask, apr_pool_t *pool) { - - CPTData *prefix_data = apr_palloc(pool, sizeof(CPTData)); - - if (prefix_data == NULL) { - return NULL; - } - - memset(prefix_data, 0, sizeof(CPTData)); - - prefix_data->netmask = netmask; - - return prefix_data; -} - -TreePrefix *InsertDataPrefix(TreePrefix *prefix, unsigned char *ipdata, unsigned int ip_bitmask, - unsigned char netmask, apr_pool_t *pool) { - - if(prefix == NULL) - return NULL; - - memcpy(prefix->buffer, ipdata, ip_bitmask/8); - prefix->bitlen = ip_bitmask; - - prefix->prefix_data = CPTCreateCPTData(netmask, pool); - - if(prefix->prefix_data == NULL) - return NULL; - - return prefix; -} - -TreePrefix *CPTCreatePrefix(unsigned char *ipdata, unsigned int ip_bitmask, - unsigned char netmask, apr_pool_t *pool) { - - TreePrefix *prefix = NULL; - int bytes = ip_bitmask/8; - - if ((ip_bitmask % 8 != 0) || (ipdata == NULL)) { - return NULL; - } - - prefix = apr_palloc(pool, sizeof(TreePrefix)); - if (prefix == NULL) - return NULL; - - memset(prefix, 0, sizeof(TreePrefix)); - - prefix->buffer = apr_palloc(pool, bytes); - - if(prefix->buffer == NULL) - return NULL; - - memset(prefix->buffer, 0, bytes); - - return InsertDataPrefix(prefix, ipdata, ip_bitmask, netmask, pool); -} - -void CPTAppendToCPTDataList(CPTData *new, CPTData **list) { - CPTData *temp = NULL, *prev = NULL; - - if (new == NULL) { - return; - } - - if (list == NULL) { - return; - } - - prev = *list; - temp = *list; - - while (temp != NULL) { - if (new->netmask > temp->netmask) - break; - prev = temp; - temp = temp->next; - } - - if (temp == *list) { - new->next = *list; - *list = new; - } else { - new->next = prev->next; - prev->next = new; - } - - return; -} - -int TreePrefixContainNetmask(TreePrefix *prefix, unsigned char netmask) { - CPTData *prefix_data = NULL; - - if (prefix == NULL) { - return 0; - } - - prefix_data = prefix->prefix_data; - - while (prefix_data != NULL) { - if (prefix_data->netmask == netmask) - return 1; - prefix_data = prefix_data->next; - } - - return 0; -} - -int CheckBitmask(unsigned char netmask, unsigned int ip_bitmask) { - - switch(netmask) { - - case 0xff: - return 1; - case 0x20: - if(ip_bitmask == 0x20) - return 1; - break; - case 0x80: - if(ip_bitmask == 0x80) - return 1; - break; - } - - return 0; -} - -TreeNode *CPTCreateHead(TreePrefix *prefix, TreeNode *node, CPTTree *tree, unsigned char netmask, unsigned int ip_bitmask) { - - if(tree == NULL) - return NULL; - - if(prefix == NULL) - return NULL; - - if (node != NULL) { - - node->prefix = prefix; - node->bit = prefix->bitlen; - tree->head = node; - - if(CheckBitmask(netmask, ip_bitmask)) - return node; - - node->count++; - node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char))); - - if(node->netmasks) - node->netmasks[0] = netmask; - - return node; - - } else { - return NULL; - } - - return NULL; -} - -TreeNode *SetParentNode(TreeNode *node, TreeNode *new_node, CPTTree *tree) { - - if (node->parent == NULL) - tree->head = new_node; - else if (node->parent->right == node) - node->parent->right = new_node; - else - node->parent->left = new_node; - - return new_node; -} - -int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node, - CPTTree *tree, unsigned char netmask, unsigned char bitlen) { - int i; - - if (netmask != NETMASK_256-1 && netmask != NETMASK_128) { - if ((netmask != NETMASK_32 || (netmask == NETMASK_32 && bitlen != NETMASK_32))) { - - node = new_node; - parent = new_node->parent; - - while (parent != NULL && netmask < (parent->bit + 1)) { - node = parent; - parent = parent->parent; - } - - node->count++; - node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char))); - - if(node->netmasks == NULL) - return 0; - if ((node->count-1) == 0) { - node->netmasks[0] = netmask; - return 1; - } - - node->netmasks[node->count - 1] = netmask; - - i = node->count - 2; - while (i >= 0) { - if (netmask < node->netmasks[i]) { - node->netmasks[i + 1] = netmask; - break; - } - - node->netmasks[i + 1] = node->netmasks[i]; - node->netmasks[i] = netmask; - i--; - } - } - } - - return 0; -} - -TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree, unsigned char netmask) { - unsigned char *buffer = NULL; - unsigned char bitlen = 0; - int bit_validation = 0, test_bit = 0; - int i = 0, j = 0, temp = 0; - unsigned int x, y; - TreeNode *node = NULL, *new_node = NULL; - TreeNode *parent = NULL, *i_node = NULL; - TreeNode *bottom_node = NULL; - TreePrefix *prefix = NULL; - - if (tree == NULL) { - return NULL; - } - - ConvertIPNetmask(ipdata, netmask, ip_bitmask); - - prefix = CPTCreatePrefix(ipdata, ip_bitmask, netmask, tree->pool); - - if (prefix == NULL) { - return NULL; - } - - if (tree->head == NULL) { - node = CPTCreateNode(tree->pool); - return CPTCreateHead(prefix, node, tree, netmask, ip_bitmask); - } - - node = tree->head; - buffer = prefix->buffer; - bitlen = prefix->bitlen; - - while (node->bit < bitlen || node->prefix == NULL) { - - if (bitlen < node->bit) { - if (node->right == NULL) - break; - else - node = node->right; - } else { - x = SHIFT_RIGHT_MASK(node->bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (node->bit % 8)); - - if (TREE_CHECK(buffer[x],y)) { - if (node->right == NULL) - break; - node = node->right; - } else { - if (node->left == NULL) - break; - else - node = node->left; - } - } - } - - bottom_node = node; - - if(node->bit < bitlen) - bit_validation = node->bit; - else - bit_validation = bitlen; - - for (i = 0; (i * NETMASK_8) < bit_validation; i++) { - int net = 0, div = 0; - int cnt = 0; - - if ((temp = (buffer[i] ^ bottom_node->prefix->buffer[i])) == 0) { - test_bit = (i + 1) * NETMASK_8; - continue; - } - - temp += temp; - - for(cnt = 0, net = NETMASK_256, div = 2; net >= NETMASK_2; net = NETMASK_256/div, - div += div, cnt++) { - if(temp >= net) { - test_bit = (i * NETMASK_8) + cnt; - break; - } - } - break; - } - - if (bit_validation < test_bit) - test_bit = bit_validation; - - parent = node->parent; - - while (parent && test_bit <= parent->bit) { - node = parent; - parent = node->parent; - } - - if (test_bit == bitlen && node->bit == bitlen) { - if (node->prefix != NULL) { - int found = 0; - CPTData *prefix_data = NULL; - - prefix_data = node->prefix->prefix_data; - - while(prefix_data != NULL) { - if (prefix_data->netmask == netmask) - ++found; - prefix_data = prefix_data->next; - } - - if (found != 0) { - - CPTData *prefix_data = CPTCreateCPTData(netmask, tree->pool); - CPTAppendToCPTDataList(prefix_data, &prefix->prefix_data); - - if(CheckBitmask(netmask, ip_bitmask)) - return node; - - parent = node->parent; - while (parent != NULL && netmask < (parent->bit + 1)) { - node = parent; - parent = parent->parent; - } - - node->count++; - new_node = node; - node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char))); - - if ((node->count -1) == 0) { - node->netmasks[0] = netmask; - return new_node; - } - - node->netmasks[node->count - 1] = netmask; - - i = node->count - 2; - while (i >= 0) { - if (netmask < node->netmasks[i]) { - node->netmasks[i + 1] = netmask; - break; - } - - node->netmasks[i + 1] = node->netmasks[i]; - node->netmasks[i] = netmask; - i--; - } - } - } else { - node->prefix = CPTCreatePrefix(prefix->buffer, prefix->bitlen, - NETMASK_256-1, tree->pool); - } - return node; - } - - new_node = CPTCreateNode(tree->pool); - - if(new_node == NULL) - return NULL; - - new_node->prefix = prefix; - new_node->bit = prefix->bitlen; - - if (test_bit == bitlen) { - - x = SHIFT_RIGHT_MASK(test_bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (test_bit % 8)); - - if (TREE_CHECK(bottom_node->prefix->buffer[x],y)) { - new_node->right = node; - } else { - new_node->left = node; - } - - new_node->parent = node->parent; - node->parent = SetParentNode(node, new_node, tree); - - } else { - i_node = CPTCreateNode(tree->pool); - - if(i_node == NULL) - return NULL; - - //i_node->prefix = NULL; - i_node->bit = test_bit; - i_node->parent = node->parent; - - if (node->netmasks != NULL) { - i = 0; - while(i < node->count) { - if (node->netmasks[i] < test_bit + 1) - break; - i++; - } - - i_node->netmasks = apr_palloc(tree->pool, (node->count - i) * sizeof(unsigned char)); - - if(i_node->netmasks == NULL) { - return NULL; - } - - j = 0; - while (j < (node->count - i)) { - i_node->netmasks[j] = node->netmasks[i + j]; - j++; - } - - i_node->count = (node->count - i); - node->count = i; - - if (node->count == 0) { - node->netmasks = NULL; - } - } - - x = SHIFT_RIGHT_MASK(test_bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (test_bit % 8)); - - if (TREE_CHECK(buffer[x],y)) { - i_node->left = node; - i_node->right = new_node; - } else { - i_node->left = new_node; - i_node->right = node; - } - - new_node->parent = i_node; - node->parent = SetParentNode(node, i_node, tree); - } - - if (InsertNetmask(node, parent, new_node, tree, netmask, bitlen)) - return new_node; - - return new_node; -} - -int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, unsigned int netmask) { - - while(prefix_data != NULL) { - if (prefix_data->netmask == netmask) { - return 1; - } - prefix_data = prefix_data->next; - } - - return 0; -} - -int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, unsigned int netmask, int flag) { - CPTData *prefix_data = NULL; - int ret = 0; - - if (prefix == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "TreePrefixNetmask: prefix is NULL."); - } - return 0; - } - - prefix_data = prefix->prefix_data; - - if (flag == 1) { - - if(prefix_data == NULL) return 0; - - if (prefix_data->netmask != netmask) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "TreePrefixNetmask: Cannot find a prefix with correct netmask."); - } - return 0; - } else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "TreePrefixNetmask: Found a prefix with correct netmask."); - } - return 1; - } - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "TreePrefixNetmask: Check if a prefix has a the correct netmask"); - } - - ret = TreeCheckData(prefix, prefix_data, netmask); - - return ret; -} - -TreeNode *CPTRetriveNode(modsec_rec *msr, unsigned char *buffer, unsigned int ip_bitmask, TreeNode *node) { - unsigned int x, y; - - if(node == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTRetriveNode: Node tree is NULL."); - } - return NULL; - } - - if(buffer == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTRetriveNode: Empty ip address. Nothing to search for."); - } - return NULL; - } - - while (node->bit < ip_bitmask) { - - x = SHIFT_RIGHT_MASK(node->bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (node->bit % 8)); - - if (TREE_CHECK(buffer[x], y)) { - node = node->right; - if (node == NULL) return NULL; - } else { - node = node->left; - if (node == NULL) return NULL; - } - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTRetriveNode: Found the node for provided ip address."); - } - - - return node; -} - -TreeNode *CPTRetriveParentNode(TreeNode *node) { - - while (node != NULL && node->netmasks == NULL) - node = node->parent; - - return node; -} - -TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsigned char ip_bitmask, TreeNode *node) { - TreeNode *netmask_node = NULL; - int mask = 0, bytes = 0; - int i = 0, j = 0; - int mask_bits = 0; - - node = CPTRetriveParentNode(node); - - if (node == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElementIPNetblock: Node tree is NULL."); - } - return NULL; - } - - netmask_node = node; - - while(j < netmask_node->count) { - bytes = ip_bitmask / 8; - - while( i < bytes ) { - - mask = -1; - mask_bits = ((i + 1) * 8); - - if (mask_bits > netmask_node->netmasks[j]) { - if ((mask_bits - netmask_node->netmasks[j]) < 8) - mask = SHIFT_LEFT_MASK(mask_bits - netmask_node->netmasks[j]); - else - mask = 0; - } - - ipdata[i] &= mask; - i++; - } - - node = CPTRetriveNode(msr, ipdata, ip_bitmask, node); - - if (node && node->bit != ip_bitmask) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but netmask is different."); - } - return NULL; - } - - if (node && node->prefix == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but prefix is NULL."); - } - return NULL; - } - - if (memcmp(node->prefix->buffer, ipdata, bytes) == 0) { - mask = SHIFT_LEFT_MASK(8 - ip_bitmask % 8); - - if ((ip_bitmask % 8) == 0) { - if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address"); - } - return node; - } - } - - if ((node->prefix->buffer[bytes] & mask) == (ipdata[bytes] & mask)) { - if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address"); - } - return node; - } - } - } - - j++; - } - - return CPTFindElementIPNetblock(msr, ipdata, ip_bitmask, netmask_node->parent); -} - -TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree) { - TreeNode *node = NULL; - int mask = 0, bytes = 0; - unsigned char temp_data[NETMASK_256-1]; - - if (tree == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Tree is NULL. Cannot proceed searching the ip."); - } - return node; - } - - if (tree->head == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Tree head is NULL. Cannot proceed searching the ip."); - } - return node; - } - - node = tree->head; - - if (ip_bitmask > (NETMASK_256-1)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Netmask cannot be greater than 255"); - } - return NULL; - } - - bytes = ip_bitmask/8; - - memset(temp_data, 0, NETMASK_256-1); - memcpy(temp_data, ipdata, bytes); - - node = CPTRetriveNode(msr, temp_data, ip_bitmask, node); - - if (node && (node->bit != ip_bitmask)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Found a tree node but netmask is different."); - } - return NULL; - } - - if(node == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Node tree is NULL."); - } - return node; - } - - if(node->prefix == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Found a tree node but prefix is NULL."); - } - return node; - } - - if (memcmp(node->prefix->buffer, temp_data, bytes) == 0) { - mask = SHIFT_LEFT_MASK(8 - ip_bitmask % 8); - - if ((ip_bitmask % 8) == 0) { - if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Node found for provided ip address"); - } - return node; - } - } - - if ((node->prefix->buffer[bytes] & mask) == (temp_data[bytes] & mask)) { - if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTFindElement: Node found for provided ip address"); - } - return node; - } - } - } - - return CPTFindElementIPNetblock(msr, temp_data, ip_bitmask, node); -} - -TreeNode *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type) { - - if(tree == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTIpMatch: Tree is NULL. Cannot proceed searching the ip."); - } - return NULL; - } - - if(ipdata == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTIpMatch: Empty ip address. Nothing to search for."); - } - return NULL; - } - - switch(type) { - case IPV4_TREE: - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type); - } - return CPTFindElement(msr, ipdata, NETMASK_32, tree); - case IPV6_TREE: - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type); - } - return CPTFindElement(msr, ipdata, NETMASK_128, tree); - default: - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPTIpMatch: Unknown ip type 0x%x", type); - } - return NULL; - } -} - -TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { - unsigned long ip, ret; - unsigned char netmask_v4 = NETMASK_32, netmask_v6 = NETMASK_128; - char ip_strv4[NETMASK_32], ip_strv6[NETMASK_128]; - struct in_addr addr4; - struct in6_addr addr6; - char *ptr = NULL; - - if(tree == NULL) - return NULL; - - switch(type) { - - case IPV4_TREE: - memset(&addr4, 0, sizeof(addr4)); - memset(ip_strv4, 0x0, NETMASK_32); - - strncpy(ip_strv4, buffer, sizeof(ip_strv4) - 2); - *(ip_strv4 + (sizeof(ip_strv4) - 1)) = '\0'; - - ptr = strdup(ip_strv4); - netmask_v4 = is_netmask_v4(ptr); - - if(ptr != NULL) { - free(ptr); - ptr = NULL; - } - - if(netmask_v4 == 0) - return NULL; - else if(netmask_v4 != NETMASK_32) { - ip_strv4[strlen(ip_strv4)-3] = '\0'; - } - - ret = inet_pton(AF_INET, ip_strv4, &addr4); - if (ret <= 0) { - return NULL; - } - - ip = addr4.s_addr; - - tree->count++; - - return CPTAddElement((unsigned char *)&ip, NETMASK_32, tree, netmask_v4); - - case IPV6_TREE: - memset(&addr6, 0, sizeof(addr6)); - memset(ip_strv6, 0x0, NETMASK_128); - - strncpy(ip_strv6, buffer, sizeof(ip_strv6) - 2); - *(ip_strv6 + sizeof(ip_strv6) - 1) = '\0'; - - ptr = strdup(ip_strv6); - netmask_v6 = is_netmask_v6(ptr); - - if(ptr != NULL) { - free(ptr); - ptr = NULL; - } - - if(netmask_v6 == 0) - return NULL; - else if (netmask_v6 != NETMASK_64) { - ip_strv4[strlen(ip_strv6)-3] = '\0'; - } - - ret = inet_pton(AF_INET6, ip_strv6, &addr6); - if(ret <= 0) - return NULL; - - tree->count++; - - return CPTAddElement((unsigned char *)&addr6.s6_addr, NETMASK_128, tree, netmask_v6); - default: - return NULL; - } - - return NULL; -} diff --git a/apache2/msc_tree.h b/apache2/msc_tree.h deleted file mode 100644 index c82ffbc124..0000000000 --- a/apache2/msc_tree.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef __MSC_TREE_H__ -#define __MSC_TREE_H__ - -#include "modsecurity.h" - -typedef struct CPTData CPTData; -typedef struct TreePrefix TreePrefix; -typedef struct TreeNode TreeNode; -typedef struct CPTTree CPTTree; -typedef struct TreeRoot TreeRoot; - -#define IPV4_TREE 0x1 -#define IPV6_TREE 0x2 - -#define IPV4_LEN 0x20 -#define IPV6_LEN 0x80 - -#define TREE_CHECK(x, y) ((x) & (y)) -#define MASK_BITS(x) ((x + 1) * 8) -#define SHIFT_LEFT_MASK(x) ((-1) << (x)) -#define SHIFT_RIGHT_MASK(x,y) ((x) >> (y)) - -#define NETMASK_256 0x100 -#define NETMASK_128 0x80 -#define NETMASK_64 0x40 -#define NETMASK_32 0x20 -#define NETMASK_16 0x10 -#define NETMASK_8 0x8 -#define NETMASK_4 0x4 -#define NETMASK_2 0x2 - -struct CPTData { - unsigned char netmask; - struct CPTData *next; -}; - -struct TreePrefix { - unsigned char *buffer; - unsigned int bitlen; - CPTData *prefix_data; -}; - -struct TreeNode { - unsigned int bit; - int count; - unsigned char *netmasks; - TreePrefix *prefix; - struct TreeNode *left, *right; - struct TreeNode *parent; -}; - -struct CPTTree { - int count; - apr_pool_t *pool; - TreeNode *head; -}; - -struct TreeRoot { - CPTTree *ipv4_tree; - CPTTree *ipv6_tree; -}; - -CPTTree DSOLOCAL *CPTCreateRadixTree(apr_pool_t *pool); -TreeNode DSOLOCAL *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type); -TreeNode DSOLOCAL *TreeAddIP(const char *buffer, CPTTree *tree, int type); - -#endif /*__MSC_TREE_H__ */ diff --git a/apache2/msc_unicode.c b/apache2/msc_unicode.c deleted file mode 100644 index 85ab192f5f..0000000000 --- a/apache2/msc_unicode.c +++ /dev/null @@ -1,168 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_unicode.h" - -#define CODEPAGE_SEPARATORS " \t\n\r" - -/** \brief Load Unicode file - * - * \param dcfg Pointer to directory configuration - * \param error_msg Error message - * - * \retval 1 On Success - * \retval 0 On Fail - */ -static int unicode_map_create(directory_config *dcfg, char **error_msg) -{ - char errstr[1024]; - apr_pool_t *mp = dcfg->mp; - unicode_map *u_map = dcfg->u_map; - apr_int32_t wanted = APR_FINFO_SIZE; - apr_finfo_t finfo; - apr_status_t rc; - apr_size_t nbytes; - unsigned int codepage = 0; - char *buf = NULL, *p = NULL, *savedptr = NULL; - char *ucode = NULL, *hmap = NULL; - int found = 0, processing = 0; - int Code = 0, Map = 0; - - if(unicode_map_table != NULL) { - free(unicode_map_table); - unicode_map_table = NULL; - } - - if ((rc = apr_file_open(&u_map->map, u_map->mapfn, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) { - *error_msg = apr_psprintf(mp, "Could not open unicode map file \"%s\": %s", u_map->mapfn, apr_strerror(rc, errstr, 1024)); - return 0; - } - - if ((rc = apr_file_info_get(&finfo, wanted, u_map->map)) != APR_SUCCESS) { - *error_msg = apr_psprintf(mp, "Could not cannot get unicode map file information \"%s\": %s", u_map->mapfn, apr_strerror(rc, errstr, 1024)); - apr_file_close(u_map->map); - return 0; - } - - buf = (char *)malloc(finfo.size+1); - - if (buf == NULL) { - *error_msg = apr_psprintf(mp, "Could not alloc memory for unicode map"); - apr_file_close(u_map->map); - return 0; - } - - rc = apr_file_read_full(u_map->map, buf, finfo.size, &nbytes); - - if (unicode_map_table != NULL) { - memset(unicode_map_table, -1, (sizeof(int)*65536)); - } else { - unicode_map_table = (int *)malloc(sizeof(int) * 65536); - - if(unicode_map_table == NULL) { - *error_msg = apr_psprintf(mp, "Could not alloc memory for unicode map"); - free(buf); - buf = NULL; - apr_file_close(u_map->map); - return 0; - } - - memset(unicode_map_table, -1, (sizeof(int)*65536)); - } - - /* Setting some unicode values - http://tools.ietf.org/html/rfc3490#section-3.1 */ - - /* Set 0x3002 -> 0x2e */ - unicode_map_table[0x3002] = 0x2e; - /* Set 0xFF61 -> 0x2e */ - unicode_map_table[0xff61] = 0x2e; - /* Set 0xFF0E -> 0x2e */ - unicode_map_table[0xff0e] = 0x2e; - /* Set 0x002E -> 0x2e */ - unicode_map_table[0x002e] = 0x2e; - - p = apr_strtok(buf,CODEPAGE_SEPARATORS,&savedptr); - - while (p != NULL) { - - codepage = atol(p); - - if (codepage == unicode_codepage) { - found = 1; - } - - if (found == 1 && (strchr(p,':') != NULL)) { - char *mapping = strdup(p); - processing = 1; - - if(mapping != NULL) { - ucode = apr_strtok(mapping,":", &hmap); - sscanf(ucode,"%x",&Code); - sscanf(hmap,"%x",&Map); - if(Code >= 0 && Code <= 65535) { - unicode_map_table[Code] = Map; - } - - free(mapping); - mapping = NULL; - } - } - - if (processing == 1 && (strchr(p,':') == NULL)) { - free(buf); - buf = NULL; - break; - } - - p = apr_strtok(NULL,CODEPAGE_SEPARATORS,&savedptr); - } - - apr_file_close(u_map->map); - - if(buf) { - free(buf); - buf = NULL; - } - - return 1; -} - - -/** \brief Init unicode map - * - * \param dcfg Pointer to directory configuration - * \param mapfn Unicode map filename - * \param error_msg Error message - * - * \retval unicode_map_create On Success - * \retval -1 On Fail - */ -int unicode_map_init(directory_config *dcfg, const char *mapfn, char **error_msg) -{ - - *error_msg = NULL; - - if ((dcfg->u_map == NULL) || (dcfg->u_map == NOT_SET_P)) { - dcfg->u_map = apr_pcalloc(dcfg->mp, sizeof(unicode_map)); - if (dcfg->u_map == NULL) { - return -1; - } - } - - dcfg->u_map->map = NULL; - dcfg->u_map->mapfn = apr_pstrdup(dcfg->mp, mapfn); - - return unicode_map_create(dcfg, error_msg); -} - diff --git a/apache2/msc_unicode.h b/apache2/msc_unicode.h deleted file mode 100644 index 1eaf3d6625..0000000000 --- a/apache2/msc_unicode.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_UNICODE_H_ -#define _MSC_UNICODE_H_ - -typedef struct unicode_map unicode_map; - -#include -#include "modsecurity.h" -#include "apr_hash.h" - -struct unicode_map { - apr_file_t *map; - const char *mapfn; -}; - -int DSOLOCAL unicode_map_init(directory_config *dcfg, const char *mapfn, char **error_msg); - -#endif diff --git a/apache2/msc_util.c b/apache2/msc_util.c deleted file mode 100644 index 23ae03750a..0000000000 --- a/apache2/msc_util.c +++ /dev/null @@ -1,2388 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "modsecurity.h" -#include -#include -#include -#include -#include - -#include "msc_release.h" -#include "msc_util.h" - -#include -#include "modsecurity_config.h" - -/** - * NOTE: Be careful as these can ONLY be used on static values for X. - * (i.e. VALID_HEX(c++) will NOT work) - */ -#define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) -#define ISODIGIT(X) ((X >= '0')&&(X <= '7')) - -#if (defined(WIN32) || defined(NETWARE)) -/** Windows does not define all the octal modes */ -#define S_IXOTH 00001 -#define S_IWOTH 00002 -#define S_IROTH 00004 -#define S_IXGRP 00010 -#define S_IWGRP 00020 -#define S_IRGRP 00040 -#define S_IXUSR 00100 -#define S_IWUSR 00200 -#define S_IRUSR 00400 -#define S_ISVTX 01000 -#define S_ISGID 02000 -#define S_ISUID 04000 -#endif /* defined(WIN32 || NETWARE) */ - -/* Base64 tables used in decodeBase64Ext */ -static const char b64_pad = '='; - -static const short b64_reverse_t[256] = { - -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2, - -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2, - -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 -}; - -static unsigned char *c2x(unsigned what, unsigned char *where); -static unsigned char x2c(unsigned char *what); -static unsigned char xsingle2c(unsigned char *what); - -#ifdef LINUX_S390 -int swap_int32(int x) { - int swap = ((x>>24)&0xff) | ((x<<8)&0xff0000) | - ((x>>8)&0xff00) | ((x<<24)&0xff000000); - return swap; -} -#endif - - -/** \brief Decode utf-8 to unicode format. - * - * \param mp Pointer to memory pool - * \param input Pointer to input data - * \param input_len Input data length - * \param changed Set if data is changed - * - * \retval rval On Success - */ -char *utf8_unicode_inplace_ex(apr_pool_t *mp, unsigned char *input, long int input_len, int *changed) { - int unicode_len = 0, length = 0; - unsigned int d = 0, count = 0; - unsigned char c, *utf; - char *rval, *data; - unsigned int i, len, j; - unsigned int bytes_left = input_len; - unsigned char *unicode = NULL; - - *changed = 0; - - len = input_len * 7 + 1; - data = rval = apr_palloc(mp, len); - if (rval == NULL) return NULL; - - - if (input == NULL) return NULL; - - for(i = 0; i < bytes_left;) { - unicode_len = 0; d = 0; - utf = (unsigned char *)&input[i]; - - c = *utf; - - /* If first byte begins with binary 0 it is single byte encoding */ - if ((c & 0x80) == 0) { - /* single byte unicode (7 bit ASCII equivilent) has no validation */ - count++; - if(count <= len) { - if(c == 0) - *data = x2c(&c); - else - *data++ = c; - } - - } - /* If first byte begins with binary 110 it is two byte encoding*/ - else if ((c & 0xE0) == 0xC0) { - /* check we have at least two bytes */ - if (bytes_left < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; - /* check second byte starts with binary 10 */ - else if (((*(utf + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - else { - unicode_len = 2; - count+=6; - if(count <= len) { - /* compute character number */ - d = ((c & 0x1F) << 6) | (*(utf + 1) & 0x3F); - *data++ = '%'; - *data++ = 'u'; - unicode = apr_psprintf(mp, "%x", d); - length = strlen(unicode); - - switch(length) { - case 1: - *data++ = '0'; - *data++ = '0'; - *data++ = '0'; - break; - case 2: - *data++ = '0'; - *data++ = '0'; - break; - case 3: - *data++ = '0'; - break; - case 4: - case 5: - break; - } - - for(j=0; j= 0xF5) { - *data++ = c; - } - /* check we have at least four bytes */ - if (bytes_left < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; - /* check second byte starts with binary 10 */ - else if (((*(utf + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - /* check third byte starts with binary 10 */ - else if (((*(utf + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - /* check forth byte starts with binary 10 */ - else if (((*(utf + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - else { - unicode_len = 4; - count+=7; - if(count <= len) { - /* compute character number */ - d = ((c & 0x07) << 18) | ((*(utf + 1) & 0x3F) << 12) | ((*(utf + 2) & 0x3F) < 6) | (*(utf + 3) & 0x3F); - *data++ = '%'; - *data++ = 'u'; - unicode = apr_psprintf(mp, "%x", d); - length = strlen(unicode); - - switch(length) { - case 1: - *data++ = '0'; - *data++ = '0'; - *data++ = '0'; - break; - case 2: - *data++ = '0'; - *data++ = '0'; - break; - case 3: - *data++ = '0'; - break; - case 4: - case 5: - break; - } - - for(j=0; j= 0xD800) && (d <= 0xDFFF)) { - count++; - if(count <= len) - *data++ = c; - } - - /* check for overlong */ - if ((unicode_len == 4) && (d < 0x010000)) { - /* four byte could be represented with less bytes */ - count++; - if(count <= len) - *data++ = c; - } - else if ((unicode_len == 3) && (d < 0x0800)) { - /* three byte could be represented with less bytes */ - count++; - if(count <= len) - *data++ = c; - } - else if ((unicode_len == 2) && (d < 0x80)) { - /* two byte could be represented with less bytes */ - count++; - if(count <= len) - *data++ = c; - } - - if(unicode_len > 0) { - i += unicode_len; - } else { - i++; - } - } - - *data ='\0'; - - return rval; -} - -/** \brief Validate IPv4 Netmask - * - * \param ip_strv6 Pointer to ipv6 address - * - * \retval netmask_v4 On Success - */ -unsigned char is_netmask_v4(char *ip_strv4) { - unsigned char netmask_v4 = 32; - char *mask_str = NULL; - int cidr; - - if(ip_strv4 == NULL) - return netmask_v4; - - if ((mask_str = strchr(ip_strv4, '/'))) { - *(mask_str++) = '\0'; - - if (strchr(mask_str, '.') != NULL) { - return 0; - } - - cidr = atoi(mask_str); - if ((cidr < 0) || (cidr > 32)) { - return 0; - } - - netmask_v4 = (unsigned char)cidr; - } - - return netmask_v4; -} - -/** \brief Validate IPv6 Netmask - * - * \param ip_strv6 Pointer to ipv6 address - * - * \retval netmask_v6 On Success - */ -unsigned char is_netmask_v6(char *ip_strv6) { - unsigned char netmask_v6 = 128; - char *mask_str = NULL; - int cidr; - - if(ip_strv6 == NULL) - return netmask_v6; - - if ((mask_str = strchr(ip_strv6, '/'))) { - *(mask_str++) = '\0'; - - if (strchr(mask_str, '.') != NULL) { - return 0; - } - - cidr = atoi(mask_str); - if ((cidr < 0) || (cidr > 64)) { - return 0; - } - netmask_v6 = (unsigned char)cidr; - } - - return netmask_v6; -} - -/** \brief Interpret |HEX| syntax - * - * \param op_parm Pointer to operator input - * \param op_len Operator input lenght - * \param rule Pointer to rule struct - * \param error_msg Pointer to error message - * - * \retval string On Success - */ -char *parse_pm_content(const char *op_parm, unsigned short int op_len, msre_rule *rule, char **error_msg) { - char *parm = NULL; - char *content = NULL; - unsigned short int offset = 0; - char converted = 0; - int i, x; - unsigned char bin = 0, esc = 0, bin_offset = 0; - unsigned char bin_parm[3], c = 0; - char *processed = NULL; - - content = apr_pstrdup(rule->ruleset->mp, op_parm); - - if (content == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content."); - return NULL; - } - - while (offset < op_len && apr_isspace(content[offset])) { - offset++; - }; - - op_len = strlen(content); - - if (content[offset] == '\"' && content[op_len-1] == '\"') { - parm = apr_pstrdup(rule->ruleset->mp, content + offset + 1); - if (parm == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content."); - return NULL; - } - parm[op_len - offset - 2] = '\0'; - } else { - parm = apr_pstrdup(rule->ruleset->mp, content + offset); - if (parm == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content."); - return NULL; - } - } - - op_len = strlen(parm); - - if (op_len == 0) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Content length is 0."); - return NULL; - } - - - for (i = 0, x = 0; i < op_len; i++) { - if (parm[i] == '|') { - if (bin) { - bin = 0; - } else { - bin = 1; - } - } else if(!esc && parm[i] == '\\') { - esc = 1; - } else { - if (bin) { - if (apr_isdigit(parm[i]) || - parm[i] == 'A' || parm[i] == 'a' || - parm[i] == 'B' || parm[i] == 'b' || - parm[i] == 'C' || parm[i] == 'c' || - parm[i] == 'D' || parm[i] == 'd' || - parm[i] == 'E' || parm[i] == 'e' || - parm[i] == 'F' || parm[i] == 'f') - { - bin_parm[bin_offset] = (char)parm[i]; - bin_offset++; - if (bin_offset == 2) { - c = strtol((char *)bin_parm, (char **) NULL, 16) & 0xFF; - bin_offset = 0; - parm[x] = c; - x++; - converted = 1; - } - } else if (parm[i] == ' ') { - } - } else if (esc) { - if (parm[i] == ':' || - parm[i] == ';' || - parm[i] == '\\' || - parm[i] == '\"') - { - parm[x] = parm[i]; - x++; - } else { - *error_msg = apr_psprintf(rule->ruleset->mp, "Unsupported escape sequence."); - return NULL; - } - esc = 0; - converted = 1; - } else { - parm[x] = parm[i]; - x++; - } - } - } - - if (converted) { - op_len = x; - } - - processed = apr_pstrmemdup(rule->ruleset->mp, parm, op_len); - - if (processed == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content."); - return NULL; - } - - return processed; -} - - -/** \brief Remove quotes - * - * \param mptmp Pointer to the pool - * \param input Pointer to input string - * \param input_len Input data length - * - * \retval string On Success - */ -char *remove_quotes(apr_pool_t *mptmp, const char *input, int input_len) { - char *parm = apr_palloc(mptmp, input_len); - char *ret = parm; - int len = input_len; - - for(; *input !='\0' && len >=0; input++, len--) { - if(*input != '\'' && *input != '\"') { - *parm++ = *input; - } - } - - *parm = '\0'; - return ret; -} - -/** \brief Remove escape char - * - * \param mptmp Pointer to the pool - * \param input Pointer to input string - * \param input_len Input data length - * - * \retval string On Success - */ -char *remove_escape(apr_pool_t *mptmp, const char *input, int input_len) { - char *parm = apr_palloc(mptmp, input_len); - char *ret = parm; - int len = input_len; - - for(; *input !='\0' && len >=0; input++, len--) { - if(*input != '\\') { - *parm++ = *input; - } - } - - *parm = '\0'; - return ret; -} - -/** - * - */ -int parse_boolean(const char *input) { - if (input == NULL) return -1; - if (strcasecmp(input, "on") == 0) return 1; - if (strcasecmp(input, "true") == 0) return 1; - if (strcasecmp(input, "1") == 0) return 1; - if (strcasecmp(input, "off") == 0) return 0; - if (strcasecmp(input, "false") == 0) return 0; - if (strcasecmp(input, "0") == 0) return 0; - - return -1; -} - -/** \brief Decode Base64 data with special chars - * - * \param plain_text Pointer to plain text data - * \param input Pointer to input data - * \param input_len Input data length - * - * \retval 0 On failure - * \retval string length On Success - */ -int decode_base64_ext(char *plain_text, const unsigned char *input, int input_len) { - const unsigned char *encoded = input; - int i = 0, j = 0, k = 0; - int ch = 0; - - while ((ch = *encoded++) != '\0' && input_len-- > 0) { - if (ch == b64_pad) { - if (*encoded != '=' && (i % 4) == 1) { - return 0; - } - continue; - } - - ch = b64_reverse_t[ch]; - if (ch < 0 || ch == -1) { - continue; - } else if (ch == -2) { - return 0; - } - switch(i % 4) { - case 0: - plain_text[j] = ch << 2; - break; - case 1: - plain_text[j++] |= ch >> 4; - plain_text[j] = (ch & 0x0f) << 4; - break; - case 2: - plain_text[j++] |= ch >>2; - plain_text[j] = (ch & 0x03) << 6; - break; - case 3: - plain_text[j++] |= ch; - break; - } - i++; - } - - k = j; - if (ch == b64_pad) { - switch(i % 4) { - case 1: - return 0; - case 2: - k++; - case 3: - plain_text[k] = 0; - } - } - - plain_text[j] = '\0'; - - return j; -} - -/** \brief Convert const char to int - * - * \param c number string - * - * \retval n The converted number - */ -int convert_to_int(const char c) -{ - int n; - if ((c>='0') && (c<='9')) - n = c - '0'; - else if ((c>='A') && (c<='F')) - n = c - 'A' + 10; - else if ((c>='a') && (c<='f')) - n = c - 'a' + 10; - else - n = 0; - return n; -} - -/** \brief Set a match to tx.N - * - * \param msr Pointer to modsec resource - * \param capture If ON match will be saved - * \param match Pointer to captured string - *\parm tx_n The tx number to save the data - * - * \retval 0 On Sucess|Fail - */ -int set_match_to_tx(modsec_rec *msr, int capture, const char *match, int tx_n) { - - if (capture) { - msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - - if (s == NULL) return -1; - - s->name = apr_psprintf(msr->mp,"%d", tx_n); - 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); - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added phrase match to TX.%d: %s", - tx_n, log_escape_nq_ex(msr->mp, s->value, s->value_len)); - } - - } - - return 0; -} - - -/** - * Parses a string that contains a name-value pair in the form "name=value". - * IMP1 It does not check for whitespace between tokens. - */ -int parse_name_eq_value(apr_pool_t *mp, const char *input, char **name, char **value) { - char *p = NULL; - - if ((name == NULL)||(value == NULL)) return -1; - if (input == NULL) return 0; - - *name = NULL; - *value = NULL; - p = (char *)input; - - while((*p != '=')&&(*p != '\0')) p++; - if (*p == '\0') { - *name = (char *)input; - return 1; - } - - *name = apr_pstrmemdup(mp, input, p - input); - if (*name == NULL) return -1; - p++; - - *value = apr_pstrdup(mp, p); - if (*value == NULL) return -1; - - return 1; -} - -/** - * - * IMP1 Assumes NUL-terminated - */ -char *url_encode(apr_pool_t *mp, char *input, unsigned int input_len, int *changed) { - char *rval, *d; - unsigned int i, len; - - *changed = 0; - - len = input_len * 3 + 1; - d = rval = apr_palloc(mp, len); - if (rval == NULL) return NULL; - - /* ENH Only encode the characters that really need to be encoded. */ - - for(i = 0; i < input_len; i++) { - unsigned char c = input[i]; - - if (c == ' ') { - *d++ = '+'; - *changed = 1; - } else - if ( (c == 42) || ((c >= 48)&&(c <= 57)) || ((c >= 65)&&(c <= 90)) - || ((c >= 97)&&(c <= 122)) - ) { - *d++ = c; - } else { - *d++ = '%'; - c2x(c, (unsigned char *)d); - d += 2; - *changed = 1; - } - } - - *d = '\0'; - - return rval; -} - -/** - * Appends an URL-encoded version of the source string to the - * destination string, but makes sure that no more than "maxlen" - * bytes are added. - */ -char *strnurlencat(char *destination, char *source, unsigned int maxlen) { - char *s = source; - char *d = destination; - - /* ENH Only encode the characters that really need to be encoded. */ - - /* Advance to the end of destination string. */ - while(*d != '\0') d++; - - /* Loop while there's bytes in the source string or - * until we reach the output limit. - */ - while((*s != '\0')&&(maxlen > 0)) { - unsigned char c = *s; - - if (c == ' ') { - *d++ = '+'; - maxlen--; - } else - if ( (c == 42) || ((c >= 48)&&(c <= 57)) || ((c >= 65)&&(c <= 90)) - || ((c >= 97)&&(c <= 122)) - ) { - *d++ = c; - maxlen--; - } else { - if (maxlen >= 3) { - *d++ = '%'; - c2x(c, (unsigned char *)d); - d += 2; - maxlen -= 3; - } else { - /* If there's not enough room for the encoded - * byte we ignore it. - */ - maxlen = 0; - } - } - - s++; - } - - *d++ = '\0'; - - return destination; -} - -/** - * - */ -char *file_basename(apr_pool_t *mp, const char *filename) { - char *d, *p; - - if (filename == NULL) return NULL; - d = apr_pstrdup(mp, filename); - if (d == NULL) return NULL; - - p = strrchr(d, '/'); - if (p != NULL) d = p + 1; - p = strrchr(d, '\\'); - if (p != NULL) d = p + 1; - - return d; -} - -char *m_strcasestr(const char *haystack, const char *needle) { - char aux, lower_aux; - int length; - - if ((aux = *needle++) != 0) { - aux = (char)tolower((unsigned char)aux); - length = strlen(needle); - do { - do { - if ((lower_aux = *haystack++) == 0) - return NULL; - } while ((char)tolower((unsigned char)lower_aux) != aux); - } while (strncasecmp(haystack, needle, length) != 0); - haystack--; - } - return ((char *)haystack); -} - -#ifdef WIN32 -int inet_pton(int family, const char *src, void *dst) { - struct addrinfo addr; - struct sockaddr_in *in = NULL; -#if APR_HAVE_IPV6 - struct sockaddr_in6 *in6 = NULL; -#endif - struct addrinfo *addr_info = NULL; - - memset(&addr, 0, sizeof(struct addrinfo)); - addr.ai_family = family; - - if (getaddrinfo(src, NULL, &addr, &addr_info) != 0) - return -1; - - if (addr_info) { - if (addr_info->ai_family == AF_INET) { - in = (struct sockaddr_in*)addr_info->ai_addr; - if(in != NULL) - memcpy(dst, &in->sin_addr, 4); - } -#if APR_HAVE_IPV6 - else if (addr_info->ai_family == AF_INET6) { - in6 = (struct sockaddr_in6*)addr_info->ai_addr; - if(in6 != NULL) - memcpy(dst, &in6->sin6_addr, 16); - } -#endif - else { - freeaddrinfo(addr_info); - return -1; - } - - freeaddrinfo(addr_info); - return 1; - } - - return -1; -} -#endif - -/** - * - */ -#ifdef WIN32 -char *file_dirname(apr_pool_t *p, const char *filename) { - char *b, *c, *d; - - if (filename == NULL) return NULL; - b = apr_pstrdup(p, filename); - if (b == NULL) return NULL; - - c = strrchr(b, '/'); - if (c != NULL) { - d = strrchr(c, '\\'); - if (d != NULL) *d = '\0'; - else *c = '\0'; - } else { - d = strrchr(b, '\\'); - if (d != NULL) *d = '\0'; - } - - return b; -} -#else -char *file_dirname(apr_pool_t *p, const char *filename) { - char *b, *c; - - if (filename == NULL) return NULL; - b = apr_pstrdup(p, filename); - if (b == NULL) return NULL; - - c = strrchr(b, '/'); - if (c != NULL) *c = '\0'; - - return b; -} -#endif - - -/** - * - */ -int sql_hex2bytes_inplace(unsigned char *data, int len) { - unsigned char *d, *begin = data; - - if ((data == NULL)||(len == 0)) return 0; - - for( d = data; *data; *d++ = *data++) { - if ( *data != '0' ) continue; - if ( tolower(*++data) != 'x' ) { - data--; - continue; - } - - data++; - - // Do we need to keep "0x" if no hexa after? - if ( !VALID_HEX(data[0]) || !VALID_HEX(data[1]) ) { - data-=2; - continue; - } - - while ( VALID_HEX(data[0]) && VALID_HEX(data[1]) ) { - *d++ = x2c(data); - data += 2; - } - } - - *d = '\0'; - return strlen((char *)begin); -} - -/** - * - * - */ -int hex2bytes_inplace(unsigned char *data, int len) { - unsigned char *d = data; - int i, count = 0; - - if ((data == NULL)||(len == 0)) return 0; - - for(i = 0; i <= len - 2; i += 2) { - *d++ = x2c(&data[i]); - count++; - } - *d = '\0'; - - return count; -} - -/** - * Converts a series of bytes into its hexadecimal - * representation. - */ -char *bytes2hex(apr_pool_t *pool, unsigned char *data, int len) { - static const unsigned char b2hex[] = "0123456789abcdef"; - char *hex = NULL; - int i, j; - - hex = apr_palloc(pool, (len * 2) + 1); - if (hex == NULL) return NULL; - - j = 0; - for(i = 0; i < len; i++) { - hex[j++] = b2hex[data[i] >> 4]; - hex[j++] = b2hex[data[i] & 0x0f]; - } - hex[j] = 0; - - return hex; -} - -/** - * - */ -int is_token_char(unsigned char c) { - /* ENH Is the performance important at all? We could use a table instead. */ - - /* CTLs not allowed */ - if ((c <= 32)||(c >= 127)) return 0; - - switch(c) { - case '(' : - case ')' : - case '<' : - case '>' : - case '@' : - case ',' : - case ';' : - case ':' : - case '\\' : - case '"' : - case '/' : - case '[' : - case ']' : - case '?' : - case '=' : - return 0; - } - - return 1; -} - -/** - * - */ -int remove_lf_crlf_inplace(char *text) { - char *p = text; - int count = 0; - - if (text == NULL) return -1; - - while(*p != '\0') { - count++; - p++; - } - - if (count > 0) { - if (*(p - 1) == '\n') { - *(p - 1) = '\0'; - if (count > 1) { - if (*(p - 2) == '\r') { - *(p - 2) = '\0'; - } - } - } - } - - return 1; -} - -/** - * Converts a byte given as its hexadecimal representation - * into a proper byte. Handles uppercase and lowercase letters - * but does not check for overflows. - */ -static unsigned char x2c(unsigned char *what) { - register unsigned char digit; - - digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); - digit *= 16; - digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); - - return digit; -} - -/** - * Converts a single hexadecimal digit into a decimal value. - */ -static unsigned char xsingle2c(unsigned char *what) { - register unsigned char digit; - - digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); - - return digit; -} - -/** - * - */ -char *guess_tmp_dir(apr_pool_t *p) { - char *filename = NULL; - - /* ENH Use apr_temp_dir_get instead. */ - -#ifdef WIN32 - filename = apr_pcalloc(p, 256); - if (filename == NULL) return ""; - if (GetTempPath(255, filename) != 0) return filename; -#endif - - filename = getenv("TMPDIR"); - if (filename != NULL) return filename; - - filename = getenv("TEMP"); - if (filename != NULL) return filename; - - filename = getenv("TMP"); - if (filename != NULL) return filename; - -#if defined NETWARE - return("sys:/tmp/"); -#elif defined WIN32 - return(""); -#else - return("/tmp/"); -#endif -} - -/** - * - */ -char *current_logtime(apr_pool_t *mp) { - apr_time_exp_t t; - char tstr[100]; - apr_size_t len; - - apr_time_exp_lt(&t, apr_time_now()); - - apr_strftime(tstr, &len, 80, "%d/%b/%Y:%H:%M:%S ", &t); - apr_snprintf(tstr + strlen(tstr), 80 - strlen(tstr), "%c%.2d%.2d", - t.tm_gmtoff < 0 ? '-' : '+', - t.tm_gmtoff / (60 * 60), (t.tm_gmtoff / 60) % 60); - return apr_pstrdup(mp, tstr); -} - -/** - * - */ -char *current_filetime(apr_pool_t *mp) { - apr_time_exp_t t; - char tstr[100]; - apr_size_t len; - - apr_time_exp_lt(&t, apr_time_now()); - - apr_strftime(tstr, &len, 80, "%Y%m%d-%H%M%S", &t); - return apr_pstrdup(mp, tstr); -} - -/** - * - */ -int msc_mkstemp_ex(char *templat, int mode) { - int fd = -1; - - /* ENH Use apr_file_mktemp instead. */ - -#if !(defined(WIN32)||defined(NETWARE)) - fd = mkstemp(templat); -#ifdef HAVE_FCHMOD - if ((fd != -1) && (mode != 0)) { - if (fchmod(fd, mode) == -1) { - return -1; - } - } -#endif /* HAVE_FCHMOD */ -#else - if (mktemp(templat) == NULL) return -1; - fd = open(templat, O_WRONLY | O_APPEND | O_CREAT | O_BINARY, mode); -#endif /* !(defined(WIN32)||defined(NETWARE)) */ - - return fd; -} - -/** - * - */ -int msc_mkstemp(char *templat) { - return msc_mkstemp_ex(templat, CREATEMODE_UNISTD); -} - -/** - * Converts the input string to lowercase (in-place). - */ -char *strtolower_inplace(unsigned char *str) { - unsigned char *c = str; - - if (str == NULL) return NULL; - - while(*c != 0) { - *c = tolower(*c); - c++; - } - - return (char *)str; -} - -/** - * Converts a single byte into its hexadecimal representation. - * Will overwrite two bytes at the destination. - */ -static unsigned char *c2x(unsigned what, unsigned char *where) { - static const char c2x_table[] = "0123456789abcdef"; - - what = what & 0xff; - *where++ = c2x_table[what >> 4]; - *where++ = c2x_table[what & 0x0f]; - - return where; -} - -static char *_log_escape(apr_pool_t *p, const unsigned char *input, - unsigned long int input_length, int escape_quotes, int escape_colon, int escape_re); - -char *log_escape_re(apr_pool_t *mp, const char *text) { - return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 1, 1, 1); -} - -char *log_escape(apr_pool_t *mp, const char *text) { - return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 1, 0, 0); -} - -char *log_escape_nq(apr_pool_t *mp, const char *text) { -#ifdef VERSION_IIS - int l = 0; - - // this is a workaround for unknown bug that causes 'text' sometimes to lack zero-termination - // - __try - { - l = text ? strlen(text) : 0; - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - l = -1; - } - if(l < 0) - return _log_escape(mp, "BUG: see log_escape_nq()", 24, 0, 0, 0); - - return _log_escape(mp, (const unsigned char *)text, l, 0, 0, 0); -#else - return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 0, 0, 0); -#endif -} - -char *log_escape_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) { - return _log_escape(mp, (const unsigned char *)text, text_length, 1, 0, 0); -} - -char *log_escape_nq_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) { - return _log_escape(mp, (const unsigned char *)text, text_length, 0, 0, 0); -} - -char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { - unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); - unsigned long int i, j; - - for (i = 0, j = 0; i < text_length; i++, j += 4) { - ret[j] = '\\'; - ret[j+1] = 'x'; - c2x(text[i], ret+j+2); - } - ret[text_length * 4] = '\0'; - - return (char *)ret; -} - -char *log_escape_nul(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { - unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); - unsigned long int i, j; - - for (i = 0, j = 0; i < text_length; i++) { - if (text[i] == '\0') { - ret[j] = '\\'; - ret[j+1] = 'x'; - c2x(text[i], ret+j+2); - j += 4; - } - else { - ret[j] = text[i]; - j++; - } - } - ret[j] = '\0'; - - return (char *)ret; -} - -/** - * Transform text to ASCII printable or hex escaped - */ -char *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { - unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); - unsigned long int i, j; - - for (i = 0, j = 0; i < text_length; i++) { - if ( (text[i] == '"') - ||(text[i] == '\\') - ||(text[i] <= 0x1f) - ||(text[i] >= 0x7f)) - { - ret[j] = '\\'; - ret[j+1] = 'x'; - c2x(text[i], ret+j+2); - j += 4; - } - else { - ret[j] = text[i]; - j ++; - } - } - ret[j] = '\0'; - - return (char *)ret; -} - -/** - * Transform input into a form safe for logging. - */ -static char *_log_escape(apr_pool_t *mp, const unsigned char *input, unsigned long int input_len, - int escape_quotes, int escape_colon, int escape_re) -{ - unsigned char *d = NULL; - char *ret = NULL; - unsigned long int i; - - if (input == NULL) return NULL; - - ret = apr_palloc(mp, input_len * 4 + 1); - if (ret == NULL) return NULL; - d = (unsigned char *)ret; - - i = 0; - while(i < input_len) { - switch(input[i]) { - case ':' : - if (escape_colon) { - *d++ = '\\'; - *d++ = ':'; - } else { - *d++ = input[i]; - } - break; - case '"' : - if (escape_quotes) { - *d++ = '\\'; - *d++ = '"'; - } else { - *d++ = input[i]; - } - break; - case '+' : - if (escape_re) { - *d++ = '\\'; - *d++ = '+'; - } else { - *d++ = input[i]; - } - break; - case '.' : - if (escape_re) { - *d++ = '\\'; - *d++ = '.'; - } else { - *d++ = input[i]; - } - break; - case ']' : - if (escape_re) { - *d++ = '\\'; - *d++ = ']'; - } else { - *d++ = input[i]; - } - break; - case '[' : - if (escape_re) { - *d++ = '\\'; - *d++ = '['; - } else { - *d++ = input[i]; - } - break; - case '(' : - if (escape_re) { - *d++ = '\\'; - *d++ = '('; - } else { - *d++ = input[i]; - } - break; - case ')' : - if (escape_re) { - *d++ = '\\'; - *d++ = ')'; - } else { - *d++ = input[i]; - } - break; - case '?' : - if (escape_re) { - *d++ = '\\'; - *d++ = '?'; - } else { - *d++ = input[i]; - } - break; - case '/' : - if (escape_re) { - *d++ = '\\'; - *d++ = '/'; - } else { - *d++ = input[i]; - } - break; - case '\b' : - *d++ = '\\'; - *d++ = 'b'; - break; - case '\n' : - *d++ = '\\'; - *d++ = 'n'; - break; - case '\r' : - *d++ = '\\'; - *d++ = 'r'; - break; - case '\t' : - *d++ = '\\'; - *d++ = 't'; - break; - case '\v' : - *d++ = '\\'; - *d++ = 'v'; - break; - case '\\' : - *d++ = '\\'; - *d++ = '\\'; - break; - default : - if ((input[i] <= 0x1f)||(input[i] >= 0x7f)) { - *d++ = '\\'; - *d++ = 'x'; - c2x(input[i], d); - d += 2; - } else { - *d++ = input[i]; - } - break; - } - - i++; - } - - *d = 0; - - return ret; -} - -/** - * JavaScript decoding. - * IMP1 Assumes NUL-terminated - */ - -int js_decode_nonstrict_inplace(unsigned char *input, long int input_len) { - unsigned char *d = (unsigned char *)input; - long int i, count; - - if (input == NULL) return -1; - - i = count = 0; - while (i < input_len) { - if (input[i] == '\\') { - /* Character is an escape. */ - - if ( (i + 5 < input_len) && (input[i + 1] == 'u') - && (VALID_HEX(input[i + 2])) && (VALID_HEX(input[i + 3])) - && (VALID_HEX(input[i + 4])) && (VALID_HEX(input[i + 5])) ) - { - /* \uHHHH */ - - /* Use only the lower byte. */ - *d = x2c(&input[i + 4]); - - /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ - if ( (*d > 0x00) && (*d < 0x5f) - && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) - && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) - { - (*d) += 0x20; - } - - d++; - count++; - i += 6; - } - else if ( (i + 3 < input_len) && (input[i + 1] == 'x') - && VALID_HEX(input[i + 2]) && VALID_HEX(input[i + 3])) { - /* \xHH */ - *d++ = x2c(&input[i + 2]); - count++; - i += 4; - } - else if ((i + 1 < input_len) && ISODIGIT(input[i + 1])) { - /* \OOO (only one byte, \000 - \377) */ - char buf[4]; - int j = 0; - - while((i + 1 + j < input_len)&&(j < 3)) { - buf[j] = input[i + 1 + j]; - j++; - if (!ISODIGIT(input[i + 1 + j])) break; - } - buf[j] = '\0'; - - if (j > 0) { - /* Do not use 3 characters if we will be > 1 byte */ - if ((j == 3) && (buf[0] > '3')) { - j = 2; - buf[j] = '\0'; - } - *d++ = (unsigned char)strtol(buf, NULL, 8); - i += 1 + j; - count++; - } - } - else if (i + 1 < input_len) { - /* \C */ - unsigned char c = input[i + 1]; - switch(input[i + 1]) { - case 'a' : - c = '\a'; - break; - case 'b' : - c = '\b'; - break; - case 'f' : - c = '\f'; - break; - case 'n' : - c = '\n'; - break; - case 'r' : - c = '\r'; - break; - case 't' : - c = '\t'; - break; - case 'v' : - c = '\v'; - break; - /* The remaining (\?,\\,\',\") are just a removal - * of the escape char which is default. - */ - } - - *d++ = c; - i += 2; - count++; - } - else { - /* Not enough bytes */ - while(i < input_len) { - *d++ = input[i++]; - count++; - } - } - } - else { - *d++ = input[i++]; - count++; - } - } - - *d = '\0'; - - return count; -} - -/** - * - * IMP1 Assumes NUL-terminated - */ -int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *changed) { - unsigned char *d = input; - long int i, count, fact, j, xv; - int Code, hmap = -1; - - *changed = 0; - - if (input == NULL) return -1; - - i = count = 0; - while (i < input_len) { - if (input[i] == '%') { - /* Character is a percent sign. */ - - if ((i + 1 < input_len)&&( (input[i + 1] == 'u')||(input[i + 1] == 'U') )) { - /* IIS-specific %u encoding. */ - if (i + 5 < input_len) { - /* We have at least 4 data bytes. */ - if ( (VALID_HEX(input[i + 2]))&&(VALID_HEX(input[i + 3])) - &&(VALID_HEX(input[i + 4]))&&(VALID_HEX(input[i + 5])) ) - { - - Code = 0; - fact = 1; - - if (unicode_map_table != NULL && unicode_codepage > 0) { - - for(j=5; j>=2; j--) { - if (isxdigit((input[i+j]))) { - if ((input[i+j])>=97) { - xv = ( (input[i+j]) - 97) + 10; - } else if ( (input[i+j]) >= 65) { - xv = ((input[i+j]) - 65) + 10; - } else { - xv = (input[i+j]) - 48; - } - Code += (xv * fact); - fact *= 16; - } - } - - if(Code >= 0 && Code <= 65535) { - hmap = unicode_map_table[Code]; - } - } - - if(hmap != -1) { - *d = hmap; - } else { - /* We first make use of the lower byte here, ignoring the higher byte. */ - *d = x2c(&input[i + 4]); - - /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ - if ( (*d > 0x00) && (*d < 0x5f) - && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) - && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) - { - (*d) += 0x20; - } - } - d++; - count++; - i += 6; - *changed = 1; - } else { - /* Invalid data, skip %u. */ - *d++ = input[i++]; - *d++ = input[i++]; - count += 2; - } - } else { - /* Not enough bytes (4 data bytes), skip %u. */ - *d++ = input[i++]; - *d++ = input[i++]; - count += 2; - } - } - else { - /* Standard URL encoding. */ - - /* Are there enough bytes available? */ - if (i + 2 < input_len) { - /* Yes. */ - - /* Decode a %xx combo only if it is valid. - */ - char c1 = input[i + 1]; - char c2 = input[i + 2]; - - if (VALID_HEX(c1) && VALID_HEX(c2)) { - *d++ = x2c(&input[i + 1]); - count++; - i += 3; - *changed = 1; - } else { - /* Not a valid encoding, skip this % */ - *d++ = input[i++]; - count++; - } - } else { - /* Not enough bytes available, skip this % */ - *d++ = input[i++]; - count++; - } - } - } - else { - /* Character is not a percent sign. */ - if (input[i] == '+') { - *d++ = ' '; - *changed = 1; - } else { - *d++ = input[i]; - } - - count++; - i++; - } - } - - *d = '\0'; - - return count; -} - -/** - * - * IMP1 Assumes NUL-terminated - */ -int urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *invalid_count, int *changed) { - unsigned char *d = (unsigned char *)input; - long int i, count; - - *changed = 0; - - if (input == NULL) return -1; - - i = count = 0; - while (i < input_len) { - if (input[i] == '%') { - /* Character is a percent sign. */ - - /* Are there enough bytes available? */ - if (i + 2 < input_len) { - char c1 = input[i + 1]; - char c2 = input[i + 2]; - - if (VALID_HEX(c1) && VALID_HEX(c2)) { - /* Valid encoding - decode it. */ - *d++ = x2c(&input[i + 1]); - count++; - i += 3; - *changed = 1; - } else { - /* Not a valid encoding, skip this % */ - *d++ = input[i++]; - count ++; - (*invalid_count)++; - } - } else { - /* Not enough bytes available, copy the raw bytes. */ - *d++ = input[i++]; - count ++; - (*invalid_count)++; - } - } else { - /* Character is not a percent sign. */ - if (input[i] == '+') { - *d++ = ' '; - *changed = 1; - } else { - *d++ = input[i]; - } - count++; - i++; - } - } - - *d = '\0'; - - return count; -} - -/** - * - * IMP1 Assumes NUL-terminated - */ -int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input_len) { - unsigned char *d = input; - int i, count; - - if ((input == NULL)||(input_len <= 0)) return 0; - - i = count = 0; - while((i < input_len)&&(count < input_len)) { - int z, copy = 1; - - /* Require an ampersand and at least one character to - * start looking into the entity. - */ - if ((input[i] == '&')&&(i + 1 < input_len)) { - int k, j = i + 1; - - if (input[j] == '#') { - /* Numerical entity. */ - copy++; - - if (!(j + 1 < input_len)) goto HTML_ENT_OUT; /* Not enough bytes. */ - j++; - - if ((input[j] == 'x')||(input[j] == 'X')) { - /* Hexadecimal entity. */ - copy++; - - if (!(j + 1 < input_len)) goto HTML_ENT_OUT; /* Not enough bytes. */ - j++; /* j is the position of the first digit now. */ - - k = j; - while((j < input_len)&&(isxdigit(input[j]))) j++; - if (j > k) { /* Do we have at least one digit? */ - /* Decode the entity. */ - char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); - *d++ = (unsigned char)strtol(x, NULL, 16); - count++; - - /* Skip over the semicolon if it's there. */ - if ((j < input_len)&&(input[j] == ';')) i = j + 1; - else i = j; - - continue; - } else { - goto HTML_ENT_OUT; - } - } else { - /* Decimal entity. */ - k = j; - while((j < input_len)&&(isdigit(input[j]))) j++; - if (j > k) { /* Do we have at least one digit? */ - /* Decode the entity. */ - char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); - *d++ = (unsigned char)strtol(x, NULL, 10); - count++; - - /* Skip over the semicolon if it's there. */ - if ((j < input_len)&&(input[j] == ';')) i = j + 1; - else i = j; - - continue; - } else { - goto HTML_ENT_OUT; - } - } - } else { - /* Text entity. */ - - k = j; - while((j < input_len)&&(isalnum(input[j]))) j++; - if (j > k) { /* Do we have at least one digit? */ - char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); - - /* Decode the entity. */ - /* ENH What about others? */ - if (strcasecmp(x, "quot") == 0) *d++ = '"'; - else - if (strcasecmp(x, "amp") == 0) *d++ = '&'; - else - if (strcasecmp(x, "lt") == 0) *d++ = '<'; - else - if (strcasecmp(x, "gt") == 0) *d++ = '>'; - else - if (strcasecmp(x, "nbsp") == 0) *d++ = NBSP; - else { - /* We do no want to convert this entity, copy the raw data over. */ - copy = j - k + 1; - goto HTML_ENT_OUT; - } - - count++; - - /* Skip over the semicolon if it's there. */ - if ((j < input_len)&&(input[j] == ';')) i = j + 1; - else i = j; - - continue; - } - } - } - -HTML_ENT_OUT: - - for(z = 0; ((z < copy) && (count < input_len)); z++) { - *d++ = input[i++]; - count++; - } - } - - *d = '\0'; - - return count; -} - -/** - * - * IMP1 Assumes NUL-terminated - */ -int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) { - unsigned char *d = input; - int i, count; - - i = count = 0; - while(i < input_len) { - if ((input[i] == '\\')&&(i + 1 < input_len)) { - int c = -1; - - switch(input[i + 1]) { - case 'a' : - c = '\a'; - break; - case 'b' : - c = '\b'; - break; - case 'f' : - c = '\f'; - break; - case 'n' : - c = '\n'; - break; - case 'r' : - c = '\r'; - break; - case 't' : - c = '\t'; - break; - case 'v' : - c = '\v'; - break; - case '\\' : - c = '\\'; - break; - case '?' : - c = '?'; - break; - case '\'' : - c = '\''; - break; - case '"' : - c = '"'; - break; - } - - if (c != -1) i += 2; - - /* Hexadecimal or octal? */ - if (c == -1) { - if ((input[i + 1] == 'x')||(input[i + 1] == 'X')) { - /* Hexadecimal. */ - if ((i + 3 < input_len)&&(isxdigit(input[i + 2]))&&(isxdigit(input[i + 3]))) { - /* Two digits. */ - c = x2c(&input[i + 2]); - i += 4; - } else { - /* Invalid encoding, do nothing. */ - } - } - else - if (ISODIGIT(input[i + 1])) { /* Octal. */ - char buf[4]; - int j = 0; - - while((i + 1 + j < input_len)&&(j < 3)) { - buf[j] = input[i + 1 + j]; - j++; - if (!ISODIGIT(input[i + 1 + j])) break; - } - buf[j] = '\0'; - - if (j > 0) { - c = strtol(buf, NULL, 8); - i += 1 + j; - } - } - } - - if (c == -1) { - /* Didn't recognise encoding, copy raw bytes. */ - *d++ = input[i + 1]; - count++; - i += 2; - } else { - /* Converted the encoding. */ - *d++ = c; - count++; - } - } else { - /* Input character not a backslash, copy it. */ - *d++ = input[i++]; - count++; - } - } - - *d = '\0'; - - return count; -} - -/** - * - * IMP1 Assumes NUL-terminated - */ -int normalize_path_inplace(unsigned char *input, int input_len, int win, int *changed) { - unsigned char *src; - unsigned char *dst; - unsigned char *end; - int ldst = 0; - int hitroot = 0; - int done = 0; - int relative; - int trailing; - - *changed = 0; - - /* 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) { - if (*src == '\\') { - *src = '/'; - *changed = 1; - } - if ((src < end) && (*(src + 1) == '\\')) { - *(src + 1) = '/'; - *changed = 1; - } - } - - /* 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); - } - - /* 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--; - } - - /* Always NUL terminate */ - *dst = '\0'; - - return ldst; -} - -char *modsec_build(apr_pool_t *mp) { - return apr_psprintf(mp, "%02i%02i%02i%1i%02i", - atoi(MODSEC_VERSION_MAJOR), - atoi(MODSEC_VERSION_MINOR), - atoi(MODSEC_VERSION_MAINT), - get_modsec_build_type(NULL), - atoi(MODSEC_VERSION_RELEASE)); -} - -int is_empty_string(const char *string) { - unsigned int i; - - if (string == NULL) return 1; - - for(i = 0; string[i] != '\0'; i++) { - if (!isspace(string[i])) { - return 0; - } - } - - return 1; -} - -char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) { - if (filename == NULL) return NULL; - // TODO Support paths on operating systems other than Unix. - if (filename[0] == '/') return (char *)filename; - - return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename, - strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))), - filename, NULL); -} - -/** - * Decode a string that contains CSS-escaped characters. - * - * References: - * http://www.w3.org/TR/REC-CSS2/syndata.html#q4 - * http://www.unicode.org/roadmaps/ - */ -int css_decode_inplace(unsigned char *input, long int input_len) { - unsigned char *d = (unsigned char *)input; - long int i, j, count; - - if (input == NULL) return -1; - - i = count = 0; - while (i < input_len) { - - /* Is the character a backslash? */ - if (input[i] == '\\') { - - /* Is there at least one more byte? */ - if (i + 1 < input_len) { - i++; /* We are not going to need the backslash. */ - - /* Check for 1-6 hex characters following the backslash */ - j = 0; - while ( (j < 6) - && (i + j < input_len) - && (VALID_HEX(input[i + j]))) - { - j++; - } - - if (j > 0) { /* We have at least one valid hexadecimal character. */ - int fullcheck = 0; - - /* For now just use the last two bytes. */ - switch (j) { - /* Number of hex characters */ - case 1: - *d++ = xsingle2c(&input[i]); - break; - - case 2: - case 3: - /* Use the last two from the end. */ - *d++ = x2c(&input[i + j - 2]); - break; - - case 4: - /* Use the last two from the end, but request - * a full width check. - */ - *d = x2c(&input[i + j - 2]); - fullcheck = 1; - break; - - case 5: - /* Use the last two from the end, but request - * a full width check if the number is greater - * or equal to 0xFFFF. - */ - *d = x2c(&input[i + j - 2]); - - /* Do full check if first byte is 0 */ - if (input[i] == '0') { - fullcheck = 1; - } - else { - d++; - } - break; - - case 6: - /* Use the last two from the end, but request - * a full width check if the number is greater - * or equal to 0xFFFF. - */ - *d = x2c(&input[i + j - 2]); - - /* Do full check if first/second bytes are 0 */ - if ( (input[i] == '0') - && (input[i + 1] == '0') - ) { - fullcheck = 1; - } - else { - d++; - } - break; - } - - /* Full width ASCII (0xff01 - 0xff5e) needs 0x20 added */ - if (fullcheck) { - if ( (*d > 0x00) && (*d < 0x5f) - && ((input[i + j - 3] == 'f') || - (input[i + j - 3] == 'F')) - && ((input[i + j - 4] == 'f') || - (input[i + j - 4] == 'F'))) - { - (*d) += 0x20; - } - - d++; - } - - /* We must ignore a single whitespace after a hex escape */ - if ((i + j < input_len) && isspace(input[i + j])) { - j++; - } - - /* Move over. */ - count++; - i += j; - } - - /* No hexadecimal digits after backslash */ - else if (input[i] == '\n') { - /* A newline character following backslash is ignored. */ - i++; - } - - /* The character after backslash is not a hexadecimal digit, nor a newline. */ - else { - /* Use one character after backslash as is. */ - *d++ = input[i++]; - count++; - } - } - - /* No characters after backslash. */ - else { - /* Do not include backslash in output (continuation to nothing) */ - i++; - } - } - - /* Character is not a backslash. */ - else { - /* Copy one normal character to output. */ - *d++ = input[i++]; - count++; - } - } - - /* Terminate output string. */ - *d = '\0'; - - return count; -} - -/** - * Translate UNIX octal umask/mode to APR apr_fileperms_t - */ -apr_fileperms_t mode2fileperms(int mode) { - apr_fileperms_t perms = 0; - - if (mode & S_IXOTH) perms |= APR_WEXECUTE; - if (mode & S_IWOTH) perms |= APR_WWRITE; - if (mode & S_IROTH) perms |= APR_WREAD; - if (mode & S_IXGRP) perms |= APR_GEXECUTE; - if (mode & S_IWGRP) perms |= APR_GWRITE; - if (mode & S_IRGRP) perms |= APR_GREAD; - if (mode & S_IXUSR) perms |= APR_UEXECUTE; - if (mode & S_IWUSR) perms |= APR_UWRITE; - if (mode & S_IRUSR) perms |= APR_UREAD; - if (mode & S_ISVTX) perms |= APR_WSTICKY; - if (mode & S_ISGID) perms |= APR_GSETID; - if (mode & S_ISUID) perms |= APR_USETID; - - return perms; -} - -/** - * Generate a single variable. - */ -char *construct_single_var(modsec_rec *msr, char *name) { - char *varname = NULL; - char *param = NULL; - msre_var *var = NULL; - msre_var *vx = NULL; - char *my_error_msg = NULL; - - /* Extract variable name and its parameter from the script. */ - varname = apr_pstrdup(msr->mp, name); - param = strchr(varname, '.'); - if (param != NULL) { - *param = '\0'; - param++; - } - - /* Resolve variable. */ - var = msre_create_var_ex(msr->mp, msr->modsecurity->msre, - varname, param, msr, &my_error_msg); - if (var == NULL) return NULL; - - /* Generate variable. */ - vx = generate_single_var(msr, var, NULL, NULL, msr->msc_rule_mptmp); - if (vx == NULL) return NULL; - - return (char *)vx->value; -} diff --git a/apache2/msc_util.h b/apache2/msc_util.h deleted file mode 100644 index ea716d336c..0000000000 --- a/apache2/msc_util.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _UTIL_H_ -#define _UTIL_H_ - -#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" -#include "re.h" - -#ifdef WIN32 -#include -// This is a trick: for ModSecurity modules this will declare inet_pton, -// but for mymodule.cpp (IIS module) this will skip, because we include -// windows.h before including msc_util.h -// Without the trick we have redefinition conflict. -// -#if !(NTDDI_VERSION >= NTDDI_VISTA) -int DSOLOCAL inet_pton(int family, const char *src, void *dst); -#endif -#endif - -#define UNICODE_ERROR_CHARACTERS_MISSING -1 -#define UNICODE_ERROR_INVALID_ENCODING -2 -#define UNICODE_ERROR_OVERLONG_CHARACTER -3 -#define UNICODE_ERROR_RESTRICTED_CHARACTER -4 -#define UNICODE_ERROR_DECODING_ERROR -5 - -#ifdef LINUX_S390 -int DSOLOCAL swap_int32(int x); -#endif - - -char DSOLOCAL *utf8_unicode_inplace_ex(apr_pool_t *mp, unsigned char *input, long int input_len, int *changed); - -char DSOLOCAL *m_strcasestr(const char *haystack, const char *needle); - -int DSOLOCAL normalize_path_inplace(unsigned char *input, int len, int win, int *changed); - -int DSOLOCAL parse_boolean(const char *input); - -char DSOLOCAL *remove_quotes(apr_pool_t *mptmp, const char *input, int input_len); - -char DSOLOCAL *parse_pm_content(const char *op_parm, unsigned short int op_len, msre_rule *rule, char **error_msg); - -char DSOLOCAL *remove_escape(apr_pool_t *mptmp, const char *input, int input_len); - -int DSOLOCAL parse_name_eq_value(apr_pool_t *mp, const char *input, char **name, char **value); - -char DSOLOCAL *url_encode(apr_pool_t *mp, char *input, unsigned int input_len, int *changed); - -char DSOLOCAL *strnurlencat(char *destination, char *source, unsigned int maxlen); - -char DSOLOCAL *file_dirname(apr_pool_t *p, const char *filename); - -char DSOLOCAL *file_basename(apr_pool_t *p, const char *filename); - -int DSOLOCAL sql_hex2bytes_inplace(unsigned char *data, int len); - -int DSOLOCAL hex2bytes_inplace(unsigned char *data, int len); - -char DSOLOCAL *bytes2hex(apr_pool_t *pool, unsigned char *data, int len); - -int DSOLOCAL is_token_char(unsigned char c); - -int DSOLOCAL remove_lf_crlf_inplace(char *text); - -char DSOLOCAL *guess_tmp_dir(apr_pool_t *p); - -char DSOLOCAL *current_logtime(apr_pool_t *mp); - -char DSOLOCAL *current_filetime(apr_pool_t *mp); - -int DSOLOCAL msc_mkstemp_ex(char *templat, int mode); - -int DSOLOCAL msc_mkstemp(char *templat); - -char DSOLOCAL *strtolower_inplace(unsigned char *str); - -char DSOLOCAL *log_escape_re(apr_pool_t *p, const char *text); - -char DSOLOCAL *log_escape(apr_pool_t *p, const char *text); - -char DSOLOCAL *log_escape_nq(apr_pool_t *p, const char *text); - -char DSOLOCAL *log_escape_ex(apr_pool_t *p, const char *text, unsigned long int text_length); - -char DSOLOCAL *log_escape_nq_ex(apr_pool_t *p, const char *text, unsigned long int text_length); - -char DSOLOCAL *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length); - -char DSOLOCAL *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length); - -char DSOLOCAL *log_escape_nul(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length); - -int DSOLOCAL decode_base64_ext(char *plain_text, const unsigned char *input, int input_len); - -int DSOLOCAL convert_to_int(const char c); - -int DSOLOCAL set_match_to_tx(modsec_rec *msr, int capture, const char *match, int tx_n); - -int DSOLOCAL js_decode_nonstrict_inplace(unsigned char *input, long int input_len); - -int DSOLOCAL urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_length, int * changed); - -int DSOLOCAL urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_length, int *invalid_count, int *changed); - -int DSOLOCAL html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int len); - -int DSOLOCAL ansi_c_sequences_decode_inplace(unsigned char *input, int len); - -char DSOLOCAL *modsec_build(apr_pool_t *mp); - -int DSOLOCAL is_empty_string(const char *string); - -char DSOLOCAL *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename); - -int DSOLOCAL css_decode_inplace(unsigned char *input, long int input_len); - -apr_fileperms_t DSOLOCAL mode2fileperms(int mode); - -char DSOLOCAL *construct_single_var(modsec_rec *msr, char *name); - -char DSOLOCAL *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp); - -unsigned char DSOLOCAL is_netmask_v4(char *ip_strv4); - -unsigned char DSOLOCAL is_netmask_v6(char *ip_strv6); -#endif diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c deleted file mode 100644 index a31decb5e1..0000000000 --- a/apache2/msc_xml.c +++ /dev/null @@ -1,146 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "msc_xml.h" - -static xmlParserInputBufferPtr -xml_unload_external_entity(const char *URI, xmlCharEncoding enc) { - return NULL; -} - - -/** - * Initialise XML parser. - */ -int xml_init(modsec_rec *msr, char **error_msg) { - xmlParserInputBufferCreateFilenameFunc entity; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - msr->xml = apr_pcalloc(msr->mp, sizeof(xml_data)); - if (msr->xml == NULL) return -1; - - if(msr->txcfg->xml_external_entity == 0) { - entity = xmlParserInputBufferCreateFilenameDefault(xml_unload_external_entity); - } - - return 1; -} - -#if 0 -static void xml_receive_sax_error(void *data, const char *msg, ...) { - modsec_rec *msr = (modsec_rec *)data; - char message[256]; - - if (msr == NULL) return; - - apr_snprintf(message, sizeof(message), "%s (line %d offset %d)", - log_escape_nq(msr->mp, msr->xml->parsing_ctx->lastError.message), - msr->xml->parsing_ctx->lastError.line, - msr->xml->parsing_ctx->lastError.int2); - - msr_log(msr, 5, "XML: Parsing error: %s", message); -} -#endif - -/** - * Feed one chunk of data to the XML parser. - */ -int xml_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char **error_msg) { - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* We want to initialise our parsing context here, to - * enable us to pass it the first chunk of data so that - * it can attempt to auto-detect the encoding. - */ - if (msr->xml->parsing_ctx == NULL) { - - /* First invocation. */ - - msr_log(msr, 4, "XML: Initialising parser."); - - /* NOTE When Sax interface is used libxml will not - * create the document object, but we need it. - - msr->xml->sax_handler = (xmlSAXHandler *)apr_pcalloc(msr->mp, sizeof(xmlSAXHandler)); - if (msr->xml->sax_handler == NULL) return -1; - msr->xml->sax_handler->error = xml_receive_sax_error; - msr->xml->sax_handler->warning = xml_receive_sax_error; - msr->xml->parsing_ctx = xmlCreatePushParserCtxt(msr->xml->sax_handler, msr, - buf, size, "body.xml"); - - */ - - msr->xml->parsing_ctx = xmlCreatePushParserCtxt(NULL, NULL, buf, size, "body.xml"); - if (msr->xml->parsing_ctx == NULL) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed to create parsing context."); - return -1; - } - } else { - - /* Not a first invocation. */ - - xmlParseChunk(msr->xml->parsing_ctx, buf, size, 0); - if (msr->xml->parsing_ctx->wellFormed != 1) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed parsing document."); - return -1; - } - } - - return 1; -} - -/** - * Finalise XML parsing. - */ -int xml_complete(modsec_rec *msr, char **error_msg) { - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Only if we have a context, meaning we've done some work. */ - if (msr->xml->parsing_ctx != NULL) { - /* This is how we signalise the end of parsing to libxml. */ - xmlParseChunk(msr->xml->parsing_ctx, NULL, 0, 1); - - /* Preserve the results for our reference. */ - msr->xml->well_formed = msr->xml->parsing_ctx->wellFormed; - msr->xml->doc = msr->xml->parsing_ctx->myDoc; - - /* Clean up everything else. */ - xmlFreeParserCtxt(msr->xml->parsing_ctx); - msr->xml->parsing_ctx = NULL; - msr_log(msr, 4, "XML: Parsing complete (well_formed %u).", msr->xml->well_formed); - - if (msr->xml->well_formed != 1) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed parsing document."); - return -1; - } - } - - return 1; -} - -/** - * Frees the resources used for XML parsing. - */ -apr_status_t xml_cleanup(modsec_rec *msr) { - if (msr->xml->doc != NULL) { - xmlFreeDoc(msr->xml->doc); - msr->xml->doc = NULL; - } - - return 1; -} diff --git a/apache2/msc_xml.h b/apache2/msc_xml.h deleted file mode 100644 index f239068672..0000000000 --- a/apache2/msc_xml.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_XML_H_ -#define _MSC_XML_H_ - -typedef struct xml_data xml_data; - -#include "modsecurity.h" -#include -#include - -/* Structures */ - -struct xml_data { - xmlSAXHandler *sax_handler; - xmlParserCtxtPtr parsing_ctx; - xmlDocPtr doc; - - unsigned int well_formed; -}; - -/* Functions */ - -int DSOLOCAL xml_init(modsec_rec *msr, char **error_msg); - -int DSOLOCAL xml_process_chunk(modsec_rec *msr, const char *buf, - unsigned int size, char **error_msg); - -int DSOLOCAL xml_complete(modsec_rec *msr, char **error_msg); - -apr_status_t DSOLOCAL xml_cleanup(modsec_rec *msr); - -#endif diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c deleted file mode 100644 index b698e7913f..0000000000 --- a/apache2/persist_dbm.c +++ /dev/null @@ -1,709 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "persist_dbm.h" -#include "apr_sdbm.h" - -/** - * - */ -static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob, unsigned int blob_size, - int log_vars) -{ - apr_table_t *col = NULL; - unsigned int blob_offset; - - col = apr_table_make(msr->mp, 32); - if (col == NULL) return NULL; - - /* ENH verify the first 3 bytes (header) */ - - blob_offset = 3; - while (blob_offset + 1 < blob_size) { - msc_string *var = apr_pcalloc(msr->mp, sizeof(msc_string)); - - var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; - if (var->name_len == 0) { - /* Is the length a name length, or just the end of the blob? */ - if (blob_offset < blob_size - 2) { - /* This should never happen as the name length - * includes the terminating NUL and should be 1 for "" - */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); - } - msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1); - } - break; - } - else if (var->name_len > 65536) { - /* This should never happen as the length is restricted on store - * to 65536. - */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); - } - msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1); - break; - } - - blob_offset += 2; - if (blob_offset + var->name_len > blob_size) return NULL; - var->name = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->name_len - 1); - blob_offset += var->name_len; - var->name_len--; - - var->value_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; - blob_offset += 2; - - if (blob_offset + var->value_len > blob_size) return NULL; - var->value = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->value_len - 1); - blob_offset += var->value_len; - var->value_len--; - - if (log_vars && (msr->txcfg->debuglog_level >= 9)) { - msr_log(msr, 9, "collection_unpack: Read variable: name \"%s\", value \"%s\".", - log_escape_ex(msr->mp, var->name, var->name_len), - log_escape_ex(msr->mp, var->value, var->value_len)); - } - - apr_table_addn(col, var->name, (void *)var); - } - - return col; -} - -/** - * - */ -static apr_table_t *collection_retrieve_ex(apr_sdbm_t *existing_dbm, modsec_rec *msr, const char *col_name, - const char *col_key, int col_key_len) -{ - char *dbm_filename = NULL; - apr_status_t rc; - apr_sdbm_datum_t key; - apr_sdbm_datum_t *value = NULL; - apr_sdbm_t *dbm = NULL; - apr_table_t *col = NULL; - const apr_array_header_t *arr; - apr_table_entry_t *te; - int expired = 0; - int i; - - if (msr->txcfg->data_dir == NULL) { - msr_log(msr, 1, "collection_retrieve_ex: Unable to retrieve collection (name \"%s\", key \"%s\"). Use " - "SecDataDir to define data directory first.", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, col_key, col_key_len)); - goto cleanup; - } - - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_retrieve_ex: collection_retrieve_ex: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name), - log_escape(msr->mp, dbm_filename)); - } - - key.dptr = (char *)col_key; - key.dsize = col_key_len + 1; - - if (existing_dbm == NULL) { - rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - dbm = NULL; - goto cleanup; - } - } - else { - dbm = existing_dbm; - } - - value = (apr_sdbm_datum_t *)apr_pcalloc(msr->mp, sizeof(apr_sdbm_datum_t)); - rc = apr_sdbm_fetch(dbm, value, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed to read from DBM file \"%s\": %s", log_escape(msr->mp, - dbm_filename), get_apr_error(msr->mp, rc)); - goto cleanup; - } - - if (value->dptr == NULL) { /* Key not found in DBM file. */ - goto cleanup; - } - - /* ENH Need expiration (and perhaps other metadata) accessible in blob - * form to determine if converting to a table is needed. This will - * save some cycles. - */ - - /* Transform raw data into a table. */ - col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1); - if (col == NULL) { - goto cleanup; - } - - /* Close after "value" used from fetch or memory may be overwritten. */ - if (existing_dbm == NULL) { - apr_sdbm_close(dbm); - dbm = NULL; - } - - /* Remove expired variables. */ - do { - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - if (strncmp(te[i].key, "__expire_", 9) == 0) { - msc_string *var = (msc_string *)te[i].val; - int expiry_time = atoi(var->value); - - if (expiry_time <= apr_time_sec(msr->request_time)) { - char *key_to_expire = te[i].key; - - /* Done early if the col expired */ - if (strcmp(key_to_expire, "__expire_KEY") == 0) { - expired = 1; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire + 9); - msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire); - } - - apr_table_unset(col, key_to_expire + 9); - apr_table_unset(col, key_to_expire); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_retrieve_ex: Removed expired variable \"%s\".", key_to_expire + 9); - } - - break; - } - } - } - } while(!expired && (i != arr->nelts)); - - /* Delete the collection if the variable "KEY" does not exist. - * - * ENH It would probably be more efficient to hold the DBM - * open until determined if it needs deleted than to open a second - * time. - */ - if (apr_table_get(col, "KEY") == NULL) { - if (existing_dbm == NULL) { - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed to access DBM file \"%s\": %s", - log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); - dbm = NULL; - goto cleanup; - } - } - else { - dbm = existing_dbm; - } - - rc = apr_sdbm_delete(dbm, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed deleting collection (name \"%s\", " - "key \"%s\"): %s", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, col_key, col_key_len), get_apr_error(msr->mp, rc)); - msr->msc_sdbm_delete_error = 1; - goto cleanup; - } - - - if (existing_dbm == NULL) { - apr_sdbm_close(dbm); - dbm = NULL; - } - - if (expired && (msr->txcfg->debuglog_level >= 9)) { - msr_log(msr, 9, "collection_retrieve_ex: Collection expired (name \"%s\", key \"%s\").", col_name, - log_escape_ex(msr->mp, col_key, col_key_len)); - } - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_retrieve_ex: Deleted collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); - } - goto cleanup; - } - - /* Update UPDATE_RATE */ - { - msc_string *var; - int create_time, counter; - - var = (msc_string *)apr_table_get(col, "CREATE_TIME"); - if (var == NULL) { - /* Error. */ - } else { - create_time = atoi(var->value); - var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER"); - if (var == NULL) { - /* Error. */ - } else { - apr_time_t td; - counter = atoi(var->value); - - /* UPDATE_RATE is removed on store, so add it back here */ - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "UPDATE_RATE"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - - /* NOTE: No rate if there has been no time elapsed */ - td = (apr_time_sec(apr_time_now()) - create_time); - if (td == 0) { - var->value = apr_psprintf(msr->mp, "%d", 0); - } - else { - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, - (apr_time_t)((60 * counter)/td)); - } - var->value_len = strlen(var->value); - } - } - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_retrieve_ex: Retrieved collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); - } - - if ((existing_dbm == NULL) && dbm) { - /* Should not ever get here */ - msr_log(msr, 1, "collection_retrieve_ex: Internal Error: Collection remained open (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); - - apr_sdbm_close(dbm); - } - - return col; - -cleanup: - - if ((existing_dbm == NULL) && dbm) { - apr_sdbm_close(dbm); - } - - return NULL; -} - -/** - * - */ -apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, - const char *col_key, int col_key_len) -{ - apr_time_t time_before = apr_time_now(); - apr_table_t *rtable = NULL; - - rtable = collection_retrieve_ex(NULL, msr, col_name, col_key, col_key_len); - - msr->time_storage_read += apr_time_now() - time_before; - - return rtable; -} - -/** - * - */ -int collection_store(modsec_rec *msr, apr_table_t *col) { - char *dbm_filename = NULL; - msc_string *var_name = NULL, *var_key = NULL; - unsigned char *blob = NULL; - unsigned int blob_size, blob_offset; - apr_status_t rc; - apr_sdbm_datum_t key; - apr_sdbm_datum_t value; - apr_sdbm_t *dbm = NULL; - const apr_array_header_t *arr; - apr_table_entry_t *te; - int i; - const apr_table_t *stored_col = NULL; - const apr_table_t *orig_col = NULL; - - var_name = (msc_string *)apr_table_get(col, "__name"); - if (var_name == NULL) { - goto error; - } - - var_key = (msc_string *)apr_table_get(col, "__key"); - if (var_key == NULL) { - goto error; - } - - if (msr->txcfg->data_dir == NULL) { - msr_log(msr, 1, "collection_store: Unable to store collection (name \"%s\", key \"%s\"). Use " - "SecDataDir to define data directory first.", log_escape_ex(msr->mp, var_name->value, var_name->value_len), - log_escape_ex(msr->mp, var_key->value, var_key->value_len)); - goto error; - } - - // ENH: lowercase the var name in the filename - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value), - log_escape(msr->mp, dbm_filename)); - } - - /* Delete IS_NEW on store. */ - apr_table_unset(col, "IS_NEW"); - - /* Delete UPDATE_RATE on store to save space as it is calculated */ - apr_table_unset(col, "UPDATE_RATE"); - - /* Update the timeout value. */ - { - msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT"); - if (var != NULL) { - int timeout = atoi(var->value); - var = (msc_string *)apr_table_get(col, "__expire_KEY"); - if (var != NULL) { - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout)); - var->value_len = strlen(var->value); - } - } - } - - /* LAST_UPDATE_TIME */ - { - msc_string *var = (msc_string *)apr_table_get(col, "LAST_UPDATE_TIME"); - if (var == NULL) { - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "LAST_UPDATE_TIME"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - } - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()))); - var->value_len = strlen(var->value); - } - - /* UPDATE_COUNTER */ - { - msc_string *var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER"); - int counter = 0; - if (var == NULL) { - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "UPDATE_COUNTER"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - } else { - counter = atoi(var->value); - } - var->value = apr_psprintf(msr->mp, "%d", counter + 1); - var->value_len = strlen(var->value); - } - - /* ENH Make the expiration timestamp accessible in blob form so that - * it is easier/faster to determine expiration without having to - * convert back to table form - */ - - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_store: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - dbm = NULL; - goto error; - } - - /* Need to lock to pull in the stored data again and apply deltas. */ - rc = apr_sdbm_lock(dbm, APR_FLOCK_EXCLUSIVE); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_store: Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - goto error; - } - - /* If there is an original value, then create a delta and - * apply the delta to the current value */ - orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value); - if (orig_col != NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Re-retrieving collection prior to store: %s", - apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value)); - } - - stored_col = (const apr_table_t *)collection_retrieve_ex(dbm, msr, var_name->value, var_key->value, var_key->value_len); - } - - /* Merge deltas and calculate the size first. */ - blob_size = 3 + 2; - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *var = (msc_string *)te[i].val; - int len; - - /* If there is an original value, then apply the delta - * to the latest stored value */ - if (stored_col != NULL) { - const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name); - if (orig_var != NULL) { - const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name); - if (stored_var != NULL) { - int origval = atoi(orig_var->value); - int ourval = atoi(var->value); - int storedval = atoi(stored_var->value); - int delta = ourval - origval; - int newval = storedval + delta; - - if (newval < 0) newval = 0; /* Counters never go below zero. */ - - var->value = apr_psprintf(msr->mp, "%d", newval); - var->value_len = strlen(var->value); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]", - log_escape_ex(msr->mp, var_name->value, var_name->value_len), - log_escape_ex(msr->mp, var->name, var->name_len), - origval, ourval, delta, storedval, delta, newval, var->value, var->value_len); - } - } - } - } - - len = var->name_len + 1; - if (len >= 65536) len = 65536; - blob_size += len + 2; - - len = var->value_len + 1; - if (len >= 65536) len = 65536; - blob_size += len + 2; - } - - /* Now generate the binary object. */ - blob = apr_pcalloc(msr->mp, blob_size); - if (blob == NULL) { - if (dbm != NULL) { - apr_sdbm_unlock(dbm); - apr_sdbm_close(dbm); - } - - return -1; - } - - blob[0] = 0x49; - blob[1] = 0x52; - blob[2] = 0x01; - - blob_offset = 3; - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *var = (msc_string *)te[i].val; - int len; - - len = var->name_len + 1; - if (len >= 65536) len = 65536; - - blob[blob_offset + 0] = (len & 0xff00) >> 8; - blob[blob_offset + 1] = len & 0x00ff; - memcpy(blob + blob_offset + 2, var->name, len - 1); - blob[blob_offset + 2 + len - 1] = '\0'; - blob_offset += 2 + len; - - len = var->value_len + 1; - if (len >= 65536) len = 65536; - - blob[blob_offset + 0] = (len & 0xff00) >> 8; - blob[blob_offset + 1] = len & 0x00ff; - memcpy(blob + blob_offset + 2, var->value, len - 1); - blob[blob_offset + 2 + len - 1] = '\0'; - blob_offset += 2 + len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Wrote variable: name \"%s\", value \"%s\".", - log_escape_ex(msr->mp, var->name, var->name_len), - log_escape_ex(msr->mp, var->value, var->value_len)); - } - } - - blob[blob_offset] = 0; - blob[blob_offset + 1] = 0; - - /* And, finally, store it. */ - key.dptr = var_key->value; - key.dsize = var_key->value_len + 1; - - value.dptr = (char *)blob; - value.dsize = blob_size; - - rc = apr_sdbm_store(dbm, key, value, APR_SDBM_REPLACE); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_store: Failed to write to DBM file \"%s\": %s", dbm_filename, - get_apr_error(msr->mp, rc)); - if (dbm != NULL) { - apr_sdbm_unlock(dbm); - apr_sdbm_close(dbm); - } - - return -1; - } - - apr_sdbm_unlock(dbm); - apr_sdbm_close(dbm); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_store: Persisted collection (name \"%s\", key \"%s\").", - log_escape_ex(msr->mp, var_name->value, var_name->value_len), - log_escape_ex(msr->mp, var_key->value, var_key->value_len)); - } - - return 0; - -error: - return -1; -} - -/** - * - */ -int collections_remove_stale(modsec_rec *msr, const char *col_name) { - char *dbm_filename = NULL; - apr_sdbm_datum_t key, value; - apr_sdbm_t *dbm = NULL; - apr_status_t rc; - apr_array_header_t *keys_arr; - char **keys; - apr_time_t now = apr_time_sec(msr->request_time); - int i; - - if (msr->txcfg->data_dir == NULL) { - /* The user has been warned about this problem enough times already by now. - * msr_log(msr, 1, "Unable to access collection file (name \"%s\"). Use SecDataDir to " - * "define data directory first.", log_escape(msr->mp, col_name)); - */ - goto error; - } - - if(strstr(col_name,"USER") || strstr(col_name,"SESSION") || strstr(col_name, "RESOURCE")) - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", msr->txcfg->webappid, "_", col_name, NULL); - else - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collections_remove_stale: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name), - log_escape(msr->mp, dbm_filename)); - } - - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - dbm = NULL; - goto error; - } - - /* First get a list of all keys. */ - keys_arr = apr_array_make(msr->mp, 256, sizeof(char *)); - rc = apr_sdbm_lock(dbm, APR_FLOCK_SHARED); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed to lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - goto error; - } - - /* No one can write to the file while doing this so - * do it as fast as possible. - */ - rc = apr_sdbm_firstkey(dbm, &key); - while(rc == APR_SUCCESS) { - char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize - 1); - *(char **)apr_array_push(keys_arr) = s; - rc = apr_sdbm_nextkey(dbm, &key); - } - apr_sdbm_unlock(dbm); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collections_remove_stale: Found %d record(s) in file \"%s\".", keys_arr->nelts, - log_escape(msr->mp, dbm_filename)); - } - - /* Now retrieve the entires one by one. */ - keys = (char **)keys_arr->elts; - for (i = 0; i < keys_arr->nelts; i++) { - key.dptr = keys[i]; - key.dsize = strlen(key.dptr) + 1; - - rc = apr_sdbm_fetch(dbm, &value, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed reading DBM file \"%s\": %s", - log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); - goto error; - } - - if (value.dptr != NULL) { - apr_table_t *col = NULL; - msc_string *var = NULL; - - col = collection_unpack(msr, (const unsigned char *)value.dptr, value.dsize, 0); - if (col == NULL) { - goto error; - } - - var = (msc_string *)apr_table_get(col, "__expire_KEY"); - if (var == NULL) { - msr_log(msr, 1, "collections_remove_stale: Collection cleanup discovered entry with no " - "__expire_KEY (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); - } else { - unsigned int expiry_time = atoi(var->value); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collections_remove_stale: Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1), - expiry_time - now); - } - - if (expiry_time <= now) { - rc = apr_sdbm_delete(dbm, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed deleting collection (name \"%s\", " - "key \"%s\"): %s", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, key.dptr, key.dsize - 1), get_apr_error(msr->mp, rc)); - msr->msc_sdbm_delete_error = 1; - goto error; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collections_remove_stale: Removed stale collection (name \"%s\", " - "key \"%s\").", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); - } - } - } - } else { - /* Ignore entry not found - it may have been removed in the meantime. */ - } - } - - apr_sdbm_close(dbm); - - return 1; - -error: - - if (dbm) { - apr_sdbm_close(dbm); - } - - return -1; -} diff --git a/apache2/persist_dbm.h b/apache2/persist_dbm.h deleted file mode 100644 index 57d85bddca..0000000000 --- a/apache2/persist_dbm.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _PERSIST_DBM_H_ -#define _PERSIST_DBM_H_ - -#include "apr_general.h" -#include "modsecurity.h" - -apr_table_t DSOLOCAL *collection_retrieve(modsec_rec *msr, const char *col_name, - const char *col_value, int col_value_length); - -int DSOLOCAL collection_store(modsec_rec *msr, apr_table_t *collection); - -int DSOLOCAL collections_remove_stale(modsec_rec *msr, const char *col_name); - -#endif diff --git a/apache2/re.c b/apache2/re.c deleted file mode 100644 index 952a786781..0000000000 --- a/apache2/re.c +++ /dev/null @@ -1,3323 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include - -#include "re.h" - -#if defined(WITH_LUA) -#include "msc_lua.h" -#endif - -static const char *const severities[] = { - "EMERGENCY", - "ALERT", - "CRITICAL", - "ERROR", - "WARNING", - "NOTICE", - "INFO", - "DEBUG", - NULL, -}; - -static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *var, const char *exceptions); -static apr_status_t msre_parse_targets(msre_ruleset *ruleset, const char *text, - apr_array_header_t *arr, char **error_msg); -static char *msre_generate_target_string(apr_pool_t *pool, msre_rule *rule); -static msre_var *msre_create_var(msre_ruleset *ruleset, const char *name, const char *param, - modsec_rec *msr, char **error_msg); -static msre_action *msre_create_action(msre_engine *engine, const char *name, - const char *param, char **error_msg); -static apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr); - -/* -- Actions, variables, functions and operator functions ----------------- */ - -/** - * \brief Remove rule targets to be processed - * - * \param rule Pointer to the rule - * \param msr ModSecurity transaction resource - * \param var Pointer to target structure. - * \param targets Exception list. - */ -static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *var, const char *exceptions) { - const char *targets = NULL; - char *savedptr = NULL, *target = NULL; - char *c = NULL, *name = NULL, *value = NULL; - char *variable = NULL, *myvar = NULL; - char *myvalue = NULL, *myname = NULL; - int match = 0; - - if(msr == NULL) - return 0; - - if(var == NULL) - return 0; - - if(rule == NULL) - return 0; - - if(rule->actionset == NULL) - return 0; - - if(rule->actionset->id !=NULL) { - - myvar = apr_pstrdup(msr->mp, var->name); - - c = strchr(myvar,':'); - - if(c != NULL) { - myname = apr_strtok(myvar,":",&myvalue); - } else { - myname = myvar; - } - - match = 0; - - targets = apr_pstrdup(msr->mp, exceptions); - - if(targets != NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: Found exception target list [%s] for rule id %s", targets, rule->actionset->id); - } - target = apr_strtok((char *)targets, ",", &savedptr); - - while(target != NULL) { - - variable = apr_pstrdup(msr->mp, target); - - c = strchr(variable,':'); - - if(c != NULL) { - name = apr_strtok(variable,":",&value); - } else { - name = variable; - value = NULL; - } - - if((strlen(myname) == strlen(name)) && - (strncasecmp(myname, name,strlen(myname)) == 0)) { - - if(value != NULL && myvalue != NULL) { - if((strlen(myvalue) == strlen(value)) && - strncasecmp(myvalue,value,strlen(myvalue)) == 0) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: Target %s will not be processed.", target); - } - match = 1; - } - } else if (value == NULL && myvalue == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: Target %s will not be processed.", target); - } - match = 1; - } else if (value == NULL && myvalue != NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: Target %s will not be processed.", target); - } - match = 1; - } - } - - target = apr_strtok(NULL, ",", &savedptr); - } - } else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: No exception target found for rule id %s.", rule->actionset->id); - - } - } - - } - - if(match == 1) - return 1; - - return 0; -} - -/** - * \brief Update target for all matching rules in set, in any phase - * - * \param msr ModSecurity transaction resource - * \param ruleset Pointer to set of rules to modify - * \param re Pointer to exception object describing which rules to modify - * \param p2 Pointer to configuration option TARGET - * \param p3 Pointer to configuration option REPLACED_TARGET - */ -char *msre_ruleset_rule_update_target_matching_exception(modsec_rec *msr, msre_ruleset *ruleset, rule_exception *re, const char *p2, const char *p3) { - char *err; - - if(ruleset == NULL) - return NULL; - - if(p2 == NULL) { - return apr_psprintf(ruleset->mp, "Trying to update without a target"); - } - - if (NULL != (err = msre_ruleset_phase_rule_update_target_matching_exception(msr, ruleset, re, ruleset->phase_request_headers, p2, p3))) - return err; - if (NULL != (err = msre_ruleset_phase_rule_update_target_matching_exception(msr, ruleset, re, ruleset->phase_request_body, p2, p3))) - return err; - if (NULL != (err = msre_ruleset_phase_rule_update_target_matching_exception(msr, ruleset, re, ruleset->phase_response_headers, p2, p3))) - return err; - if (NULL != (err = msre_ruleset_phase_rule_update_target_matching_exception(msr, ruleset, re, ruleset->phase_response_body, p2, p3))) - return err; - if (NULL != (err = msre_ruleset_phase_rule_update_target_matching_exception(msr, ruleset, re, ruleset->phase_logging, p2, p3))) - return err; - - /* Everything worked! */ - return NULL; -} - - -/** - * \brief Update target for all matching rules in set for a specific phase - * - * \param msr ModSecurity transaction resource - * \param ruleset Pointer to set of rules to modify - * \param re Pointer to exception object describing which rules to modify - * \param phase_arr Pointer to phase that should be edited - * \param p2 Pointer to configuration option TARGET - * \param p3 Pointer to configuration option REPLACED_TARGET - * - * \todo Figure out error checking - */ -char *msre_ruleset_phase_rule_update_target_matching_exception(modsec_rec *msr, msre_ruleset *ruleset, rule_exception *re, - apr_array_header_t *phase_arr, const char *p2, - const char *p3) -{ - msre_rule **rules; - int i, j, mode; - char *err; - - j = 0; - mode = 0; - rules = (msre_rule **)phase_arr->elts; - for (i = 0; i < phase_arr->nelts; i++) { - msre_rule *rule = (msre_rule *)rules[i]; - - if (mode == 0) { /* Looking for next rule. */ - if (msre_ruleset_rule_matches_exception(rule, re)) { - - err = update_rule_target_ex(NULL, ruleset, rule, p2, p3); - if (err) return err; - if (rule->actionset->is_chained) mode = 2; /* Match all rules in this chain. */ - } else { - if (rule->actionset->is_chained) mode = 1; /* Skip all rules in this chain. */ - } - } else { /* Handling rule that is part of a chain. */ - if (mode == 2) { /* We want to change the rule. */ - err = update_rule_target_ex(msr, ruleset, rule, p2, p3); - if (err) return err; - } - - if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0; - } - } - - return NULL; -} - - -char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *rule, const char *p2, - const char *p3) { - - msre_var **targets = NULL; - const char *current_targets = NULL; - char *my_error_msg = NULL, *target = NULL; - char *p = NULL, *savedptr = NULL; - unsigned int is_negated = 0, is_counting = 0; - int name_len = 0, value_len = 0; - char *name = NULL, *value = NULL; - char *opt = NULL, *param = NULL; - char *target_list = NULL, *replace = NULL; - int i, rc, match = 0, var_appended = 0; - - if(rule != NULL) { - - target_list = strdup(p2); - if(target_list == NULL) - return apr_psprintf(ruleset->mp, "Error to update target - memory allocation");; - - if(p3 != NULL) { - replace = strdup(p3); - if(replace == NULL) { - free(target_list); - target_list = NULL; - return apr_psprintf(ruleset->mp, "Error to update target - memory allocation");; - } - } - - if(replace != NULL) { - - opt = strchr(replace,'!'); - - if(opt != NULL) { - *opt = '\0'; - opt++; - param = opt; - is_negated = 1; - } else if ((opt = strchr(replace,'&')) != NULL) { - *opt = '\0'; - opt++; - param = opt; - is_counting = 1; - } else { - param = replace; - } - - opt = strchr(param,':'); - - if(opt != NULL) { - name = apr_strtok(param,":",&value); - } else { - name = param; - } - - if(apr_table_get(ruleset->engine->variables, name) == NULL) { - if(target_list != NULL) - free(target_list); - if(replace != NULL) - free(replace); - if(msr) { - msr_log(msr, 9, "Error to update target - [%s] is not valid target", name); - } - return apr_psprintf(ruleset->mp, "Error to update target - [%s] is not valid target", name); - } - - name_len = strlen(name); - - if(value != NULL) - value_len = strlen(value); - - if(msr) { - msr_log(msr, 9, "Trying to replace by variable name [%s] value [%s]", name, value); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Trying to replace by variable name [%s] value [%s]", name, value); - } -#endif - - targets = (msre_var **)rule->targets->elts; - // TODO need a good way to remove the element from array, maybe change array by tables or rings - for (i = 0; i < rule->targets->nelts; i++) { - if((strlen(targets[i]->name) == strlen(name)) && - (strncasecmp(targets[i]->name,name,strlen(targets[i]->name)) == 0) && - (targets[i]->is_negated == is_negated) && - (targets[i]->is_counting == is_counting)) { - - if(value != NULL && targets[i]->param != NULL) { - if((strlen(targets[i]->param) == strlen(value)) && - strncasecmp(targets[i]->param,value,strlen(targets[i]->param)) == 0) { - memset(targets[i]->name,0,strlen(targets[i]->name)); - memset(targets[i]->param,0,strlen(targets[i]->param)); - targets[i]->is_counting = 0; - targets[i]->is_negated = 1; - match = 1; - } - } else if (value == NULL && targets[i]->param == NULL){ - memset(targets[i]->name,0,strlen(targets[i]->name)); - targets[i]->is_counting = 0; - targets[i]->is_negated = 1; - match = 1; - } else - continue; - - } - } - } - - p = apr_strtok(target_list, ",", &savedptr); - - while(p != NULL) { - if(replace != NULL) { - if(match == 1) { - rc = msre_parse_targets(ruleset, p, rule->targets, &my_error_msg); - if (rc < 0) { - if(msr) { - msr_log(msr, 9, "Error parsing rule targets to replace variable"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Error parsing rule targets to replace variable"); - } -#endif - goto end; - } - if(msr) { - msr_log(msr, 9, "Successfully replaced variable"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Successfully replaced variable"); - } -#endif - var_appended = 1; - - } else { - if(msr) { - msr_log(msr, 9, "Cannot find variable to replace"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Cannot find varibale to replace"); - } -#endif - goto end; - } - } else { - - target = strdup(p); - if(target == NULL) - return NULL; - - is_negated = is_counting = 0; - param = name = value = NULL; - - opt = strchr(target,'!'); - - if(opt != NULL) { - *opt = '\0'; - opt++; - param = opt; - is_negated = 1; - } else if ((opt = strchr(target,'&')) != NULL) { - *opt = '\0'; - opt++; - param = opt; - is_counting = 1; - } else { - param = target; - } - - opt = strchr(param,':'); - - if(opt != NULL) { - name = apr_strtok(param,":",&value); - } else { - name = param; - } - - if(apr_table_get(ruleset->engine->variables, name) == NULL) { - if(target_list != NULL) - free(target_list); - if(replace != NULL) - free(replace); - if(msr) { - msr_log(msr, 9, "Error to update target - [%s] is not valid target", name); - } - return apr_psprintf(ruleset->mp, "Error to update target - [%s] is not valid target", name); - } - - name_len = strlen(name); - - if(value != NULL) - value_len = strlen(value); - - if(msr) { - msr_log(msr, 9, "Trying to append variable name [%s] value [%s]", name, value); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Trying to append variable name [%s] value [%s]", name, value); - } -#endif - match = 0; - - targets = (msre_var **)rule->targets->elts; - for (i = 0; i < rule->targets->nelts; i++) { - if((strlen(targets[i]->name) == strlen(name)) && - (strncasecmp(targets[i]->name,name,strlen(targets[i]->name)) == 0) && - (targets[i]->is_negated == is_negated) && - (targets[i]->is_counting == is_counting)) { - - if(value != NULL && targets[i]->param != NULL) { - if((strlen(targets[i]->param) == strlen(value)) && - strncasecmp(targets[i]->param,value,strlen(targets[i]->param)) == 0) { - match = 1; - } - } else if (value == NULL && targets[i]->param == NULL){ - match = 1; - } else - continue; - - } - } - - if(target != NULL) { - free(target); - target = NULL; - } - - if(match == 0 ) { - rc = msre_parse_targets(ruleset, p, rule->targets, &my_error_msg); - if (rc < 0) { - if(msr) { - msr_log(msr, 9, "Error parsing rule targets to append variable"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Error parsing rule targets to append variable"); - } -#endif - goto end; - } - var_appended = 1; - } else { - if(msr) { - msr_log(msr, 9, "Skipping variable, already appended"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Skipping variable, already appended"); - } -#endif - } - } - - p = apr_strtok(NULL,",",&savedptr); - } - - if(var_appended == 1) { - current_targets = msre_generate_target_string(ruleset->mp, rule); - rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, current_targets, NULL, NULL); - rule->p1 = apr_pstrdup(ruleset->mp, current_targets); - if(msr) { - msr_log(msr, 9, "Successfully appended variable"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Successfully appended variable"); - } -#endif - } - } - -end: - if(target_list != NULL) { - free(target_list); - target_list = NULL; - } - if(replace != NULL) { - free(replace); - replace = NULL; - } - if(target != NULL) { - free(target); - target = NULL; - } - return NULL; -} - -int msre_ruleset_rule_matches_exception(msre_rule *rule, rule_exception *re) { - int match = 0; - - /* Only remove non-placeholder rules */ - if (rule->placeholder == RULE_PH_NONE) { - switch(re->type) { - case RULE_EXCEPTION_REMOVE_ID : - if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { - int ruleid = atoi(rule->actionset->id); - - if (rule_id_in_range(ruleid, re->param)) { - match = 1; - } - } - - break; - case RULE_EXCEPTION_REMOVE_MSG : - if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { - char *my_error_msg = NULL; - - int rc = msc_regexec(re->param_data, - rule->actionset->msg, strlen(rule->actionset->msg), - &my_error_msg); - if (rc >= 0) { - match = 1; - } - } - - break; - case RULE_EXCEPTION_REMOVE_TAG : - if ((rule->actionset != NULL)&&(apr_is_empty_table(rule->actionset->actions) == 0)) { - char *my_error_msg = NULL; - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - int act; - - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - for (act = 0; act < tarr->nelts; act++) { - msre_action *action = (msre_action *)telts[act].val; - if((action != NULL) && (action->metadata != NULL) && (strcmp("tag", action->metadata->name) == 0)) { - - int rc = msc_regexec(re->param_data, - action->param, strlen(action->param), - &my_error_msg); - if (rc >= 0) { - match = 1; - } - } - } - } - break; - } - } - return match; -} - - - -/** - * Remove actions with the same cardinality group from the actionset. - */ -static void msre_actionset_cardinality_fixup(msre_actionset *actionset, msre_action *action) { - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - int i; - - if ((actionset == NULL) || (action == NULL)) return; - - tarr = apr_table_elts(actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - for (i = 0; i < tarr->nelts; i++) { - msre_action *target = (msre_action *)telts[i].val; - if (target->metadata->cardinality_group == action->metadata->cardinality_group) { - - apr_table_unset(actionset->actions, target->metadata->name); - } - } -} - -static char *msre_generate_target_string(apr_pool_t *pool, msre_rule *rule) { - - char *target_str = NULL; - msre_var **targets = NULL; - int i = 0; - - targets = (msre_var **)rule->targets->elts; - - for (i = 0; i < rule->targets->nelts; i++) { - - if(targets[i]->name != NULL && strlen(targets[i]->name) > 0) { - target_str = apr_pstrcat(pool, - (target_str == NULL) ? "" : apr_psprintf(pool, "%s|", target_str), - (targets[i]->is_negated == 0) ? "" : "!", - (targets[i]->is_counting == 0) ? "" : "&", - (targets[i]->name == NULL) ? "" : targets[i]->name, - (targets[i]->param == NULL) ? "" : apr_psprintf(pool, ":%s", targets[i]->param), - NULL); - } - - } - - return target_str; -} - -/** - * Generate an action string from an actionset. - */ -static char *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset) { - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - char *actions = NULL; - int chain; - int i; - - if (actionset == NULL) return NULL; - - chain = ((actionset->rule != NOT_SET_P) && actionset->rule->chain_starter) ? 1 : 0; - - tarr = apr_table_elts(actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - for (i = 0; i < tarr->nelts; i++) { - msre_action *action = (msre_action *)telts[i].val; - int use_quotes = 0; - - if (chain) { - /* Skip some actions that are not used in a chain. */ - if ( (action->metadata->type == ACTION_DISRUPTIVE) - || (action->metadata->type == ACTION_METADATA) - || (strcmp("log", action->metadata->name) == 0) - || (strcmp("auditlog", action->metadata->name) == 0) - || (strcmp("nolog", action->metadata->name) == 0) - || (strcmp("noauditlog", action->metadata->name) == 0) - || (strcmp("severity", action->metadata->name) == 0) - || (strcmp("ver", action->metadata->name) == 0) - || (strcmp("maturity", action->metadata->name) == 0) - || (strcmp("accuracy", action->metadata->name) == 0) - || (strcmp("tag", action->metadata->name) == 0) - || (strcmp("phase", action->metadata->name) == 0)) - { - continue; - } - } - - /* Check if we need any quotes */ - if (action->param != NULL) { - int j; - for(j = 0; action->param[j] != '\0'; j++) { - if (isspace(action->param[j])) { - use_quotes = 1; - break; - } - } - if (j == 0) use_quotes = 1; - } - - actions = apr_pstrcat(pool, - (actions == NULL) ? "" : actions, - (actions == NULL) ? "" : ",", - action->metadata->name, - (action->param == NULL) ? "" : ":", - (use_quotes) ? "'" : "", - (action->param == NULL) ? "" : action->param, - (use_quotes) ? "'" : "", - NULL); - } - - return actions; -} - -/** - * Add an action to an actionset. - */ -static void msre_actionset_action_add(msre_actionset *actionset, msre_action *action) -{ - msre_action *add_action = action; - - if ((actionset == NULL)) return; - - /** - * The "block" action is just a placeholder for the parent action. - */ - if ((actionset->parent_intercept_action_rec != NULL) && (actionset->parent_intercept_action_rec != NOT_SET_P) && (strcmp("block", action->metadata->name) == 0) && (strcmp("block", action->metadata->name) == 0)) { - /* revert back to parent */ - actionset->intercept_action = actionset->parent_intercept_action; - add_action = actionset->parent_intercept_action_rec; - } - - if ((add_action == NULL)) return; - - if (add_action->metadata->cardinality_group != ACTION_CGROUP_NONE) { - msre_actionset_cardinality_fixup(actionset, add_action); - } - - if (add_action->metadata->cardinality == ACTION_CARDINALITY_ONE) { - /* One action per actionlist. */ - apr_table_setn(actionset->actions, add_action->metadata->name, (void *)add_action); - } else { - /* Multiple actions per actionlist. */ - apr_table_addn(actionset->actions, add_action->metadata->name, (void *)add_action); - } -} - -/** - * Creates msre_var instances (rule variables) out of the - * given text string and places them into the supplied table. - */ -static apr_status_t msre_parse_targets(msre_ruleset *ruleset, const char *text, - apr_array_header_t *arr, char **error_msg) -{ - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - apr_table_t *vartable; - unsigned int count = 0; - apr_status_t rc; - msre_var *var; - int i; - - if (text == NULL) return -1; - - /* Extract name & value pairs first */ - vartable = apr_table_make(ruleset->mp, 10); - if (vartable == NULL) return -1; - rc = msre_parse_generic(ruleset->mp, text, vartable, error_msg); - if (rc < 0) return rc; - - /* Loop through the table and create variables */ - tarr = apr_table_elts(vartable); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - var = msre_create_var(ruleset, telts[i].key, telts[i].val, NULL, error_msg); - if (var == NULL) return -1; - *(msre_var **)apr_array_push(arr) = var; - count++; - } - - return count; -} - -/** - * Creates msre_action instances by parsing the given string, placing - * them into the supplied array. - */ -static apr_status_t msre_parse_actions(msre_engine *engine, msre_actionset *actionset, - const char *text, char **error_msg) -{ - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - apr_table_t *vartable; - unsigned int count = 0; - apr_status_t rc; - msre_action *action; - int i; - - if (text == NULL) return -1; - - /* Extract name & value pairs first */ - vartable = apr_table_make(engine->mp, 10); - if (vartable == NULL) return -1; - rc = msre_parse_generic(engine->mp, text, vartable, error_msg); - if (rc < 0) return rc; - - /* Loop through the table and create actions */ - tarr = apr_table_elts(vartable); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - /* Create action. */ - action = msre_create_action(engine, telts[i].key, telts[i].val, error_msg); - if (action == NULL) return -1; - - /* Initialise action (option). */ - if (action->metadata->init != NULL) { - action->metadata->init(engine, actionset, action); - } - - msre_actionset_action_add(actionset, action); - - count++; - } - - return count; -} - -/** - * Locates variable metadata given the variable name. - */ -msre_var_metadata *msre_resolve_var(msre_engine *engine, const char *name) -{ - return (msre_var_metadata *)apr_table_get(engine->variables, name); -} - -/** - * Locates action metadata given the action name. - */ -static msre_action_metadata *msre_resolve_action(msre_engine *engine, const char *name) -{ - return (msre_action_metadata *)apr_table_get(engine->actions, name); -} - -/** - * Creates a new variable instance given the variable name - * and an (optional) parameter. - */ -msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char *name, const char *param, - modsec_rec *msr, char **error_msg) -{ - const char *varparam = param; - msre_var *var = apr_pcalloc(pool, sizeof(msre_var)); - if (var == NULL) return NULL; - - if (error_msg == NULL) return NULL; - *error_msg = NULL; - - /* Handle negation and member counting */ - if (name[0] == '!') { - var->is_negated = 1; - var->name = (char *)name + 1; - } - else - if (name[0] == '&') { - var->is_counting = 1; - var->name = (char *)name + 1; - } - else { - var->name = (char *)name; - } - - /* Treat HTTP_* targets as an alias for REQUEST_HEADERS:* */ - if ( (var->name != NULL) - && (strlen(var->name) > 5) - && (strncmp("HTTP_", var->name, 5) == 0)) - { - const char *oldname = var->name; - var->name = apr_pstrdup(pool, "REQUEST_HEADERS"); - varparam = apr_pstrdup(pool, oldname + 5); - } - - - /* Resolve variable */ - var->metadata = msre_resolve_var(engine, var->name); - if (var->metadata == NULL) { - *error_msg = apr_psprintf(engine->mp, "Unknown variable: %s", name); - return NULL; - } - - /* The counting operator "&" can only be used against collections. */ - if (var->is_counting) { - if (var->metadata->type == VAR_SIMPLE) { - *error_msg = apr_psprintf(engine->mp, "The & modificator does not apply to " - "non-collection variables."); - return NULL; - } - } - - /* Check the parameter. */ - if (varparam == NULL) { - if (var->metadata->argc_min > 0) { - *error_msg = apr_psprintf(engine->mp, "Missing mandatory parameter for variable %s.", - name); - return NULL; - } - } else { /* Parameter present */ - - /* Do we allow a parameter? */ - if (var->metadata->argc_max == 0) { - *error_msg = apr_psprintf(engine->mp, "Variable %s does not support parameters.", - name); - return NULL; - } - - var->param = (char *)varparam; - - } - - return var; -} - -/** - * Create a new variable object from the provided name and value. - * - * NOTE: this allocates out of the global pool and should not be used - * per-request - */ -static msre_var *msre_create_var(msre_ruleset *ruleset, const char *name, const char *param, - modsec_rec *msr, char **error_msg) -{ - msre_var *var = msre_create_var_ex(ruleset->engine->mp, ruleset->engine, name, param, msr, error_msg); - if (var == NULL) return NULL; - - /* Validate & initialise variable */ - if (var->metadata->validate != NULL) { - *error_msg = var->metadata->validate(ruleset, var); - if (*error_msg != NULL) { - return NULL; - } - } - - return var; -} - -/** - * Creates a new action instance given its name and an (optional) parameter. - */ -msre_action *msre_create_action(msre_engine *engine, const char *name, const char *param, - char **error_msg) -{ - msre_action *action = apr_pcalloc(engine->mp, sizeof(msre_action)); - if (action == NULL) return NULL; - - if (error_msg == NULL) return NULL; - *error_msg = NULL; - - /* Resolve action */ - action->metadata = msre_resolve_action(engine, name); - if (action->metadata == NULL) { - *error_msg = apr_psprintf(engine->mp, "Unknown action: %s", name); - return NULL; - } - - if (param == NULL) { /* Parameter not present */ - if (action->metadata->argc_min > 0) { - *error_msg = apr_psprintf(engine->mp, "Missing mandatory parameter for action %s", - name); - return NULL; - } - } else { /* Parameter present */ - - /* Should we allow the parameter? */ - if (action->metadata->argc_max == 0) { - *error_msg = apr_psprintf(engine->mp, "Extra parameter provided to action %s", name); - return NULL; - } - - /* Handle +/- modificators */ - if ((param[0] == '+')||(param[0] == '-')) { - if (action->metadata->allow_param_plusminus == 0) { - *error_msg = apr_psprintf(engine->mp, - "Action %s does not allow +/- modificators.", name); - return NULL; - } - else { /* Modificators allowed. */ - if (param[0] == '+') { - action->param = param + 1; - action->param_plusminus = POSITIVE_VALUE; - } else - if (param[0] == '-') { - action->param = param + 1; - action->param_plusminus = NEGATIVE_VALUE; - } - } - } else { - action->param = param; - } - - /* Validate parameter */ - if (action->metadata->validate != NULL) { - *error_msg = action->metadata->validate(engine, action); - if (*error_msg != NULL) return NULL; - } - } - - return action; -} - -/** - * Generic parser that is used as basis for target and action parsing. - * It breaks up the input string into name-parameter pairs and places - * them into the given table. - */ -int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, - char **error_msg) -{ - char *p = (char *)text; - int count = 0; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - count = 0; - while(*p != '\0') { - char *name = NULL, *value = NULL; - - /* ignore whitespace */ - while(isspace(*p)) p++; - if (*p == '\0') return count; - - /* we are at the beginning of the name */ - name = p; - while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; /* ENH replace with isvarnamechar() */ - - /* get the name */ - name = apr_pstrmemdup(mp, name, p - name); - - if (*p != ':') { /* we don't have a parameter */ - /* add to the table with no value */ - apr_table_addn(vartable, name, NULL); - count++; - - /* go over any whitespace present */ - while(isspace(*p)) p++; - - /* we're done */ - if (*p == '\0') { - return count; - } - - /* skip over the separator character and continue */ - if ((*p == ',')||(*p == '|')) { - p++; - continue; - } - - *error_msg = apr_psprintf(mp, "Unexpected character at position %d: %s", - (int)(p - text), text); - return -1; - } - - /* we have a parameter */ - - p++; /* move over the colon */ - - /* we'll allow empty values */ - if (*p == '\0') { - apr_table_addn(vartable, name, NULL); - count++; - return count; - } - - if ((*p == ',')||(*p == '|')) { - apr_table_addn(vartable, name, NULL); - count++; - /* move over the separator char and continue */ - p++; - continue; - } - - /* we really have a parameter */ - - if (*p == '\'') { /* quoted value */ - char *d = NULL; - - p++; /* go over the openning quote */ - value = d = strdup(p); - if (d == NULL) return -1; - - for(;;) { - if (*p == '\0') { - *error_msg = apr_psprintf(mp, "Missing closing quote at position %d: %s", - (int)(p - text), text); - free(value); - return -1; - } else - if (*p == '\\') { - if ( (*(p + 1) == '\0') || ((*(p + 1) != '\'')&&(*(p + 1) != '\\')) ) { - *error_msg = apr_psprintf(mp, "Invalid quoted pair at position %d: %s", - (int)(p - text), text); - free(value); - return -1; - } - p++; - *(d++) = *(p++); - } else - if (*p == '\'') { - *d = '\0'; - p++; - break; - } - else { - *(d++) = *(p++); - } - } - - d = value; - value = apr_pstrdup(mp, d); - free(d); - } else { /* non-quoted value */ - value = p; - while((*p != '\0')&&(*p != ',')&&(*p != '|')&&(!isspace(*p))) p++; - value = apr_pstrmemdup(mp, value, p - value); - } - - /* add to table */ - apr_table_addn(vartable, name, value); - count++; - - /* move to the first character of the next name-value pair */ - while(isspace(*p)||(*p == ',')||(*p == '|')) p++; - } - - return count; -} - - -/* -- Actionset functions -------------------------------------------------- */ - -/** - * Creates an actionset instance and (as an option) populates it by - * parsing the given string which contains a list of actions. - */ -msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, - char **error_msg) -{ - msre_actionset *actionset = (msre_actionset *)apr_pcalloc(engine->mp, - sizeof(msre_actionset)); - if (actionset == NULL) return NULL; - - actionset->actions = apr_table_make(engine->mp, 25); - if (actionset->actions == NULL) return NULL; - - /* Metadata */ - actionset->id = NOT_SET_P; - actionset->rev = NOT_SET_P; - actionset->msg = NOT_SET_P; - actionset->version = NOT_SET_P; - actionset->logdata = NOT_SET_P; - actionset->phase = NOT_SET; - actionset->severity = -1; - actionset->accuracy = -1; - actionset->maturity = -1; - actionset->rule = NOT_SET_P; - actionset->arg_max = -1; - actionset->arg_min = -1; - - /* Flow */ - actionset->is_chained = NOT_SET; - actionset->skip_count = NOT_SET; - actionset->skip_after = NOT_SET_P; - - /* Disruptive */ - actionset->parent_intercept_action_rec = NOT_SET_P; - actionset->intercept_action_rec = NOT_SET_P; - actionset->parent_intercept_action = NOT_SET; - actionset->intercept_action = NOT_SET; - actionset->intercept_uri = NOT_SET_P; - actionset->intercept_status = NOT_SET; - actionset->intercept_pause = NOT_SET_P; - - /* Other */ - actionset->auditlog = NOT_SET; - actionset->log = NOT_SET; - - /* Parse the list of actions, if it's present */ - if (text != NULL) { - if (msre_parse_actions(engine, actionset, text, error_msg) < 0) { - return NULL; - } - } - - return actionset; -} - -/** - * Create a (shallow) copy of the supplied actionset. - */ -static msre_actionset *msre_actionset_copy(apr_pool_t *mp, msre_actionset *orig) { - msre_actionset *copy = NULL; - - if (orig == NULL) return NULL; - copy = (msre_actionset *)apr_pmemdup(mp, orig, sizeof(msre_actionset)); - if (copy == NULL) return NULL; - copy->actions = apr_table_copy(mp, orig->actions); - - return copy; -} - -/** - * Merges two actionsets into one. - */ -msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent, - msre_actionset *child, int inherit_by_default) -{ - msre_actionset *merged = NULL; - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - int i; - - if (inherit_by_default == 0) { - /* There is nothing to merge in this case. */ - return msre_actionset_copy(engine->mp, child); - } - - /* Start with a copy of the parent configuration. */ - merged = msre_actionset_copy(engine->mp, parent); - if (merged == NULL) return NULL; - - if (child == NULL) { - /* The child actionset does not exist, hence - * go with the parent one. - */ - return merged; - } - - /* First merge the hard-coded stuff. */ - - /* Metadata */ - if (child->id != NOT_SET_P) merged->id = child->id; - if (child->rev != NOT_SET_P) merged->rev = child->rev; - if (child->msg != NOT_SET_P) merged->msg = child->msg; - if (child->version != NOT_SET_P) merged->version = child->version; - if (child->logdata != NOT_SET_P) merged->logdata = child->logdata; - if (child->severity != NOT_SET) merged->severity = child->severity; - if (child->accuracy != NOT_SET) merged->accuracy = child->accuracy; - if (child->maturity != NOT_SET) merged->maturity = child->maturity; - if (child->phase != NOT_SET) merged->phase = child->phase; - if (child->rule != NOT_SET_P) merged->rule = child->rule; - if (child->arg_min != NOT_SET) merged->arg_min = child->arg_min; - if (child->arg_max != NOT_SET) merged->arg_max = child->arg_max; - - /* Flow */ - merged->is_chained = child->is_chained; - if (child->skip_count != NOT_SET) merged->skip_count = child->skip_count; - if (child->skip_after != NOT_SET_P) merged->skip_after = child->skip_after; - - /* Disruptive */ - if (child->intercept_action != NOT_SET) { - merged->intercept_action_rec = child->intercept_action_rec; - merged->intercept_action = child->intercept_action; - merged->intercept_uri = child->intercept_uri; - } - - if (child->intercept_status != NOT_SET) merged->intercept_status = child->intercept_status; - if (child->intercept_pause != NOT_SET_P) merged->intercept_pause = child->intercept_pause; - - /* Other */ - if (child->auditlog != NOT_SET) merged->auditlog = child->auditlog; - if (child->log != NOT_SET) merged->log = child->log; - - - /* Now merge the actions. */ - - tarr = apr_table_elts(child->actions); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_actionset_action_add(merged, (msre_action *)telts[i].val); - } - - return merged; -} - -/** - * Creates an actionset that contains a default list of actions. - */ -msre_actionset *msre_actionset_create_default(msre_engine *engine) { - char *my_error_msg = NULL; - return msre_actionset_create(engine, - "phase:2,log,auditlog,pass", - &my_error_msg); -} - -/** - * Sets the default values for the hard-coded actionset configuration. - */ -void msre_actionset_set_defaults(msre_actionset *actionset) { - /* Metadata */ - if (actionset->id == NOT_SET_P) actionset->id = NULL; - if (actionset->rev == NOT_SET_P) actionset->rev = NULL; - if (actionset->msg == NOT_SET_P) actionset->msg = NULL; - if (actionset->version == NOT_SET_P) actionset->version = NULL; - if (actionset->logdata == NOT_SET_P) actionset->logdata = NULL; - if (actionset->phase == NOT_SET) actionset->phase = 2; - if (actionset->severity == -1) {} /* leave at -1 */ - if (actionset->accuracy == -1) {} /* leave at -1 */ - if (actionset->maturity == -1) {} /* leave at -1 */ - if (actionset->rule == NOT_SET_P) actionset->rule = NULL; - if (actionset->arg_max == NOT_SET) actionset->arg_max = -1; - if (actionset->arg_min == NOT_SET) actionset->arg_min = -1; - - /* Flow */ - if (actionset->is_chained == NOT_SET) actionset->is_chained = 0; - if (actionset->skip_count == NOT_SET) actionset->skip_count = 0; - if (actionset->skip_after == NOT_SET_P) actionset->skip_after = NULL; - - /* Disruptive */ - if (actionset->parent_intercept_action_rec == NOT_SET_P) actionset->parent_intercept_action_rec = NULL; - if (actionset->intercept_action_rec == NOT_SET_P) actionset->intercept_action_rec = NULL; - if (actionset->parent_intercept_action == NOT_SET) actionset->parent_intercept_action = ACTION_NONE; - if (actionset->intercept_action == NOT_SET) actionset->intercept_action = ACTION_NONE; - if (actionset->intercept_uri == NOT_SET_P) actionset->intercept_uri = NULL; - if (actionset->intercept_status == NOT_SET) actionset->intercept_status = 403; - if (actionset->intercept_pause == NOT_SET_P) actionset->intercept_pause = NULL; - - /* Other */ - if (actionset->auditlog == NOT_SET) actionset->auditlog = 1; - if (actionset->log == NOT_SET) actionset->log = 1; -} - -/* -- Engine functions ----------------------------------------------------- */ - -/** - * Creates a new engine instance. - */ -msre_engine *msre_engine_create(apr_pool_t *parent_pool) { - msre_engine *engine; - apr_pool_t *mp; - - /* Create new memory pool */ - if (apr_pool_create(&mp, parent_pool) != APR_SUCCESS) return NULL; - - /* Init fields */ - engine = apr_pcalloc(mp, sizeof(msre_engine)); - if (engine == NULL) return NULL; - engine->mp = mp; - engine->tfns = apr_table_make(mp, 50); - if (engine->tfns == NULL) return NULL; - engine->operators = apr_table_make(mp, 25); - if (engine->operators == NULL) return NULL; - engine->variables = apr_table_make(mp, 100); - if (engine->variables == NULL) return NULL; - engine->actions = apr_table_make(mp, 50); - if (engine->actions == NULL) return NULL; - engine->reqbody_processors = apr_table_make(mp, 10); - if (engine->reqbody_processors == NULL) return NULL; - - return engine; -} - - -/* -- Recipe functions ----------------------------------------------------- */ - -#define NEXT_CHAIN 1 -#define NEXT_RULE 2 -#define SKIP_RULES 3 - - - -/** - * Default implementation of the ruleset phase processing; it processes - * the rules in the ruleset attached to the currently active - * transaction phase. - */ -#if defined(PERFORMANCE_MEASUREMENT) - -#define PERFORMANCE_MEASUREMENT_LOOP 5000 - -static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr); - -apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { - apr_array_header_t *arr = NULL; - msre_rule **rules = NULL; - apr_status_t rc; - int i; - - switch (msr->phase) { - case PHASE_REQUEST_HEADERS : - arr = ruleset->phase_request_headers; - break; - case PHASE_REQUEST_BODY : - arr = ruleset->phase_request_body; - break; - case PHASE_RESPONSE_HEADERS : - arr = ruleset->phase_response_headers; - break; - case PHASE_RESPONSE_BODY : - arr = ruleset->phase_response_body; - break; - case PHASE_LOGGING : - arr = ruleset->phase_logging; - break; - default : - msr_log(msr, 1, "Internal Error: Invalid phase %d", msr->phase); - return -1; - } - - rules = (msre_rule **)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msre_rule *rule = rules[i]; - rule->execution_time = 0; - } - - for (i = 0; i < PERFORMANCE_MEASUREMENT_LOOP; i++) { - rc = msre_ruleset_process_phase_(ruleset, msr); - } - - msr_log(msr, 1, "Phase %d", msr->phase); - - rules = (msre_rule **)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msre_rule *rule = rules[i]; - - /* Ignore markers, which are never processed. */ - if (rule->placeholder == RULE_PH_MARKER) continue; - - msr_log(msr, 1, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"]: %u usec", rule, - ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num, - (rule->execution_time / PERFORMANCE_MEASUREMENT_LOOP)); - } - - return rc; -} - -static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr) { -#else - apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { -#endif - apr_array_header_t *arr = NULL; - msre_rule **rules; - apr_status_t rc; - const char *skip_after = NULL; - msre_rule *last_rule = NULL; - msre_rule *rule_starter = NULL; - int i, mode, skip, skipped, saw_starter; - - /* First determine which set of rules we need to use. */ - switch (msr->phase) { - case PHASE_REQUEST_HEADERS : - arr = ruleset->phase_request_headers; - break; - case PHASE_REQUEST_BODY : - arr = ruleset->phase_request_body; - break; - case PHASE_RESPONSE_HEADERS : - arr = ruleset->phase_response_headers; - break; - case PHASE_RESPONSE_BODY : - arr = ruleset->phase_response_body; - break; - case PHASE_LOGGING : - arr = ruleset->phase_logging; - break; - default : - msr_log(msr, 1, "Internal Error: Invalid phase %d", msr->phase); - return -1; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "This phase consists of %d rule(s).", arr->nelts); - } - - apr_table_clear(msr->matched_vars); - - /* Loop through the rules in the selected set. */ - skip = 0; - skipped = 0; - saw_starter = 0; - mode = NEXT_RULE; - rules = (msre_rule **)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msre_rule *rule = rules[i]; -#if defined(PERFORMANCE_MEASUREMENT) - apr_time_t time1 = 0; -#endif - - /* Reset the rule interception flag */ - msr->rule_was_intercepted = 0; - - /* SKIP_RULES is used to skip all rules until we hit a placeholder - * with the specified rule ID and then resume execution after that. - */ - if (mode == SKIP_RULES) { - /* Go to the next rule if we have not yet hit the skip_after ID */ - - if ((rule->placeholder == RULE_PH_NONE) || (rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { - - if(i-1 >=0) - last_rule = rules[i-1]; - else - last_rule = rules[0]; - - if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained && (saw_starter == 1)) { - mode = NEXT_RULE; - skipped = 1; - --i; - } else { - mode = SKIP_RULES; - skipped = 0; - saw_starter = 0; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Current rule is id=\"%s\" [chained %d] is trying to find the SecMarker=\"%s\" [stater %d]",rule->actionset->id,last_rule->actionset->is_chained,skip_after,saw_starter); - } - - } - - continue; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Found rule %pp id=\"%s\".", rule, skip_after); - } - - /* Go to the rule *after* this one to continue execution. */ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Continuing execution after rule id=\"%s\".", skip_after); - } - - saw_starter = 0; - skipped = 0; - skip_after = NULL; - mode = NEXT_RULE; - apr_table_clear(msr->matched_vars); - continue; - } - - /* Skip any rule marked as a placeholder */ - if (rule->placeholder != RULE_PH_NONE) { - continue; - } - - /* NEXT_CHAIN is used when one of the rules in a chain - * fails to match and then we need to skip the remaining - * rules in that chain in order to get to the next - * rule that can execute. - */ - if (mode == NEXT_CHAIN) { - if (rule->actionset->is_chained == 0) { - mode = NEXT_RULE; - } - - /* Go to the next rule. */ - apr_table_clear(msr->matched_vars); - continue; - } - - /* If we are here that means the mode is NEXT_RULE, which - * then means we have done processing any chains. However, - * if the "skip" parameter is set we need to skip over. - */ - if ((mode == NEXT_RULE)&&(skip > 0)) { - /* Decrement the skip counter by one. */ - skip--; - - /* If the current rule is part of a chain then - * we need to skip over the entire chain. Thus - * we change the mode to NEXT_CHAIN. The skip - * counter will not decrement as we are moving - * over the rules belonging to the chain. - */ - if (rule->actionset->is_chained) { - mode = NEXT_CHAIN; - } - - /* Go to the next rule. */ - apr_table_clear(msr->matched_vars); - continue; - } - - /* Check if this rule was removed at runtime */ - if (((rule->actionset->id !=NULL) && !apr_is_empty_array(msr->removed_rules)) || - (apr_is_empty_array(msr->removed_rules_tag)==0) || (apr_is_empty_array(msr->removed_rules_msg)==0)) { - int j, act, rc; - int do_process = 1; - const char *range = NULL; - rule_exception *re = NULL; - char *my_error_msg; - const apr_array_header_t *tag_tarr = NULL; - const apr_table_entry_t *tag_telts = NULL; - - for(j = 0; j < msr->removed_rules_msg->nelts; j++) { - re = ((rule_exception **)msr->removed_rules_msg->elts)[j]; - - if(rule->actionset->msg !=NULL) { - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Checking removal of rule msg=\"%s\" against: %s", rule->actionset->msg, re->param); - } - - rc = msc_regexec(re->param_data, - rule->actionset->msg, strlen(rule->actionset->msg), - &my_error_msg); - if (rc >= 0) { - do_process = 0; - break; - } - } - } - - for(j = 0; j < msr->removed_rules->nelts; j++) { - range = ((const char**)msr->removed_rules->elts)[j]; - - if(rule->actionset->id !=NULL) { - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Checking removal of rule id=\"%s\" against: %s", rule->actionset->id, range); - } - - if (rule_id_in_range(atoi(rule->actionset->id), range)) { - do_process = 0; - break; - } - } - } - - tag_tarr = apr_table_elts(rule->actionset->actions); - tag_telts = (const apr_table_entry_t*)tag_tarr->elts; - - for (act = 0; act < tag_tarr->nelts; act++) { - msre_action *action = (msre_action *)tag_telts[act].val; - - if((action != NULL) && (action->metadata != NULL ) && strcmp("tag", action->metadata->name) == 0) { - - for(j = 0; j < msr->removed_rules_tag->nelts; j++) { - re = ((rule_exception **)msr->removed_rules_tag->elts)[j]; - - - if(action->param != NULL) { - /* Expand variables in the tag argument. */ - msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - - var->value = (char *)action->param; - var->value_len = strlen(action->param); - expand_macros(msr, var, NULL, msr->mp); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Checking removal of rule tag=\"%s\" against: %s", var->value, re->param); - } - - rc = msc_regexec(re->param_data, - var->value, strlen(var->value), - &my_error_msg); - if (rc >= 0) { - do_process = 0; - break; - } - - } - } - } - } - - /* Go to the next rule if this one has been removed. */ - if (do_process == 0) { - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "Not processing %srule id=\"%s\": " - "removed by ctl action", - rule->actionset->is_chained ? "chained " : "", - rule->actionset->id); - } - - /* Skip the whole chain, if this is a chained rule */ - if (rule->actionset->is_chained) { - mode = NEXT_CHAIN; - } - - skipped = 0; - saw_starter = 0; - apr_table_clear(msr->matched_vars); - continue; - } - } - - if(msr->txcfg->is_enabled == MODSEC_DISABLED) { - saw_starter = 0; - skipped = 0; - skip_after = NULL; - mode = NEXT_RULE; - apr_table_clear(msr->matched_vars); - continue; - } - - if (msr->txcfg->debuglog_level >= 4) { - apr_pool_t *p = msr->mp; - const char *fn = NULL; - const char *id = NULL; - const char *rev = NULL; - - if (rule->filename != NULL) { - fn = apr_psprintf(p, " [file \"%s\"] [line \"%d\"]", rule->filename, rule->line_num); - } - - if (rule->actionset != NULL && rule->actionset->id != NULL) { - id = apr_psprintf(p, " [id \"%s\"]", rule->actionset->id); - } - - if (rule->actionset != NULL && rule->actionset->rev != NULL) { - rev = apr_psprintf(p, " [rev \"%s\"]", rule->actionset->rev); - } - - msr_log(msr, 4, "Recipe: Invoking rule %pp;%s%s%s.", - rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : "")); - msr_log(msr, 5, "Rule %pp: %s", rule, rule->unparsed); - } - -#if defined(PERFORMANCE_MEASUREMENT) - time1 = apr_time_now(); -#endif - - rc = msre_rule_process(rule, msr); - -#if defined(PERFORMANCE_MEASUREMENT) - rule->execution_time += (apr_time_now() - time1); -#endif - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Rule returned %d.", rc); - } - - if (rc == RULE_NO_MATCH) { - if (rule->actionset->is_chained) { - /* If the current rule is part of a chain then - * we need to skip over all the rules in the chain. - */ - mode = NEXT_CHAIN; - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "No match, chained -> mode NEXT_CHAIN."); - } - } else { - /* This rule is not part of a chain so we simply - * move to the next rule. - */ - mode = NEXT_RULE; - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "No match, not chained -> mode NEXT_RULE."); - } - } - - apr_table_clear(msr->matched_vars); - skipped = 0; - saw_starter = 0; - } - 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 - * rule to know if it is a part of a chain and - * not intercept if it is. - */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Match, intercepted -> returning."); - } - - if(i-1 >= 0) - last_rule = rules[i-1]; - else - last_rule = rules[0]; - - if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained) { - - int st = 0; - - for(st=i;st>=0;st--) { - - rule_starter = rules[st]; - - if(rule_starter != NULL && rule_starter->chain_starter != NULL) { - if((msr != NULL) && (msr->intercept_actionset != NULL) && (rule_starter->actionset != NULL)) - msr->intercept_actionset->intercept_uri = rule_starter->actionset->intercept_uri; - break; - } - } - - } - - apr_table_clear(msr->matched_vars); - return 1; - } - - if (rule->actionset->skip_after != NULL) { - skip_after = rule->actionset->skip_after; - mode = SKIP_RULES; - saw_starter = 1; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Skipping after rule %pp id=\"%s\" -> mode SKIP_RULES.", rule, skip_after); - } - - continue; - } - - if(skipped == 1) { - mode = SKIP_RULES; - continue; - } - - /* We had a match but the transaction was not - * intercepted. In that case we proceed with the - * next rule... - */ - mode = NEXT_RULE; - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Match -> mode NEXT_RULE."); - } - - /* ...unless we need to skip, in which case we - * determine how many rules/chains we need to - * skip and configure the counter accordingly. - */ - if (rule->actionset->is_chained == 0) { - apr_table_clear(msr->matched_vars); - if (rule->chain_starter != NULL) { - if (rule->chain_starter->actionset->skip_count > 0) { - skip = rule->chain_starter->actionset->skip_count; - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping %d rules/chains (from a chain).", skip); - } - } - } - else if (rule->actionset->skip_count > 0) { - skip = rule->actionset->skip_count; - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping %d rules/chains.", skip); - } - } - } - } - else if (rc < 0) { - msr_log(msr, 1, "Rule processing failed."); - - - if (msr->txcfg->reqintercept_oe == 1) { - apr_table_clear(msr->matched_vars); - return -1; - } else { - if (rule->actionset->is_chained) { - /* If the current rule is part of a chain then - * we need to skip over all the rules in the chain. - */ - mode = NEXT_CHAIN; - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Ruled failed, chained -> mode NEXT_CHAIN."); - } - } else { - /* This rule is not part of a chain so we simply - * move to the next rule. - */ - mode = NEXT_RULE; - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Rule failed, not chained -> mode NEXT_RULE."); - } - } - - apr_table_clear(msr->matched_vars); - skipped = 0; - saw_starter = 0; - } - } - else { - msr_log(msr, 1, "Rule processing failed with unknown return code: %d.", rc); - apr_table_clear(msr->matched_vars); - return -1; - } - } - - /* ENH warn if chained rules are missing. */ - apr_table_clear(msr->matched_vars); - return 0; -} - -/** - * Creates a ruleset that will be handled by the default - * implementation. - */ -msre_ruleset *msre_ruleset_create(msre_engine *engine, apr_pool_t *mp) { - msre_ruleset *ruleset; - - ruleset = apr_pcalloc(mp, sizeof(msre_ruleset)); - if (ruleset == NULL) return NULL; - ruleset->mp = mp; - ruleset->engine = engine; - - ruleset->phase_request_headers = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); - ruleset->phase_request_body = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); - ruleset->phase_response_headers = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); - ruleset->phase_response_body = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); - ruleset->phase_logging = apr_array_make(ruleset->mp, 25, sizeof(const msre_rule *)); - - return ruleset; -} - -/** - * Adds one rule to the given phase of the ruleset. - */ -int msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase) { - apr_array_header_t *arr = NULL; - - switch (phase) { - case PHASE_REQUEST_HEADERS : - arr = ruleset->phase_request_headers; - break; - case PHASE_REQUEST_BODY : - arr = ruleset->phase_request_body; - break; - case PHASE_RESPONSE_HEADERS : - arr = ruleset->phase_response_headers; - break; - case PHASE_RESPONSE_BODY : - arr = ruleset->phase_response_body; - break; - case PHASE_LOGGING : - arr = ruleset->phase_logging; - break; - default : - return -1; - } - - /* ENH verify the rule's use of targets is consistent with - * the phase it selected to run at. - */ - - msre_actionset_set_defaults(rule->actionset); - rule->actionset->rule = rule; - - *(const msre_rule **)apr_array_push(arr) = rule; - - return 1; -} - -static msre_rule * msre_ruleset_fetch_phase_rule(const msre_ruleset *ruleset, const char *id, - const apr_array_header_t *phase_arr, int offset) -{ - msre_rule **rules = (msre_rule **)phase_arr->elts; - int i; - - for (i = 0; i < phase_arr->nelts; i++) { - msre_rule *rule = (msre_rule *)rules[i]; - - /* Rule with an action, not a sub-rule (chain) and a matching id */ - if ( (rule->actionset != NULL) - && (!rule->actionset->is_chained || !rule->chain_starter) - && (rule->actionset->id != NULL) - && (strcmp(rule->actionset->id, id) == 0)) - { - /* Return rule that matched unless it is a placeholder */ - if(offset == 0) { - return (rule->placeholder == RULE_PH_NONE) ? rule : NULL; - } - else { - if (i+offset < phase_arr->nelts) { - msre_rule *rule_off = (msre_rule *)rules[i+offset]; - return (rule_off->placeholder == RULE_PH_NONE) ? rule_off : NULL; - } - } - } - } - - return NULL; -} - -/** - * Fetches rule from the ruleset all rules that match the given exception. - */ -msre_rule * msre_ruleset_fetch_rule(msre_ruleset *ruleset, const char *id, int offset) { - msre_rule *rule = NULL; - - if (ruleset == NULL) return NULL; - - rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_request_headers, offset); - if (rule != NULL) return rule; - - rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_request_body, offset); - if (rule != NULL) return rule; - - rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_response_headers, offset); - if (rule != NULL) return rule; - - rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_response_body, offset); - if (rule != NULL) return rule; - - rule = msre_ruleset_fetch_phase_rule(ruleset, id, ruleset->phase_logging, offset); - - return rule; -} - -static int msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re, - apr_array_header_t *phase_arr) -{ - msre_rule **rules; - int i, j, mode, removed_count; - - j = 0; - mode = 0; - removed_count = 0; - rules = (msre_rule **)phase_arr->elts; - for (i = 0; i < phase_arr->nelts; i++) { - msre_rule *rule = (msre_rule *)rules[i]; - - if (mode == 0) { /* Looking for next rule. */ - int remove_rule = 0; - - /* Only remove non-placeholder rules */ - if (rule->placeholder == RULE_PH_NONE) { - switch(re->type) { - case RULE_EXCEPTION_REMOVE_ID : - if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { - int ruleid = atoi(rule->actionset->id); - - if (rule_id_in_range(ruleid, re->param)) { - remove_rule = 1; - } - } - - break; - - case RULE_EXCEPTION_REMOVE_MSG : - if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { - char *my_error_msg = NULL; - - int rc = msc_regexec(re->param_data, - rule->actionset->msg, strlen(rule->actionset->msg), - &my_error_msg); - if (rc >= 0) { - remove_rule = 1; - } - } - - break; - case RULE_EXCEPTION_REMOVE_TAG : - if ((rule->actionset != NULL)&&(apr_is_empty_table(rule->actionset->actions) == 0)) { - char *my_error_msg = NULL; - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - int act; - - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - for (act = 0; act < tarr->nelts; act++) { - msre_action *action = (msre_action *)telts[act].val; - if((action != NULL) && (action->metadata != NULL) && (strcmp("tag", action->metadata->name) == 0)) { - - int rc = msc_regexec(re->param_data, - action->param, strlen(action->param), - &my_error_msg); - if (rc >= 0) { - remove_rule = 1; - } - } - } - } - break; - } - } - - if (remove_rule) { - /* Do not increment j. */ - removed_count++; - if (rule->actionset->is_chained) mode = 2; /* Remove rules in this chain. */ - } else { - if (rule->actionset->is_chained) mode = 1; /* Keep rules in this chain. */ - rules[j++] = rules[i]; - } - } else { /* Handling rule that is part of a chain. */ - if (mode == 2) { /* We want to remove the rule. */ - /* Do not increment j. */ - removed_count++; - } else { - rules[j++] = rules[i]; - } - - if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0; - } - } - - /* Update the number of rules in the array. */ - phase_arr->nelts -= removed_count; - - return 0; -} - -/** - * Removes from the ruleset all rules that match the given exception. - */ -int msre_ruleset_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re) { - int count = 0; - - if (ruleset == NULL) return 0; - - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_request_headers); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_request_body); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_response_headers); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_response_body); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_logging); - - return count; -} - - -/* -- Rule functions ------------------------------------------------------- */ - -/** - * Returns the name of the supplied severity level. - */ -static const char *msre_format_severity(int severity) { - if ((severity >= 0)&&(severity <= 7)) { - return severities[severity]; - } - else { - return "(invalid value)"; - } -} - -/** - * Creates a string containing the metadata of the supplied rule. - */ -char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - char *id = ""; - char *rev = ""; - char *msg = ""; - char *logdata = ""; - char *severity = ""; - char *accuracy = ""; - char *maturity = ""; - char *version = ""; - char *tags = ""; - char *fn = ""; - int k; - - if (actionset == NULL) return ""; - - if ((actionset->rule != NULL) && (actionset->rule->filename != NULL)) { - fn = apr_psprintf(msr->mp, " [file \"%s\"] [line \"%d\"]", - actionset->rule->filename, actionset->rule->line_num); - } - if (actionset->id != NULL) { - id = apr_psprintf(msr->mp, " [id \"%s\"]", - log_escape(msr->mp, actionset->id)); - } - if (actionset->rev != NULL) { - rev = apr_psprintf(msr->mp, " [rev \"%s\"]", - log_escape(msr->mp, actionset->rev)); - } - if (actionset->msg != NULL) { - /* Expand variables in the message string. */ - msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->value = (char *)actionset->msg; - var->value_len = strlen(actionset->msg); - expand_macros(msr, var, NULL, msr->mp); - - msg = apr_psprintf(msr->mp, " [msg \"%s\"]", - log_escape_ex(msr->mp, var->value, var->value_len)); - } - if (actionset->logdata != NULL) { - /* Expand variables in the message string. */ - msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->value = (char *)actionset->logdata; - var->value_len = strlen(actionset->logdata); - expand_macros(msr, var, NULL, msr->mp); - - logdata = apr_psprintf(msr->mp, " [data \"%s", - log_escape_hex(msr->mp, (unsigned char *)var->value, var->value_len)); - logdata = apr_pstrcat(msr->mp, logdata, "\"]", NULL); - - /* If it is > 512 bytes, then truncate at 512 with ellipsis. - * NOTE: 512 actual data + 9 bytes of label = 521 - */ - if (strlen(logdata) > 521) { - logdata[517] = '.'; - logdata[518] = '.'; - logdata[519] = '.'; - logdata[520] = '"'; - logdata[521] = ']'; - logdata[522] = '\0'; - } - } - if ((actionset->severity >= 0)&&(actionset->severity <= 7)) { - severity = apr_psprintf(msr->mp, " [severity \"%s\"]", - msre_format_severity(actionset->severity)); - } - if (actionset->version != NULL) { - version = apr_psprintf(msr->mp, " [ver \"%s\"]", - log_escape(msr->mp, actionset->version)); - } - if (actionset->maturity >= 0) { - maturity = apr_psprintf(msr->mp, " [maturity \"%d\"]", - actionset->maturity); - } - if (actionset->accuracy >= 0) { - accuracy = apr_psprintf(msr->mp, " [accuracy \"%d\"]", - actionset->accuracy); - } - - /* Extract rule tags from the action list. */ - tarr = apr_table_elts(actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - for (k = 0; k < tarr->nelts; k++) { - msre_action *action = (msre_action *)telts[k].val; - if (strcmp(telts[k].key, "tag") == 0) { - /* Expand variables in the tag argument. */ - msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - - var->value = (char *)action->param; - var->value_len = strlen(action->param); - expand_macros(msr, var, NULL, msr->mp); - - tags = apr_psprintf(msr->mp, "%s [tag \"%s\"]", tags, - log_escape(msr->mp, var->value)); - } - } - - return apr_pstrcat(msr->mp, fn, id, rev, msg, logdata, severity, version, maturity, accuracy, tags, NULL); -} - -char * msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, const char *targets, - const char *args, const char *actions) -{ - char *unparsed = NULL; - const char *r_targets = targets; - const char *r_args = args; - const char *r_actions = actions; - - if (r_targets == NULL) { - r_targets = rule->p1; - } - if (r_args == NULL) { - r_args = apr_pstrcat(pool, (rule->op_negated ? "!" : ""), "@", rule->op_name, " ", rule->op_param, NULL); - } - if (r_actions == NULL) { - r_actions = msre_actionset_generate_action_string(pool, rule->actionset); - } - - switch (rule->type) { - case RULE_TYPE_NORMAL: - if (r_actions == NULL) { - unparsed = apr_psprintf(pool, "SecRule \"%s\" \"%s\"", - log_escape(pool, r_targets), log_escape(pool, r_args)); - } - else { - unparsed = apr_psprintf(pool, "SecRule \"%s\" \"%s\" \"%s\"", - log_escape(pool, r_targets), log_escape(pool, r_args), - log_escape(pool, r_actions)); - } - break; - case RULE_TYPE_ACTION: - unparsed = apr_psprintf(pool, "SecAction \"%s\"", - log_escape(pool, r_actions)); - break; - case RULE_TYPE_MARKER: - unparsed = apr_psprintf(pool, "SecMarker \"%s\"", rule->actionset->id); - break; -#if defined(WITH_LUA) - case RULE_TYPE_LUA: - /* SecRuleScript */ - if (r_actions == NULL) { - unparsed = apr_psprintf(pool, "SecRuleScript \"%s\"", r_args); - } - else { - unparsed = apr_psprintf(pool, "SecRuleScript \"%s\" \"%s\"", - r_args, log_escape(pool, r_actions)); - } - break; -#endif - } - - return unparsed; -} - -/** - * Assembles a new rule using the strings that contain a list - * of targets (variables), arguments, and actions. - */ -msre_rule *msre_rule_create(msre_ruleset *ruleset, int type, - const char *fn, int line, const char *targets, - const char *args, const char *actions, char **error_msg) -{ - msre_rule *rule; - char *my_error_msg; - const char *argsp; - int rc; - - if (error_msg == NULL) return NULL; - *error_msg = NULL; - - rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule)); - if (rule == NULL) return NULL; - - rule->type = type; - rule->ruleset = ruleset; - rule->targets = apr_array_make(ruleset->mp, 10, sizeof(const msre_var *)); - rule->p1 = apr_pstrdup(ruleset->mp, targets); - rule->filename = apr_pstrdup(ruleset->mp, fn); - rule->line_num = line; - - /* Parse targets */ - rc = msre_parse_targets(ruleset, targets, rule->targets, &my_error_msg); - if (rc < 0) { - *error_msg = apr_psprintf(ruleset->mp, "Error creating rule: %s", my_error_msg); - return NULL; - } - - /* Parse args */ - argsp = args; - - /* Is negation used? */ - if (*argsp == '!') { - rule->op_negated = 1; - argsp++; - while((isspace(*argsp))&&(*argsp != '\0')) argsp++; - } - - /* Is the operator explicitly selected? */ - if (*argsp != '@') { - /* Go with a regular expression. */ - rule->op_name = "rx"; - rule->op_param = argsp; - } else { - /* Explicitly selected operator. */ - char *p = (char *)(argsp + 1); - while((!isspace(*p))&&(*p != '\0')) p++; - rule->op_name = apr_pstrmemdup(ruleset->mp, argsp + 1, p - (argsp + 1)); - while(isspace(*p)) p++; /* skip over the whitespace at the end*/ - rule->op_param = p; /* IMP1 So we always have a parameter even when it's empty? */ - } - - /* Find the operator. */ - rule->op_metadata = msre_engine_op_resolve(ruleset->engine, rule->op_name); - if (rule->op_metadata == NULL) { - *error_msg = apr_psprintf(ruleset->mp, - "Error creating rule: Failed to resolve operator: %s", rule->op_name); - return NULL; - } - - /* Initialise & validate parameter */ - if (rule->op_metadata->param_init != NULL) { - if (rule->op_metadata->param_init(rule, &my_error_msg) <= 0) { - *error_msg = apr_psprintf(ruleset->mp, "Error creating rule: %s", my_error_msg); - return NULL; - } - } - - /* Parse actions */ - if (actions != NULL) { - /* Create per-rule actionset */ - rule->actionset = msre_actionset_create(ruleset->engine, actions, &my_error_msg); - if (rule->actionset == NULL) { - *error_msg = apr_psprintf(ruleset->mp, "Error parsing actions: %s", my_error_msg); - return NULL; - } - } - - /* Add the unparsed rule */ - rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, targets, args, NULL); - - return rule; -} - -#if defined(WITH_LUA) -/** - * - */ -msre_rule *msre_rule_lua_create(msre_ruleset *ruleset, - const char *fn, int line, const char *script_filename, - const char *actions, char **error_msg) -{ - msre_rule *rule; - char *my_error_msg; - - if (error_msg == NULL) return NULL; - *error_msg = NULL; - - rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule)); - if (rule == NULL) return NULL; - - rule->type = RULE_TYPE_LUA; - rule->ruleset = ruleset; - rule->filename = apr_pstrdup(ruleset->mp, fn); - rule->line_num = line; - - /* Compile script. */ - *error_msg = lua_compile(&rule->script, script_filename, ruleset->mp); - if (*error_msg != NULL) { - return NULL; - } - - /* Parse actions */ - if (actions != NULL) { - /* Create per-rule actionset */ - rule->actionset = msre_actionset_create(ruleset->engine, actions, &my_error_msg); - if (rule->actionset == NULL) { - *error_msg = apr_psprintf(ruleset->mp, "Error parsing actions: %s", my_error_msg); - return NULL; - } - } - - /* Add the unparsed rule */ - rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, script_filename, NULL); - - return rule; -} -#endif - -/** - * Perform non-disruptive actions associated with the provided actionset. - */ -static void msre_perform_nondisruptive_actions(modsec_rec *msr, msre_rule *rule, - msre_actionset *actionset, apr_pool_t *mptmp) -{ - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - int i; - - tarr = apr_table_elts(actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_action *action = (msre_action *)telts[i].val; - if (action->metadata->type == ACTION_NON_DISRUPTIVE) { - if (action->metadata->execute != NULL) { - action->metadata->execute(msr, mptmp, rule, action); - } - } - } -} - -/** - * Perform the disruptive actions associated with the given actionset. - */ -static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, - msre_actionset *actionset, apr_pool_t *mptmp, const char *message) -{ - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - int i; - - /* Execute the disruptive actions. Do note that this does - * not mean the request will be interrupted straight away. All - * disruptive actions need to do here is update the information - * that will be used to act later. - */ - tarr = apr_table_elts(actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_action *action = (msre_action *)telts[i].val; - if (action->metadata->type == ACTION_DISRUPTIVE) { - if (action->metadata->execute != NULL) { - action->metadata->execute(msr, mptmp, rule, action); - } - } - } - - /* If "noauditlog" was used do not mark the transaction relevant. */ - if (actionset->auditlog != 0) { - msr->is_relevant++; - } - - /* We only do stuff when in ONLINE mode. In all other - * cases we only emit warnings. - */ - if ((msr->phase == PHASE_LOGGING) - || (msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) - || (msr->modsecurity->processing_mode == MODSEC_OFFLINE) - || (actionset->intercept_action == ACTION_NONE)) - { - int log_level; - - /* If "nolog" was used log at a higher level to prevent an "alert". */ - if (actionset->log == 0) { - log_level = 4; - - /* But, if "auditlog" is enabled, then still add the message. */ - if (actionset->auditlog != 0) { - *(const char **)apr_array_push(msr->alerts) = msc_alert_message(msr, actionset, NULL, message); - } - - } - else { - log_level = 2; - } - - msc_alert(msr, log_level, actionset, "Warning.", 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 (log_level <= 3) { - msr->is_relevant--; - } - - return; - } - - /* Signal to the engine we need to intercept this - * transaction, and rememer the rule that caused it. - */ - msr->was_intercepted = 1; - msr->rule_was_intercepted = 1; - msr->intercept_phase = msr->phase; - msr->intercept_actionset = actionset; - msr->intercept_message = message; -} - -/** - * Invokes the rule operator against the given value. - */ -static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, - msre_actionset *acting_actionset, apr_pool_t *mptmp) -{ - apr_time_t time_before_op = 0; - char *my_error_msg = NULL; - const char *full_varname = NULL; - const apr_array_header_t *tarr = NULL; - const apr_table_entry_t *telts = NULL; - rule_exception *re = NULL; - char *exceptions = NULL; - int rc, i; - - /* determine the full var name if not already resolved - * - * NOTE: this can happen if the var does not match but it is - * being tested for non-existance as in: - * @REQUEST_HEADERS:Foo "@eq 0" - * @REQUEST_HEADERS:Foo "!@eq 1" - */ - if ((var->param != NULL) && (var->name != NULL) && (strchr(var->name,':') == NULL)) { - full_varname = apr_psprintf(mptmp, "%s%s:%s", - (var->is_counting ? "&" : ""), - var->name, var->param); - } - else if ((var->name != NULL) && var->is_counting && (*var->name != '&')) { - full_varname = apr_pstrcat(mptmp, "&", var->name, NULL); - } - else { - full_varname = var->name; - } - - tarr = apr_table_elts(msr->removed_targets); - telts = (const apr_table_entry_t*)tarr->elts; - - for (i = 0; i < tarr->nelts; i++) { - exceptions = (char *)telts[i].key; - re = (rule_exception *)telts[i].val; - - rc = msre_ruleset_rule_matches_exception(rule, re); - - if (rc > 0) { - rc = fetch_target_exception(rule, msr, var, exceptions); - - if(rc > 0) { - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Executing operator \"%s%s\" with param \"%s\" against %s skipped.", - (rule->op_negated ? "!" : ""), rule->op_name, - log_escape(msr->mp, rule->op_param), full_varname); - } - - return RULE_NO_MATCH; - - } - } - - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Executing operator \"%s%s\" with param \"%s\" against %s.", - (rule->op_negated ? "!" : ""), rule->op_name, - log_escape(msr->mp, rule->op_param), full_varname); - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Target value: \"%s\"", log_escape_nq_ex(msr->mp, var->value, - var->value_len)); - } - -#if defined(PERFORMANCE_MEASUREMENT) - time_before_op = apr_time_now(); -#else - if (msr->txcfg->debuglog_level >= 4 || msr->txcfg->max_rule_time > 0) { - time_before_op = apr_time_now(); - } -#endif - - rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg); - -#if defined(PERFORMANCE_MEASUREMENT) - { - /* Record performance but do not log anything. */ - apr_time_t t1 = apr_time_now(); - rule->op_time += (t1 - time_before_op); - } - #else - if (msr->txcfg->debuglog_level >= 4) { - apr_time_t t1 = apr_time_now(); - msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_op)); - } - - if(msr->txcfg->max_rule_time > 0) { - apr_time_t t1 = apr_time_now(); - apr_time_t rule_time = 0; - const char *rt_time = NULL; - - if(rule->actionset->id != NULL) { - rt_time = apr_table_get(msr->perf_rules, rule->actionset->id); - if(rt_time == NULL) { - rt_time = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (t1 - time_before_op)); - rule_time = (apr_time_t)atoi(rt_time); - if(rule_time >= msr->txcfg->max_rule_time) - apr_table_setn(msr->perf_rules, rule->actionset->id, rt_time); - } else { - rule_time = (apr_time_t)atoi(rt_time); - rule_time += (t1 - time_before_op); - if(rule_time >= msr->txcfg->max_rule_time) { - rt_time = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, rule_time); - apr_table_setn(msr->perf_rules, rule->actionset->id, rt_time); - } - } - } - } -#endif - - if (rc < 0) { - msr_log(msr, 4, "Operator error: %s", my_error_msg); - return -1; - } - - if (((rc == 0)&&(rule->op_negated == 0))||((rc == 1)&&(rule->op_negated == 1))) { - /* No match, do nothing. */ - return RULE_NO_MATCH; - } - else { - /* Match. */ - if (rc == 0) { - char *op_param = log_escape(msr->mp, rule->op_param); - - /* Truncate op parameter. */ - if (strlen(op_param) > 252) { - op_param = apr_psprintf(msr->mp, "%.252s ...", op_param); - } - - /* Operator did not match so we need to provide a message. */ - my_error_msg = apr_psprintf(msr->mp, "Match of \"%s %s\" against \"%s\" required.", - log_escape(msr->mp, rule->op_name), op_param, - log_escape(msr->mp, full_varname)); - } - - /* Save the rules that match */ - *(const msre_rule **)apr_array_push(msr->matched_rules) = rule; - - /* Save the last matched var data */ - if(var != NULL && msr != NULL) { - msc_string *mvar = NULL; - - msr->matched_var->name = apr_pstrdup(msr->mp, var->name); - msr->matched_var->name_len = strlen(msr->matched_var->name); - msr->matched_var->value = apr_pmemdup(msr->mp, var->value, var->value_len); - msr->matched_var->value_len = var->value_len; - - mvar = apr_palloc(msr->mp, sizeof(msc_string)); - mvar->name = apr_pstrdup(msr->mp, var->name); - mvar->name_len = strlen(mvar->name); - mvar->value = apr_pmemdup(msr->mp, var->value, var->value_len); - mvar->value_len = var->value_len; - - apr_table_addn(msr->matched_vars, mvar->name, (void *)mvar); - - } - - /* Keep track of the highest severity matched so far */ - if ((acting_actionset->severity > 0) && (acting_actionset->severity < msr->highest_severity) - && !rule->actionset->is_chained) { - msr->highest_severity = acting_actionset->severity; - } - - - /* Perform non-disruptive actions. */ - msre_perform_nondisruptive_actions(msr, rule, rule->actionset, mptmp); - - /* Perform disruptive actions, but only if - * this rule is not part of a chain. - */ - if (rule->actionset->is_chained == 0) { - msre_perform_disruptive_actions(msr, rule, acting_actionset, mptmp, my_error_msg); - } - - return RULE_MATCH; - } -} - -/** - * Executes rule against the given transaction. - */ -static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - msre_actionset *acting_actionset = NULL; - msre_var **targets = NULL; - apr_pool_t *mptmp = msr->msc_rule_mptmp; - apr_table_t *tartab = NULL; - apr_table_t *vartab = NULL; - int i, rc = 0, match_count = 0; - int invocations = 0; - int multi_match = 0; - - /* Choose the correct metadata/disruptive action actionset. */ - acting_actionset = rule->actionset; - if (rule->chain_starter != NULL) { - acting_actionset = rule->chain_starter->actionset; - } - - /* Configure recursive matching. */ - if (apr_table_get(rule->actionset->actions, "multiMatch") != NULL) { - multi_match = 1; - } - - /* ENH: What is a good initial size? */ - tartab = apr_table_make(mptmp, 24); - if (tartab == NULL) return -1; - vartab = apr_table_make(mptmp, 24); - if (vartab == NULL) return -1; - - /* Expand variables to create a list of targets. */ - - targets = (msre_var **)rule->targets->elts; - for (i = 0; i < rule->targets->nelts; i++) { - int j, list_count; - - apr_table_clear(vartab); - - /* ENH Introduce a new variable hook that would allow the code - * behind the variable to return the size of the collection - * without having to generate the variables. - */ - - /* Expand individual variables first. */ - list_count = targets[i]->metadata->generate(msr, targets[i], rule, vartab, mptmp); - - if (targets[i]->is_counting) { - /* Count how many there are and just add the score to the target list. */ - msre_var *newvar = (msre_var *)apr_pmemdup(mptmp, targets[i], sizeof(msre_var)); - newvar->value = apr_psprintf(mptmp, "%d", list_count); - newvar->value_len = strlen(newvar->value); - apr_table_addn(tartab, newvar->name, (void *)newvar); - } else { - /* And either add them or remove from the final target list. */ - arr = apr_table_elts(vartab); - te = (apr_table_entry_t *)arr->elts; - for(j = 0; j < arr->nelts; j++) { - if (targets[i]->is_negated == 0) { - apr_table_addn(tartab, te[j].key, te[j].val); - } else { - apr_table_unset(tartab, te[j].key); - } - } - } - } - - /* Log the target variable expansion */ - if (msr->txcfg->debuglog_level >= 4) { - const char *expnames = NULL; - - arr = apr_table_elts(tartab); - if (arr->nelts > 1) { - te = (apr_table_entry_t *)arr->elts; - expnames = apr_pstrdup(mptmp, ((msre_var *)te[0].val)->name); - for(i = 1; i < arr->nelts; i++) { - expnames = apr_psprintf(mptmp, "%s|%s", expnames, ((msre_var *)te[i].val)->name); - } - if (strcmp(rule->p1, expnames) != 0) { - msr_log(msr, 4, "Expanded \"%s\" to \"%s\".", rule->p1, expnames); - } - } - } - - /* Loop through targets on the final target list, - * perform transformations as necessary, and invoke - * the operator. - */ - - arr = apr_table_elts(tartab); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int changed; - int usecache = 0; - apr_table_t *cachetab = NULL; - apr_time_t time_before_trans = 0; - msre_var *var; - - /* Take one target. */ - var = (msre_var *)te[i].val; - - /* Is this var cacheable? */ - if (msr->txcfg->cache_trans != MODSEC_CACHE_DISABLED) { - usecache = 1; - - /* Counting vars are not cacheable due to them being created - * in a local per-rule pool. - */ - if (var->is_counting) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: Disabled - &%s is dynamic", var->name); - } - - usecache = 0; - } - /* Only cache if if the variable is available in this phase */ - else if (msr->phase < var->metadata->availability) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: Disabled - %s is not yet available in phase %d (requires phase %d or later)", var->name, msr->phase, var->metadata->availability); - } - - usecache = 0; - } - /* check the cache options */ - else if (var->value_len < msr->txcfg->cache_trans_min) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: Disabled - %s value length=%u, smaller than minlen=%" APR_SIZE_T_FMT, var->name, var->value_len, msr->txcfg->cache_trans_min); - } - - usecache = 0; - } - else if ((msr->txcfg->cache_trans_max != 0) && (var->value_len > msr->txcfg->cache_trans_max)) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: Disabled - %s value length=%u, larger than maxlen=%" APR_SIZE_T_FMT, var->name, var->value_len, msr->txcfg->cache_trans_max); - } - - usecache = 0; - } - - /* if cache is still enabled, check the VAR for cacheablity */ - if (usecache) { - if (var->metadata->is_cacheable == VAR_CACHE) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: Enabled"); - } - - #ifdef CACHE_DEBUG - msr_log(msr, 9, "CACHE: Fetching cache entry from hash=%pp: %pp=%s", msr->tcache, var, var->name); - #endif - - /* Fetch cache table for this target */ - cachetab = (apr_table_t *)apr_hash_get(msr->tcache, var->value, sizeof(var->value)); - - /* Create an empty cache table if this is the first time */ - #ifdef CACHE_DEBUG - if (cachetab) { - msr_log(msr, 9, "CACHE: Using cache table %pp", cachetab); - } - else - #else - if (cachetab == NULL) - #endif - { - /* NOTE: We use the pointer to the var value as a hash - * key as it is unique. This pointer *must* - * remain valid through the entire phase. If - * it does not, then we will not receive a cache - * hit and just wasted RAM. So, it is important - * that any such vars be marked as VAR_DONT_CACHE. - * - * ENH: Only use pointer for non-scalar vars - */ - cachetab = apr_table_make(msr->mp, 3); - apr_hash_set(msr->tcache, var->value, sizeof(var->value), cachetab); - - #ifdef CACHE_DEBUG - msr_log(msr, 9, "CACHE: Created a new cache table %pp for %pp", cachetab, var->value); - #endif - } - - } - else { - usecache = 0; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: %s transformations are not cacheable", var->name); - } - } - } - } - - #if defined(PERFORMANCE_MEASUREMENT) - time_before_trans = apr_time_now(); - #else - if (msr->txcfg->debuglog_level >= 4) { - time_before_trans = apr_time_now(); - } - #endif - - /* Transform target. */ - { - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - const char *tfnspath = NULL; - char *tfnskey = NULL; - int tfnscount = 0; - int last_cached_tfn = 0; - msre_cache_rec *crec = NULL; - msre_cache_rec *last_crec = NULL; - int k; - msre_action *action; - msre_tfn_metadata *metadata; - apr_table_t *normtab; - const char *lastvarval = NULL; - apr_size_t lastvarlen = 0; - int tfnchanged = 0; - - changed = 0; - normtab = apr_table_make(mptmp, 10); - if (normtab == NULL) return -1; - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - - /* Build the final list of transformation functions. */ - for (k = 0; k < tarr->nelts; k++) { - action = (msre_action *)telts[k].val; - - if (strcmp(telts[k].key, "t") == 0) { - if (strcmp(action->param, "none") == 0) { - apr_table_clear(normtab); - tfnspath = NULL; - tfnskey = NULL; - tfnscount = 0; - last_crec = NULL; - last_cached_tfn = 0; - continue; - } - - if (action->param_plusminus == NEGATIVE_VALUE) { - apr_table_unset(normtab, action->param); - } - else { - tfnscount++; - - apr_table_addn(normtab, action->param, (void *)action); - - /* Check the cache, saving the 'most complete' as a - * starting point - */ - if (usecache) { - tfnspath = apr_psprintf(mptmp, "%s%s%s", (tfnspath?tfnspath:""), (tfnspath?",":""), action->param); - tfnskey = apr_psprintf(mptmp, "%x;%s", tfnscount, tfnspath); - crec = (msre_cache_rec *)apr_table_get(cachetab, tfnskey); - - #ifdef CACHE_DEBUG - msr_log(msr, 9, "CACHE: %s %s cached=%d", var->name, tfnskey, (crec ? 1 : 0)); - #endif - - if (crec != NULL) { - last_crec = crec; - last_cached_tfn = tfnscount; - } - } - } - } - } - - /* If the last cached tfn is the last in the list - * then we can stop here and just execute the action immediatly - */ - if (usecache && !multi_match && - (crec != NULL) && (crec == last_crec)) - { - crec->hits++; - - if (crec->changed) { - var->value = apr_pmemdup(mptmp, crec->val, crec->val_len); - var->value_len = crec->val_len; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\" [fully cached hits=%d]", crec->changed, crec->path, - log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); - } - - #if defined(PERFORMANCE_MEASUREMENT) - { - apr_time_t t1 = apr_time_now(); - rule->trans_time += (t1 - time_before_trans); - } - #else - if (msr->txcfg->debuglog_level >= 4) { - apr_time_t t1 = apr_time_now(); - - msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", - (t1 - time_before_trans)); - } - #endif - - rc = execute_operator(var, rule, msr, acting_actionset, mptmp); - - if (rc < 0) { - return -1; - } - - if (rc == RULE_MATCH) { - match_count++; - - /* Return straight away if the transaction - * was intercepted - no need to process the remaining - * targets. - */ - if (msr->rule_was_intercepted) { - return RULE_MATCH; - } - } - - continue; /* next target */ - } - - - /* Perform transformations. */ - - tarr = apr_table_elts(normtab); - - /* Execute transformations in a loop. */ - - /* Start after the last known cached transformation if we can */ - if (!multi_match && (last_crec != NULL)) { - k = last_cached_tfn; - tfnspath = last_crec->path; - last_crec->hits++; - - if ((changed = last_crec->changed) > 0) { - var->value = last_crec->val; - var->value_len = last_crec->val_len; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\" [partially cached hits=%d]", last_crec->changed, - tfnspath, log_escape_nq_ex(mptmp, var->value, var->value_len), last_crec->hits); - } - } - else { - tfnspath = NULL; - k = 0; - } - - /* Make a copy of the value so that we can change it in-place. */ - if (tarr->nelts) { - var->value = apr_pstrmemdup(mptmp, var->value, var->value_len); - /* var->value_len remains the same */ - } - - telts = (const apr_table_entry_t*)tarr->elts; - for (; k < tarr->nelts; k++) { - char *rval = NULL; - long int rval_length = -1; - - /* In multi-match mode we execute the operator - * once at the beginning and then once every - * time the variable is changed by the transformation - * function. - */ - if (multi_match && (k == 0 || tfnchanged)) { - invocations++; - - #if defined(PERFORMANCE_MEASUREMENT) - { - apr_time_t t1 = apr_time_now(); - rule->trans_time += (t1 - time_before_trans); - } - #else - if (msr->txcfg->debuglog_level >= 4) { - apr_time_t t1 = apr_time_now(); - - msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", - (t1 - time_before_trans)); - } - #endif - - rc = execute_operator(var, rule, msr, acting_actionset, mptmp); - - if (rc < 0) { - return -1; - } - - if (rc == RULE_MATCH) { - match_count++; - - /* Return straight away if the transaction - * was intercepted - no need to process the remaining - * targets. - */ - if (msr->rule_was_intercepted) { - return RULE_MATCH; - } - } - } - - /* Perform one transformation. */ - action = (msre_action *)telts[k].val; - metadata = (msre_tfn_metadata *)action->param_data; - tfnchanged = metadata->execute(mptmp, - (unsigned char *)var->value, var->value_len, - &rval, &rval_length); - - if (tfnchanged < 0) { - return -1; - } - - if (tfnchanged) { - changed++; - } - - /* Use the new values */ - var->value = rval; - var->value_len = rval_length; - - /* Cache the transformation */ - if (usecache) { - int tfnsnum = k + 1; - - /* Generate the cache key */ - tfnspath = apr_psprintf(msr->mp, "%s%s%s", (tfnspath ? tfnspath : ""), - (tfnspath ? "," : ""), action->param); - tfnskey = apr_psprintf(msr->mp, "%x;%s", tfnsnum, tfnspath); - - if ((msr->txcfg->cache_trans_maxitems != 0) && - (msr->tcache_items >= msr->txcfg->cache_trans_maxitems)) - { - /* Warn only once if we attempt to go over the cache limit. */ - if (msr->tcache_items == msr->txcfg->cache_trans_maxitems) { - msr->tcache_items++; - msr_log(msr, 4, "CACHE: Disabled - phase=%d" - " maxitems=%" APR_SIZE_T_FMT - " limit reached.", - msr->phase, - msr->txcfg->cache_trans_maxitems); - } - } - else if (msr->txcfg->cache_trans_incremental || - (tfnsnum == tarr->nelts)) - { - /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ - crec = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); - if (crec == NULL) return -1; - - crec->hits = 0; - crec->changed = changed; - crec->num = k + 1; - crec->path = tfnspath; - - /* We want to cache a copy if it changed otherwise - * we just want to use a pointer to the last changed value. - */ - crec->val = (!lastvarval || tfnchanged) ? apr_pmemdup(msr->mp, var->value, var->value_len) : lastvarval; - crec->val_len = changed ? ((!lastvarval || tfnchanged) ? var->value_len : lastvarlen) : 0; - - /* Keep track of the last changed var value */ - if (tfnchanged) { - lastvarval = crec->val; - lastvarlen = crec->val_len; - } - - #ifdef CACHE_DEBUG - if (changed) { - msr_log(msr, 9, "CACHE: Caching %s=\"%s\" (%pp)", - tfnskey, - log_escape_nq_ex(mptmp, - crec->val, - crec->val_len), - var); - } - else { - msr_log(msr, 9, "CACHE: Caching %s= (%pp)", - tfnskey, - var); - } - #endif - - msr->tcache_items++; - - apr_table_setn(cachetab, tfnskey, (void *)crec); - } - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, metadata->name, - log_escape_nq_ex(mptmp, var->value, var->value_len)); - } - } - } - - /* Execute operator if multi-matching is not enabled, - * or if it is and we need to process the result of the - * last transformation. - */ - if (!multi_match || changed) { - invocations++; - - #if defined(PERFORMANCE_MEASUREMENT) - { - apr_time_t t1 = apr_time_now(); - rule->trans_time += (t1 - time_before_trans); - } - #else - if (msr->txcfg->debuglog_level >= 4) { - apr_time_t t1 = apr_time_now(); - - msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", - (t1 - time_before_trans)); - } - #endif - - rc = execute_operator(var, rule, msr, acting_actionset, mptmp); - - if (rc < 0) { - return -1; - } - - if (rc == RULE_MATCH) { - match_count++; - - /* Return straight away if the transaction - * was intercepted - no need to process the remaining - * targets. - */ - if (msr->rule_was_intercepted) { - return RULE_MATCH; - } - } - } - } - - - return (match_count ? RULE_MATCH : RULE_NO_MATCH); -} - -#if defined(WITH_LUA) -/** - * - */ -static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) { - msre_actionset *acting_actionset = NULL; - char *my_error_msg = NULL; - int rc; - - /* Choose the correct metadata/disruptive action actionset. */ - acting_actionset = rule->actionset; - if (rule->chain_starter != NULL) { - acting_actionset = rule->chain_starter->actionset; - } - - rc = lua_execute(rule->script, NULL, msr, rule, &my_error_msg); - if (rc < 0) { - msr_log(msr, 1, "%s", my_error_msg); - return -1; - } - - /* A non-NULL error message means the rule matched. */ - if (my_error_msg != NULL) { - /* Perform non-disruptive actions. */ - msre_perform_nondisruptive_actions(msr, rule, rule->actionset, msr->msc_rule_mptmp); - - /* Perform disruptive actions, but only if - * this rule is not part of a chain. - */ - if (rule->actionset->is_chained == 0) { - msre_perform_disruptive_actions(msr, rule, acting_actionset, msr->msc_rule_mptmp, my_error_msg); - } - } - - return rc; -} -#endif - -/** - * - */ -static apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { - /* Use a fresh memory sub-pool for processing each rule */ - if (msr->msc_rule_mptmp == NULL) { - if (apr_pool_create(&msr->msc_rule_mptmp, msr->mp) != APR_SUCCESS) { - return -1; - } - } else { - apr_pool_clear(msr->msc_rule_mptmp); - } - - #if defined(WITH_LUA) - if (rule->type == RULE_TYPE_LUA) { - return msre_rule_process_lua(rule, msr); - } - #endif - - return msre_rule_process_normal(rule, msr); -} - -/** - * Checks whether the given rule ID is in the given range. - */ -int rule_id_in_range(int ruleid, const char *range) { - char *p = NULL, *saveptr = NULL; - char *data = NULL; - - if (range == NULL) return 0; - data = strdup(range); - if (data == NULL) return 0; - - p = apr_strtok(data, ",", &saveptr); - while(p != NULL) { - char *s = strstr(p, "-"); - if (s == NULL) { - if (ruleid == atoi(p)) { - free(data); - return 1; - } - } else { - int start = atoi(p); - int end = atoi(s + 1); - if ((ruleid >= start)&&(ruleid <= end)) { - free(data); - return 1; - } - } - p = apr_strtok(NULL, ",", &saveptr); - } - - free(data); - - return 0; -} diff --git a/apache2/re.h b/apache2/re.h deleted file mode 100644 index dec78067ac..0000000000 --- a/apache2/re.h +++ /dev/null @@ -1,418 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#ifndef _MSC_RE_H_ -#define _MSC_RE_H_ - -#define ABSOLUTE_VALUE 0 -#define POSITIVE_VALUE 1 -#define NEGATIVE_VALUE 2 - -typedef struct msre_ipmatch msre_ipmatch; -typedef struct msre_engine msre_engine; -typedef struct msre_ruleset msre_ruleset; -typedef struct msre_ruleset_internal msre_ruleset_internal; -typedef struct msre_rule msre_rule; -typedef struct msre_var_metadata msre_var_metadata; -typedef struct msre_var msre_var; -typedef struct msre_op_metadata msre_op_metadata; -typedef struct msre_tfn_metadata msre_tfn_metadata; -typedef struct msre_actionset msre_actionset; -typedef struct msre_action_metadata msre_action_metadata; -typedef struct msre_action msre_action; -typedef struct msre_reqbody_processor_metadata msre_reqbody_processor_metadata; -typedef struct msre_cache_rec msre_cache_rec; - -#include "apr_general.h" -#include "apr_tables.h" -#include "modsecurity.h" -#include "msc_pcre.h" -#include "persist_dbm.h" -#include "apache2.h" -#include "http_config.h" - -#if defined(WITH_LUA) -#include "msc_lua.h" -#endif - -/* Actions, variables, functions and operator functions */ -char DSOLOCAL *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *rule, const char *p2, - const char *p3); - -int DSOLOCAL msre_ruleset_rule_matches_exception(msre_rule *rule, rule_exception *re); - -char DSOLOCAL *msre_ruleset_rule_update_target_matching_exception(modsec_rec *msr, msre_ruleset *ruleset, rule_exception *re, - const char *p2, const char *p3); - -char DSOLOCAL *msre_ruleset_phase_rule_update_target_matching_exception(modsec_rec *msr, msre_ruleset *ruleset, rule_exception *re, - apr_array_header_t *phase_arr, const char *p2, const char *p3); - -apr_status_t DSOLOCAL collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var); - -int DSOLOCAL expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp); - -msre_var_metadata DSOLOCAL *msre_resolve_var(msre_engine *engine, const char *name); - -msre_var DSOLOCAL *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char *name, const char *param, - modsec_rec *msr, char **error_msg); - -int DSOLOCAL msre_parse_generic(apr_pool_t *pool, const char *text, apr_table_t *vartable, - char **error_msg); - -int DSOLOCAL rule_id_in_range(int ruleid, const char *range); - -msre_var DSOLOCAL *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, - msre_rule *rule, apr_pool_t *mptmp); - -#if defined(WITH_LUA) -apr_table_t DSOLOCAL *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, - msre_rule *rule, apr_pool_t *mptmp); -#endif - -/* Structures with the corresponding functions */ - -struct msre_engine { - apr_pool_t *mp; - apr_table_t *variables; - apr_table_t *operators; - apr_table_t *actions; - apr_table_t *tfns; - apr_table_t *reqbody_processors; -}; - -msre_engine DSOLOCAL *msre_engine_create(apr_pool_t *parent_pool); - -void DSOLOCAL msre_engine_destroy(msre_engine *engine); - -msre_op_metadata DSOLOCAL *msre_engine_op_resolve(msre_engine *engine, const char *name); - -struct msre_ruleset { - apr_pool_t *mp; - msre_engine *engine; - - apr_array_header_t *phase_request_headers; - apr_array_header_t *phase_request_body; - apr_array_header_t *phase_response_headers; - apr_array_header_t *phase_response_body; - apr_array_header_t *phase_logging; -}; - -apr_status_t DSOLOCAL msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr); - -apr_status_t DSOLOCAL msre_ruleset_process_phase_internal(msre_ruleset *ruleset, modsec_rec *msr); - -msre_ruleset DSOLOCAL *msre_ruleset_create(msre_engine *engine, apr_pool_t *mp); - -int DSOLOCAL msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase); - -msre_rule DSOLOCAL *msre_ruleset_fetch_rule(msre_ruleset *ruleset, const char *id, int offset); - -int DSOLOCAL msre_ruleset_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re); - -/* -int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re, - apr_array_header_t *phase_arr); -*/ - -#define RULE_NO_MATCH 0 -#define RULE_MATCH 1 - -#define RULE_PH_NONE 0 /* Not a placeholder */ -#define RULE_PH_SKIPAFTER 1 /* Implicit placeholder for skipAfter */ -#define RULE_PH_MARKER 2 /* Explicit placeholder for SecMarker */ - -#define RULE_TYPE_NORMAL 0 /* SecRule */ -#define RULE_TYPE_ACTION 1 /* SecAction */ -#define RULE_TYPE_MARKER 2 /* SecMarker */ -#if defined(WITH_LUA) -#define RULE_TYPE_LUA 3 /* SecRuleScript */ -#endif - -struct msre_ipmatch { - apr_ipsubnet_t *ipsubnet; - const char * address; - struct msre_ipmatch *next; -}; - -struct msre_rule { - apr_array_header_t *targets; - const char *op_name; - const char *op_param; - void *op_param_data; - msre_op_metadata *op_metadata; - unsigned int op_negated; - msre_actionset *actionset; - const char *p1; - const char *unparsed; - const char *filename; - int line_num; - int placeholder; - int type; - - msre_ruleset *ruleset; - msre_rule *chain_starter; - #if defined(PERFORMANCE_MEASUREMENT) - unsigned int execution_time; - unsigned int trans_time; - unsigned int op_time; - #endif - - #if defined(WITH_LUA) - /* Compiled Lua script. */ - msc_script *script; - #endif - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 - ap_regex_t *sub_regex; -#else - regex_t *sub_regex; -#endif - char *sub_str; - char *re_str; - int re_precomp; - int escape_re; - - msre_ipmatch *ip_op; -}; - -char DSOLOCAL *msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, const char *targets, const char *args, const char *actions); - -msre_rule DSOLOCAL *msre_rule_create(msre_ruleset *ruleset, int type, - const char *fn, int line, const char *targets, - const char *args, const char *actions, char **error_msg); - -#if defined(WITH_LUA) -msre_rule DSOLOCAL *msre_rule_lua_create(msre_ruleset *ruleset, - const char *fn, int line, const char *script_filename, - const char *actions, char **error_msg); -#endif - -#define VAR_SIMPLE 0 /* REQUEST_URI */ -#define VAR_LIST 1 - -#define PHASE_REQUEST_HEADERS 1 -#define PHASE_REQUEST_BODY 2 -#define PHASE_RESPONSE_HEADERS 3 -#define PHASE_RESPONSE_BODY 4 -#define PHASE_LOGGING 5 - -typedef int (*fn_op_param_init_t)(msre_rule *rule, char **error_msg); -typedef int (*fn_op_execute_t)(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg); - -struct msre_op_metadata { - const char *name; - fn_op_param_init_t param_init; - fn_op_execute_t execute; -}; - -typedef int (*fn_tfn_execute_t)(apr_pool_t *pool, unsigned char *input, long int input_length, char **rval, long int *rval_length); - -struct msre_tfn_metadata { - const char *name; - - /* Functions should populate *rval and return 1 on - * success, or return -1 on failure (in which case *rval - * should contain the error message. Strict functions - * (those that validate in - * addition to transforming) can return 0 when input - * fails validation. Functions are free to perform - * in-place transformation, or to allocate a new buffer - * from the provideded temporary (per-rule) memory pool. - * - * NOTE Strict transformation functions not supported yet. - */ - fn_tfn_execute_t execute; -}; - -void DSOLOCAL msre_engine_tfn_register(msre_engine *engine, const char *name, - fn_tfn_execute_t execute); - -void DSOLOCAL msre_engine_op_register(msre_engine *engine, const char *name, - fn_op_param_init_t fn1, fn_op_execute_t fn2); - -void DSOLOCAL msre_engine_register_default_tfns(msre_engine *engine); - -void DSOLOCAL msre_engine_register_default_variables(msre_engine *engine); - -void DSOLOCAL msre_engine_register_default_operators(msre_engine *engine); - -void DSOLOCAL msre_engine_register_default_actions(msre_engine *engine); - -msre_tfn_metadata DSOLOCAL *msre_engine_tfn_resolve(msre_engine *engine, const char *name); - -#define VAR_DONT_CACHE 0 -#define VAR_CACHE 1 - -typedef char *(*fn_var_validate_t)(msre_ruleset *ruleset, msre_var *var); -typedef int (*fn_var_generate_t)(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *table, apr_pool_t *mptmp); - -struct msre_var_metadata { - const char *name; - unsigned int type; /* VAR_TYPE_ constants */ - unsigned int argc_min; - unsigned int argc_max; - fn_var_validate_t validate; - fn_var_generate_t generate; - unsigned int is_cacheable; /* 0 - no, 1 - yes */ - unsigned int availability; /* when does this variable become available? */ -}; - -struct msre_var { - char *name; - const char *value; - unsigned int value_len; - char *param; - const void *param_data; - msre_var_metadata *metadata; - msc_regex_t *param_regex; - unsigned int is_negated; - unsigned int is_counting; -}; - - -struct msre_actionset { - apr_table_t *actions; - - /* Metadata */ - const char *id; - const char *rev; - const char *msg; - const char *logdata; - const char *version; - int maturity; - int accuracy; - int severity; - int phase; - msre_rule *rule; - int arg_min; - int arg_max; - - /* Flow */ - int is_chained; - int skip_count; - const char *skip_after; - - /* Disruptive */ - int intercept_action; - const char *intercept_uri; - int intercept_status; - const char *intercept_pause; - - /* "block" needs parent action to reset it */ - msre_action *parent_intercept_action_rec; - msre_action *intercept_action_rec; - int parent_intercept_action; - - /* Other */ - int log; - int auditlog; - int block; -}; - -void DSOLOCAL msre_engine_variable_register(msre_engine *engine, const char *name, - unsigned int type, unsigned int argc_min, unsigned int argc_max, - fn_var_validate_t validate, fn_var_generate_t generate, - unsigned int is_cacheable, unsigned int availability); - -msre_actionset DSOLOCAL *msre_actionset_create(msre_engine *engine, const char *text, - char **error_msg); - -msre_actionset DSOLOCAL *msre_actionset_merge(msre_engine *engine, msre_actionset *parent, - msre_actionset *child, int inherit_by_default); - -msre_actionset DSOLOCAL *msre_actionset_create_default(msre_engine *engine); - -void DSOLOCAL msre_actionset_set_defaults(msre_actionset *actionset); - -void DSOLOCAL msre_actionset_init(msre_actionset *actionset, msre_rule *rule); - -typedef char *(*fn_action_validate_t)(msre_engine *engine, msre_action *action); -typedef apr_status_t (*fn_action_init_t)(msre_engine *engine, msre_actionset *actionset, msre_action *action); -typedef apr_status_t (*fn_action_execute_t)(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action); - -#define ACTION_DISRUPTIVE 1 -#define ACTION_NON_DISRUPTIVE 2 -#define ACTION_METADATA 3 -#define ACTION_FLOW 4 - -#define NO_PLUS_MINUS 0 -#define ALLOW_PLUS_MINUS 1 - -#define ACTION_CARDINALITY_ONE 1 -#define ACTION_CARDINALITY_MANY 2 - -#define ACTION_CGROUP_NONE 0 -#define ACTION_CGROUP_DISRUPTIVE 1 -#define ACTION_CGROUP_LOG 2 -#define ACTION_CGROUP_AUDITLOG 3 - -struct msre_action_metadata { - const char *name; - unsigned int type; - unsigned int argc_min; - unsigned int argc_max; - unsigned int allow_param_plusminus; - unsigned int cardinality; - unsigned int cardinality_group; - fn_action_validate_t validate; - fn_action_init_t init; - fn_action_execute_t execute; -}; - -struct msre_action { - msre_action_metadata *metadata; - const char *param; - const void *param_data; - unsigned int param_plusminus; /* ABSOLUTE_VALUE, POSITIVE_VALUE, NEGATIVE_VALUE */ -}; - -void DSOLOCAL msre_engine_reqbody_processor_register(msre_engine *engine, - const char *name, void *fn_init, void *fn_process, void *fn_complete); - -typedef int (*fn_reqbody_processor_init_t)(modsec_rec *msr, char **error_msg); -typedef int (*fn_reqbody_processor_process_t)(modsec_rec *msr, const char *buf, - unsigned int size, char **error_msg); -typedef int (*fn_reqbody_processor_complete_t)(modsec_rec *msr, char **error_msg); - -struct msre_reqbody_processor_metadata { - const char *name; - fn_reqbody_processor_init_t init; - fn_reqbody_processor_process_t process; - fn_reqbody_processor_complete_t complete; -}; - -/* -- MSRE Function Prototypes ---------------------------------------------- */ - -msre_var_metadata DSOLOCAL *msre_resolve_var(msre_engine *engine, const char *name); - -int DSOLOCAL msre_parse_generic(apr_pool_t *pool, const char *text, apr_table_t *vartable, - char **error_msg); - -apr_status_t DSOLOCAL msre_parse_vars(msre_ruleset *ruleset, const char *text, - apr_array_header_t *arr, char **error_msg); - -char DSOLOCAL *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset); - -/* -- Data Cache -- */ - -struct msre_cache_rec { - int hits; - int changed; - int num; - const char *path; - const char *val; - apr_size_t val_len; -}; - -#endif diff --git a/apache2/re_actions.c b/apache2/re_actions.c deleted file mode 100644 index 6a5fbac6f9..0000000000 --- a/apache2/re_actions.c +++ /dev/null @@ -1,2956 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "re.h" -#include -#include "apr_lib.h" -#include "apr_strmatch.h" - - -/** - * Register action with the engine. - */ -static void msre_engine_action_register(msre_engine *engine, const char *name, - unsigned int type, unsigned int argc_min, unsigned int argc_max, - unsigned int allow_param_plusminus, unsigned int cardinality, - unsigned int cardinality_group, fn_action_validate_t validate, - fn_action_init_t init, fn_action_execute_t execute) -{ - msre_action_metadata *metadata = (msre_action_metadata *)apr_pcalloc(engine->mp, - sizeof(msre_action_metadata)); - if (metadata == NULL) return; - - metadata->name = name; - metadata->type = type; - metadata->argc_min = argc_min; - metadata->argc_max = argc_max; - metadata->allow_param_plusminus = allow_param_plusminus; - metadata->cardinality = cardinality; - metadata->cardinality_group = cardinality_group; - metadata->validate = validate; - metadata->init = init; - metadata->execute = execute; - - apr_table_setn(engine->actions, name, (void *)metadata); -} - -/** - * Generates a single variable (from the supplied metadata). - */ -msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, - msre_rule *rule, apr_pool_t *mptmp) -{ - apr_table_t *vartab = NULL; - const apr_table_entry_t *te = NULL; - const apr_array_header_t *arr = NULL; - msre_var *rvar = NULL; - int i; - - /* Sanity check. */ - if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; - - vartab = apr_table_make(mptmp, 16); - var->metadata->generate(msr, var, rule, vartab, mptmp); - - arr = apr_table_elts(vartab); - if (arr->nelts == 0) return NULL; - te = (apr_table_entry_t *)arr->elts; - - rvar = (msre_var *)te[0].val; - - /* Return straight away if there were no - * transformation functions supplied. - */ - if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { - return rvar; - } - - /* Copy the value so that we can transform it in place. */ - rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); - - /* Transform rvar in a loop. */ - for (i = 0; i < tfn_arr->nelts; i++) { - msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; - char *rval; - int rc; - long int rval_len; - - rc = tfn->execute(mptmp, (unsigned char *)rvar->value, - rvar->value_len, &rval, &rval_len); - - rvar->value = rval; - rvar->value_len = rval_len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, - log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); - } - } - - return rvar; -} - -#if defined(WITH_LUA) -/** - * - */ -apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, - msre_rule *rule, apr_pool_t *mptmp) -{ - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - apr_table_t *vartab = NULL, *tvartab = NULL; - msre_var *rvar = NULL; - int i, j; - - /* Sanity check. */ - if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; - - /* Generate variables. */ - vartab = apr_table_make(mptmp, 16); - var->metadata->generate(msr, var, rule, vartab, mptmp); - - /* Return straight away if there were no - * transformation functions supplied. - */ - if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { - return vartab; - } - - tvartab = apr_table_make(mptmp, 16); - - tarr = apr_table_elts(vartab); - telts = (const apr_table_entry_t*)tarr->elts; - for (j = 0; j < tarr->nelts; j++) { - rvar = (msre_var *)telts[j].val; - - /* Copy the value so that we can transform it in place. */ - rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); - - /* Transform rvar in a loop. */ - for (i = 0; i < tfn_arr->nelts; i++) { - msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; - char *rval; - int rc; - long int rval_len; - - rc = tfn->execute(mptmp, (unsigned char *)rvar->value, - rvar->value_len, &rval, &rval_len); - - rvar->value = rval; - rvar->value_len = rval_len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, - log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); - } - } - - apr_table_addn(tvartab, rvar->name, (void *)rvar); - } - - return tvartab; -} -#endif - -/** - * Expands macros ("%{NAME}" entities) if present - * in the given variable. - */ -int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp) { - char *data = NULL; - apr_array_header_t *arr = NULL; - char *p = NULL, *q = NULL, *t = NULL; - char *text_start = NULL, *next_text_start = NULL; - msc_string *part = NULL; - int i, offset = 0; - - if (var->value == NULL) return 0; - - /* IMP1 Duplicate the string and create the array on - * demand, thus not having to do it if there are - * no macros in the input data. - */ - - data = apr_pstrdup(mptmp, var->value); /* IMP1 Are we modifying data anywhere? */ - arr = apr_array_make(mptmp, 16, sizeof(msc_string *)); - if ((data == NULL)||(arr == NULL)) return -1; - - text_start = next_text_start = data; - do { - text_start = next_text_start; - p = strstr(text_start, "%"); - if (p != NULL) { - char *var_name = NULL; - char *var_value = NULL; - - if ((*(p + 1) == '{')&&(*(p + 2) != '\0')) { - char *var_start = p + 2; - - t = var_start; - while((*t != '\0')&&(*t != '}')) t++; - if (*t == '}') { - /* Named variable. */ - - var_name = apr_pstrmemdup(mptmp, var_start, t - var_start); - q = strstr(var_name, "."); - if (q != NULL) { - var_value = q + 1; - *q = '\0'; - } - - next_text_start = t + 1; /* *t was '}' */ - } else { - /* Warn about a possiblly forgotten '}' */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Warning: Possibly unterminated macro: \"%s\"", - log_escape_ex(mptmp, var_start - 2, t - var_start + 2)); - } - - next_text_start = t; /* *t was '\0' */ - } - } - - if (var_name != NULL) { - char *my_error_msg = NULL; - msre_var *var_generated = NULL; - msre_var *var_resolved = NULL; - - /* Add the text part before the macro to the array. */ - part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); - if (part == NULL) return -1; - part->value_len = p - text_start; - part->value = apr_pstrmemdup(mptmp, text_start, part->value_len); - *(msc_string **)apr_array_push(arr) = part; - - /* Resolve the macro and add that to the array. */ - var_resolved = msre_create_var_ex(mptmp, msr->modsecurity->msre, var_name, var_value, - msr, &my_error_msg); - if (var_resolved != NULL) { - var_generated = generate_single_var(msr, var_resolved, NULL, rule, mptmp); - if (var_generated != NULL) { - part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); - if (part == NULL) return -1; - part->value_len = var_generated->value_len; - part->value = (char *)var_generated->value; - *(msc_string **)apr_array_push(arr) = part; - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Resolved macro %%{%s%s%s} to: %s", - var_name, - (var_value ? "." : ""), - (var_value ? var_value : ""), - log_escape_nq_ex(mptmp, part->value, part->value_len)); - } - } - } else { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Failed to resolve macro %%{%s%s%s}: %s", - var_name, - (var_value ? "." : ""), - (var_value ? var_value : ""), - my_error_msg); - } - } - } else { - /* We could not identify a valid macro so add it as text. */ - part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); - if (part == NULL) return -1; - part->value_len = p - text_start + 1; /* len(text)+len("%") */ - part->value = apr_pstrmemdup(mptmp, text_start, part->value_len); - *(msc_string **)apr_array_push(arr) = part; - - next_text_start = p + 1; - } - } else { - /* Text part. */ - part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); - part->value = apr_pstrdup(mptmp, text_start); - part->value_len = strlen(part->value); - *(msc_string **)apr_array_push(arr) = part; - } - } while (p != NULL); - - /* If there's more than one member of the array that - * means there was at least one macro present. Combine - * text parts into a single string now. - */ - if (arr->nelts > 1) { - /* Figure out the required size for the string. */ - var->value_len = 0; - for(i = 0; i < arr->nelts; i++) { - part = ((msc_string **)arr->elts)[i]; - var->value_len += part->value_len; - } - - /* Allocate the string. */ - var->value = apr_palloc(msr->mp, var->value_len + 1); - if (var->value == NULL) return -1; - - /* Combine the parts. */ - offset = 0; - for(i = 0; i < arr->nelts; i++) { - part = ((msc_string **)arr->elts)[i]; - memcpy((char *)(var->value + offset), part->value, part->value_len); - offset += part->value_len; - } - var->value[offset] = '\0'; - } - - return 1; -} - -/** - * Record the original collection values to use to calculate deltas. - * This can be called multiple times and will not overwrite the first - * value that is set. - */ -apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var) { - apr_table_t *table = NULL; - msc_string *var = NULL; - const char *var_name = NULL; - - if (orig_var == NULL) { - msr_log(msr, 1, "Internal Error: Attempt to record NULL original variable."); - return -1; - } - - var_name = orig_var->name; - table = (apr_table_t *)apr_table_get(msr->collections_original, col_name); - - /* Does the collection exist already? */ - if (table == NULL) { - table = apr_table_make(msr->mp, 24); - if (table == NULL) { - msr_log(msr, 1, "Failed to allocate space for original collection."); - return -1; - } - apr_table_setn(msr->collections_original, apr_pstrdup(msr->mp, col_name), (void *)table); - } - else { - /* Does the variable exist already? */ - var = (msc_string *)apr_table_get(table, var_name); - if (var != NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Original collection variable: %s.%s = \"%s\"", col_name, var_name, - log_escape_ex(msr->mp, orig_var->value, orig_var->value_len)); - } - return 1; - } - } - - var = (msc_string *)apr_palloc(msr->mp, sizeof(msc_string)); - if (var == NULL) { - msr_log(msr, 1, "Failed to allocate space for original collection variable."); - return -1; - } - - /* Copy the original var and add to collection. */ - var->name = orig_var->name ? apr_pstrmemdup(msr->mp, orig_var->name, orig_var->name_len) : NULL; - var->name_len = orig_var->name_len; - var->value = orig_var->value ? apr_pstrmemdup(msr->mp, orig_var->value, orig_var->value_len) : NULL; - var->value_len = orig_var->value_len; - apr_table_setn(table, apr_pstrmemdup(msr->mp, var->name, var->name_len), (void *)var); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Recorded original collection variable: %s.%s = \"%s\"", col_name, var_name, - log_escape_ex(msr->mp, var->value, var->value_len)); - } - - return 0; -} - -/* marker */ -static apr_status_t msre_action_marker_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->id = action->param; - return 1; -} - -/* id */ - -static apr_status_t msre_action_id_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->id = action->param; - return 1; -} - -static char *msre_action_id_validate(msre_engine *engine, msre_action *action) { - int id; - - if(action != NULL && action->param != NULL) { - for(id=0;idparam);id++) { - if(!apr_isdigit(action->param[id])) - return apr_psprintf(engine->mp, "ModSecurity: Invalid value for action ID: %s", action->param); - } - id = atoi(action->param); - if ((id == LONG_MAX)||(id == LONG_MIN)||(id <= 0)) { - return apr_psprintf(engine->mp, "ModSecurity: Invalid value for action ID: %s", action->param); - } - } - - return NULL; -} - -/* rev */ - -static apr_status_t msre_action_rev_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->rev = action->param; - return 1; -} - -/* msg */ - -static apr_status_t msre_action_msg_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->msg = action->param; - return 1; -} - -/* logdata */ - -static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->logdata = action->param; - return 1; -} - -/* SanitizeMatchedBytes init */ - -static apr_status_t msre_action_sanitizeMatchedBytes_init(msre_engine *engine, - msre_actionset *actionset, msre_action *action) -{ - char *parse_parm = NULL; - char *ac_param = NULL; - char *savedptr = NULL; - int arg_min = 0; - int arg_max = 0; - - if (action->param != NULL && strlen(action->param) == 3) { - - ac_param = apr_pstrdup(engine->mp, action->param); - parse_parm = apr_strtok(ac_param,"/",&savedptr); - - if(apr_isdigit(*parse_parm) && apr_isdigit(*savedptr)) { - arg_max = atoi(parse_parm); - arg_min = atoi(savedptr); - } - } - - actionset->arg_min = arg_min; - actionset->arg_max = arg_max; - - return 1; -} - -/* accuracy */ - -static apr_status_t msre_action_accuracy_init(msre_engine *engine, - msre_actionset *actionset, msre_action *action) -{ - actionset->accuracy = atoi(action->param); - return 1; -} - -/* maturity */ - -static apr_status_t msre_action_maturity_init(msre_engine *engine, - msre_actionset *actionset, msre_action *action) -{ - actionset->maturity = atoi(action->param); - return 1; -} - -/* ver */ - -static apr_status_t msre_action_ver_init(msre_engine *engine, - msre_actionset *actionset, msre_action *action) -{ - actionset->version = action->param; - return 1; -} - -/* severity */ - -static apr_status_t msre_action_severity_init(msre_engine *engine, - msre_actionset *actionset, msre_action *action) -{ - if (strcasecmp(action->param, "emergency") == 0) { - actionset->severity = 0; - } else if (strcasecmp(action->param, "alert") == 0) { - actionset->severity = 1; - } else if (strcasecmp(action->param, "critical") == 0) { - actionset->severity = 2; - } else if (strcasecmp(action->param, "error") == 0) { - actionset->severity = 3; - } else if (strcasecmp(action->param, "warning") == 0) { - actionset->severity = 4; - } else if (strcasecmp(action->param, "notice") == 0) { - actionset->severity = 5; - } else if (strcasecmp(action->param, "info") == 0) { - actionset->severity = 6; - } else if (strcasecmp(action->param, "debug") == 0) { - actionset->severity = 7; - } else { - actionset->severity = atoi(action->param); - } - return 1; -} - -/* chain */ - -static apr_status_t msre_action_chain_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->is_chained = 1; - return 1; -} - -/* log */ -static apr_status_t msre_action_log_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->log = 1; - return 1; -} - -/* nolog */ -static apr_status_t msre_action_nolog_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->log = 0; - actionset->auditlog = 0; - return 1; -} - -/* auditlog */ -static apr_status_t msre_action_auditlog_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->auditlog = 1; - return 1; -} - -/* noauditlog */ -static apr_status_t msre_action_noauditlog_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->auditlog = 0; - return 1; -} - -/* block */ -static apr_status_t msre_action_block_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - /* Right now we just set a flag and inherit the real disruptive action */ - actionset->block = 1; - return 1; -} - -/* deny */ -static apr_status_t msre_action_deny_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_DENY; - actionset->intercept_action_rec = action; - return 1; -} - -/* status */ -static char *msre_action_status_validate(msre_engine *engine, msre_action *action) { - /* ENH action->param must be a valid HTTP status code. */ - return NULL; -} - -static apr_status_t msre_action_status_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_status = atoi(action->param); - return 1; -} - -/* drop */ -static apr_status_t msre_action_drop_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_DROP; - actionset->intercept_action_rec = action; - return 1; -} - -/* pause */ -static char *msre_action_pause_validate(msre_engine *engine, msre_action *action) { - /* ENH Validate a positive number. */ - return NULL; -} - -static apr_status_t msre_action_pause_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_PAUSE; - actionset->intercept_pause = action->param; - return 1; -} - -/* redirect */ - -static char *msre_action_redirect_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - -static apr_status_t msre_action_redirect_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_REDIRECT; - actionset->intercept_uri = action->param; - actionset->intercept_action_rec = action; - return 1; -} - -static apr_status_t msre_action_redirect_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - - var = apr_pcalloc(mptmp, sizeof(msc_string)); - if (var == NULL) return -1; - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - - rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len); - - return 1; -} - -/* proxy */ - -static char *msre_action_proxy_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - -static apr_status_t msre_action_proxy_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_PROXY; - actionset->intercept_uri = action->param; - actionset->intercept_action_rec = action; - return 1; -} - -static apr_status_t msre_action_proxy_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - - var = apr_pcalloc(mptmp, sizeof(msc_string)); - if (var == NULL) return -1; - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - - rule->actionset->intercept_uri = apr_pstrmemdup(msr->mp, var->value, var->value_len); - - return 1; -} - -/* pass */ - -static apr_status_t msre_action_pass_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_NONE; - actionset->intercept_action_rec = action; - return 1; -} - -/* skip */ - -static char *msre_action_skip_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - -static apr_status_t msre_action_skip_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->skip_count = atoi(action->param); - if (actionset->skip_count <= 0) actionset->skip_count = 1; - return 1; -} - -/* skipAfter */ - -static char *msre_action_skipAfter_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - -static apr_status_t msre_action_skipAfter_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->skip_after = action->param; - return 1; -} - -/* allow */ - -static apr_status_t msre_action_allow_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - actionset->intercept_action = ACTION_ALLOW; - actionset->intercept_action_rec = action; - - if (action->param != NULL) { - if (strcasecmp(action->param, "phase") == 0) { - actionset->intercept_action = ACTION_ALLOW_PHASE; - } else - if (strcasecmp(action->param, "request") == 0) { - actionset->intercept_action = ACTION_ALLOW_REQUEST; - } - } - - return 1; -} - -static char *msre_action_allow_validate(msre_engine *engine, msre_action *action) { - if (action->param != NULL) { - if (strcasecmp(action->param, "phase") == 0) { - return NULL; - } else - if (strcasecmp(action->param, "request") == 0) { - return NULL; - } else { - return apr_psprintf(engine->mp, "Invalid parameter for allow: %s", action->param); - } - } - - return NULL; -} - -/* phase */ - -static char *msre_action_phase_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - -static apr_status_t msre_action_phase_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - if(strcasecmp(action->param,"request") == 0) - actionset->phase = 2; - else if(strcasecmp(action->param,"response") == 0) - actionset->phase = 4; - else if(strcasecmp(action->param,"logging") == 0) - actionset->phase = 5; - else - actionset->phase = atoi(action->param); - - return 1; -} - -/* t */ - -static char *msre_action_t_validate(msre_engine *engine, msre_action *action) { - msre_tfn_metadata *metadata = NULL; - metadata = msre_engine_tfn_resolve(engine, action->param); - if (metadata == NULL) return apr_psprintf(engine->mp, "Invalid transformation function: %s", - action->param); - action->param_data = metadata; - return NULL; -} - -static apr_status_t msre_action_t_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - msre_tfn_metadata *metadata = (msre_tfn_metadata *)action->param_data; - action->param_data = metadata; - return 1; -} - -/* ctl */ -static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) { - char *name = NULL; - char *value = NULL; - - /* Parse first. */ - if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) { - return FATAL_ERROR; - } - if (value == NULL) { - return apr_psprintf(engine->mp, "Missing ctl value for name: %s", name); - } - - /* Validate value. */ - if (strcasecmp(name, "ruleEngine") == 0) { - if (strcasecmp(value, "on") == 0) return NULL; - if (strcasecmp(value, "off") == 0) return NULL; - if (strcasecmp(value, "detectiononly") == 0) return NULL; - return apr_psprintf(engine->mp, "Invalid setting for ctl name ruleEngine: %s", value); - } else - if (strcasecmp(name, "ruleRemoveById") == 0) { - /* ENH nothing yet */ - return NULL; - } else - if (strcasecmp(name, "ruleRemoveByTag") == 0) { - if (!msc_pregcomp(engine->mp, value, 0, NULL, NULL)) - return apr_psprintf(engine->mp, "ModSecurity: Invalid regular expression \"%s\"", value); - return NULL; - } else - if (strcasecmp(name, "ruleRemoveByMsg") == 0) { - if (!msc_pregcomp(engine->mp, value, 0, NULL, NULL)) - return apr_psprintf(engine->mp, "ModSecurity: Invalid regular expression \"%s\"", value); - return NULL; - } else - if (strcasecmp(name, "requestBodyAccess") == 0) { - if (parse_boolean(value) == -1) { - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - " requestBodyAccess: %s", value); - } - return NULL; - } else - if (strcasecmp(name, "requestBodyProcessor") == 0) { - /* ENH We will accept anything for now but it'd be nice - * to add a check here that the processor name is a valid one. - */ - return NULL; - } else - if (strcasecmp(name, "forceRequestBodyVariable") == 0) { - if (strcasecmp(value, "on") == 0) return NULL; - if (strcasecmp(value, "off") == 0) return NULL; - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - " forceRequestBodyVariable: %s", value); - } else - if (strcasecmp(name, "responseBodyAccess") == 0) { - if (parse_boolean(value) == -1) { - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - " responseBodyAccess: %s", value); - } - return NULL; - } else - if (strcasecmp(name, "auditEngine") == 0) { - if (strcasecmp(value, "on") == 0) return NULL; - if (strcasecmp(value, "off") == 0) return NULL; - if (strcasecmp(value, "relevantonly") == 0) return NULL; - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - " auditEngine: %s", value); - } else - if (strcasecmp(name, "auditLogParts") == 0) { - if ((value[0] == '+')||(value[0] == '-')) { - if (is_valid_parts_specification(value + 1) != 1) { - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - "auditLogParts: %s", value); - } - } - else - if (is_valid_parts_specification(value) != 1) { - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - "auditLogParts: %s", value); - } - return NULL; - } else - if (strcasecmp(name, "debugLogLevel") == 0) { - if ((atoi(value) >= 0)&&(atoi(value) <= 9)) return NULL; - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - "debugLogLevel: %s", value); - } else - if (strcasecmp(name, "requestBodyLimit") == 0) { - long int limit = strtol(value, NULL, 10); - - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - "requestBodyLimit: %s", value); - } - - if (limit > REQUEST_BODY_HARD_LIMIT) { - return apr_psprintf(engine->mp, "Request size limit cannot exceed " - "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT); - } - - return NULL; - } else - if (strcasecmp(name, "responseBodyLimit") == 0) { - long int limit = strtol(value, NULL, 10); - - if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { - return apr_psprintf(engine->mp, "Invalid setting for ctl name " - "responseBodyLimit: %s", value); - } - - if (limit > RESPONSE_BODY_HARD_LIMIT) { - return apr_psprintf(engine->mp, "Response size limit cannot exceed " - "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT); - } - - return NULL; - } else - if (strcasecmp(name, "ruleRemoveTargetById") == 0) { - char *parm = NULL; - char *savedptr = NULL; - - parm = apr_strtok(value,";",&savedptr); - - if(parm == NULL && savedptr == NULL) - return apr_psprintf(engine->mp, "ruleRemoveTargetById must has at least id;VARIABLE"); - - return NULL; - } else - if (strcasecmp(name,"ruleRemoveTargetByTag") == 0) { - char *parm = NULL; - char *savedptr = NULL; - - parm = apr_strtok(value,";",&savedptr); - if(parm == NULL && savedptr == NULL) - return apr_psprintf(engine->mp, "ruleRemoveTargetByTag must has at least tag;VARIABLE"); - if (!msc_pregcomp(engine->mp, parm, 0, NULL, NULL)) { - return apr_psprintf(engine->mp, "ModSecurity: Invalid regular expression \"%s\"", parm); - } - return NULL; - } else - if (strcasecmp(name,"ruleRemoveTargetByMsg") == 0) { - char *parm = NULL; - char *savedptr = NULL; - - parm = apr_strtok(value,";",&savedptr); - if(parm == NULL && savedptr == NULL) - return apr_psprintf(engine->mp, "ruleRemoveTargetByMsg must has at least msg;VARIABLE"); - if (!msc_pregcomp(engine->mp, parm, 0, NULL, NULL)) { - return apr_psprintf(engine->mp, "ModSecurity: Invalid regular expression \"%s\"", parm); - } - return NULL; - } else - if (strcasecmp(name, "HashEnforcement") == 0) { - if (strcasecmp(value, "on") == 0) return NULL; - if (strcasecmp(value, "off") == 0) return NULL; - return apr_psprintf(engine->mp, "Invalid setting for ctl name HashEnforcement: %s", value); - } else - if (strcasecmp(name, "HashEngine") == 0) { - if (strcasecmp(value, "on") == 0) return NULL; - if (strcasecmp(value, "off") == 0) return NULL; - return apr_psprintf(engine->mp, "Invalid setting for ctl name HashEngine: %s", value); - } else { - return apr_psprintf(engine->mp, "Invalid ctl name setting: %s", name); - } -} - -static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - /* Do nothing. */ - return 1; -} - -static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - char *name = NULL; - char *value = NULL; - - /* Parse first. */ - if (parse_name_eq_value(msr->mp, action->param, &name, &value) < 0) return -1; - if (value == NULL) return -1; - - /* Validate value. */ - if (strcasecmp(name, "ruleEngine") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->is_enabled = MODSEC_ENABLED; - msr->usercfg->is_enabled = MODSEC_ENABLED; - } - else - if (strcasecmp(value, "off") == 0) { - msr->txcfg->is_enabled = MODSEC_DISABLED; - msr->usercfg->is_enabled = MODSEC_DISABLED; - } - else - if (strcasecmp(value, "detectiononly") == 0) { - msr->txcfg->is_enabled = MODSEC_DETECTION_ONLY; - msr->usercfg->is_enabled = MODSEC_DETECTION_ONLY; - msr->txcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - msr->usercfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - msr->txcfg->of_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - msr->usercfg->of_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set ruleEngine to %s.", value); - } - - return 1; - } else - if (strcasecmp(name, "HashEnforcement") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->hash_enforcement = HASH_ENABLED; - msr->usercfg->hash_enforcement = HASH_ENABLED; - } - if (strcasecmp(value, "off") == 0) { - msr->txcfg->hash_enforcement = HASH_DISABLED; - msr->usercfg->hash_enforcement = HASH_DISABLED; - } - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set HashEnforcement to %s.", value); - } - return 1; - } else - if (strcasecmp(name, "HashEngine") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->hash_is_enabled = HASH_ENABLED; - msr->usercfg->hash_is_enabled = HASH_ENABLED; - } - if (strcasecmp(value, "off") == 0) { - msr->txcfg->hash_is_enabled = HASH_DISABLED; - msr->usercfg->hash_is_enabled = HASH_DISABLED; - } - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set HashEngine to %s.", value); - } - return 1; - } else - if (strcasecmp(name, "ruleRemoveById") == 0) { - *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Removed rule by id : %s.", value); - } - return 1; - } else - if (strcasecmp(name, "ruleRemoveByTag") == 0) { - rule_exception *re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_TAG; - re->param = (const char *)apr_pstrdup(msr->mp, value); - re->param_data = msc_pregcomp(msr->mp, re->param, 0, NULL, NULL); - if (re->param_data == NULL) { - msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", re->param); - return -1; - } - - *(rule_exception **)apr_array_push(msr->removed_rules_tag) = re; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Removed rule by tag : %s.", value); - } - - return 1; - } else - if (strcasecmp(name, "ruleRemoveByMsg") == 0) { - rule_exception *re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_MSG; - re->param = (const char *)apr_pstrdup(msr->mp, value); - re->param_data = msc_pregcomp(msr->mp, re->param, 0, NULL, NULL); - if (re->param_data == NULL) { - msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", re->param); - return -1; - } - - *(rule_exception **)apr_array_push(msr->removed_rules_msg) = re; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Removed rule by msg : %s.", value); - } - - return 1; - } else - if (strcasecmp(name, "requestBodyAccess") == 0) { - int pv = parse_boolean(value); - - if (pv == -1) return -1; - msr->txcfg->reqbody_access = pv; - msr->usercfg->reqbody_access = pv; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", pv); - } - - return 1; - } else - if (strcasecmp(name, "forceRequestBodyVariable") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_ON; - msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_ON; - } - else - if (strcasecmp(value, "off") == 0) { - msr->txcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF; - msr->usercfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", msr->txcfg->reqbody_buffering); - } - - return 1; - } else - if (strcasecmp(name, "requestBodyProcessor") == 0) { - msr->msc_reqbody_processor = value; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value); - } - - return 1; - } else - if (strcasecmp(name, "responseBodyAccess") == 0) { - int pv = parse_boolean(value); - - if (pv == -1) return -1; - msr->txcfg->resbody_access = pv; - msr->usercfg->resbody_access = pv; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set responseBodyAccess to %d.", pv); - } - - return 1; - } else - if (strcasecmp(name, "auditEngine") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_ON; - msr->usercfg->auditlog_flag = AUDITLOG_ON; - } - else - if (strcasecmp(value, "off") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_OFF; - msr->usercfg->auditlog_flag = AUDITLOG_OFF; - } - else - if (strcasecmp(value, "relevantonly") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_RELEVANT; - msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); - } - - return 1; - } else - if (strcasecmp(name, "auditLogParts") == 0) { - char *new_value = value; - - if (value[0] == '+') { - /* Add the listed parts. */ - new_value = apr_pstrcat(msr->mp, msr->txcfg->auditlog_parts, value + 1, NULL); - } - else - if (value[0] == '-') { /* Remove the listed parts. */ - char c, *t = value + 1; - - /* Start with the current value. */ - new_value = apr_pstrdup(msr->mp, msr->txcfg->auditlog_parts); - - while((c = *t++) != '\0') { - char *s = new_value; - char *d = new_value; - - while(*s != '\0') { - if (*s != c) { - *(d++) = *(s++); - } else { - s++; - } - } - *d = '\0'; - } - } - - /* Set the new value. */ - msr->txcfg->auditlog_parts = new_value; - msr->usercfg->auditlog_parts = new_value; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set auditLogParts to %s.", msr->txcfg->auditlog_parts); - } - - return 1; - } else - if (strcasecmp(name, "debugLogLevel") == 0) { - msr->txcfg->debuglog_level = atoi(value); - msr->usercfg->debuglog_level = atoi(value); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set debugLogLevel to %d.", msr->txcfg->debuglog_level); - } - - return 1; - } else - if (strcasecmp(name, "requestBodyLimit") == 0) { - long int limit = strtol(value, NULL, 10); - - /* ENH Accept only in correct phase warn otherwise. */ - msr->txcfg->reqbody_limit = limit; - msr->usercfg->reqbody_limit = limit; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set requestBodyLimit to %ld.", limit); - } - - return 1; - } else - if (strcasecmp(name, "responseBodyLimit") == 0) { - long int limit = strtol(value, NULL, 10); - - /* ENH Accept only in correct phase warn otherwise. */ - msr->txcfg->of_limit = limit; - msr->usercfg->of_limit = limit; - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: Set responseBodyLimit to %ld.", limit); - } - - return 1; - } else - if (strcasecmp(name, "ruleRemoveTargetById") == 0) { - rule_exception *re = NULL; - char *p1 = NULL, *p2 = NULL; - char *savedptr = NULL; - - p1 = apr_strtok(value,";",&savedptr); - - p2 = apr_strtok(NULL,";",&savedptr); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: ruleRemoveTargetById id=%s targets=%s", p1, p2); - } - re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_ID; - re->param = (const char *)apr_pstrdup(msr->mp, p1); - apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); - return 1; - } else - if (strcasecmp(name, "ruleRemoveTargetByTag") == 0) { - rule_exception *re = NULL; - char *p1 = NULL, *p2 = NULL; - char *savedptr = NULL; - - p1 = apr_strtok(value,";",&savedptr); - - p2 = apr_strtok(NULL,";",&savedptr); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: ruleRemoveTargetByTag tag=%s targets=%s", p1, p2); - } - - re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_TAG; - re->param = (const char *)apr_pstrdup(msr->mp, p1); - re->param_data = msc_pregcomp(msr->mp, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", p1); - return -1; - } - apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); - return 1; - } else - if (strcasecmp(name, "ruleRemoveTargetByMsg") == 0) { - rule_exception *re = NULL; - char *p1 = NULL, *p2 = NULL; - char *savedptr = NULL; - - p1 = apr_strtok(value,";",&savedptr); - - p2 = apr_strtok(NULL,";",&savedptr); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Ctl: ruleRemoveTargetByMsg msg=%s targets=%s", p1, p2); - } - - re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_MSG; - re->param = apr_pstrdup(msr->mp, p1); - re->param_data = msc_pregcomp(msr->mp, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", p1); - return -1; - } - apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); - return 1; - } - else { - /* Should never happen, but log if it does. */ - msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name); - return -1; - } - - return -1; -} - -/* xmlns */ -static char *msre_action_xmlns_validate(msre_engine *engine, msre_action *action) { - char *name = NULL; - char *value = NULL; - - /* Parse first. */ - if (parse_name_eq_value(engine->mp, action->param, &name, &value) < 0) { - return FATAL_ERROR; - } - if (value == NULL) { - return apr_psprintf(engine->mp, "Missing xmlns href for prefix: %s", name); - } - - /* Don't do anything else right now, we are just storing - * the value for the variable, which is the real consumer - * for the namespace information. - */ - - return NULL; -} - -/* sanitizeArg */ -static apr_status_t msre_action_sanitizeArg_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - const char *sargname = NULL; - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - int i; - - sargname = action->param; - - tarr = apr_table_elts(msr->arguments); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msc_arg *arg = (msc_arg *)telts[i].val; - - if (strcasecmp(sargname, arg->name) == 0) { - apr_table_addn(msr->arguments_to_sanitize, arg->name, (void *)arg); - } - } - - return 1; -} - -#define SANITISE_ARG 1 -#define SANITISE_REQUEST_HEADER 2 -#define SANITISE_RESPONSE_HEADER 3 - -/* sanitizeMatched */ -static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - const char *sargname = NULL; - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - int i, type = 0; - msc_string *mvar = msr->matched_var; - - if (mvar->name_len == 0) return 0; - - /* IMP1 We need to extract the variable name properly here, - * taking into account it may have been escaped. - */ - if ((mvar->name_len > 5) && (strncmp(mvar->name, "ARGS:", 5) == 0)) { - sargname = apr_pstrdup(msr->mp, mvar->name + 5); - type = SANITISE_ARG; - } else - if ((mvar->name_len > 11) && (strncmp(mvar->name, "ARGS_NAMES:", 11) == 0)) { - sargname = apr_pstrdup(msr->mp, mvar->name + 11); - type = SANITISE_ARG; - } else - if ((mvar->name_len > 16) && (strncmp(mvar->name, "REQUEST_HEADERS:", 16) == 0)) { - sargname = apr_pstrdup(msr->mp, mvar->name + 16); - type = SANITISE_REQUEST_HEADER; - } else - if ((mvar->name_len > 22) && (strncmp(mvar->name, "REQUEST_HEADERS_NAMES:", 22) == 0)) { - sargname = apr_pstrdup(msr->mp, mvar->name + 22); - type = SANITISE_REQUEST_HEADER; - } else - if ((mvar->name_len > 17) && (strncmp(mvar->name, "RESPONSE_HEADERS:", 17) == 0)) { - sargname = apr_pstrdup(msr->mp, mvar->name + 17); - type = SANITISE_RESPONSE_HEADER; - } else - if ((mvar->name_len > 23) && (strncmp(mvar->name, "RESPONSE_HEADERS_NAMES:", 23) == 0)) { - sargname = apr_pstrdup(msr->mp, mvar->name + 23); - type = SANITISE_RESPONSE_HEADER; - } - else { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "sanitizeMatched: Don't know how to handle variable: %s", - mvar->name); - } - return 0; - } - - switch(type) { - case SANITISE_ARG : - tarr = apr_table_elts(msr->arguments); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msc_arg *arg = (msc_arg *)telts[i].val; - if (strcasecmp(sargname, arg->name) == 0) { - apr_table_addn(msr->arguments_to_sanitize, arg->name, (void *)arg); - } - } - break; - - case SANITISE_REQUEST_HEADER : - apr_table_set(msr->request_headers_to_sanitize, sargname, "1"); - break; - - case SANITISE_RESPONSE_HEADER : - apr_table_set(msr->response_headers_to_sanitize, sargname, "1"); - break; - - default : - /* do nothing */ - break; - } - - return 1; -} - -/* sanitizeRequestHeader */ -static apr_status_t msre_action_sanitizeRequestHeader_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - apr_table_set(msr->request_headers_to_sanitize, action->param, "1"); - return 1; -} - -/* sanitizeResponseHeader */ -static apr_status_t msre_action_sanitizeResponseHeader_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - apr_table_set(msr->response_headers_to_sanitize, action->param, "1"); - return 1; -} - -/* setenv */ -static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - char *data = apr_pstrdup(mptmp, action->param); - char *env_name = NULL, *env_value = NULL; - char *s = NULL; - msc_string *env = NULL; - - /* Extract the name and the value. */ - /* IMP1 We have a function for this now, parse_name_eq_value? */ - s = strstr(data, "="); - if (s == NULL) { - env_name = data; - env_value = "1"; - } else { - env_name = data; - env_value = s + 1; - *s = '\0'; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Setting env variable: %s=%s", env_name, env_value); - } - - /* Expand and escape any macros in the name */ - env = apr_palloc(msr->mp, sizeof(msc_string)); - if (env == NULL) { - msr_log(msr, 1, "Failed to allocate space to expand name macros"); - return -1; - } - env->value = env_name; - env->value_len = strlen(env->value); - expand_macros(msr, env, rule, mptmp); - env_name = log_escape_nq_ex(msr->mp, env->value, env->value_len); - - /* Execute the requested action. */ - if (env_name[0] == '!') { - /* Delete */ - apr_table_unset(msr->r->subprocess_env, env_name + 1); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Unset env variable \"%s\".", env_name); - } - } else { - /* Set */ - char * val_value = NULL; - msc_string *val = apr_palloc(msr->mp, sizeof(msc_string)); - if (val == NULL) { - msr_log(msr, 1, "Failed to allocate space to expand value macros"); - return -1; - } - - /* Expand values in value */ - val->value = env_value; - val->value_len = strlen(val->value); - expand_macros(msr, val, rule, mptmp); - - /* To be safe, we escape NULs as it goes in subprocess_env. */ - val_value = log_escape_nul(msr->mp, (const unsigned char *)val->value, val->value_len); - - apr_table_set(msr->r->subprocess_env, env_name, val_value); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Set env variable \"%s\" to: %s", - env_name, - log_escape_nq(mptmp, val_value)); - } - } - - return 1; -} - -/* setvar */ -apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, char *var_name, char *var_value) -{ - char *col_name = NULL; - char *s = NULL; - apr_table_t *target_col = NULL; - int is_negated = 0; - msc_string *var = NULL; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Setting variable: %s=%s", var_name, var_value); - } - - /* Expand and escape any macros in the name */ - var = apr_palloc(msr->mp, sizeof(msc_string)); - if (var == NULL) { - msr_log(msr, 1, "Failed to allocate space to expand name macros"); - return -1; - } - var->value = var_name; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); - - /* Handle the exclamation mark. */ - if (var_name[0] == '!') { - var_name = var_name + 1; - is_negated = 1; - } - - /* ENH Not possible to use ! and = at the same time. */ - /* ENH Not possible to change variable "KEY". */ - - /* Figure out the collection name. */ - target_col = msr->tx_vars; - s = strstr(var_name, "."); - if (s == NULL) { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "Asked to set variable \"%s\", but no collection name specified. ", - log_escape(msr->mp, var_name)); - } - - return 0; - } - - col_name = var_name; - var_name = s + 1; - *s = '\0'; - - /* Locate the collection. */ - if (strcasecmp(col_name, "tx") == 0) { /* Special case for TX variables. */ - target_col = msr->tx_vars; - } else { - target_col = (apr_table_t *)apr_table_get(msr->collections, col_name); - if (target_col == NULL) { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "Could not set variable \"%s.%s\" as the collection does not exist.", - log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); - } - - return 0; - } - } - - if (is_negated) { - /* Unset variable. */ - - /* ENH Refuse to remove certain variables, e.g. TIMEOUT, internal variables, etc... */ - - apr_table_unset(target_col, var_name); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Unset variable \"%s.%s\".", col_name, var_name); - } - } - else { - /* Set or change variable. */ - - if ((var_value[0] == '+')||(var_value[0] == '-')) { - /* Relative change. */ - msc_string *rec = NULL; - msc_string *val = apr_palloc(msr->mp, sizeof(msc_string)); - int value = 0; - - if (val == NULL) { - msr_log(msr, 1, "Failed to allocate space to expand value macros"); - return -1; - } - - /* Retrieve variable or generate (if it does not exist). */ - rec = (msc_string *)apr_table_get(target_col, var_name); - if (rec == NULL) { - rec = var; /* use the already allocated space for var */ - rec->name = apr_pstrdup(msr->mp, var_name); - rec->name_len = strlen(rec->name); - value = 0; - rec->value = apr_psprintf(msr->mp, "%d", value); - rec->value_len = strlen(rec->value); - } - else { - value = atoi(rec->value); - } - - /* Record the original value before we change it */ - collection_original_setvar(msr, col_name, rec); - - /* Expand values in value */ - val->value = var_value; - val->value_len = strlen(val->value); - expand_macros(msr, val, rule, mptmp); - var_value = val->value; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Relative change: %s=%d%s", var_name, value, var_value); - } - - /* Change value. */ - value += atoi(var_value); - if (value < 0) value = 0; /* Counters never go below zero. */ - - /* Put the variable back. */ - rec->value = apr_psprintf(msr->mp, "%d", value); - rec->value_len = strlen(rec->value); - apr_table_setn(target_col, rec->name, (void *)rec); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", - col_name, rec->name, - log_escape_ex(mptmp, rec->value, rec->value_len)); - } - } - else { - /* Absolute change. */ - - var->name = apr_pstrdup(msr->mp, var_name); - var->name_len = strlen(var->name); - var->value = apr_pstrdup(msr->mp, var_value); - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - apr_table_setn(target_col, var->name, (void *)var); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", - log_escape(mptmp, col_name), - log_escape_ex(mptmp, var->name, var->name_len), - log_escape_ex(mptmp, var->value, var->value_len)); - } - } - } - - /* Make note of the change so that we know later - * we need to persist the collection. - */ - apr_table_set(msr->collections_dirty, col_name, "1"); - - return 1; -} - -/* -* \brief Parse fuction for setvar input -* -* \param msr Pointer to the engine -* \param mptmp Pointer to the pool -* \param rule Pointer to rule struct -* \param action input data -* -* \retval -1 On failure -* \retval 0 On Collection failure -* \retval 1 On Success -*/ -static apr_status_t msre_action_setvar_parse(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - char *data = apr_pstrdup(mptmp, action->param); - char *var_name = NULL, *var_value = NULL; - char *s = NULL; - - /* Extract the name and the value. */ - /* IMP1 We have a function for this now, parse_name_eq_value? */ - s = strstr(data, "="); - if (s == NULL) { - var_name = data; - var_value = "1"; - } else { - var_name = data; - var_value = s + 1; - *s = '\0'; - - while ((*var_value != '\0')&&(isspace(*var_value))) var_value++; - } - - return msre_action_setvar_execute(msr,mptmp,rule,var_name,var_value); -} - -/* expirevar */ -static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - char *data = apr_pstrdup(mptmp, action->param); - char *col_name = NULL, *var_name = NULL, *var_value = NULL; - char *s = NULL; - apr_table_t *target_col = NULL; - msc_string *var = NULL; - - /* Extract the name and the value. */ - /* IMP1 We have a function for this now, parse_name_eq_value? */ - s = strstr(data, "="); - if (s == NULL) { - var_name = data; - var_value = "1"; - } else { - var_name = data; - var_value = s + 1; - *s = '\0'; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Expiring variable: %s=%s", var_name, var_value); - } - - /* Expand and escape any macros in the name */ - var = apr_palloc(msr->mp, sizeof(msc_string)); - if (var == NULL) { - msr_log(msr, 1, "Failed to allocate space to expand name macros"); - return -1; - } - var->value = var_name; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); - - /* Choose the collection to work with. */ - s = strstr(var_name, "."); - if (s != NULL) { - col_name = var_name; - var_name = s + 1; - *s = '\0'; - - /* IMP1 No need to handle TX here because TX variables cannot expire, - * but we definitely need to have a better error message. - */ - - target_col = (apr_table_t *)apr_table_get(msr->collections, col_name); - if (target_col == NULL) { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "Could not expire variable \"%s.%s\" as the collection does not exist.", - log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); - } - - return 0; - } - } else { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "Asked to expire variable \"%s\", but no collection name specified. ", - log_escape(msr->mp, var_name)); - } - - return 0; - } - - /* To expire a variable we just place a special variable into - * the collection. Expiry actually happens when the collection - * is retrieved from storage the next time. - */ - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = apr_psprintf(msr->mp, "__expire_%s", var_name); - var->name_len = strlen(var->name); - - /* Expand macros in value */ - var->value = var_value; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, msr->mp); - var_value = var->value; - - /* Calculate with the expanded value */ - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) - + atoi(var_value))); - var->value_len = strlen(var->value); - - apr_table_setn(target_col, var->name, (void *)var); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Variable \"%s.%s\" set to expire in %s seconds.", col_name, - var_name, var_value); - } - - apr_table_set(msr->collections_dirty, col_name, "1"); - - return 1; -} - -/* deprecatevar */ -static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - char *data = apr_pstrdup(mptmp, action->param); - char *col_name = NULL, *var_name = NULL, *var_value = NULL; - char *s = NULL; - apr_table_t *target_col = NULL; - msc_string *var = NULL, *var_last_update_time = NULL; - apr_time_t last_update_time, current_time; - long current_value, new_value; - - /* Extract the name and the value. */ - /* IMP1 We have a function for this now, parse_name_eq_value? */ - s = strstr(data, "="); - if (s == NULL) { - var_name = data; - var_value = "1"; - } else { - var_name = data; - var_value = s + 1; - *s = '\0'; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Deprecating variable: %s=%s", var_name, var_value); - } - - /* Expand and escape any macros in the name */ - var = apr_palloc(msr->mp, sizeof(msc_string)); - if (var == NULL) { - msr_log(msr, 1, "Failed to allocate space to expand name macros"); - return -1; - } - var->value = var_name; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); - - /* Expand macros in value */ - var->value = var_value; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, msr->mp); - var_value = var->value; - - /* Choose the collection to work with. */ - s = strstr(var_name, "."); - if (s != NULL) { - col_name = var_name; - var_name = s + 1; - *s = '\0'; - - /* IMP1 Add message TX variables cannot deprecate in value. */ - - target_col = (apr_table_t *)apr_table_get(msr->collections, col_name); - if (target_col == NULL) { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "Could not deprecate variable \"%s.%s\" as the collection does " - "not exist.", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); - } - - return 0; - } - } else { - if (msr->txcfg->debuglog_level >= 3) { - msr_log(msr, 3, "Asked to deprecate variable \"%s\", but no collection name specified. ", - log_escape(msr->mp, var_name)); - } - - return 0; - } - - /* Find the current value. */ - var = (msc_string *)apr_table_get(target_col, var_name); - if (var == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Asked to deprecate variable \"%s.%s\", but it does not exist.", - log_escape(msr->mp, col_name), log_escape(msr->mp, var_name)); - } - return 0; - } - current_value = atoi(var->value); - - /* Find the last update time (of the collection). */ - var_last_update_time = (msc_string *)apr_table_get(target_col, "LAST_UPDATE_TIME"); - if (var_last_update_time == NULL) { - /* This is all right. If collection was created (and not restored from - * storage) then it won't have LAST_UPDATE_TIME - it was never updated. - */ - return 0; - } - - current_time = apr_time_sec(apr_time_now()); - last_update_time = atoi(var_last_update_time->value); - - s = strstr(var_value, "/"); - if (s == NULL) { - msr_log(msr, 3, "Incorrect format for the deprecatevar argument: \"%s\"", - log_escape(msr->mp, var_value)); - return 0; - } - *s = '\0'; - s++; - - /* Deprecate the value using the given speed and the - * time elapsed since the last update. - */ - new_value = current_value - - (atol(var_value) * ((current_time - last_update_time) / atol(s))); - if (new_value < 0) new_value = 0; - - /* Only change the value if it differs. */ - if (new_value != current_value) { - var->value = apr_psprintf(msr->mp, "%ld", new_value); - var->value_len = strlen(var->value); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %ld to %ld (%" APR_TIME_T_FMT " seconds since " - "last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), - current_value, new_value, (apr_time_t)(current_time - last_update_time)); - } - - apr_table_set(msr->collections_dirty, col_name, "1"); - } else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Not deprecating variable \"%s.%s\" because the new value (%ld) is " - "the same as the old one (%ld) (%" APR_TIME_T_FMT " seconds since last update).", - log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), current_value, - new_value, (apr_time_t)(current_time - last_update_time)); - } - } - - return 1; -} - -static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, - const char *col_name, const char *col_key, unsigned int col_key_len) -{ - apr_table_t *table = NULL; - msc_string *var = NULL; - - /* IMP1 Cannot initialise the built-in collections this way. */ - - /* Does the collection exist already? */ - if (apr_table_get(msr->collections, col_name) != NULL) { - /* ENH Warn about this. */ - return 0; - } - - /* Init collection from storage. */ - table = collection_retrieve(msr, real_col_name, col_key, col_key_len); - - if (table == NULL) { - /* Does not exist yet - create new. */ - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Creating collection (name \"%s\", key \"%s\").", - real_col_name, col_key); - } - - table = apr_table_make(msr->mp, 24); - if (table == NULL) return -1; - - /* IMP1 Is the timeout hard-coded to 3600? */ - - if(msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Setting default timeout collection value %d.",msr->txcfg->col_timeout); - } - - /* Add default timeout. */ - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "__expire_KEY"; - var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) + msr->txcfg->col_timeout)); - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); - - /* Remember the key. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "KEY"; - var->name_len = strlen(var->name); - var->value = apr_pstrmemdup(msr->mp, col_key, col_key_len); - var->value_len = col_key_len; - apr_table_setn(table, var->name, (void *)var); - - /* The timeout. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "TIMEOUT"; - var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%d", msr->txcfg->col_timeout); - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); - - /* We may want to allow the user to unset KEY - * but we still need to preserve value to identify - * the collection in storage. - */ - - /* IMP1 Actually I want a better way to delete collections, - * perhaps a dedicated action. - */ - - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "__key"; - var->name_len = strlen(var->name); - var->value = apr_pstrmemdup(msr->mp, col_key, col_key_len); - var->value_len = col_key_len; - apr_table_setn(table, var->name, (void *)var); - - /* Peristence code will need to know the name of the collection. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "__name"; - var->name_len = strlen(var->name); - var->value = apr_pstrdup(msr->mp, real_col_name); - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); - - /* Create time. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "CREATE_TIME"; - var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)apr_time_sec(msr->request_time)); - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); - - /* Update counter. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "UPDATE_COUNTER"; - var->name_len = strlen(var->name); - var->value = "0"; - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); - - /* This is a new collection. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "IS_NEW"; - var->name_len = strlen(var->name); - var->value = "1"; - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); - } - - /* Record the original counter value before we change it */ - var = (msc_string *)apr_table_get(table, "UPDATE_COUNTER"); - if (var != NULL) { - collection_original_setvar(msr, col_name, var); - } - - /* Add the collection to the list. */ - apr_table_setn(msr->collections, apr_pstrdup(msr->mp, col_name), (void *)table); - - if (msr->txcfg->debuglog_level >= 4) { - if (strcmp(col_name, real_col_name) != 0) { - msr_log(msr, 4, "Added collection \"%s\" to the list as \"%s\".", - log_escape(msr->mp, real_col_name), log_escape(msr->mp, col_name)); - } else { - msr_log(msr, 4, "Added collection \"%s\" to the list.", - log_escape(msr->mp, real_col_name)); - } - } - - return 1; -} - -/* initcol */ -static apr_status_t msre_action_initcol_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - char *data = apr_pstrdup(msr->mp, action->param); - char *col_name = NULL, *col_key = NULL; - unsigned int col_key_len; - - msc_string *var = NULL; - char *s = NULL; - - /* Extract the name and the value. */ - /* IMP1 We have a function for this now, parse_name_eq_value? */ - s = strstr(data, "="); - if (s == NULL) return 0; - col_name = strtolower_inplace((unsigned char *)data); - col_key = s + 1; - *s = '\0'; - - /* Expand the key and init collection from storage. */ - var = apr_pcalloc(mptmp, sizeof(msc_string)); - var->value = col_key; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - - col_key = var->value; - col_key_len = var->value_len; - - return init_collection(msr, col_name, col_name, col_key, col_key_len); -} - -/* setsid */ -static apr_status_t msre_action_setsid_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - char *real_col_name = NULL, *col_key = NULL; - unsigned int col_key_len; - - /* Construct session ID. */ - var = apr_pcalloc(mptmp, sizeof(msc_string)); - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - msr->sessionid = apr_pstrdup(msr->mp, var->value); - - /* Construct collection name. */ - col_key = var->value; - col_key_len = var->value_len; - real_col_name = apr_psprintf(mptmp, "%s_SESSION", msr->txcfg->webappid); - - /* Initialise collection. */ - return init_collection(msr, real_col_name, "SESSION", col_key, col_key_len); -} - -/* setuid */ -static apr_status_t msre_action_setuid_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - char *real_col_name = NULL, *col_key = NULL; - unsigned int col_key_len; - - /* Construct user ID. */ - var = apr_pcalloc(mptmp, sizeof(msc_string)); - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - msr->userid = apr_pstrdup(msr->mp, var->value); - - /* Construct collection name. */ - col_key = var->value; - col_key_len = var->value_len; - real_col_name = apr_psprintf(mptmp, "%s_USER", msr->txcfg->webappid); - - /* Initialise collection. */ - return init_collection(msr, real_col_name, "USER", col_key, col_key_len); -} - -/* setrsc */ -static apr_status_t msre_action_setrsc_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - char *real_col_name = NULL, *col_key = NULL; - unsigned int col_key_len; - - /* Construct user ID. */ - var = apr_pcalloc(mptmp, sizeof(msc_string)); - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - - /* Construct collection name. */ - col_key = var->value; - col_key_len = var->value_len; - real_col_name = apr_psprintf(mptmp, "%s_RESOURCE", msr->txcfg->webappid); - - /* Initialise collection. */ - return init_collection(msr, real_col_name, "RESOURCE", col_key, col_key_len); -} - -/* exec */ -static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) { - #if defined(WITH_LUA) - char *filename = (char *)action->param; - - /* TODO Support relative filenames. */ - - /* Process Lua scripts internally. */ - if (strlen(filename) > 4) { - char *p = filename + strlen(filename) - 4; - if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) { - /* It's a Lua script. */ - msc_script *script = NULL; - - /* Compile script. */ - char *msg = lua_compile(&script, filename, engine->mp); - if (msg != NULL) return msg; - - action->param_data = script; - } - } - #endif - - return NULL; -} - -static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - #if defined(WITH_LUA) - if (action->param_data != NULL) { /* Lua */ - msc_script *script = (msc_script *)action->param_data; - char *my_error_msg = NULL; - - if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) { - msr_log(msr, 1, "%s", my_error_msg); - return 0; - } - } else - #endif - { /* Execute as shell script. */ - char *script_output = NULL; - - int rc = apache2_exec(msr, action->param, NULL, &script_output); - if (rc != 1) { - msr_log(msr, 1, "Failed to execute: %s", action->param); - return 0; - } - } - - return 1; -} - -/* prepend */ -static apr_status_t msre_action_prepend_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - - /* Expand any macros in the text */ - var = apr_pcalloc(mptmp, sizeof(msc_string)); - if (var == NULL) return -1; - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - - /* ENH: Verify we really have to dup the data here. */ - msr->content_prepend = apr_pstrndup(msr->mp, var->value, var->value_len); - msr->content_prepend_len = var->value_len; - - return 1; -} - -/* append */ -static apr_status_t msre_action_append_execute(modsec_rec *msr, apr_pool_t *mptmp, - msre_rule *rule, msre_action *action) -{ - msc_string *var = NULL; - - /* Expand any macros in the text */ - var = apr_pcalloc(mptmp, sizeof(msc_string)); - if (var == NULL) return -1; - var->value = (char *)action->param; - var->value_len = strlen(var->value); - expand_macros(msr, var, rule, mptmp); - - /* ENH: Verify we really have to dup the data here. */ - msr->content_append = apr_pstrndup(msr->mp, var->value, var->value_len); - msr->content_append_len = var->value_len; - - return 1; -} - -/* -- */ - -/** - * - */ -void msre_engine_register_default_actions(msre_engine *engine) { - - /* id */ - msre_engine_action_register(engine, - "id", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - msre_action_id_validate, - msre_action_id_init, - NULL - ); - - /* marker */ - msre_engine_action_register(engine, - "marker", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_marker_init, - NULL - ); - - /* rev */ - msre_engine_action_register(engine, - "rev", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_rev_init, - NULL - ); - - /* msg */ - msre_engine_action_register(engine, - "msg", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_msg_init, - NULL - ); - - /* logdata */ - msre_engine_action_register(engine, - "logdata", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_logdata_init, - NULL - ); - - /* accuracy */ - msre_engine_action_register(engine, - "accuracy", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_accuracy_init, - NULL - ); - - /* maturity */ - msre_engine_action_register(engine, - "maturity", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_maturity_init, - NULL - ); - - /* ver */ - msre_engine_action_register(engine, - "ver", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_ver_init, - NULL - ); - - /* severity */ - msre_engine_action_register(engine, - "severity", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_severity_init, - NULL - ); - - /* chain */ - msre_engine_action_register(engine, - "chain", - ACTION_FLOW, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - msre_action_chain_init, - NULL - ); - - /* log */ - msre_engine_action_register(engine, - "log", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_LOG, - NULL, - msre_action_log_init, - NULL - ); - - /* nolog */ - msre_engine_action_register(engine, - "nolog", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_LOG, - NULL, - msre_action_nolog_init, - NULL - ); - - /* auditlog */ - msre_engine_action_register(engine, - "auditlog", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_AUDITLOG, - NULL, - msre_action_auditlog_init, - NULL - ); - - /* noauditlog */ - msre_engine_action_register(engine, - "noauditlog", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_AUDITLOG, - NULL, - msre_action_noauditlog_init, - NULL - ); - - /* deny */ - msre_engine_action_register(engine, - "block", - ACTION_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - NULL, - msre_action_block_init, - NULL - ); - - /* deny */ - msre_engine_action_register(engine, - "deny", - ACTION_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - NULL, - msre_action_deny_init, - NULL - ); - - /* status */ - msre_engine_action_register(engine, - "status", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - msre_action_status_validate, - msre_action_status_init, - NULL - ); - - /* drop */ - msre_engine_action_register(engine, - "drop", - ACTION_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - NULL, - msre_action_drop_init, - NULL - ); - - /* pause */ - msre_engine_action_register(engine, - "pause", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - msre_action_pause_validate, - msre_action_pause_init, - NULL - ); - - /* redirect */ - msre_engine_action_register(engine, - "redirect", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - msre_action_redirect_validate, - msre_action_redirect_init, - msre_action_redirect_execute - ); - - /* proxy */ - msre_engine_action_register(engine, - "proxy", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - msre_action_proxy_validate, - msre_action_proxy_init, - msre_action_proxy_execute - ); - - /* pass */ - msre_engine_action_register(engine, - "pass", - ACTION_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - NULL, - msre_action_pass_init, - NULL - ); - - /* skip */ - msre_engine_action_register(engine, - "skip", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - msre_action_skip_validate, - msre_action_skip_init, - NULL - ); - - /* skipAfter */ - msre_engine_action_register(engine, - "skipAfter", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - msre_action_skipAfter_validate, - msre_action_skipAfter_init, - NULL - ); - - /* allow */ - msre_engine_action_register(engine, - "allow", - ACTION_DISRUPTIVE, - 0, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_DISRUPTIVE, - msre_action_allow_validate, - msre_action_allow_init, - NULL - ); - - /* phase */ - /* ENH: This should be ACTION_NON_DISRUPTIVE or ACTION_FLOW??? */ - msre_engine_action_register(engine, - "phase", - ACTION_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - msre_action_phase_validate, - msre_action_phase_init, - NULL - ); - - /* t */ - msre_engine_action_register(engine, - "t", - ACTION_NON_DISRUPTIVE, - 1, 1, - ALLOW_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - msre_action_t_validate, - msre_action_t_init, - NULL - ); - - /* ctl */ - msre_engine_action_register(engine, - "ctl", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - msre_action_ctl_validate, - msre_action_ctl_init, - msre_action_ctl_execute - ); - - /* xmlns */ - msre_engine_action_register(engine, - "xmlns", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - msre_action_xmlns_validate, - NULL, - NULL - ); - - /* capture */ - msre_engine_action_register(engine, - "capture", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - NULL - ); - - /* sanitiseArg */ - msre_engine_action_register(engine, - "sanitiseArg", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeArg_execute - ); - - /* sanitiseMatchedBytes */ - msre_engine_action_register(engine, - "sanitiseMatchedBytes", - ACTION_NON_DISRUPTIVE, - 0, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - msre_action_sanitizeMatchedBytes_init, - msre_action_sanitizeMatched_execute - ); - - /* sanitizeMatchedBytes */ - msre_engine_action_register(engine, - "sanitizeMatchedBytes", - ACTION_NON_DISRUPTIVE, - 0, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - msre_action_sanitizeMatchedBytes_init, - msre_action_sanitizeMatched_execute - ); - - /* sanitizeArg */ - msre_engine_action_register(engine, - "sanitizeArg", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeArg_execute - ); - - /* sanitiseMatched */ - msre_engine_action_register(engine, - "sanitiseMatched", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeMatched_execute - ); - - /* sanitizeMatched */ - msre_engine_action_register(engine, - "sanitizeMatched", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeMatched_execute - ); - - /* sanitiseRequestHeader */ - msre_engine_action_register(engine, - "sanitiseRequestHeader", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeRequestHeader_execute - ); - - /* sanitizeRequestHeader */ - msre_engine_action_register(engine, - "sanitizeRequestHeader", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeRequestHeader_execute - ); - - /* sanitiseResponseHeader */ - msre_engine_action_register(engine, - "sanitiseResponseHeader", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeResponseHeader_execute - ); - - /* sanitizeResponseHeader */ - msre_engine_action_register(engine, - "sanitizeResponseHeader", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_sanitizeResponseHeader_execute - ); - - /* setenv */ - msre_engine_action_register(engine, - "setenv", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_setenv_execute - ); - - /* setvar */ - msre_engine_action_register(engine, - "setvar", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_setvar_parse - ); - - /* expirevar */ - msre_engine_action_register(engine, - "expirevar", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_expirevar_execute - ); - - /* deprecatevar */ - msre_engine_action_register(engine, - "deprecatevar", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_deprecatevar_execute - ); - - /* initcol */ - msre_engine_action_register(engine, - "initcol", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_initcol_execute - ); - - /* setsid */ - msre_engine_action_register(engine, - "setsid", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_setsid_execute - ); - - /* setuid */ - msre_engine_action_register(engine, - "setrsc", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_setrsc_execute - ); - - /* setuid */ - msre_engine_action_register(engine, - "setuid", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_setuid_execute - ); - - /* exec */ - msre_engine_action_register(engine, - "exec", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - msre_action_exec_validate, - NULL, - msre_action_exec_execute - ); - - /* multiMatch */ - msre_engine_action_register(engine, - "multiMatch", - ACTION_NON_DISRUPTIVE, - 0, 0, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - NULL - ); - - /* tag */ - msre_engine_action_register(engine, - "tag", - ACTION_METADATA, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_MANY, - ACTION_CGROUP_NONE, - NULL, - NULL, - NULL - ); - - /* prepend */ - msre_engine_action_register(engine, - "prepend", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_prepend_execute - ); - - /* append */ - msre_engine_action_register(engine, - "append", - ACTION_NON_DISRUPTIVE, - 1, 1, - NO_PLUS_MINUS, - ACTION_CARDINALITY_ONE, - ACTION_CGROUP_NONE, - NULL, - NULL, - msre_action_append_execute - ); -} diff --git a/apache2/re_operators.c b/apache2/re_operators.c deleted file mode 100644 index 7bd822c29f..0000000000 --- a/apache2/re_operators.c +++ /dev/null @@ -1,4695 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "re.h" -#include "msc_pcre.h" -#include "msc_geo.h" -#include "msc_gsb.h" -#include "apr_lib.h" -#include "apr_strmatch.h" -#include "acmp.h" -#include "msc_util.h" -#include "msc_tree.h" -#include "msc_crypt.h" - -#if APR_HAVE_ARPA_INET_H -#include -#endif - -#include "libinjection/libinjection.h" - -/** - * - */ -void msre_engine_op_register(msre_engine *engine, const char *name, - fn_op_param_init_t fn1, fn_op_execute_t fn2) -{ - msre_op_metadata *metadata = (msre_op_metadata *)apr_pcalloc(engine->mp, - sizeof(msre_op_metadata)); - if (metadata == NULL) return; - - metadata->name = name; - metadata->param_init = fn1; - metadata->execute = fn2; - apr_table_setn(engine->operators, name, (void *)metadata); -} - -/** - * - */ -msre_op_metadata *msre_engine_op_resolve(msre_engine *engine, const char *name) { - return (msre_op_metadata *)apr_table_get(engine->operators, name); -} - - - -/* -- Operators -- */ - -/* unconditionalMatch */ - -static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule, - msre_var *var, char **error_msg) -{ - *error_msg = "Unconditional match in SecAction."; - - /* Always match. */ - return 1; -} - -/* noMatch */ - -static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule, - msre_var *var, char **error_msg) -{ - *error_msg = "No match."; - - /* Never match. */ - return 0; -} - -/* ipmatch */ - -/** -* \brief Init function to ipmatch operator -* -* \param rule ModSecurity rule struct -* \param error_msg Error message -* -* \retval 1 On Success -* \retval 0 On Fail -*/ -static int msre_op_ipmatch_param_init(msre_rule *rule, char **error_msg) { - apr_status_t rv; - char *str = NULL; - char *saved = NULL; - char *param = NULL; - msre_ipmatch *current; - msre_ipmatch **last = &rule->ip_op; - - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; - - param = apr_pstrdup(rule->ruleset->mp, rule->op_param); - - str = apr_strtok(param, ",", &saved); - while( str != NULL) { - const char *ipstr, *mask, *sep; - - /* get the IP address and mask strings */ - sep = strchr(str, '/'); - if (sep) { - ipstr = apr_pstrndup(rule->ruleset->mp, str, (sep - str) ); - mask = apr_pstrdup(rule->ruleset->mp, (sep + 1) ); - } - else { - ipstr = apr_pstrdup(rule->ruleset->mp, str); - mask = NULL; - } - /* create a new msre_ipmatch containing a new apr_ipsubnet_t*, and add it to the linked list */ - current = apr_pcalloc(rule->ruleset->mp, sizeof(msre_ipmatch)); - rv = apr_ipsubnet_create(¤t->ipsubnet, ipstr, mask, rule->ruleset->mp); - if ( rv != APR_SUCCESS ) { - char msgbuf[120]; - apr_strerror(rv, msgbuf, sizeof msgbuf); - *error_msg = apr_pstrcat(rule->ruleset->mp, "Error: ", msgbuf, NULL); - return -1; - } - current->address = str; - current->next = NULL; - *last = current; - last = ¤t->next; - - str = apr_strtok(NULL, ",",&saved); - } - - return 1; -} - -/** -* \brief Execution function to ipmatch operator -* -* \param msr Pointer internal modsec request structure -* \param rule Pointer to the rule -* \param var Pointer to variable structure -* \param error_msg Pointer to error msg -* -* \retval -1 On Failure -* \retval 1 On Match -* \retval 0 On No Match -*/ -static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msre_ipmatch *current = rule->ip_op; - apr_sockaddr_t *sa; - - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; - - if(current == NULL) { - msr_log(msr, 1, "ipMatch Internal Error: ipmatch value is null."); - return 0; - } - - /* create an apr_sockaddr_t for the value string */ - if ( apr_sockaddr_info_get(&sa, var->value, APR_UNSPEC, 0, 0, msr->mp) != APR_SUCCESS ) { - msr_log(msr, 1, "ipMatch Internal Error: Invalid ip address."); - return 0; - } - - /* look through the linked list for a match */ - while (current) { - if (apr_ipsubnet_test(current->ipsubnet, sa)) { - *error_msg = apr_psprintf(msr->mp, "IPmatch \"%s\" matched \"%s\" at %s.", var->value, current->address, var->name); - return 1; - } - current = current->next; - } - - return 0; -} - -/** -* \brief Init function to ipmatchFromFile operator -* -* \param rule Pointer to the rule -* \param error_msg Pointer to error msg -* -* \retval 1 On Success -* \retval 0 On Fail -*/ -static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) { - char errstr[1024]; - char buf[HUGE_STRING_LEN + 1]; - const char *rootpath = NULL; - const char *filepath = NULL; - char *fn; - char *start; - char *end; - const char *ipfile_path; - int line = 0; - unsigned short int op_len; - apr_status_t rc; - apr_file_t *fd; - TreeRoot *rtree = NULL; - TreeNode *tnode = NULL; - - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; - - rtree = apr_palloc(rule->ruleset->mp, sizeof(TreeRoot)); - if(rtree == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Failed allocating memory to TreeRoot."); - return 0; - } - - memset(rtree, 0, sizeof(TreeRoot)); - - if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'ipmatchFromFile'."); - return 0; - } - - rtree->ipv4_tree = CPTCreateRadixTree(rule->ruleset->mp); - if (rtree->ipv4_tree == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "ipmatchFromFile: Tree tree initialization failed."); - return 0; - } - rtree->ipv6_tree = CPTCreateRadixTree(rule->ruleset->mp); - if (rtree->ipv6_tree == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "ipmatchFromFile: Tree tree initialization failed."); - return 0; - } - - fn = apr_pstrdup(rule->ruleset->mp, rule->op_param); - - ipfile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); - - while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++; - if (*fn == '\0') { - *error_msg = apr_psprintf(rule->ruleset->mp, "Empty file specification for operator ipmatchFromFile \"%s\"", fn); - return 0; - } - - filepath = fn; - if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) { - apr_filepath_merge(&fn, ipfile_path, fn, APR_FILEPATH_TRUENAME, 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 ipmatch file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); - return 0; - } - - while((rc = apr_file_gets(buf, HUGE_STRING_LEN, fd)) != APR_EOF) { - line++; - if (rc != APR_SUCCESS) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); - return 0; - } - - op_len = strlen(buf); - - start = buf; - while ((apr_isspace(*start) != 0) && (*start != '\0')) start++; - for (end = start; end != NULL || *end != '\0' || *end != '\n'; end++) { - if (apr_isxdigit(*end) || *end == '.' || *end == '/' || *end == ':') { - continue; - } - else { - if (*end != '\n') { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid char \"%c\" in line %d of file %s", *end, line, fn); - } - break; - } - } - *end = '\0'; - - if ((start == end) || (*start == '#')) continue; - - if (strchr(start, ':') == NULL) { - tnode = TreeAddIP(start, rtree->ipv4_tree, IPV4_TREE); - } - else { - tnode = TreeAddIP(start, rtree->ipv6_tree, IPV6_TREE); - } - if (tnode == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Could not add entry \"%s\" in line %d of file %s to IP list", start, line, fn); - } - } - - if (fd != NULL) apr_file_close(fd); - rule->op_param_data = rtree; - return 1; -} - -/** -* \brief Execution function to ipmatchFromFile operator -* -* \param msr Pointer internal modsec request structure -* \param rule Pointer to the rule -* \param var Pointer to variable structure -* \param error_msg Pointer to error msg -* -* \retval -1 On Failure -* \retval 1 On Match -* \retval 0 On No Match -*/ - -static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - TreeRoot *rtree = (TreeRoot *)rule->op_param_data; - struct in_addr in; -#if APR_HAVE_IPV6 - struct in6_addr in6; -#endif - - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; - - if(rtree == NULL) { - msr_log(msr, 1, "ipMatchFromFile Internal Error: tree value is null."); - return 0; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "IPmatchFromFile: Total tree entries: %d, ipv4 %d ipv6 %d", rtree->ipv4_tree->count+rtree->ipv6_tree->count, - rtree->ipv4_tree->count, rtree->ipv6_tree->count); - } - - if (strchr(var->value, ':') == NULL) { - if (inet_pton(AF_INET, var->value, &in) <= 0) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "IPmatchFromFile: bad IPv4 specification \"%s\".", var->value); - } - *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: bad IP specification \"%s\".", var->value); - return 0; - } - - if (CPTIpMatch(msr, (unsigned char *)&in.s_addr, rtree->ipv4_tree, IPV4_TREE) != NULL) { - *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name); - return 1; - } - } -#if APR_HAVE_IPV6 - else { - if (inet_pton(AF_INET6, var->value, &in6) <= 0) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "IPmatchFromFile: bad IPv6 specification \"%s\".", var->value); - } - *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: bad IP specification \"%s\".", var->value); - return 0; - } - - if (CPTIpMatch(msr, (unsigned char *)&in6.s6_addr, rtree->ipv6_tree, IPV6_TREE) != NULL) { - *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name); - return 1; - } - } -#endif - - return 0; -} - -/* rsub */ - -static char *param_remove_escape(msre_rule *rule, char *str, int len) { - char *parm = apr_pcalloc(rule->ruleset->mp, len); - char *ret = parm; - - for(;*str!='\0';str++) { - if(*str != '\\') { - *parm++ = *str; - } else { - str++; - if(*str != '/') { - str--; - *parm++ = *str; - } else { - *parm++ = *str; - } - } - } - - *parm = '\0'; - return ret; -} - -/** - * \brief Init function to rsub operator - * - * \param rule Pointer to the rule - * \param error_msg Pointer to error msg - * - * \retval 1 On Success - * \retval 0 On Fail - */ -#if !defined(MSC_TEST) -static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 - ap_regex_t *regex; -#else - regex_t *regex; -#endif - const char *pattern = NULL; - const char *line = NULL; - char *reg_pattern = NULL; - char *replace = NULL; - char *e_pattern = NULL; - char *parsed_replace = NULL; - char *flags = NULL; - char *data = NULL; - char delim; - int ignore_case = 0; - unsigned short int op_len = 0; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - line = rule->op_param; - - if (apr_tolower(*line) != 's') { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format, must be s/ pattern"); - return -1; - } - - data = apr_pstrdup(rule->ruleset->mp, line); - delim = *++data; - if (delim) - reg_pattern = ++data; - if (reg_pattern) { - - if (*data != delim) { - for(;*data != '\0' ;data++) { - if(*data == delim) { - data--; - if(*data == '\\') { - data++; - continue; - } - break; - } - } - } - - if (*data) { - *++data = '\0'; - ++data; - replace = data; - } - - } - - if (replace) { - - if (*data != delim) { - for(;*data != '\0' ;data++) { - if(*data == delim) { - data--; - if(*data == '\\') { - data++; - continue; - } - break; - } - } - } - - if (*data) { - *++data = '\0'; - flags = ++data; - } - - } - - if (!delim || !reg_pattern || !replace) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format - must be s/regex/str/[flags]"); - return -1; - } - - op_len = strlen(replace); - parsed_replace = apr_pstrdup(rule->ruleset->mp, parse_pm_content(param_remove_escape(rule, replace, strlen(replace)), - op_len, rule, error_msg)); - - if(!parsed_replace) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator parsing input data"); - return -1; - } - - rule->sub_str = apr_pstrmemdup(rule->ruleset->mp, parsed_replace, strlen(parsed_replace)); - - if (flags) { - while (*flags) { - delim = apr_tolower(*flags); - if (delim == 'i') - ignore_case = 1; - else if (delim == 'd') - rule->escape_re = 1; - else - *error_msg = apr_psprintf(rule->ruleset->mp, "Regex flag not supported"); - flags++; - } - } - - e_pattern = param_remove_escape(rule, reg_pattern, strlen(reg_pattern)); - pattern = apr_pstrndup(rule->ruleset->mp, e_pattern, strlen(e_pattern)); - - if(strstr(pattern,"%{") == NULL) { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 - regex = ap_pregcomp(rule->ruleset->mp, pattern, AP_REG_EXTENDED | - (ignore_case ? AP_REG_ICASE : 0)); -#else - regex = ap_pregcomp(rule->ruleset->mp, pattern, REG_EXTENDED | - (ignore_case ? REG_ICASE : 0)); -#endif - rule->sub_regex = regex; - } else { - rule->re_precomp = 1; - rule->re_str = apr_pstrndup(rule->ruleset->mp, pattern, strlen(pattern)); - rule->sub_regex = NULL; - } - - return 1; /* OK */ -} - -/** -* \brief Execution function to rsub operator -* -* \param msr Pointer internal modsec request structure -* \param rule Pointer to the rule -* \param var Pointer to variable structure -* \param error_msg Pointer to error msg -* -* \retval -1 On Failure -* \retval 1 On Match -* \retval 0 On No Match -*/ -static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - char *offset = NULL; - char *data = NULL, *pattern = NULL; - char *data_out = NULL; - unsigned int size = 0; - unsigned int maxsize=0; - int output_body = 0, input_body = 0, sl; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 - ap_regmatch_t pmatch[AP_MAX_REG_MATCH]; -#else - regmatch_t pmatch[AP_MAX_REG_MATCH]; -#endif - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if(strcmp(var->name,"STREAM_OUTPUT_BODY") == 0 ) { - output_body = 1; - } else if(strcmp(var->name,"STREAM_INPUT_BODY") == 0 ) { - input_body = 1; - } else { - msr_log(msr,9,"Operator rsub only works with STREAM_* variables"); - return -1; - } - if(rule->re_precomp == 1) { - re_pattern->value = apr_pstrndup(msr->mp, rule->re_str, strlen(rule->re_str)); - re_pattern->value_len = strlen(re_pattern->value); - - expand_macros(msr, re_pattern, rule, msr->mp); - - if(strlen(re_pattern->value) > 0) { - if(rule->escape_re == 1) { - pattern = log_escape_re(msr->mp, re_pattern->value); - if (msr->txcfg->debuglog_level >= 6) { - msr_log(msr, 6, "Escaping pattern [%s]",pattern); - } -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 - rule->sub_regex = ap_pregcomp(msr->mp, pattern, AP_REG_EXTENDED); -#else - rule->sub_regex = ap_pregcomp(msr->mp, pattern, REG_EXTENDED); -#endif - } else { -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 - rule->sub_regex = ap_pregcomp(msr->mp, re_pattern->value, AP_REG_EXTENDED); -#else - rule->sub_regex = ap_pregcomp(msr->mp, re_pattern->value, REG_EXTENDED); -#endif - } - } else { - rule->sub_regex = NULL; - } - - } - - if(rule->sub_regex == NULL) { - *error_msg = "Internal Error: regex data is null."; - return -1; - } - - str->value = apr_pstrndup(msr->mp, rule->sub_str, strlen(rule->sub_str)); - str->value_len = strlen(str->value); - - if(strstr(rule->sub_str,"%{") != NULL) - expand_macros(msr, str, rule, msr->mp); - - maxsize=var->value_len+(AP_MAX_REG_MATCH*1024)+1; - -nextround: - - data = apr_pcalloc(msr->mp, maxsize+1); - - if(data == NULL) { - *error_msg = "Internal Error: cannot allocate memory"; - return -1; - } - - data_out=data; - size=0; - for (offset = (char*)var->value; !ap_regexec(rule->sub_regex, offset, AP_MAX_REG_MATCH, pmatch, 0); ) { - //Copy of data before the regex match - int i; - int s = pmatch [0].rm_so; - int p_len=pmatch [0].rm_eo - pmatch [0].rm_so; - if (size+s>maxsize) { - maxsize*=2; - goto nextround; - } - memcpy(data_out,offset,s); - data_out+=s; - size+=s; - - //Copy of regex match with replacing data \1..\9 - for(i=0;ivalue_len;) { - char *x = str->value+i; - if (*x == '\\' && *(x + 1) > '0' && *(x + 1) <= '9') { - int capture=*(x + 1) - 48; - int capture_len=pmatch[capture].rm_eo-pmatch[capture].rm_so; - - if (size+capture_len>maxsize) - { - maxsize*=2; - goto nextround; - } - memcpy(data_out,offset+pmatch[capture].rm_so,capture_len); - data_out+= capture_len; - size+=capture_len; - i+=2; - } else { - - if (size+1>maxsize) { - maxsize*=2; - goto nextround; - } - *data_out=*(str->value+i); - data_out++; - size++; - i++; - } - } - offset+=s; - offset+=p_len; - } - - //Copy of data after the last regex match - sl = strlen(offset); - if (size+sl>maxsize) { - maxsize*=2; - goto nextround; - } - memcpy(data_out,offset,sl); - data_out+=sl; - size+=sl; - *data_out=0; - - if(msr->stream_output_data != NULL && output_body == 1) { - - memset(msr->stream_output_data, 0x0, msr->stream_output_length); - free(msr->stream_output_data); - msr->stream_output_data = NULL; - msr->stream_output_length = 0; - - msr->stream_output_data = (char *)malloc(size+1); - - if(msr->stream_output_data == NULL) { - return -1; - } - - msr->stream_output_length = size; - memset(msr->stream_output_data, 0x0, size+1); - - msr->of_stream_changed = 1; - - memcpy(msr->stream_output_data, data, size); - msr->stream_output_data[size] = '\0'; - - var->value_len = size; - var->value = msr->stream_output_data; - } - - if(msr->stream_input_data != NULL && input_body == 1) { - memset(msr->stream_input_data, 0x0, msr->stream_input_length); - free(msr->stream_input_data); - msr->stream_input_data = NULL; - msr->stream_input_length = 0; - - msr->stream_input_data = (char *)malloc(size+1); - - if(msr->stream_input_data == NULL) { - return -1; - } - - msr->stream_input_length = size; - memset(msr->stream_input_data, 0x0, size+1); - - msr->if_stream_changed = 1; - - memcpy(msr->stream_input_data, data, size); - msr->stream_input_data[size] = '\0'; - - var->value_len = size; - var->value = msr->stream_input_data; - } - - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Operator rsub succeeded."); - } - - return 1; -} -#endif /* MSC_TEST */ - -/** - * \brief Init function to validateHash - * - * \param rule ModSecurity rule struct - * \param error_msg Error message - * - * \retval 1 On success - * \retval 0 On fail - */ -static int msre_op_validateHash_param_init(msre_rule *rule, char **error_msg) { - const char *errptr = NULL; - int erroffset; - msc_regex_t *regex; - const char *pattern = rule->op_param; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int rc, jit; - #endif - #endif - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Compile pattern */ - if(strstr(pattern,"%{") == NULL) { - 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); - return 0; - } - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - } - #endif - #endif - - rule->op_param_data = regex; - } else { - rule->re_precomp = 1; - rule->re_str = apr_pstrndup(rule->ruleset->mp, pattern, strlen(pattern)); - rule->op_param_data = NULL; - } - - return 1; /* OK */ -} - -/** - * \brief Execute function to validateHash - * - * \param msr ModSecurity transaction resource - * \param rule ModSecurity rule struct - * \param var ModSecurity variable struct - * \param error_msg Error message - * - * \retval 1 On success - * \retval 0 On fail - */ -static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; - msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *target; - const char *errptr = NULL; - int erroffset; - unsigned int target_length; - char *my_error_msg = NULL; - int ovector[33]; - int rc; - const char *pattern = NULL; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int jit; - #endif - #endif - - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (msr->txcfg->hash_enforcement == HASH_DISABLED || msr->txcfg->hash_is_enabled == HASH_DISABLED) - return 0; - - if (regex == NULL) { - if(rule->re_precomp == 0) { - *error_msg = "Internal Error: regex data is null."; - return -1; - } else { - - if(re_pattern == NULL) { - *error_msg = "Internal Error: regex variable data is null."; - return -1; - } - - re_pattern->value = apr_pstrndup(msr->mp, rule->re_str, strlen(rule->re_str)); - re_pattern->value_len = strlen(re_pattern->value); - - expand_macros(msr, re_pattern, rule, msr->mp); - - pattern = log_escape_re(msr->mp, re_pattern->value); - if (msr->txcfg->debuglog_level >= 6) { - msr_log(msr, 6, "Escaping pattern [%s]",pattern); - } - - 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); - return 0; - } - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - if (msr->txcfg->debuglog_level >= 4) { - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - msr_log(msr, 4, "%s.", *error_msg); - } - } - #endif - #endif - } - } - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* We always use capture so that ovector can be used as working space - * 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 == 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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - *error_msg = apr_psprintf(msr->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "PCRE limits exceeded (%d): %s", - rule,((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,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; - } - - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - /* We no longer escape the pattern here as it is done when logging */ - char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "")); - char *hmac = NULL, *valid = NULL; - char *hash_link = NULL, *nlink = NULL; - - if (strlen(pattern) > 252) { - *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s.", - pattern, var->name); - } else { - *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s.", - pattern, var->name); - } - - valid = strstr(target, msr->txcfg->crypto_param_name); - - if(valid == NULL) { - - if (msr->txcfg->debuglog_level >= 9) - msr_log(msr, 9, "Request URI without hash parameter [%s]", target); - - if (strlen(pattern) > 252) { - *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s. No Hash parameter", - pattern, var->name); - } else { - *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s. No Hash parameter", - pattern, var->name); - } - return 1; - } else { - - if(strlen(valid) < strlen(msr->txcfg->crypto_param_name)+1) - return 1; - - hmac = valid+strlen(msr->txcfg->crypto_param_name)+1; - - nlink = apr_pstrmemdup(msr->mp, target, strlen(target) - strlen(valid) - 1); - - msr_log(msr, 9, "Validating URI %s size %d",nlink,strlen(nlink)); - - hash_link = do_hash_link(msr, (char *)nlink, HASH_ONLY); - - if(strcmp(hmac, hash_link) != 0) { - - if (strlen(pattern) > 252) { - *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s. Hash parameter hash value = [%s] Requested URI hash value = [%s]", - pattern, var->name, hmac, hash_link); - } else { - *error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s. Hash parameter hash value = [%s] Requested URI hash value = [%s]", - pattern, var->name, hmac, hash_link); - } - return 1; - } - } - - return 0; - } - - return 0; -} - -/* rx */ - -static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { - const char *errptr = NULL; - int erroffset; - msc_regex_t *regex; - const char *pattern = rule->op_param; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int rc, jit; - #endif - #endif - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Compile pattern */ - if(strstr(pattern,"%{") == NULL) { - 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); - return 0; - } - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - } - #endif - #endif - - rule->op_param_data = regex; - } else { - rule->re_precomp = 1; - rule->re_str = apr_pstrndup(rule->ruleset->mp, pattern, strlen(pattern)); - rule->op_param_data = NULL; - } - - return 1; /* OK */ -} - -static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; - msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *target; - const char *errptr = NULL; - int erroffset; - unsigned int target_length; - char *my_error_msg = NULL; - int ovector[33]; - int capture = 0; - int matched_bytes = 0; - int matched = 0; - int rc; - char *qspos = NULL; - const char *parm = NULL, *pattern = NULL; - msc_parm *mparm = NULL; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int jit; - #endif - #endif - - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (regex == NULL) { - if(rule->re_precomp == 0) { - *error_msg = "Internal Error: regex data is null."; - return -1; - } else { - - if(re_pattern == NULL) { - *error_msg = "Internal Error: regex variable data is null."; - return -1; - } - - re_pattern->value = apr_pstrndup(msr->mp, rule->re_str, strlen(rule->re_str)); - re_pattern->value_len = strlen(re_pattern->value); - - expand_macros(msr, re_pattern, rule, msr->mp); - - pattern = log_escape_re(msr->mp, re_pattern->value); - if (msr->txcfg->debuglog_level >= 6) { - msr_log(msr, 6, "Escaping pattern [%s]",pattern); - } - - 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); - return 0; - } - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - if (msr->txcfg->debuglog_level >= 4) { - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - msr_log(msr, 4, "%s.", *error_msg); - } - } - #endif - #endif - - - } - } - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* Are we supposed to capture subexpressions? */ - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; - if(!matched_bytes) - matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; - - matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0; - if(!matched) - matched = apr_table_get(rule->actionset->actions, "sanitiseMatched") ? 1 : 0; - - /* Show when the regex captures but "capture" is not set */ - if (msr->txcfg->debuglog_level >= 6) { - int capcount = 0; - rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount); - if (msr->txcfg->debuglog_level >= 6) { - if ((capture == 0) && (capcount > 0)) { - msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled."); - } - } - } - - /* We always use capture so that ovector can be used as working space - * 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 == 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"); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, "1"); - if (s->value == NULL) return -1; - s->value_len = 1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - *error_msg = apr_psprintf(msr->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "PCRE limits exceeded (%d): %s", - rule,((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,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; - } - - /* Handle captured subexpressions. */ - if (capture && rc > 0) { - int i; - - /* Unset any of the previously set capture variables. */ - apr_table_unset(msr->tx_vars, "0"); - apr_table_unset(msr->tx_vars, "1"); - apr_table_unset(msr->tx_vars, "2"); - apr_table_unset(msr->tx_vars, "3"); - apr_table_unset(msr->tx_vars, "4"); - apr_table_unset(msr->tx_vars, "5"); - apr_table_unset(msr->tx_vars, "6"); - apr_table_unset(msr->tx_vars, "7"); - apr_table_unset(msr->tx_vars, "8"); - apr_table_unset(msr->tx_vars, "9"); - - /* Use the available captures. */ - for(i = 0; i < rc; i++) { - 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); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrmemdup(msr->mp, - target + ovector[2 * i], ovector[2 * i + 1] - ovector[2 * i]); - if (s->value == NULL) return -1; - - s->value_len = (ovector[2 * i + 1] - ovector[2 * i]); - - apr_table_addn(msr->tx_vars, s->name, (void *)s); - - if(((matched == 1) || (matched_bytes == 1)) && (var != NULL) && (var->name != NULL)) { - qspos = apr_psprintf(msr->mp, "%s", var->name); - parm = strstr(qspos, ":"); - if (parm != NULL) { - parm++; - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - mparm->pad_1 = rule->actionset->arg_min; - mparm->pad_2 = rule->actionset->arg_max; - apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm); - } else { - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm); - } - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, - log_escape_nq_ex(msr->mp, s->value, s->value_len)); - } - } - } - - if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - /* We no longer escape the pattern here as it is done when logging */ - char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "")); - - /* This message will be logged. */ - if (strlen(pattern) > 252) { - *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.", - pattern, var->name); - } else { - *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", - pattern, var->name); - } - - return 1; - } - - /* No match. */ - return 0; -} - -/* pm */ - -static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { - ACMP *p; - const char *phrase; - const char *next; - unsigned short int op_len; - - if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'."); - return 0; /* ERROR */ - } - - op_len = strlen(rule->op_param); - - p = acmp_create(0, rule->ruleset->mp); - if (p == NULL) return 0; - - phrase = apr_pstrdup(rule->ruleset->mp, parse_pm_content(rule->op_param, op_len, rule, error_msg)); - - if(phrase == NULL) - phrase = apr_pstrdup(rule->ruleset->mp, rule->op_param); - - /* Loop through phrases */ - /* ENH: Need to allow quoted phrases w/space */ - for (;;) { - while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++; - if (*phrase == '\0') break; - next = phrase; - while((apr_isspace(*next) == 0) && (*next != 0)) next++; - acmp_add_pattern(p, phrase, NULL, NULL, next - phrase); - phrase = next; - } - acmp_prepare(p); - rule->op_param_data = p; - return 1; -} - -/* pmFromFile */ - -static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { - char errstr[1024]; - char buf[HUGE_STRING_LEN + 1]; - char *fn = NULL; - char *next = NULL; - char *start = NULL; - char *end = NULL; - const char *rulefile_path; - char *processed = NULL; - unsigned short int op_len; - apr_status_t rc; - apr_file_t *fd = NULL; - ACMP *p; - - if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pmFromFile'."); - return 0; /* ERROR */ - } - - p = acmp_create(0, rule->ruleset->mp); - if (p == NULL) return 0; - - fn = apr_pstrdup(rule->ruleset->mp, rule->op_param); - - /* Get the path of the rule filename to use as a base */ - rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); - - #ifdef DEBUG_CONF - fprintf(stderr, "Rulefile path: \"%s\"\n", rulefile_path); - #endif - - /* Loop through filenames */ - /* ENH: Need to allow quoted filenames w/space */ - for (;;) { - const char *rootpath = NULL; - const char *filepath = NULL; - int line = 0; - - /* Trim whitespace */ - while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++; - if (*fn == '\0') break; - next = fn; - while((apr_isspace(*next) == 0) && (*next != '\0')) next++; - while((apr_isspace(*next) != 0) && (*next != '\0')) *(next++) = '\0'; - - /* Add path of the rule filename for a relative phrase filename */ - filepath = fn; - if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) { - /* We are not an absolute path. It could mean an error, but - * let that pass through to the open call for a better error */ - apr_filepath_merge(&fn, rulefile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp); - } - - /* Open file and read */ - 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; - } - - #ifdef DEBUG_CONF - fprintf(stderr, "Loading phrase file: \"%s\"\n", fn); - #endif - - /* Read one pattern per line skipping empty/commented */ - for(;;) { - line++; - rc = apr_file_gets(buf, HUGE_STRING_LEN, fd); - if (rc == APR_EOF) break; - if (rc != APR_SUCCESS) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); - return 0; - } - - op_len = strlen(buf); - processed = apr_pstrdup(rule->ruleset->mp, parse_pm_content(buf, op_len, rule, error_msg)); - - /* Trim Whitespace */ - if(processed != NULL) - start = processed; - else - start = buf; - - while ((apr_isspace(*start) != 0) && (*start != '\0')) start++; - if(processed != NULL) - end = processed + strlen(processed); - else - 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 */ - if ((start == end) || (*start == '#')) continue; - - acmp_add_pattern(p, start, NULL, NULL, (end - start)); - } - fn = next; - } - if (fd != NULL) apr_file_close(fd); - acmp_prepare(p); - rule->op_param_data = p; - return 1; -} - -static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = NULL; - apr_status_t rc = 0; - int capture; - ACMPT pt; - - /* Nothing to read */ - if ((var->value == NULL) || (var->value_len == 0)) return 0; - - /* Are we supposed to capture subexpressions? */ - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - - pt.parser = (ACMP *)rule->op_param_data; - pt.ptr = NULL; - - rc = acmp_process_quick(&pt, &match, var->value, var->value_len); - if (rc) { - char *match_escaped = log_escape(msr->mp, match ? match : ""); - - /* This message will be logged. */ - if (strlen(match_escaped) > 252) { - *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.", - match_escaped, var->name); - } else { - *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.", - match_escaped, var->name); - } - - /* Handle capture as tx.0=match */ - if (capture) { - int i; - msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - - 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); - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added phrase match to TX.0: %s", - log_escape_nq_ex(msr->mp, s->value, s->value_len)); - } - - /* Unset the remaining ones (from previous invocations). */ - for(i = rc; i <= 9; i++) { - char buf[2]; - apr_snprintf(buf, sizeof(buf), "%d", i); - apr_table_unset(msr->tx_vars, buf); - } - } - - return 1; - } - return rc; -} - -/* gsbLookup */ - -/** - * \brief Reduce /./ to / - * - * \param pool Pointer to the memory pool - * \param domain Input data - * - * \retval domain On Failure - * \retval url On Success - */ -static const char *gsb_replace_tpath(apr_pool_t *pool, const char *domain, int len) { - - char *pos = NULL, *data = NULL; - char *url = NULL; - int match = 0; - - url = apr_palloc(pool, len + 1); - data = apr_palloc(pool, len + 1); - - memset(data, 0, len+1); - memset(url, 0, len+1); - - memcpy(url, domain, len); - - while(( pos = strstr(url , "/./" )) != NULL) { - match = 1; - data[0] = '\0'; - strncat(data, url, pos - url); - strcat(data , "/"); - strcat(data ,pos + strlen("/./")); - strncpy(url , data, len); - } - - if(match == 0) - return domain; - - return url; -} - -/** - * \brief Reduce double dot to single dot - * - * \param msr ModSecurity transation resource - * \param domain Input data - * - * \retval domain On Failure - * \retval reduced On Success - */ -static const char *gsb_reduce_char(apr_pool_t *pool, const char *domain) { - - char *ptr = apr_pstrdup(pool, domain); - char *data = NULL; - char *reduced = NULL; - int skip = 0; - - - if(ptr == NULL) - return domain; - - data = apr_pcalloc(pool, strlen(ptr)); - - if(data == NULL) - return domain; - - reduced = data; - - while(*ptr != '\0') { - - switch(*ptr) { - case '.': - ptr++; - if(*ptr == '.') - skip = 1; - - ptr--; - break; - case '/': - ptr++; - if(*ptr == '/') - skip = 1; - - ptr--; - break; - } - - if(skip == 0) { - *data = *ptr; - data++; - } - ptr++; - skip = 0; - } - - *data = '\0'; --data; - - if(*data == '.') - *data = '\0'; - else - ++data; - - return reduced; -} - - -/** - * \brief Verify function to gsbLookup operator - * - * \param msr ModSecurity transaction resource - * \param match Pointer to input data - * \param match_length Input size - * - * \retval -1 On Failure - * \retval 1 On Match - * \retval 0 On No Match - */ -static int verify_gsb(gsb_db *gsb, modsec_rec *msr, const char *match, unsigned int match_length) { - apr_md5_ctx_t ctx; - apr_status_t rc; - unsigned char digest[APR_MD5_DIGESTSIZE]; - const char *hash = NULL; - const char *search = NULL; - - memset(digest, 0, sizeof(digest)); - - apr_md5_init(&ctx); - - if ((rc = apr_md5_update(&ctx, match, match_length)) != APR_SUCCESS) - return -1; - - apr_md5_final(digest, &ctx); - - hash = apr_psprintf(msr->mp, "%s", bytes2hex(msr->mp, digest, 16)); - - if ((hash != NULL) && (gsb->gsb_table != NULL)) { - search = apr_hash_get(gsb->gsb_table, hash, APR_HASH_KEY_STRING); - - if (search != NULL) - return 1; - } - - return 0; -} - -/** - * \brief Init function to gsbLookup operator - * - * \param rule ModSecurity rule struct - * \param error_msg Error message - * - * \retval 1 On Success - * \retval 0 On Fail - */ -static int msre_op_gsbLookup_param_init(msre_rule *rule, char **error_msg) { - const char *errptr = NULL; - int erroffset; - msc_regex_t *regex; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Compile rule->op_param */ - 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); - return 0; - } - - rule->op_param_data = regex; - - return 1; /* OK */ -} - -/** - * \brief Execution function to gsbLookup operator - * - * \param msr ModSecurity transaction resource - * \param rule ModSecurity rule struct - * \param var ModSecurity variable struct - * \param error_msg Error message - * - * \retval -1 On Failure - * \retval 1 On Match - * \retval 0 On No Match - */ -static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; - char *my_error_msg = NULL; - int ovector[33]; - unsigned int offset = 0; - gsb_db *gsb = msr->txcfg->gsb; - const char *match = NULL; - unsigned int match_length; - unsigned int canon_length; - int rv, i, ret, count_slash; - unsigned int j = 0; - unsigned int size = var->value_len; - char *base = NULL, *domain = NULL, *savedptr = NULL; - char *str = NULL, *canon = NULL, *dot = NULL; - char *data = NULL, *ptr = NULL, *url = NULL; - int capture, domain_len; - int d_pos = -1; - int s_pos = -1; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if(regex == NULL) { - *error_msg = "Internal Error: regex is null."; - return 0; - } - - if(gsb == NULL) { - msr_log(msr, 1, "GSB lookup failed without a database. Set SecGsbLookupDB."); - return 0; - } - - data = apr_pcalloc(rule->ruleset->mp, var->value_len+1); - - if(data == NULL) { - *error_msg = "Internal Error: cannot allocate memory for data."; - return -1; - } - - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - - memcpy(data,var->value,var->value_len); - - while (offset < size && (rv = msc_regexec_ex(regex, data, size, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg)) >= 0) - { - for(i = 0; i < rv; ++i) - { - match = apr_psprintf(rule->ruleset->mp, "%.*s", ovector[2*i+1] - ovector[2*i], data + ovector[2*i]); - - if (match == NULL) { - *error_msg = "Internal Error: cannot allocate memory for match."; - return -1; - } - - match = remove_escape(rule->ruleset->mp, match, strlen(match)); - - match = gsb_replace_tpath(rule->ruleset->mp, match, strlen(match)); - - match = gsb_reduce_char(rule->ruleset->mp, match); - - match_length = strlen(match); - - strtolower_inplace((unsigned char *)match); - - if((strstr(match,"http") == NULL) && (match_length > 0) && (strchr(match,'.'))) { - - /* full url */ - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "GSB: Successfully extracted url: %s", match); - } - - ret = verify_gsb(gsb, msr, match, match_length); - - if(ret > 0) { - set_match_to_tx(msr, capture, match, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, match)); - } - - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - - return 1; - } - - /* append / in the end of full url */ - if ((match[match_length -1] != '/') && (strchr(match,'?') == NULL)) { - - canon = apr_psprintf(rule->ruleset->mp, "%s/", match); - if (canon != NULL) { - - canon_length = strlen(canon); - ret = verify_gsb(gsb, msr, canon, canon_length); - - if(ret > 0) { - set_match_to_tx(msr, capture, match, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, canon)); - } - - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - - return 1; - } - } - } - - /* Parsing full url */ - - domain = apr_pstrdup(rule->ruleset->mp, match); - - domain_len = strlen(domain); - - if(*domain != '/') { - - if(domain[domain_len-1] == '.') - domain[domain_len-1] = '\0'; - if(domain[domain_len-1] == '/' && domain[domain_len-2] == '.') { - domain[domain_len-2] = '/'; - domain[domain_len-1] = '\0'; - } - - dot = strchr(domain,'.'); - if(dot != NULL) { - canon = apr_pstrdup(rule->ruleset->mp, domain); - - ret = verify_gsb(gsb, msr, canon, strlen(canon)); - - if(ret > 0) { - set_match_to_tx(msr, capture, canon, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, canon)); - } - - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - - return 1; - } - - - base = apr_strtok(canon,"?",&savedptr); - - if(base != NULL) { - ret = verify_gsb(gsb, msr, base, strlen(base)); - - if(ret > 0) { - set_match_to_tx(msr, capture, base, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, base)); - } - - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - - return 1; - } - - } - - url = apr_palloc(rule->ruleset->mp, strlen(canon)); - count_slash = 0; - - while(*canon != '\0') { - switch (*canon) { - case '/': - ptr = apr_psprintf(rule->ruleset->mp,"%s/",url); - ret = verify_gsb(gsb, msr, ptr, strlen(ptr)); - if(ret > 0) { - set_match_to_tx(msr, capture, ptr, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, ptr)); - } - - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - return 1; - } - - break; - } - url[count_slash] = *canon; - count_slash++; - canon++; - } - } - } - - /* Do the same for subdomains */ - - for(j=0; jruleset->mp, match); - - while (*str != '\0') { - - switch(*str) { - case '.': - domain++; - domain_len = strlen(domain); - - d_pos = strchr(domain,'.') - domain; - - if(s_pos >= 0 && d_pos >= 0 && d_pos > s_pos) - break; - - if(*domain != '/') { - - if(domain[domain_len-1] == '.') - domain[domain_len-1] = '\0'; - if(domain[domain_len-1] == '/' && domain[domain_len-2] == '.') { - domain[domain_len-2] = '/'; - domain[domain_len-1] = '\0'; - } - - dot = strchr(domain,'.'); - if(dot != NULL) { - canon = apr_pstrdup(rule->ruleset->mp, domain); - - ret = verify_gsb(gsb, msr, canon, strlen(canon)); - - if(ret > 0) { - set_match_to_tx(msr, capture, canon, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, canon)); - } - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - return 1; - } - - - base = apr_strtok(canon,"?",&savedptr); - - if(base != NULL) { - ret = verify_gsb(gsb, msr, base, strlen(base)); - - if(ret > 0) { - set_match_to_tx(msr, capture, base, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, base)); - } - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - return 1; - } - - } - - url = apr_palloc(rule->ruleset->mp, strlen(canon)); - count_slash = 0; - - while(*canon != '\0') { - switch (*canon) { - case '/': - ptr = apr_psprintf(rule->ruleset->mp,"%s/",url); - ret = verify_gsb(gsb, msr, ptr, strlen(ptr)); - if(ret > 0) { - set_match_to_tx(msr, capture, ptr, 0); - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", - log_escape_nq(msr->mp, ptr)); - } - str = apr_pstrdup(rule->ruleset->mp,match); - - base = apr_strtok(str,"/",&savedptr); - if(base != NULL) - set_match_to_tx(msr, capture, base, 1); - return 1; - } - - break; - } - url[count_slash] = *canon; - count_slash++; - canon++; - } - } - } - break; - } - - domain = str; - domain++; - str++; - } - - } - } - - offset = ovector[1]; - } - - return 0; -} - -/* within */ - -static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *match = NULL; - const char *target; - unsigned int match_length; - unsigned int target_length = 0; - unsigned int i, i_max; - - str->value = (char *)rule->op_param; - - if (str->value == NULL) { - *error_msg = "Internal Error: match string is null."; - return -1; - } - - str->value_len = strlen(str->value); - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - expand_macros(msr, str, rule, msr->mp); - - match = (const char *)str->value; - match_length = str->value_len; - - /* If the given target is null we give up without a match */ - if (var->value == NULL) { - /* No match. */ - return 0; - } - - target = var->value; - target_length = var->value_len; - - /* The empty string always matches */ - if (target_length == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match within \"\" at %s.", - var->name); - return 1; - } - - /* This is impossible to match */ - if (target_length > match_length) { - /* No match. */ - return 0; - } - - /* scan for first character, then compare from there until we - * have a match or there is no room left in the target - */ - i_max = match_length - target_length; - for (i = 0; i <= i_max; i++) { - if (match[i] == target[0]) { - if (memcmp((target + 1), (match + i + 1), (target_length - 1)) == 0) { - /* match. */ - *error_msg = apr_psprintf(msr->mp, "String match within \"%s\" at %s.", - log_escape_ex(msr->mp, match, match_length), - var->name); - return 1; - } - } - } - - /* No match. */ - return 0; -} - -/* contains */ - -static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *match = NULL; - const char *target; - unsigned int match_length; - unsigned int target_length = 0; - unsigned int i, i_max; - - str->value = (char *)rule->op_param; - - if (str->value == NULL) { - *error_msg = "Internal Error: match string is null."; - return -1; - } - - str->value_len = strlen(str->value); - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - expand_macros(msr, str, rule, msr->mp); - - match = (const char *)str->value; - match_length = str->value_len; - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* The empty string always matches */ - if (match_length == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); - return 1; - } - - /* This is impossible to match */ - if (match_length > target_length) { - /* No match. */ - return 0; - } - - /* scan for first character, then compare from there until we - * have a match or there is no room left in the target - */ - i_max = target_length - match_length; - for (i = 0; i <= i_max; i++) { - /* First character matched - avoid func call */ - if (target[i] == match[0]) { - /* See if remaining matches */ - if ( (match_length == 1) - || (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0)) - { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", - log_escape_ex(msr->mp, match, match_length), - var->name); - return 1; - } - } - } - - /* No match. */ - return 0; -} - -/** libinjection detectSQLi - * links against files in libinjection directory - * See www.client9.com/libinjection for details - */ -static int msre_op_detectSQLi_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) { - - struct libinjection_sqli_state sqli_state; - int issqli; - int capture; - - libinjection_sqli_init(&sqli_state, var->value, var->value_len, 0); - issqli = libinjection_is_sqli(&sqli_state); - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - - if (issqli) { - set_match_to_tx(msr, capture, sqli_state.fingerprint, 0); - - *error_msg = apr_psprintf(msr->mp, "detected SQLi using libinjection with fingerprint '%s'", - sqli_state.fingerprint); - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "ISSQL: libinjection fingerprint '%s' matched input '%s'", - sqli_state.fingerprint, - log_escape_ex(msr->mp, var->value, var->value_len)); - } - } else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "ISSQL: not sqli, no libinjection sqli fingerprint matched input '%s'", - log_escape_ex(msr->mp, var->value, var->value_len)); - } - } - - return issqli; -} - -/* containsWord */ - -static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *match = NULL; - const char *target; - unsigned int match_length; - unsigned int target_length = 0; - unsigned int i, i_max; - int rc = 0; - - str->value = (char *)rule->op_param; - - if (str->value == NULL) { - *error_msg = "Internal Error: match string is null."; - return -1; - } - - str->value_len = strlen(str->value); - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - expand_macros(msr, str, rule, msr->mp); - - match = (const char *)str->value; - match_length = str->value_len; - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* The empty string always matches */ - if (match_length == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); - return 1; - } - - /* This is impossible to match */ - if (match_length > target_length) { - /* No match. */ - return 0; - } - - /* scan for first character, then compare from there until we - * have a match or there is no room left in the target - */ - i_max = target_length - match_length; - for (i = 0; i <= i_max; i++) { - - /* Previous char must have been a start or non-word */ - if ((i > 0) && (apr_isalnum(target[i-1])||(target[i-1] == '_'))) - continue; - - /* First character matched - avoid func call */ - if (target[i] == match[0]) { - /* See if remaining matches */ - if ( (match_length == 1) - || (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0)) - { - /* check boundaries */ - if (i == i_max) { - /* exact/end word match */ - rc = 1; - } - else if (!(apr_isalnum(target[i + match_length])||(target[i + match_length] == '_'))) { - /* start/mid word match */ - rc = 1; - } - } - } - } - - if (rc == 1) { - /* Maybe a match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", - log_escape_ex(msr->mp, match, match_length), - var->name); - return 1; - } - - /* No match. */ - *error_msg = NULL; - return 0; -} - -/* streq */ - -static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *match = NULL; - const char *target; - unsigned int match_length; - unsigned int target_length; - - str->value = (char *)rule->op_param; - - if (str->value == NULL) { - *error_msg = "Internal Error: match string is null."; - return -1; - } - - str->value_len = strlen(str->value); - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - expand_macros(msr, str, rule, msr->mp); - - match = (const char *)str->value; - match_length = str->value_len; - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* These are impossible to match */ - if (match_length != target_length) { - /* No match. */ - return 0; - } - - if (memcmp(match, target, target_length) == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", - log_escape_ex(msr->mp, match, match_length), - var->name); - return 1; - } - - /* No match. */ - return 0; -} - -/* beginsWith */ - -static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *match = NULL; - const char *target; - unsigned int match_length; - unsigned int target_length; - - str->value = (char *)rule->op_param; - - if (str->value == NULL) { - *error_msg = "Internal Error: match string is null."; - return -1; - } - - str->value_len = strlen(str->value); - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - expand_macros(msr, str, rule, msr->mp); - - match = (const char *)str->value; - match_length = str->value_len; - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* The empty string always matches */ - if (match_length == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); - return 1; - } - - /* This is impossible to match */ - if (match_length > target_length) { - /* No match. */ - return 0; - } - - if (memcmp(match, target, match_length) == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", - log_escape_ex(msr->mp, match, match_length), - var->name); - return 1; - } - - /* No match. */ - return 0; -} - -/* endsWith */ - -static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - const char *match = NULL; - const char *target; - unsigned int match_length; - unsigned int target_length; - - str->value = (char *)rule->op_param; - - if (str->value == NULL) { - *error_msg = "Internal Error: match string is null."; - return -1; - } - - str->value_len = strlen(str->value); - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - expand_macros(msr, str, rule, msr->mp); - - match = (const char *)str->value; - match_length = str->value_len; - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - /* The empty string always matches */ - if (match_length == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name); - return 1; - } - - /* This is impossible to match */ - if (match_length > target_length) { - /* No match. */ - return 0; - } - - if (memcmp(match, (target + (target_length - match_length)), match_length) == 0) { - /* Match. */ - *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", - log_escape_ex(msr->mp, match, match_length), - var->name); - return 1; - } - - /* No match. */ - return 0; -} - -/* strmatch */ - -static int msre_op_strmatch_param_init(msre_rule *rule, char **error_msg) { - const apr_strmatch_pattern *compiled_pattern; - char *processed = NULL; - const char *pattern = rule->op_param; - unsigned short int op_len; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - op_len = strlen(pattern); - - /* Process pattern */ - processed = parse_pm_content(pattern, op_len, rule, error_msg); - if (processed == NULL) { - return 0; - } - - /* Compile pattern */ - compiled_pattern = apr_strmatch_precompile(rule->ruleset->mp, processed, 1); - if (compiled_pattern == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern: %s", pattern); - return 0; - } - - rule->op_param_data = (void *)compiled_pattern; - - return 1; /* OK */ -} - -static int msre_op_strmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - apr_strmatch_pattern *compiled_pattern = (apr_strmatch_pattern *)rule->op_param_data; - const char *target; - unsigned int target_length; - const char *rc; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (compiled_pattern == NULL) { - *error_msg = "Internal Error: strnmatch data is null."; - return -1; - } - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - rc = apr_strmatch(compiled_pattern, target, target_length); - if (rc == NULL) { - /* No match. */ - return 0; - } - - *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", - log_escape(msr->mp, rule->op_param), var->name); - - /* Match. */ - return 1; -} - -/* validateDTD */ - -static int msre_op_validateDTD_init(msre_rule *rule, char **error_msg) { - /* ENH Verify here the file actually exists. */ - return 1; -} - -static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - xmlValidCtxtPtr cvp; - xmlDtdPtr dtd; - - if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { - *error_msg = apr_psprintf(msr->mp, - "XML document tree could not be found for DTD validation."); - return -1; - } - - if (msr->xml->well_formed != 1) { - *error_msg = apr_psprintf(msr->mp, - "XML: DTD validation failed because content is not well formed."); - return 1; - } - - /* Make sure there were no other generic processing errors */ - if (msr->msc_reqbody_error) { - *error_msg = apr_psprintf(msr->mp, - "XML: DTD validation could not proceed due to previous" - " processing errors."); - return 1; - } - - dtd = xmlParseDTD(NULL, (const xmlChar *)rule->op_param); /* EHN support relative filenames */ - if (dtd == NULL) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed to load DTD: %s", rule->op_param); - return -1; - } - - cvp = xmlNewValidCtxt(); - if (cvp == NULL) { - *error_msg = "XML: Failed to create a validation context."; - xmlFreeDtd(dtd); - return -1; - } - - /* Send validator errors/warnings to msr_log */ - /* NOTE: No xmlDtdSetValidErrors()? */ - cvp->error = (xmlSchemaValidityErrorFunc)msr_log_error; - cvp->warning = (xmlSchemaValidityErrorFunc)msr_log_warn; - cvp->userData = msr; - - if (!xmlValidateDtd(cvp, msr->xml->doc, dtd)) { - *error_msg = "XML: DTD validation failed."; - xmlFreeValidCtxt(cvp); - xmlFreeDtd(dtd); - return 1; /* No match. */ - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "XML: Successfully validated payload against DTD: %s", rule->op_param); - } - - xmlFreeValidCtxt(cvp); - xmlFreeDtd(dtd); - - /* Match. */ - return 0; -} - -/* validateSchema */ - -static int msre_op_validateSchema_init(msre_rule *rule, char **error_msg) { - /* ENH Verify here the file actually exists. */ - return 1; -} - -static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - xmlSchemaParserCtxtPtr parserCtx; - xmlSchemaValidCtxtPtr validCtx; - xmlSchemaPtr schema; - int rc; - - if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { - *error_msg = apr_psprintf(msr->mp, - "XML document tree could not be found for schema validation."); - return -1; - } - - if (msr->xml->well_formed != 1) { - *error_msg = apr_psprintf(msr->mp, - "XML: Schema validation failed because content is not well formed."); - return 1; - } - - /* Make sure there were no other generic processing errors */ - if (msr->msc_reqbody_error) { - *error_msg = apr_psprintf(msr->mp, - "XML: Schema validation could not proceed due to previous" - " processing errors."); - return 1; - } - - parserCtx = xmlSchemaNewParserCtxt(rule->op_param); /* ENH support relative filenames */ - if (parserCtx == NULL) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema from file: %s", - rule->op_param); - return -1; - } - - /* Send parser errors/warnings to msr_log */ - xmlSchemaSetParserErrors(parserCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr); - - schema = xmlSchemaParse(parserCtx); - if (schema == NULL) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema: %s", rule->op_param); - xmlSchemaFreeParserCtxt(parserCtx); - return -1; - } - - validCtx = xmlSchemaNewValidCtxt(schema); - if (validCtx == NULL) { - *error_msg = "XML: Failed to create validation context."; - xmlSchemaFree(schema); - xmlSchemaFreeParserCtxt(parserCtx); - return -1; - } - - /* Send validator errors/warnings to msr_log */ - xmlSchemaSetValidErrors(validCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr); - - rc = xmlSchemaValidateDoc(validCtx, msr->xml->doc); - if (rc != 0) { - *error_msg = "XML: Schema validation failed."; - xmlSchemaFree(schema); - xmlSchemaFreeParserCtxt(parserCtx); - return 1; /* No match. */ - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "XML: Successfully validated payload against Schema: %s", rule->op_param); - } - - xmlSchemaFree(schema); - xmlSchemaFreeValidCtxt(validCtx); - - return 0; -} - -/* verifyCC */ - -/** - * Luhn Mod-10 Method (ISO 2894/ANSI 4.13) - */ -static int luhn_verify(const char *ccnumber, int len) { - int sum[2] = { 0, 0 }; - int odd = 0; - int digits = 0; - int i; - - /* Weighted lookup table which is just a precalculated (i = index): - * i*2 + (( (i*2) > 9 ) ? -9 : 0) - */ - static const int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; /* weight lookup table */ - - /* Add up only digits (weighted digits via lookup table) - * for both odd and even CC numbers to avoid 2 passes. - */ - for (i = 0; i < len; i++) { - if (apr_isdigit(ccnumber[i])) { - sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); - sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); - odd = 1 - odd; /* alternate weights */ - digits++; - } - } - - /* No digits extracted */ - if (digits == 0) return 0; - - /* Do a mod 10 on the sum */ - sum[odd] %= 10; - - /* If the result is a zero the card is valid. */ - return sum[odd] ? 0 : 1; -} - -static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { - const char *errptr = NULL; - int erroffset; - msc_regex_t *regex; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Compile rule->op_param */ - 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); - return 0; - } - - rule->op_param_data = regex; - - return 1; /* OK */ -} - -static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; - const char *target; - unsigned int target_length; - char *my_error_msg = NULL; - int ovector[33]; - int rc; - int is_cc = 0; - int offset; - int matched_bytes = 0; - char *qspos = NULL; - const char *parm = NULL; - msc_parm *mparm = NULL; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int jit; - #endif - #endif - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (regex == NULL) { - *error_msg = "Internal Error: regex data is null."; - return -1; - } - - memset(ovector, 0, sizeof(ovector)); - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - if (msr->txcfg->debuglog_level >= 4) { - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - msr_log(msr, 4, "%s.", *error_msg); - } - } - #endif - #endif - - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - for (offset = 0; ((unsigned int)offset < target_length) && (is_cc == 0); offset++) { - if (msr->txcfg->debuglog_level >= 9) { - if (offset > 0) { - msr_log(msr, 9, "Continuing CC# search at target offset %d.", offset); - } - } - - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); - - /* If there was no match, then we are done. */ - if (rc == PCRE_ERROR_NOMATCH) { - break; - } - - if (rc < -1) { - *error_msg = apr_psprintf(msr->mp, "CC# regex execution failed: %s", my_error_msg); - return -1; - } - - /* Verify a match. */ - if (rc > 0) { - const char *match = target + ovector[0]; - int length = ovector[1] - ovector[0]; - int i = 0; - - offset = ovector[2*i]; - - /* Check the Luhn using the match string */ - is_cc = luhn_verify(match, length); - - /* Not a CC number, then try another match where we left off. */ - if (!is_cc) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CC# Luhn check failed at target offset %d: \"%.*s\"", offset, length, match); - } - - continue; - } - - /* We have a potential CC number and need to set any captures - * and we are done. - */ - - matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; - if(!matched_bytes) - matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; - - - if (apr_table_get(rule->actionset->actions, "capture")) { - for(; i < rc; i++) { - 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); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrmemdup(msr->mp, match, length); - if (s->value == NULL) return -1; - s->value_len = length; - - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, - log_escape_nq_ex(msr->mp, s->value, s->value_len)); - } - - if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) { - qspos = apr_psprintf(msr->mp, "%s", var->name); - parm = strstr(qspos, ":"); - if (parm != NULL) { - parm++; - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - mparm->pad_1 = rule->actionset->arg_min; - mparm->pad_2 = rule->actionset->arg_max; - apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm); - } else { - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm); - } - } - - } - } - - /* Unset the remaining TX vars (from previous invocations). */ - for(; i <= 9; i++) { - char buf[24]; - apr_snprintf(buf, sizeof(buf), "%i", i); - apr_table_unset(msr->tx_vars, buf); - } - - break; - } - } - - if (is_cc) { - /* Match. */ - - /* This message will be logged. */ - *error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s. [offset \"%d\"]", - regex->pattern, var->name, offset); - - return 1; - } - - /* No match. */ - return 0; -} - -/** - * \brief Check for a valid CPF - * - * \param cpfnumber Pointer to cpf - * \param len cpf length - * - * \retval 0 On Invalid CPF - * \retval 1 On Valid CPF - */ -static int cpf_verify(const char *cpfnumber, int len) { - - int factor, part_1, part_2, var_len = len; - unsigned int sum = 0, i = 0, cpf_len = 11, c; - int cpf[11]; - char s_cpf[11]; - char bad_cpf[11][11] = { "00000000000", - "01234567890", - "11111111111", - "22222222222", - "33333333333", - "44444444444", - "55555555555", - "66666666666", - "77777777777", - "88888888888", - "99999999999"}; - - while((*cpfnumber != '\0') && ( var_len > 0)) { - - if(*cpfnumber != '-' || *cpfnumber != '.') { - if(i < cpf_len && isdigit(*cpfnumber)) { - s_cpf[i] = *cpfnumber; - cpf[i] = convert_to_int(*cpfnumber); - i++; - } - } - cpfnumber++; - var_len--; - } - - - if (i != cpf_len) - return 0; - else { - for(i = 0; i< cpf_len; i++) { - if(strncmp(s_cpf,bad_cpf[i],cpf_len) == 0) { - return 0; - } - } - } - - part_1 = convert_to_int(s_cpf[cpf_len-2]); - part_2 = convert_to_int(s_cpf[cpf_len-1]); - - c = cpf_len; - - for(i = 0; i < 9; i++) { - sum += (cpf[i] * --c); - } - - factor = (sum % cpf_len); - - if(factor < 2) { - cpf[9] = 0; - } else { - cpf[9] = cpf_len-factor; - } - - sum = 0; - c = cpf_len; - - for(i = 0;i < 10; i++) - sum += (cpf[i] * c--); - - factor = (sum % cpf_len); - - if(factor < 2) { - cpf[10] = 0; - } else { - cpf[10] = cpf_len-factor; - } - - if(part_1 == cpf[9] && part_2 == cpf[10]) - return 1; - - return 0; -} - -/** - * \brief Init function to CPF operator - * - * \param rule ModSecurity rule struct - * \param error_msg Error message - * - * \retval 0 On Failure - * \retval 1 On Success - */ -static int msre_op_verifyCPF_init(msre_rule *rule, char **error_msg) { - const char *errptr = NULL; - int erroffset; - msc_regex_t *regex; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Compile rule->op_param */ - 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); - return 0; - } - - rule->op_param_data = regex; - - return 1; /* OK */ -} - -/** - * \brief Execution function to CPF operator - * - * \param msr ModSecurity transaction resource - * \param rule ModSecurity rule struct - * \param var ModSecurity variable struct - * \param error_msg Error message - * - * \retval -1 On Failure - * \retval 1 On Match - * \retval 0 On No Match - */ -static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; - const char *target; - unsigned int target_length; - char *my_error_msg = NULL; - int ovector[33]; - int rc; - int is_cpf = 0; - int offset; - int matched_bytes = 0; - char *qspos = NULL; - const char *parm = NULL; - msc_parm *mparm = NULL; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int jit; - #endif - #endif - - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (regex == NULL) { - *error_msg = "Internal Error: regex data is null."; - return -1; - } - - memset(ovector, 0, sizeof(ovector)); - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - if (msr->txcfg->debuglog_level >= 4) { - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - msr_log(msr, 4, "%s.", *error_msg); - } - } - #endif - #endif - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - for (offset = 0; ((unsigned int)offset < target_length) && (is_cpf == 0); offset++) { - if (msr->txcfg->debuglog_level >= 9) { - if (offset > 0) { - msr_log(msr, 9, "Continuing CPF# search at target offset %d.", offset); - } - } - - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); - - /* If there was no match, then we are done. */ - if (rc == PCRE_ERROR_NOMATCH) { - break; - } - - if (rc < -1) { - *error_msg = apr_psprintf(msr->mp, "CPF# regex execution failed: %s", my_error_msg); - return -1; - } - - /* Verify a match. */ - if (rc > 0) { - const char *match = target + ovector[0]; - int length = ovector[1] - ovector[0]; - int i = 0; - - offset = ovector[2*i]; - - /* Check CPF using the match string */ - is_cpf = cpf_verify(match, length); - - /* Not a CPF number, then try another match where we left off. */ - if (!is_cpf) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CPF# check failed at target offset %d: \"%.*s\"", offset, length, match); - } - - continue; - } - - /* We have a potential CPF number and need to set any captures - * and we are done. - */ - - matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; - if(!matched_bytes) - matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; - - if (apr_table_get(rule->actionset->actions, "capture")) { - for(; i < rc; i++) { - 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); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrmemdup(msr->mp, match, length); - if (s->value == NULL) return -1; - s->value_len = length; - - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, - log_escape_nq_ex(msr->mp, s->value, s->value_len)); - } - - if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) { - qspos = apr_psprintf(msr->mp, "%s", var->name); - parm = strstr(qspos, ":"); - if (parm != NULL) { - parm++; - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - mparm->pad_1 = rule->actionset->arg_min; - mparm->pad_2 = rule->actionset->arg_max; - apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm); - } else { - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm); - } - } - - } - } - - /* Unset the remaining TX vars (from previous invocations). */ - for(; i <= 9; i++) { - char buf[24]; - apr_snprintf(buf, sizeof(buf), "%i", i); - apr_table_unset(msr->tx_vars, buf); - } - - break; - } - } - - if (is_cpf) { - /* Match. */ - - /* This message will be logged. */ - *error_msg = apr_psprintf(msr->mp, "CPF# match \"%s\" at %s. [offset \"%d\"]", - regex->pattern, var->name, offset); - - return 1; - } - - /* No match. */ - return 0; -} - -/** - * \brief Check for a valid SSN - * - * \param msr ModSecurity transaction source - * \param ssnumber Pointer to ssn - * \param len ssn length - * - * \retval 0 On Invalid SSN - * \retval 1 On Valid SSN - */ -static int ssn_verify(modsec_rec *msr, const char *ssnumber, int len) { - int i; - int num[9]; - int digits = 0; - int area, serial, grp; - int sequencial = 0; - int repetitions = 0; - char *str_area; - char *str_grp; - char *str_serial; - - for (i = 0; i < len; i++) { - if (apr_isdigit(ssnumber[i])) { - if (digits < 9) - num[digits] = convert_to_int(ssnumber[i]); - digits++; - } - } - - /* Not a valid number */ - if (digits != 9) - goto invalid; - - for (i=0; i < 8; i++) { - if (num[i] == (num[i+1]-1)) - sequencial++; - - if (num[i] == num[i+1]) - repetitions++; - } - - /* We are blocking when all numbers were sequencial or repeated */ - if (sequencial == 8) - goto invalid; - - if (repetitions == 8) - goto invalid; - - str_area = apr_psprintf(msr->mp,"%d%d%d",num[0],num[1],num[2]); - str_grp = apr_psprintf(msr->mp,"%d%d",num[3],num[4]); - str_serial = apr_psprintf(msr->mp,"%d%d%d%d",num[5],num[6],num[7],num[8]); - - if(str_area == NULL || str_grp == NULL || str_serial == NULL) - goto invalid; - - area = atoi(str_area); - grp = atoi(str_grp); - serial = atoi(str_serial); - - /* Cannot has seroed fields */ - if (area == 0 || serial == 0 || grp == 0) - goto invalid; - - /* More tests */ - if (area >= 740 || area == 666) - goto invalid; - - return 1; - -invalid: - return 0; -} - -/** -* \brief Init function to SSN operator -* -* \param rule ModSecurity rule struct -* \param error_msg Error message -* -* \retval 0 On Failure -* \retval 1 On Success -*/ -static int msre_op_verifySSN_init(msre_rule *rule, char **error_msg) { - const char *errptr = NULL; - int erroffset; - msc_regex_t *regex; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - /* Compile rule->op_param */ - 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); - return 0; - } - - rule->op_param_data = regex; - - return 1; /* OK */ -} - -/** -* \brief Execution function to SSN operator -* -* \param msr ModSecurity transaction resource -* \param rule ModSecurity rule struct -* \param var ModSecurity variable struct -* \param error_msg Error message -* -* \retval -1 On Failure -* \retval 1 On Match -* \retval 0 On No Match -*/ -static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; - const char *target; - unsigned int target_length; - char *my_error_msg = NULL; - int ovector[33]; - int rc; - int is_ssn = 0; - int offset; - int matched_bytes = 0; - char *qspos = NULL; - const char *parm = NULL; - msc_parm *mparm = NULL; - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - int jit; - #endif - #endif - - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (regex == NULL) { - *error_msg = "Internal Error: regex data is null."; - return -1; - } - - memset(ovector, 0, sizeof(ovector)); - - #ifdef WITH_PCRE_STUDY - #ifdef WITH_PCRE_JIT - if (msr->txcfg->debuglog_level >= 4) { - rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); - if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, - "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " - "Execution error - " - "Does not support JIT (%d)", - rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&& - (rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-", - rule->filename != NULL ? rule->filename : "-", - rule->line_num,rc); - msr_log(msr, 4, "%s.", *error_msg); - } - } - #endif - #endif - - /* If the given target is null run against an empty - * string. This is a behaviour consistent with previous - * releases. - */ - if (var->value == NULL) { - target = ""; - target_length = 0; - } else { - target = var->value; - target_length = var->value_len; - } - - for (offset = 0; ((unsigned int)offset < target_length) && (is_ssn == 0); offset++) { - if (msr->txcfg->debuglog_level >= 9) { - if (offset > 0) { - msr_log(msr, 9, "Continuing SSN# search at target offset %d.", offset); - } - } - - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); - - /* If there was no match, then we are done. */ - if (rc == PCRE_ERROR_NOMATCH) { - break; - } - - if (rc < -1) { - *error_msg = apr_psprintf(msr->mp, "SSN# regex execution failed: %s", my_error_msg); - return -1; - } - - /* Verify a match. */ - if (rc > 0) { - const char *match = target + ovector[0]; - int length = ovector[1] - ovector[0]; - int i = 0; - - offset = ovector[2*i]; - - /* Check SSN using the match string */ - is_ssn = ssn_verify(msr, match, length); - - /* Not a SSN number, then try another match where we left off. */ - if (!is_ssn) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "SSN# check failed at target offset %d: \"%.*s\"", offset, length, match); - } - - continue; - } - - /* We have a potential SSN number and need to set any captures - * and we are done. - */ - - matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; - if(!matched_bytes) - matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; - - if (apr_table_get(rule->actionset->actions, "capture")) { - for(; i < rc; i++) { - 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); - if (s->name == NULL) return -1; - s->name_len = strlen(s->name); - s->value = apr_pstrmemdup(msr->mp, match, length); - if (s->value == NULL) return -1; - s->value_len = length; - - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, - log_escape_nq_ex(msr->mp, s->value, s->value_len)); - } - - if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) { - qspos = apr_psprintf(msr->mp, "%s", var->name); - parm = strstr(qspos, ":"); - if (parm != NULL) { - parm++; - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - mparm->pad_1 = rule->actionset->arg_min; - mparm->pad_2 = rule->actionset->arg_max; - apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm); - } else { - mparm = apr_palloc(msr->mp, sizeof(msc_parm)); - if (mparm == NULL) - continue; - - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm); - } - } - - } - } - - /* Unset the remaining TX vars (from previous invocations). */ - for(; i <= 9; i++) { - char buf[24]; - apr_snprintf(buf, sizeof(buf), "%i", i); - apr_table_unset(msr->tx_vars, buf); - } - - break; - } - } - - if (is_ssn) { - /* Match. */ - - /* This message will be logged. */ - *error_msg = apr_psprintf(msr->mp, "SSN# match \"%s\" at %s. [offset \"%d\"]", - regex->pattern, var->name, offset); - - return 1; - } - - /* No match. */ - return 0; -} - -/** - * Perform geograpical lookups on an IP/Host. - */ -static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - geo_rec rec; - geo_db *geo = msr->txcfg->geo; - const char *geo_host = var->value; - msc_string *s = NULL; - int rc; - - *error_msg = NULL; - - if (geo == NULL) { - msr_log(msr, 1, "Geo lookup for \"%s\" attempted without a database. Set SecGeoLookupDB.", log_escape(msr->mp, geo_host)); - return 0; - } - - - rc = geo_lookup(msr, &rec, geo_host, error_msg); - if (rc <= 0) { - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed at %s.", log_escape_nq(msr->mp, geo_host), var->name); - } - apr_table_clear(msr->geo_vars); - return rc; - } - if (! *error_msg) { - *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded at %s.", - log_escape_nq(msr->mp, geo_host), var->name); - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "GEO: %s={country_code=%s, country_code3=%s, country_name=%s, country_continent=%s, region=%s, city=%s, postal_code=%s, latitude=%f, longitude=%f, dma_code=%d, area_code=%d}", - geo_host, - rec.country_code, - rec.country_code3, - rec.country_name, - rec.country_continent, - rec.region, - rec.city, - rec.postal_code, - rec.latitude, - rec.longitude, - rec.dma_code, - rec.area_code); - } - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.country_code ? rec.country_code : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE3"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.country_code3 ? rec.country_code3 : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "COUNTRY_NAME"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.country_name ? rec.country_name : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "COUNTRY_CONTINENT"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.country_continent ? rec.country_continent : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "REGION"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.region ? rec.region : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "CITY"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.city ? rec.city : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "POSTAL_CODE"); - s->name_len = strlen(s->name); - s->value = apr_pstrdup(msr->mp, rec.postal_code ? rec.postal_code : ""); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "LATITUDE"); - s->name_len = strlen(s->name); - s->value = apr_psprintf(msr->mp, "%f", rec.latitude); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "LONGITUDE"); - s->name_len = strlen(s->name); - s->value = apr_psprintf(msr->mp, "%f", rec.longitude); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "DMA_CODE"); - s->name_len = strlen(s->name); - s->value = apr_psprintf(msr->mp, "%d", rec.dma_code); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - s->name = apr_pstrdup(msr->mp, "AREA_CODE"); - s->name_len = strlen(s->name); - s->value = apr_psprintf(msr->mp, "%d", rec.area_code); - s->value_len = strlen(s->value); - apr_table_setn(msr->geo_vars, s->name, (void *)s); - - return 1; -} - -/* rbl */ - -static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - unsigned int h0, h1, h2, h3; - unsigned int high8bits = 0; - char *name_to_check = NULL; - char *target = NULL; - apr_sockaddr_t *sa = NULL; - apr_status_t rc; - int capture = 0; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - - /* ENH Add IPv6 support. */ - - target = apr_pstrmemdup(msr->mp, var->value, var->value_len); - if (target == NULL) return -1; - - /* Construct the host name we want to resolve. */ - if (sscanf(target, "%d.%d.%d.%d", &h0, &h1, &h2, &h3) == 4) { - /* IPv4 address */ - /* If we're using the httpBl blocklist, we need to add the key */ - if(strstr(rule->op_param,"httpbl.org")) { - if (msr->txcfg->httpBlkey == NULL) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "RBL httpBl called but no key defined: set SecHttpBlKey"); - } - *error_msg = "RBL httpBl called but no key defined: set SecHttpBlKey"; - } else { - name_to_check = apr_psprintf(msr->mp, "%s.%d.%d.%d.%d.%s", msr->txcfg->httpBlkey, h3, h2, h1, h0, rule->op_param); - } - } else { - /* regular IPv4 RBLs */ - name_to_check = apr_psprintf(msr->mp, "%d.%d.%d.%d.%s", h3, h2, h1, h0, rule->op_param); - } - } else { - /* Assume the input is a domain name. */ - name_to_check = apr_psprintf(msr->mp, "%s.%s", target, rule->op_param); - } - - if (name_to_check == NULL) return -1; - - rc = apr_sockaddr_info_get(&sa, name_to_check, - APR_UNSPEC/*msr->r->connection->remote_addr->family*/, 0, 0, msr->mp); - if (rc == APR_SUCCESS) { - - high8bits = sa->sa.sin.sin_addr.s_addr >> 24; - - /* multi.uribl.com */ - - if(strstr(rule->op_param,"uribl.com")) { - - switch(high8bits) { - case 2: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (BLACK).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - case 4: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (GREY).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - case 8: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (RED).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - case 14: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (BLACK,GREY,RED).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - case 255: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (DNS IS BLOCKED).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - default: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (WHITE).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - } - - set_match_to_tx(msr, capture, *error_msg, 0); - - } else - if(strstr(rule->op_param,"spamhaus.org")) { - - switch(high8bits) { - case 2: - case 3: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (Static UBE sources).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - case 4: - case 5: - case 6: - case 7: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (Illegal 3rd party exploits).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - case 10: - case 11: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (Delivering unauthenticated SMTP email).", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - default: - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s.", - log_escape_nq(msr->mp, name_to_check), var->name); - break; - } - - set_match_to_tx(msr, capture, *error_msg, 0); - - } else - if(strstr(rule->op_param,"httpbl.org")) { - char *respBl; - int first, days, score, type; - - respBl = inet_ntoa(sa->sa.sin.sin_addr); - if (sscanf(respBl, "%d.%d.%d.%d", &first, &days, &score, &type) != 4) { - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s failed: bad response", log_escape_nq(msr->mp, name_to_check)); - } else { - if (first != 127) { - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s failed: bad response", log_escape_nq(msr->mp, name_to_check)); - } - else { - char *ptype; - switch(type) { - case 0: - ptype = "Search Engine"; - break; - case 1: - ptype = "Suspicious IP"; - break; - case 2: - ptype = "Harvester IP"; - break; - case 3: - ptype = "Suspicious harvester IP"; - break; - case 4: - ptype = "Comment spammer IP"; - break; - case 5: - ptype = "Suspicious comment spammer IP"; - break; - case 6: - ptype = "Harvester and comment spammer IP"; - break; - case 7: - ptype = "Suspicious harvester comment spammer IP"; - break; - default: - ptype = " "; - } - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s. %s: %d days since last activity, threat score %d", - log_escape_nq(msr->mp, name_to_check), var->name, - ptype, days, score); - } - } - set_match_to_tx(msr, capture, *error_msg, 0); - /* end of httpBl code */ - } else { - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s.", - log_escape_nq(msr->mp, name_to_check), var->name); - - set_match_to_tx(msr, capture, *error_msg, 0); - - } - - return 1; /* Match. */ - } - - if (msr->txcfg->debuglog_level >= 5) { - msr_log(msr, 5, "RBL lookup of %s failed at %s.", log_escape_nq(msr->mp, name_to_check), var->name); - } - - /* No match. */ - return 0; -} - -/* inspectFile */ - -static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) { - char *filename = (char *)rule->op_param; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if ((filename == NULL)||(is_empty_string(filename))) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Operator @inspectFile requires parameter."); - return -1; - } - - filename = resolve_relative_path(rule->ruleset->mp, rule->filename, filename); - -#if defined(WITH_LUA) - /* ENH Write & use string_ends(s, e). */ - if (strlen(rule->op_param) > 4) { - char *p = filename + strlen(filename) - 4; - if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) - { - msc_script *script = NULL; - - /* Compile script. */ - *error_msg = lua_compile(&script, filename, rule->ruleset->mp); - if (*error_msg != NULL) return -1; - - rule->op_param_data = script; - } - } - #endif - - if (rule->op_param_data == NULL) { - /* ENH Verify the script exists and that we have - * the rights to execute it. - */ - } - - return 1; -} - -static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (rule->op_param_data == NULL) { - /* Execute externally, as native binary/shell script. */ - char *script_output = NULL; - char const *argv[5]; - const char *approver_script = rule->op_param; - const char *target_file = apr_pstrmemdup(msr->mp, var->value, var->value_len); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Executing %s to inspect %s.", approver_script, target_file); - } - - argv[0] = approver_script; - argv[1] = target_file; - argv[2] = NULL; - - if (apache2_exec(msr, approver_script, (const char **)argv, &script_output) <= 0) { - *error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (invocation failed).", - log_escape(msr->mp, approver_script)); - return -1; - } - - if (script_output == NULL) { - *error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (no output).", - log_escape(msr->mp, approver_script)); - return -1; - } - - if (script_output[0] != '1') { - *error_msg = apr_psprintf(msr->mp, "File \"%s\" rejected by the approver script \"%s\": %s", - log_escape(msr->mp, target_file), log_escape(msr->mp, approver_script), - log_escape_nq(msr->mp, script_output)); - return 1; /* Match. */ - } - } - #if defined(WITH_LUA) - else { - /* Execute internally, as Lua script. */ - char *target = apr_pstrmemdup(msr->mp, var->value, var->value_len); - msc_script *script = (msc_script *)rule->op_param_data; - int rc; - - rc = lua_execute(script, target, msr, rule, error_msg); - if (rc < 0) { - /* Error. */ - return -1; - } - - return rc; - } - #endif - - /* No match. */ - return 0; -} - -/* validateByteRange */ - -static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) { - char *p = NULL, *saveptr = NULL; - char *table = NULL, *data = NULL; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (rule->op_param == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for validateByteRange."); - return -1; - } - - /* Initialise. */ - data = apr_pstrdup(rule->ruleset->mp, rule->op_param); - rule->op_param_data = apr_pcalloc(rule->ruleset->mp, 32); - if ((data == NULL)||(rule->op_param_data == NULL)) return -1; - table = rule->op_param_data; - - /* Extract parameters and update table. */ - p = apr_strtok(data, ",", &saveptr); - while(p != NULL) { - char *s = strstr(p, "-"); - if (s == NULL) { - /* Single value. */ - int x = atoi(p); - if ((x < 0)||(x > 255)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range value: %d", x); - return 0; - } - table[x>>3] = (table[x>>3] | (1 << (x & 0x7))); - } else { - /* Range. */ - int start = atoi(p); - int end = atoi(s + 1); - - if ((start < 0)||(start > 255)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range start value: %d", - start); - return 0; - } - if ((end < 0)||(end > 255)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range end value: %d", end); - return 0; - } - if (start > end) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range: %d-%d", start, end); - return 0; - } - - while(start <= end) { - table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7))); - start++; - } - } - - p = apr_strtok(NULL, ",", &saveptr); - } - - return 1; -} - -static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - char *table = rule->op_param_data; - unsigned int i, count; - - if (error_msg == NULL) return -1; - *error_msg = NULL; - - if (table == NULL) { - *error_msg = apr_psprintf(msr->mp, "Internal Error: validateByteRange table not " - "initialised."); - return -1; - } - - /* Check every byte of the target to detect characters that are not allowed. */ - - count = 0; - for(i = 0; i < var->value_len; i++) { - int x = ((unsigned char *)var->value)[i]; - if (!(table[x >> 3] & (1 << (x & 0x7)))) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Value %d in %s outside range: %s", x, var->name, rule->op_param); - } - count++; - } - } - - if (count == 0) return 0; /* Valid - no match. */ - - *error_msg = apr_psprintf(msr->mp, "Found %d byte(s) in %s outside range: %s.", - count, var->name, rule->op_param); - - return 1; /* Invalid - match.*/ -} - -/* validateUrlEncoding */ - -static int validate_url_encoding(const char *input, long int input_length) { - int i; - - if ((input == NULL)||(input_length < 0)) return -1; - - i = 0; - while (i < input_length) { - if (input[i] == '%') { - if (i + 2 >= input_length) { - /* Not enough bytes. */ - return -3; - } - else { - /* Here we only decode a %xx combination if it is valid, - * leaving it as is otherwise. - */ - char c1 = input[i + 1]; - char c2 = input[i + 2]; - - if ( (((c1 >= '0')&&(c1 <= '9')) || ((c1 >= 'a')&&(c1 <= 'f')) || ((c1 >= 'A')&&(c1 <= 'F'))) - && (((c2 >= '0')&&(c2 <= '9')) || ((c2 >= 'a')&&(c2 <= 'f')) || ((c2 >= 'A')&&(c2 <= 'F'))) ) - { - i += 3; - } else { - /* Non-hexadecimal characters used in encoding. */ - return -2; - } - } - } else { - i++; - } - } - - return 1; -} - -static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - int rc = validate_url_encoding(var->value, var->value_len); - switch(rc) { - case 1 : - /* Encoding is valid */ - *error_msg = apr_psprintf(msr->mp, "Valid URL Encoding at %s.", var->name); - break; - case -2 : - *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Non-hexadecimal " - "digits used at %s.", var->name); - return 1; /* Invalid match. */ - break; - case -3 : - *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Not enough characters " - "at the end of input at %s.", var->name); - return 1; /* Invalid match. */ - break; - case -1 : - default : - *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Internal Error (rc = %d) at %s", rc, var->name); - return -1; - break; - - } - - /* No match. */ - return 0; -} - -/* validateUtf8Encoding */ - -/* NOTE: This is over-commented for ease of verification */ -static int detect_utf8_character(const unsigned char *p_read, unsigned int length) { - int unicode_len = 0; - unsigned int d = 0; - unsigned char c; - - if (p_read == NULL) return UNICODE_ERROR_DECODING_ERROR; - c = *p_read; - - /* If first byte begins with binary 0 it is single byte encoding */ - if ((c & 0x80) == 0) { - /* single byte unicode (7 bit ASCII equivilent) has no validation */ - return 1; - } - /* If first byte begins with binary 110 it is two byte encoding*/ - else if ((c & 0xE0) == 0xC0) { - /* check we have at least two bytes */ - if (length < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; - /* check second byte starts with binary 10 */ - else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - else { - unicode_len = 2; - /* compute character number */ - d = ((c & 0x1F) << 6) | (*(p_read + 1) & 0x3F); - } - } - /* If first byte begins with binary 1110 it is three byte encoding */ - else if ((c & 0xF0) == 0xE0) { - /* check we have at least three bytes */ - if (length < 3) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; - /* check second byte starts with binary 10 */ - else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - /* check third byte starts with binary 10 */ - else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - else { - unicode_len = 3; - /* compute character number */ - d = ((c & 0x0F) << 12) | ((*(p_read + 1) & 0x3F) << 6) | (*(p_read + 2) & 0x3F); - } - } - /* If first byte begins with binary 11110 it is four byte encoding */ - else if ((c & 0xF8) == 0xF0) { - /* restrict characters to UTF-8 range (U+0000 - U+10FFFF)*/ - if (c >= 0xF5) { - return UNICODE_ERROR_RESTRICTED_CHARACTER; - } - /* check we have at least four bytes */ - if (length < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; - /* check second byte starts with binary 10 */ - else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - /* check third byte starts with binary 10 */ - else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - /* check forth byte starts with binary 10 */ - else if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; - else { - unicode_len = 4; - /* compute character number */ - d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F); - } - } - /* any other first byte is invalid (RFC 3629) */ - else { - return UNICODE_ERROR_INVALID_ENCODING; - } - - /* invalid UTF-8 character number range (RFC 3629) */ - if ((d >= 0xD800) && (d <= 0xDFFF)) { - return UNICODE_ERROR_RESTRICTED_CHARACTER; - } - - /* check for overlong */ - if ((unicode_len == 4) && (d < 0x010000)) { - /* four byte could be represented with less bytes */ - return UNICODE_ERROR_OVERLONG_CHARACTER; - } - else if ((unicode_len == 3) && (d < 0x0800)) { - /* three byte could be represented with less bytes */ - return UNICODE_ERROR_OVERLONG_CHARACTER; - } - else if ((unicode_len == 2) && (d < 0x80)) { - /* two byte could be represented with less bytes */ - return UNICODE_ERROR_OVERLONG_CHARACTER; - } - - return unicode_len; -} - -static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, - char **error_msg) -{ - unsigned int i, bytes_left; - - bytes_left = var->value_len; - - for(i = 0; i < var->value_len;) { - int rc = detect_utf8_character((unsigned char *)&var->value[i], bytes_left); - - switch(rc) { - case UNICODE_ERROR_CHARACTERS_MISSING : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " - "not enough bytes in character " - "at %s. [offset \"%d\"]", var->name, i); - return 1; - break; - case UNICODE_ERROR_INVALID_ENCODING : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " - "invalid byte value in character " - "at %s. [offset \"%d\"]", var->name, i); - return 1; - break; - case UNICODE_ERROR_OVERLONG_CHARACTER : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " - "overlong character detected " - "at %s. [offset \"%d\"]", var->name, i); - return 1; - break; - case UNICODE_ERROR_RESTRICTED_CHARACTER : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " - "use of restricted character " - "at %s. [offset \"%d\"]", var->name, i); - return 1; - break; - case UNICODE_ERROR_DECODING_ERROR : - *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding " - "at %s. [offset \"%d\"]", var->name, i); - return 1; - break; - } - - if (rc <= 0) { - *error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation " - "at %s.", var->name); - return 1; - } - - i += rc; - bytes_left -= rc; - } - - return 0; -} - -/* eq */ - -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(str.value); - - if (left != right) { - /* No match. */ - return 0; - } - else { - *error_msg = apr_psprintf(msr->mp, "Operator EQ matched %d at %s.", right, var->name); - /* Match. */ - return 1; - } -} - -/* gt */ - -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; - - if ((var->value == NULL)||(rule->op_param == NULL)) { - /* NULL values do not match anything. */ - 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(str.value); - - if (left <= right) { - /* No match. */ - return 0; - } - else { - *error_msg = apr_psprintf(msr->mp, "Operator GT matched %d at %s.", right, var->name); - /* Match. */ - return 1; - } -} - -/* lt */ - -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; - - if ((var->value == NULL)||(rule->op_param == NULL)) { - /* NULL values do not match anything. */ - 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(str.value); - - if (left >= right) { - /* No match. */ - return 0; - } - else { - *error_msg = apr_psprintf(msr->mp, "Operator LT matched %d at %s.", right, var->name); - /* Match. */ - return 1; - } -} - -/* ge */ - -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; - - if ((var->value == NULL)||(rule->op_param == NULL)) { - /* NULL values do not match anything. */ - 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(str.value); - - if (left < right) { - /* No match. */ - return 0; - } - else { - *error_msg = apr_psprintf(msr->mp, "Operator GE matched %d at %s.", right, var->name); - /* Match. */ - return 1; - } -} - -/* le */ - -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; - - if ((var->value == NULL)||(rule->op_param == NULL)) { - /* NULL values do not match anything. */ - 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(str.value); - - if (left > right) { - /* No match. */ - return 0; - } - else { - *error_msg = apr_psprintf(msr->mp, "Operator LE matched %d at %s.", right, var->name); - /* Match. */ - return 1; - } -} - -/* -------------------------------------------------------------------------- */ - -/** - * - */ -void msre_engine_register_default_operators(msre_engine *engine) { - /* unconditionalMatch */ - msre_engine_op_register(engine, - "unconditionalMatch", - NULL, - msre_op_unconditionalmatch_execute - ); - - /* noMatch */ - msre_engine_op_register(engine, - "noMatch", - NULL, - msre_op_nomatch_execute - ); - - /* ipmatch */ - msre_engine_op_register(engine, - "ipmatch", - msre_op_ipmatch_param_init, - msre_op_ipmatch_execute - ); - - /* ipmatchFromFile */ - msre_engine_op_register(engine, - "ipmatchFromFile", - msre_op_ipmatchFromFile_param_init, - msre_op_ipmatchFromFile_execute - ); - /* ipmatchf */ - msre_engine_op_register(engine, - "ipmatchf", - msre_op_ipmatchFromFile_param_init, - msre_op_ipmatchFromFile_execute - ); - - /* rsub */ -#if !defined(MSC_TEST) - msre_engine_op_register(engine, - "rsub", - msre_op_rsub_param_init, - msre_op_rsub_execute - ); -#endif /* MSC_TEST */ - - /* rx */ - msre_engine_op_register(engine, - "rx", - msre_op_rx_param_init, - msre_op_rx_execute - ); - - /* validateEncyption */ - msre_engine_op_register(engine, - "validateHash", - msre_op_validateHash_param_init, - msre_op_validateHash_execute - ); - - /* pm */ - msre_engine_op_register(engine, - "pm", - msre_op_pm_param_init, - msre_op_pm_execute - ); - - /* pmFromFile */ - msre_engine_op_register(engine, - "pmFromFile", - msre_op_pmFromFile_param_init, - msre_op_pm_execute - ); - - /* pmf */ - msre_engine_op_register(engine, - "pmf", - msre_op_pmFromFile_param_init, - msre_op_pm_execute - ); - - /* within */ - msre_engine_op_register(engine, - "within", - NULL, /* ENH init function to flag var substitution */ - msre_op_within_execute - ); - - /* contains */ - msre_engine_op_register(engine, - "contains", - NULL, /* ENH init function to flag var substitution */ - msre_op_contains_execute - ); - - /* containsWord */ - msre_engine_op_register(engine, - "containsWord", - NULL, /* ENH init function to flag var substitution */ - msre_op_containsWord_execute - ); - - /* detectSQLi */ - msre_engine_op_register(engine, - "detectSQLi", - NULL, - msre_op_detectSQLi_execute - ); - - /* streq */ - msre_engine_op_register(engine, - "streq", - NULL, /* ENH init function to flag var substitution */ - msre_op_streq_execute - ); - - /* beginsWith */ - msre_engine_op_register(engine, - "beginsWith", - NULL, /* ENH init function to flag var substitution */ - msre_op_beginsWith_execute - ); - - /* endsWith */ - msre_engine_op_register(engine, - "endsWith", - NULL, /* ENH init function to flag var substitution */ - msre_op_endsWith_execute - ); - - /* strmatch */ - msre_engine_op_register(engine, - "strmatch", - msre_op_strmatch_param_init, - msre_op_strmatch_execute - ); - - /* validateDTD */ - msre_engine_op_register(engine, - "validateDTD", - msre_op_validateDTD_init, - msre_op_validateDTD_execute - ); - - /* validateSchema */ - msre_engine_op_register(engine, - "validateSchema", - msre_op_validateSchema_init, - msre_op_validateSchema_execute - ); - - /* verifyCC */ - msre_engine_op_register(engine, - "verifyCC", - msre_op_verifyCC_init, - msre_op_verifyCC_execute - ); - - /* verifyCPF */ - msre_engine_op_register(engine, - "verifyCPF", - msre_op_verifyCPF_init, - msre_op_verifyCPF_execute - ); - - /* verifySSN */ - msre_engine_op_register(engine, - "verifySSN", - msre_op_verifySSN_init, - msre_op_verifySSN_execute - ); - - /* geoLookup */ - msre_engine_op_register(engine, - "geoLookup", - NULL, - msre_op_geoLookup_execute - ); - - /* gsbLookup */ - msre_engine_op_register(engine, - "gsbLookup", - msre_op_gsbLookup_param_init, - msre_op_gsbLookup_execute - ); - - /* rbl */ - msre_engine_op_register(engine, - "rbl", - NULL, /* ENH init function to validate DNS server */ - msre_op_rbl_execute - ); - - /* inspectFile */ - msre_engine_op_register(engine, - "inspectFile", - msre_op_inspectFile_init, - msre_op_inspectFile_execute - ); - - /* validateByteRange */ - msre_engine_op_register(engine, - "validateByteRange", - msre_op_validateByteRange_init, - msre_op_validateByteRange_execute - ); - - /* validateUrlEncoding */ - msre_engine_op_register(engine, - "validateUrlEncoding", - NULL, - msre_op_validateUrlEncoding_execute - ); - - /* validateUtf8Encoding */ - msre_engine_op_register(engine, - "validateUtf8Encoding", - NULL, - msre_op_validateUtf8Encoding_execute - ); - - /* eq */ - msre_engine_op_register(engine, - "eq", - NULL, - msre_op_eq_execute - ); - - /* gt */ - msre_engine_op_register(engine, - "gt", - NULL, - msre_op_gt_execute - ); - - /* lt */ - msre_engine_op_register(engine, - "lt", - NULL, - msre_op_lt_execute - ); - - /* le */ - msre_engine_op_register(engine, - "le", - NULL, - msre_op_le_execute - ); - - /* ge */ - msre_engine_op_register(engine, - "ge", - NULL, - msre_op_ge_execute - ); -} diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c deleted file mode 100644 index 63068dae8c..0000000000 --- a/apache2/re_tfns.c +++ /dev/null @@ -1,1051 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include - -#include "apr_md5.h" -#include "apr_sha1.h" -#include "apr_base64.h" - -#include "msc_unicode.h" -#include "re.h" -#include "msc_util.h" - -/* cmdline */ - -/** -* \brief cmdline transformation function -* -* \param mptmp Pointer to resource pool -* \param input Pointer to input data -* \param input_len Input data length -* \param rval Pointer to decoded buffer -* \param rval_len Decoded buffer length -* -* \retval 0 On failure -* \retval 1 On Success -*/ -static int msre_fn_cmdline_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - int space = 0; - unsigned char *s = input; - - if (rval == NULL) return -1; - - *rval = (char *)input; - /* Check characters */ - for ( ; *input; input++ ) { - switch(*input) { - /* remove some characters */ - case '"': - case '\'': - case '\\': - case '^': - continue; - /* replace some characters to space (only one) */ - case ' ': - case ',': - case ';': - case '\t': - case '\r': - case '\n': - if (!space) { - *s++ = ' '; - space++; - } - break; - case '/': - case '(': - /* remove space before / or ( */ - if (space) s--; - space = 0; - *s++ = *input; - break; - /* copy normal characters */ - default : - *s++ = tolower(*input); - space = 0; - } - } - - *s = 0; - *rval_len = strlen(*rval); - return 1; -} - -/* lowercase */ - -static int msre_fn_lowercase_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - int changed = 0; - - if (rval == NULL) return -1; - *rval = NULL; - - i = 0; - while(i < input_len) { - int x = input[i]; - input[i] = tolower(x); - if (x != input[i]) changed = 1; - i++; - } - - *rval = (char *)input; - *rval_len = input_len; - - return changed; -} - -/* trimLeft */ - -static int msre_fn_trimLeft_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - - *rval = (char *)input; - for (i = 0; i < input_len; i++) { - if (isspace(**rval) == 0) { - break; - } - (*rval)++; - } - - *rval_len = input_len - i; - - return (*rval_len == input_len ? 0 : 1); -} - -/* trimRight */ - -static int msre_fn_trimRight_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - - *rval = (char *)input; - for (i = input_len - 1; i >= 0; i--) { - if (isspace((*rval)[i]) == 0) { - break; - } - (*rval)[i] = '\0'; - } - - *rval_len = i + 1; - - return (*rval_len == input_len ? 0 : 1); -} - -/* trim */ - -static int msre_fn_trim_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - int rc = 0; - - rc = msre_fn_trimLeft_execute(mptmp, input, input_len, rval, rval_len); - if (rc == 1) { - rc = msre_fn_trimRight_execute(mptmp, (unsigned char *)*rval, *rval_len, rval, rval_len); - } - else { - rc = msre_fn_trimRight_execute(mptmp, input, input_len, rval, rval_len); - } - - return (*rval_len == input_len ? 0 : 1); -} - -/* removeNulls */ - -static int msre_fn_removeNulls_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i, j; - int changed = 0; - - i = j = 0; - while(i < input_len) { - if (input[i] == '\0') { - changed = 1; - } else { - input[j] = input[i]; - j++; - } - i++; - } - - *rval = (char *)input; - *rval_len = j; - - return changed; -} - -/* replaceNulls */ - -static int msre_fn_replaceNulls_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - int changed = 0; - - if (rval == NULL) return -1; - *rval = NULL; - - i = 0; - while(i < input_len) { - if (input[i] == '\0') { - changed = 1; - input[i] = ' '; - } - i++; - } - - *rval = (char *)input; - *rval_len = input_len; - - return changed; -} - -/* compressWhitespace */ - -static int msre_fn_compressWhitespace_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i, j, count; - int changed = 0; - int inwhitespace = 0; - - i = j = count = 0; - while(i < input_len) { - if (isspace(input[i])||(input[i] == NBSP)) { - if (inwhitespace) changed = 1; - inwhitespace = 1; - count++; - } else { - inwhitespace = 0; - if (count) { - input[j] = ' '; - count = 0; - j++; - } - input[j] = input[i]; - j++; - } - i++; - } - - if (count) { - input[j] = ' '; - j++; - } - - *rval = (char *)input; - *rval_len = j; - - return changed; -} - -/* cssDecode */ - -static int msre_fn_cssDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int length; - - length = css_decode_inplace(input, input_len); - *rval = (char *)input; - *rval_len = length; - - return (*rval_len == input_len ? 0 : 1); -} - -/* removeWhitespace */ - -static int msre_fn_removeWhitespace_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i, j; - int changed = 0; - - i = j = 0; - while(i < input_len) { - if (isspace(input[i])||(input[i] == NBSP)) { - /* do nothing */ - changed = 1; - } else { - input[j] = input[i]; - j++; - } - i++; - } - - *rval = (char *)input; - *rval_len = j; - - return changed; -} - -/* removeCommentsChar */ - -static int msre_fn_removeCommentsChar_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i, j; - int changed = 0; - - i = j = 0; - while(i < input_len) { - if ((input[i] == '/')&&(i + 1 < input_len)&&(input[i + 1] == '*')) { - changed = 1; - i += 2; - } else if ((input[i] == '*')&&(i + 1 < input_len)&&(input[i + 1] == '/')) { - changed = 1; - i += 2; - } else if ((input[i] == '<')&&(i + 1 < input_len)&&(input[i + 1] == '!')&& - (i + 2 < input_len)&&(input[i+2] == '-')&&(i + 3 < input_len)&& - (input[i + 3] == '-')) { - changed = 1; - i += 4; - } else if ((input[i] == '-')&&(i + 1 < input_len)&&(input[i + 1] == '-')&& - (i + 2 < input_len)&&(input[i+2] == '>')) { - changed = 1; - i += 3; - } else if ((input[i] == '-')&&(i + 1 < input_len)&&(input[i + 1] == '-')) { - changed = 1; - i += 2; - } else if (input[i] == '#') { - changed = 1; - i++; - } else { - input[j] = input[i]; - i++; - j++; - } - } - input[j] = '\0'; - - *rval = (char *)input; - *rval_len = j; - - return changed; -} - -/* removeComments */ - -static int msre_fn_removeComments_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i, j, incomment; - int changed = 0; - - i = j = incomment = 0; - while(i < input_len) { - if (incomment == 0) { - if ((input[i] == '/')&&(i + 1 < input_len)&&(input[i + 1] == '*')) { - changed = 1; - incomment = 1; - i += 2; - } else if ((input[i] == '<')&&(i + 1 < input_len)&&(input[i + 1] == '!')&& - (i + 2 < input_len)&&(input[i+2] == '-')&&(i + 3 < input_len)&& - (input[i + 3] == '-') && (incomment == 0)) { - incomment = 1; - changed = 1; - i += 4; - } else if ((input[i] == '-')&&(i + 1 < input_len)&&(input[i + 1] == '-') - && (incomment == 0)) { - changed = 1; - input[i] = ' '; - break; - } else if (input[i] == '#' && (incomment == 0)) { - changed = 1; - input[i] = ' '; - break; - } else { - input[j] = input[i]; - i++; - j++; - } - } else { - if ((input[i] == '*')&&(i + 1 < input_len)&&(input[i + 1] == '/')) { - incomment = 0; - i += 2; - input[j] = input[i]; - i++; - j++; - } else if ((input[i] == '-')&&(i + 1 < input_len)&&(input[i + 1] == '-')&& - (i + 2 < input_len)&&(input[i+2] == '>')) { - incomment = 0; - i += 3; - input[j] = input[i]; - i++; - j++; - } else { - i++; - } - } - } - - if (incomment) { - input[j++] = ' '; - } - - *rval = (char *)input; - *rval_len = j; - - return changed; -} - -/* replaceComments */ - -static int msre_fn_replaceComments_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i, j, incomment; - int changed = 0; - - i = j = incomment = 0; - while(i < input_len) { - if (incomment == 0) { - if ((input[i] == '/')&&(i + 1 < input_len)&&(input[i + 1] == '*')) { - changed = 1; - incomment = 1; - i += 2; - } else { - input[j] = input[i]; - i++; - j++; - } - } else { - if ((input[i] == '*')&&(i + 1 < input_len)&&(input[i + 1] == '/')) { - incomment = 0; - i += 2; - input[j] = ' '; - j++; - } else { - i++; - } - } - } - - if (incomment) { - input[j++] = ' '; - } - - *rval = (char *)input; - *rval_len = j; - - return changed; -} - -/* jsDecode */ - -static int msre_fn_jsDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int length; - - length = js_decode_nonstrict_inplace(input, input_len); - *rval = (char *)input; - *rval_len = length; - - return (*rval_len == input_len ? 0 : 1); -} - -/* urlDecode */ - -static int msre_fn_urlDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int length; - int invalid_count; - int changed; - - length = urldecode_nonstrict_inplace_ex(input, input_len, &invalid_count, &changed); - *rval = (char *)input; - *rval_len = length; - - return changed; -} - -/* urlDecodeUni */ - -static int msre_fn_urlDecodeUni_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int length; - int changed; - - length = urldecode_uni_nonstrict_inplace_ex(input, input_len, &changed); - *rval = (char *)input; - *rval_len = length; - - return changed; -} - -static int msre_fn_utf8Unicode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - int changed = 0; - - *rval = (char *)utf8_unicode_inplace_ex(mptmp, input, input_len, &changed); - *rval_len = strlen(*rval); - - return changed; -} - - -/* urlEncode */ - -static int msre_fn_urlEncode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - int changed; - - *rval = url_encode(mptmp, (char *)input, input_len, &changed); - *rval_len = strlen(*rval); - - return changed; -} - -/* base64Encode */ - -static int msre_fn_base64Encode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval_len = apr_base64_encode_len(input_len); /* returns len with NULL byte included */ - *rval = apr_palloc(mptmp, *rval_len); - apr_base64_encode(*rval, (const char *)input, input_len); - (*rval_len)--; - - return *rval_len ? 1 : 0; -} - -/* base64Decode */ - -static int msre_fn_base64Decode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval_len = apr_base64_decode_len((const char *)input); /* returns len with NULL byte included */ - *rval = apr_palloc(mptmp, *rval_len); - *rval_len = apr_base64_decode(*rval, (const char *)input); - - return *rval_len ? 1 : 0; -} - -/* length */ - -static int msre_fn_length_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval = apr_psprintf(mptmp, "%ld", input_len); - *rval_len = strlen(*rval); - - return 1; -} - -/* md5 */ - -static int msre_fn_md5_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - unsigned char digest[APR_MD5_DIGESTSIZE]; - - apr_md5(digest, input, input_len); - - *rval_len = APR_MD5_DIGESTSIZE; - *rval = apr_pstrmemdup(mptmp, (const char *)digest, APR_MD5_DIGESTSIZE); - - return 1; -} - -/* sha1 */ - -static int msre_fn_sha1_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - unsigned char digest[APR_SHA1_DIGESTSIZE]; - apr_sha1_ctx_t context; - - apr_sha1_init(&context); - apr_sha1_update(&context, (const char *)input, input_len); - apr_sha1_final(digest, &context); - - *rval_len = APR_SHA1_DIGESTSIZE; - *rval = apr_pstrmemdup(mptmp, (const char *)digest, APR_SHA1_DIGESTSIZE); - - return 1; -} - -/** -* \brief SqlHexDecode transformation function. Transform xNN data. -* -* \param mptmp Pointer to resource pool -* \param input Pointer to input data -* \param input_len Input data length -* \param rval Pointer to decoded buffer -* \param rval_len Decoded buffer length -* -* \retval 0 On failure -* \retval 1 On Success -*/ -static int msre_fn_sqlHexDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval_len = sql_hex2bytes_inplace(input, input_len); - *rval = (char *)input; - - return 1; -} - -/* hexDecode */ - -static int msre_fn_hexDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval_len = hex2bytes_inplace(input, input_len); - *rval = (char *)input; - - return 1; -} - -/* hexEncode */ - -static int msre_fn_hexEncode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval = bytes2hex(mptmp, input, input_len); - *rval_len = strlen(*rval); - - return 1; -} - -/* htmlEntityDecode */ - -static int msre_fn_htmlEntityDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval_len = html_entities_decode_inplace(mptmp, input, input_len); - *rval = (char *)input; - - return (*rval_len == input_len ? 0 : 1); -} - -/* escapeSeqDecode */ - -static int msre_fn_escapeSeqDecode_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - *rval_len = ansi_c_sequences_decode_inplace(input, input_len); - *rval = (char *)input; - - return (*rval_len == input_len ? 0 : 1); -} - -/* normalizePath */ - -static int msre_fn_normalizePath_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - int changed; - - *rval_len = normalize_path_inplace(input, input_len, 0, &changed); - *rval = (char *)input; - - return changed; -} - -/* normalizePathWin */ - -static int msre_fn_normalizePathWin_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - int changed; - - *rval_len = normalize_path_inplace(input, input_len, 1, &changed); - *rval = (char *)input; - - return changed; -} - -/* parityEven7bit */ - -static int msre_fn_parityEven7bit_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - int changed = 0; - - if (rval == NULL) return -1; - *rval = NULL; - - i = 0; - while(i < input_len) { - unsigned int x = input[i]; - - input[i] ^= input[i] >> 4; - input[i] &= 0xf; - - if ((0x6996 >> input[i]) & 1) { - input[i] = x | 0x80; - } - else { - input[i] = x & 0x7f; - } - - if (x != input[i]) changed = 1; - i++; - } - - *rval = (char *)input; - *rval_len = input_len; - - return changed; -} - -/* parityZero7bit */ - -static int msre_fn_parityZero7bit_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - int changed = 0; - - if (rval == NULL) return -1; - *rval = NULL; - - i = 0; - while(i < input_len) { - unsigned char c = input[i]; - input[i] &= 0x7f; - if (c != input[i]) changed = 1; - i++; - } - - *rval = (char *)input; - *rval_len = input_len; - - return changed; -} - -/* parityOdd7bit */ - -static int msre_fn_parityOdd7bit_execute(apr_pool_t *mptmp, unsigned char *input, - long int input_len, char **rval, long int *rval_len) -{ - long int i; - int changed = 0; - - if (rval == NULL) return -1; - *rval = NULL; - - i = 0; - while(i < input_len) { - unsigned int x = input[i]; - - input[i] ^= input[i] >> 4; - input[i] &= 0xf; - - if ((0x6996 >> input[i]) & 1) { - input[i] = x & 0x7f; - } - else { - input[i] = x | 0x80; - } - - if (x != input[i]) changed = 1; - i++; - } - - *rval = (char *)input; - *rval_len = input_len; - - return changed; -} - -/** -* \brief Base64 transformation function based on RFC2045 -* -* \param mptmp Pointer to resource pool -* \param input Pointer to input data -* \param input_len Input data length -* \param rval Pointer to decoded buffer -* \param rval_len Decoded buffer length -* -* \retval 0 On failure -* \retval 1 On Success -*/ -static int msre_fn_base64DecodeExt_execute(apr_pool_t *mptmp, unsigned char *input, long int input_len, char **rval, long int *rval_len) -{ - *rval_len = input_len; - *rval = apr_palloc(mptmp, *rval_len); - *rval_len = decode_base64_ext(*rval, (const unsigned char *)input, input_len); - - return *rval_len ? 1 : 0; -} - - -/* ------------------------------------------------------------------------------ */ - -/** - * Registers one transformation function with the engine. - */ -void msre_engine_tfn_register(msre_engine *engine, const char *name, - fn_tfn_execute_t execute) -{ - msre_tfn_metadata *metadata = (msre_tfn_metadata *)apr_pcalloc(engine->mp, - sizeof(msre_tfn_metadata)); - if (metadata == NULL) return; - - metadata->name = name; - metadata->execute = execute; - - apr_table_setn(engine->tfns, name, (void *)metadata); -} - -/** - * Returns transformation function metadata given a name. - */ -msre_tfn_metadata *msre_engine_tfn_resolve(msre_engine *engine, const char *name) { - return (msre_tfn_metadata *)apr_table_get(engine->tfns, name); -} - -/** - * Register the default transformation functions. - */ -void msre_engine_register_default_tfns(msre_engine *engine) { - - /* none */ - msre_engine_tfn_register(engine, - "none", - NULL - ); - - /* base64Decode */ - msre_engine_tfn_register(engine, - "base64Decode", - msre_fn_base64Decode_execute - ); - - /* base64Encode */ - msre_engine_tfn_register(engine, - "base64Encode", - msre_fn_base64Encode_execute - ); - - /* compressWhitespace */ - msre_engine_tfn_register(engine, - "compressWhitespace", - msre_fn_compressWhitespace_execute - ); - - /* cssDecode */ - msre_engine_tfn_register(engine, - "cssDecode", - msre_fn_cssDecode_execute - ); - - /* escapeSeqDecode */ - msre_engine_tfn_register(engine, - "escapeSeqDecode", - msre_fn_escapeSeqDecode_execute - ); - - /* sqlHexDecode */ - msre_engine_tfn_register(engine, - "sqlHexDecode", - msre_fn_sqlHexDecode_execute - ); - - /* hexDecode */ - msre_engine_tfn_register(engine, - "hexDecode", - msre_fn_hexDecode_execute - ); - - /* hexEncode */ - msre_engine_tfn_register(engine, - "hexEncode", - msre_fn_hexEncode_execute - ); - - /* htmlEntityDecode */ - msre_engine_tfn_register(engine, - "htmlEntityDecode", - msre_fn_htmlEntityDecode_execute - ); - - /* jsDecode */ - msre_engine_tfn_register(engine, - "jsDecode", - msre_fn_jsDecode_execute - ); - - /* length */ - msre_engine_tfn_register(engine, - "length", - msre_fn_length_execute - ); - - /* lowercase */ - msre_engine_tfn_register(engine, - "lowercase", - msre_fn_lowercase_execute - ); - - /* md5 */ - msre_engine_tfn_register(engine, - "md5", - msre_fn_md5_execute - ); - - /* normalisePath */ - msre_engine_tfn_register(engine, - "normalisePath", - msre_fn_normalizePath_execute - ); - - /* normalizePath */ - msre_engine_tfn_register(engine, - "normalizePath", - msre_fn_normalizePath_execute - ); - - /* normalisePathWin */ - msre_engine_tfn_register(engine, - "normalisePathWin", - msre_fn_normalizePathWin_execute - ); - - /* normalizePathWin */ - msre_engine_tfn_register(engine, - "normalizePathWin", - msre_fn_normalizePathWin_execute - ); - - /* parityEven7bit */ - msre_engine_tfn_register(engine, - "parityEven7bit", - msre_fn_parityEven7bit_execute - ); - - /* parityZero7bit */ - msre_engine_tfn_register(engine, - "parityZero7bit", - msre_fn_parityZero7bit_execute - ); - - /* parityOdd7bit */ - msre_engine_tfn_register(engine, - "parityOdd7bit", - msre_fn_parityOdd7bit_execute - ); - - /* removeWhitespace */ - msre_engine_tfn_register(engine, - "removeWhitespace", - msre_fn_removeWhitespace_execute - ); - - /* removeNulls */ - msre_engine_tfn_register(engine, - "removeNulls", - msre_fn_removeNulls_execute - ); - - /* replaceNulls */ - msre_engine_tfn_register(engine, - "replaceNulls", - msre_fn_replaceNulls_execute - ); - - /* removeComments */ - msre_engine_tfn_register(engine, - "removeComments", - msre_fn_removeComments_execute - ); - - /* removeCommentsChar */ - msre_engine_tfn_register(engine, - "removeCommentsChar", - msre_fn_removeCommentsChar_execute - ); - - /* replaceComments */ - msre_engine_tfn_register(engine, - "replaceComments", - msre_fn_replaceComments_execute - ); - - /* sha1 */ - msre_engine_tfn_register(engine, - "sha1", - msre_fn_sha1_execute - ); - - /* trim */ - msre_engine_tfn_register(engine, - "trim", - msre_fn_trim_execute - ); - - /* trimLeft */ - msre_engine_tfn_register(engine, - "trimLeft", - msre_fn_trimLeft_execute - ); - - msre_engine_tfn_register(engine, - "cmdline", - msre_fn_cmdline_execute - ); - - /* trimRight */ - msre_engine_tfn_register(engine, - "trimRight", - msre_fn_trimRight_execute - ); - - /* urlDecode */ - msre_engine_tfn_register(engine, - "urlDecode", - msre_fn_urlDecode_execute - ); - - /* urlDecodeUni */ - msre_engine_tfn_register(engine, - "urlDecodeUni", - msre_fn_urlDecodeUni_execute - ); - - /* Utf8Unicode */ - msre_engine_tfn_register(engine, - "Utf8toUnicode", - msre_fn_utf8Unicode_execute - ); - - /* urlEncode */ - msre_engine_tfn_register(engine, - "urlEncode", - msre_fn_urlEncode_execute - ); - - /* base64DecodeExt */ - msre_engine_tfn_register(engine, - "base64DecodeExt", - msre_fn_base64DecodeExt_execute - ); - -} diff --git a/apache2/re_variables.c b/apache2/re_variables.c deleted file mode 100644 index ce35bc1b66..0000000000 --- a/apache2/re_variables.c +++ /dev/null @@ -1,3967 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "http_core.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "re.h" -#include "msc_util.h" - -#include "libxml/xpathInternals.h" - -/** - * Generates a variable from a string and a length. - */ -static int var_simple_generate_ex(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, - const char *value, int value_len) -{ - msre_var *rvar = NULL; - - if (value == NULL) return 0; - - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = value; - rvar->value_len = value_len; - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/** - * Generates a variable from a NULL-terminated string. - */ -static int var_simple_generate(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, - const char *value) -{ - if (value == NULL) return 0; - return var_simple_generate_ex(var, vartab, mptmp, value, strlen(value)); -} - -/** - * Validate that a target parameter is valid. We only need to take - * care of the case when the parameter is a regular expression. - */ -static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { - /* It's OK if there's no parameter. */ - if (var->param == NULL) return NULL; - - /* Is it a regular expression? */ - if ((strlen(var->param) > 2)&&(var->param[0] == '/') - &&(var->param[strlen(var->param) - 1] == '/')) - { /* Regex. */ - msc_regex_t *regex = NULL; - const char *errptr = NULL; - const char *pattern = NULL; - int erroffset; - - pattern = apr_pstrmemdup(ruleset->mp, var->param + 1, strlen(var->param + 1) - 1); - if (pattern == NULL) return FATAL_ERROR; - - regex = msc_pregcomp(ruleset->mp, pattern, PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); - if (regex == NULL) { - return apr_psprintf(ruleset->mp, "Error compiling pattern (offset %d): %s", - erroffset, errptr); - } - - /* Store the compiled regex for later. */ - var->param_data = regex; - } - - /* Simple string */ - return NULL; -} - -/* Custom parameter validation functions */ - -/* ARGS */ - -static int var_args_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - /* Loop through the arguments. */ - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - int match = 0; - - /* Figure out if we want to include this argument. */ - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - /* Run the regex against the argument name. */ - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(arg->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = arg->value; - rvar->value_len = arg->value_len; - rvar->name = apr_psprintf(mptmp, "ARGS:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* ARGS_COMBINED_SIZE */ - -static int var_args_combined_size_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - unsigned int combined_size = 0; - int i; - msre_var *rvar = NULL; - - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - combined_size += arg->name_len; - combined_size += arg->value_len; - } - - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%u", combined_size); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* ARGS_NAMES */ - -static int var_args_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(arg->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = arg->name; - rvar->value_len = arg->name_len; - rvar->name = apr_psprintf(mptmp, "ARGS_NAMES:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* ARGS_GET */ - -static int var_args_get_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - /* Loop through the arguments. */ - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - int match = 0; - - /* Only QUERY_STRING arguments */ - if (strcmp("QUERY_STRING", arg->origin) != 0) continue; - - /* Figure out if we want to include this argument. */ - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - /* Run the regex against the argument name. */ - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(arg->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = arg->value; - rvar->value_len = arg->value_len; - rvar->name = apr_psprintf(mptmp, "ARGS_GET:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* ARGS_GET_NAMES */ - -static int var_args_get_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - int match = 0; - - /* Only QUERY_STRING arguments */ - if (strcmp("QUERY_STRING", arg->origin) != 0) continue; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(arg->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = arg->name; - rvar->value_len = arg->name_len; - rvar->name = apr_psprintf(mptmp, "ARGS_GET_NAMES:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* ARGS_POST */ - -static int var_args_post_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - /* Loop through the arguments. */ - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - int match = 0; - - /* Only BODY arguments */ - if (strcmp("BODY", arg->origin) != 0) continue; - - /* Figure out if we want to include this argument. */ - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - /* Run the regex against the argument name. */ - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(arg->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = arg->value; - rvar->value_len = arg->value_len; - rvar->name = apr_psprintf(mptmp, "ARGS_POST:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* ARGS_POST_NAMES */ - -static int var_args_post_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->arguments); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_arg *arg = (msc_arg *)te[i].val; - int match = 0; - - /* Only BODY arguments */ - if (strcmp("BODY", arg->origin) != 0) continue; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(arg->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = arg->name; - rvar->value_len = arg->name_len; - rvar->name = apr_psprintf(mptmp, "ARGS_POST_NAMES:%s", log_escape_nq_ex(mptmp, arg->name, arg->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* RULE */ - -static int var_rule_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_actionset *actionset = NULL; - - if (rule == NULL) return 0; - - actionset = rule->actionset; - if (rule->chain_starter != NULL) actionset = rule->chain_starter->actionset; - - if ((strcasecmp(var->param, "id") == 0)&&(actionset->id != NULL)) { - return var_simple_generate(var, vartab, mptmp, actionset->id); - } else - if ((strcasecmp(var->param, "rev") == 0)&&(actionset->rev != NULL)) { - return var_simple_generate(var, vartab, mptmp, actionset->rev); - } else - if ((strcasecmp(var->param, "severity") == 0)&&(actionset->severity != -1)) { - char *value = apr_psprintf(mptmp, "%d", actionset->severity); - return var_simple_generate(var, vartab, mptmp, value); - } else - if ((strcasecmp(var->param, "msg") == 0)&&(actionset->msg != NULL)) { - return var_simple_generate(var, vartab, mptmp, actionset->msg); - } else - if ((strcasecmp(var->param, "logdata") == 0)&&(actionset->logdata != NULL)) { - return var_simple_generate(var, vartab, mptmp, actionset->logdata); - } else - if ((strcasecmp(var->param, "ver") == 0)&&(actionset->version != NULL)) { - return var_simple_generate(var, vartab, mptmp, actionset->version); - } else - if ((strcasecmp(var->param, "maturity") == 0)&&(actionset->maturity != -1)) { - char *value = apr_psprintf(mptmp, "%d", actionset->maturity); - return var_simple_generate(var, vartab, mptmp, value); - } else - if ((strcasecmp(var->param, "accuracy") == 0)&&(actionset->accuracy != -1)) { - char *value = apr_psprintf(mptmp, "%d", actionset->accuracy); - return var_simple_generate(var, vartab, mptmp, value); - } - - - return 0; -} - -/* ENV */ - -static char *var_env_validate(msre_ruleset *ruleset, msre_var *var) { - if (var->param == NULL) { - return apr_psprintf(ruleset->mp, "Parameter required for ENV."); - } - if ((strlen(var->param) > 2)&&(var->param[0] == '/') - &&(var->param[strlen(var->param) - 1] == '/')) - { - return apr_psprintf(ruleset->mp, "Regular expressions not supported in ENV."); - } - return NULL; -} - -static int var_env_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = get_env_var(msr->r, (char *)var->param); - if (value != NULL) { - return var_simple_generate(var, vartab, mptmp, value); - } - return 0; -} - -/* REQUEST_URI_RAW */ - -static int var_request_uri_raw_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->r->unparsed_uri); -} - -/* UNIQUE_ID */ - -static int var_uniqueid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = get_env_var(msr->r, "UNIQUE_ID"); - if (value != NULL) { - return var_simple_generate(var, vartab, mptmp, value); - } - - return 0; -} - - -/* REQUEST_URI */ - -static int var_request_uri_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) /* dynamic */ -{ - char *value = NULL; - - if (msr->r->parsed_uri.query == NULL) value = msr->r->parsed_uri.path; - else value = apr_pstrcat(mptmp, msr->r->parsed_uri.path, "?", msr->r->parsed_uri.query, NULL); - - return var_simple_generate(var, vartab, mptmp, value); -} - -/* REQBODY_PROCESSOR */ - -static int var_reqbody_processor_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - if (msr->msc_reqbody_processor == NULL) { - rvar->value = apr_pstrdup(mptmp, ""); - rvar->value_len = 0; - } else { - rvar->value = apr_pstrdup(mptmp, msr->msc_reqbody_processor); - rvar->value_len = strlen(rvar->value); - } - - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* SDBM_DELETE_ERROR */ -static int var_sdbm_delete_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = apr_psprintf(mptmp, "%d", msr->msc_sdbm_delete_error); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* REQBODY_ERROR */ - -static int var_reqbody_processor_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = apr_psprintf(mptmp, "%d", msr->msc_reqbody_error); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* REQBODY_ERROR_MSG */ - -static int var_reqbody_processor_error_msg_generate(modsec_rec *msr, msre_var *var, - msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - if (msr->msc_reqbody_error_msg == NULL) { - rvar->value = apr_pstrdup(mptmp, ""); - rvar->value_len = 0; - } else { - rvar->value = apr_psprintf(mptmp, "%s", msr->msc_reqbody_error_msg); - rvar->value_len = strlen(rvar->value); - } - - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* XML */ - -static char *var_xml_validate(msre_ruleset *ruleset, msre_var *var) { - /* It's OK if there's no parameter. */ - if (var->param == NULL) return NULL; - - /* ENH validate XPath expression in advance. */ - - return NULL; -} - -static int var_xml_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - xmlXPathContextPtr xpathCtx; - xmlXPathObjectPtr xpathObj; - xmlNodeSetPtr nodes; - const xmlChar* xpathExpr = NULL; - int i, count; - - /* Is there an XML document tree at all? */ - if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { - /* Sorry, we've got nothing to give! */ - return 0; - } - - if (var->param == NULL) { - /* Invocation without an XPath expression makes sense - * with functions that manipulate the document tree. - */ - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = apr_pstrdup(mptmp, "[XML document tree]"); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; - } - - /* Process the XPath expression. */ - - count = 0; - xpathExpr = (const xmlChar*)var->param; - - xpathCtx = xmlXPathNewContext(msr->xml->doc); - if (xpathCtx == NULL) { - msr_log(msr, 1, "XML: Unable to create new XPath context."); - return -1; - } - - /* Look through the actionset of the associated rule - * for the namespace information. Register them if any are found. - */ - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_action *action = (msre_action *)telts[i].val; - - if (strcasecmp(action->metadata->name, "xmlns") == 0) { - char *prefix, *href; - - if (parse_name_eq_value(mptmp, action->param, &prefix, &href) < 0) return -1; - if ((prefix == NULL)||(href == NULL)) return -1; - - if(xmlXPathRegisterNs(xpathCtx, (const xmlChar*)prefix, (const xmlChar*)href) != 0) { - msr_log(msr, 1, "Failed to register XML namespace href \"%s\" prefix \"%s\".", - log_escape(mptmp, prefix), log_escape(mptmp, href)); - return -1; - } - - msr_log(msr, 4, "Registered XML namespace href \"%s\" prefix \"%s\".", - log_escape(mptmp, prefix), log_escape(mptmp, href)); - } - } - - /* Initialise XPath expression. */ - xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx); - if (xpathObj == NULL) { - msr_log(msr, 1, "XML: Unable to evaluate xpath expression."); - xmlXPathFreeContext(xpathCtx); - return -1; - } - - /* Evaluate XPath expression. */ - nodes = xpathObj->nodesetval; - if (nodes == NULL) { - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - return 0; - } - - /* Create one variable for each node in the result. */ - for(i = 0; i < nodes->nodeNr; i++) { - msre_var *rvar = NULL; - char *content = NULL; - - content = (char *)xmlNodeGetContent(nodes->nodeTab[i]); - if (content != NULL) { - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_pstrdup(mptmp, content); - xmlFree(content); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - - return count; -} - -/* WEBSERVER_ERROR_LOG */ - -static int var_webserver_error_log_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - int i, count = 0; - - for(i = 0; i < msr->error_messages->nelts; i++) { - error_message_t *em = (((error_message_t **)msr->error_messages->elts)[i]); - char *fem = NULL; - - fem = format_error_log_message(mptmp, em); - if (fem != NULL) { - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_pstrdup(mptmp, fem); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 -static int var_useragent_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->useragent_ip ? msr->useragent_ip : "0.0.0.0"); -} -#endif - -/* REMOTE_ADDR */ - -static int var_remote_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ -#if !defined(MSC_TEST) -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3 - if (ap_find_linked_module("mod_remoteip.c") != NULL) { - if(msr->r->useragent_ip != NULL) msr->remote_addr = apr_pstrdup(msr->mp, msr->r->useragent_ip); - return var_simple_generate(var, vartab, mptmp, msr->remote_addr); - } -#endif -#endif - - return var_simple_generate(var, vartab, mptmp, msr->remote_addr); -} - -/* REMOTE_HOST */ - -static int var_remote_host_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value1 = ap_get_remote_host(msr->r->connection, msr->r->per_dir_config, - REMOTE_NAME, NULL); - return var_simple_generate(var, vartab, mptmp, value1); -} - -/* REMOTE_PORT */ - -static int var_remote_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = apr_psprintf(mptmp, "%u", msr->remote_port); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* REMOTE_USER */ - -static int var_remote_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->remote_user); -} - -/* TX */ - -static int var_tx_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->tx_vars); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "TX:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* GEO */ - -static int var_geo_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->geo_vars); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "GEO:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* HIGHEST_SEVERITY */ - -static int var_highest_severity_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, - apr_psprintf(mptmp, "%d", msr->highest_severity)); -} - -/* IP */ - -static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - apr_table_t *target_col = NULL; - - target_col = (apr_table_t *)apr_table_get(msr->collections, "ip"); - if (target_col == NULL) return 0; - - arr = apr_table_elts(target_col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "IP:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* MATCHED_VAR */ - -static int var_matched_var_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate_ex(var, vartab, mptmp, - apr_pmemdup(mptmp, - msr->matched_var->value, - msr->matched_var->value_len), - msr->matched_var->value_len); -} - -/* MATCHED_VAR_NAME */ - -static int var_matched_var_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate_ex(var, vartab, mptmp, - apr_pmemdup(mptmp, - msr->matched_var->name, - msr->matched_var->name_len), - msr->matched_var->name_len); -} - -/* SESSION */ - -static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - apr_table_t *target_col = NULL; - - target_col = (apr_table_t *)apr_table_get(msr->collections, "session"); - if (target_col == NULL) return 0; - - arr = apr_table_elts(target_col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional inclusion. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "SESSION:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* USER */ - -static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - apr_table_t *target_col = NULL; - - target_col = (apr_table_t *)apr_table_get(msr->collections, "user"); - if (target_col == NULL) return 0; - - arr = apr_table_elts(target_col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional match. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "USER:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* GLOBAL */ - -static int var_global_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - apr_table_t *target_col = NULL; - - target_col = (apr_table_t *)apr_table_get(msr->collections, "global"); - if (target_col == NULL) return 0; - - arr = apr_table_elts(target_col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional match. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "GLOBAL:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* RESOURCE */ - -static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - apr_table_t *target_col = NULL; - - target_col = (apr_table_t *)apr_table_get(msr->collections, "resource"); - if (target_col == NULL) return 0; - - arr = apr_table_elts(target_col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *str = (msc_string *)te[i].val; - int match; - - /* Figure out if we want to include this variable. */ - match = 0; - if (var->param == NULL) match = 1; /* Unconditional match. */ - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = str->value; - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "RESOURCE:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* FILES_TMPNAMES */ - -static int var_files_tmpnames_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - multipart_part **parts = NULL; - int i, count = 0; - - if (msr->mpd == NULL) return 0; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if ((parts[i]->type == MULTIPART_FILE)&&(parts[i]->tmp_file_name != NULL)) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, - strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = parts[i]->tmp_file_name; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "FILES_TMPNAMES:%s", - log_escape_nq(mptmp, parts[i]->name)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - } - - return count; -} - -/* FILES */ - -static int var_files_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - multipart_part **parts = NULL; - int i, count = 0; - - if (msr->mpd == NULL) return 0; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FILE) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, - strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = parts[i]->filename; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "FILES:%s", - log_escape_nq(mptmp, parts[i]->name)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - } - - return count; -} - -/* FILES_SIZES */ - -static int var_files_sizes_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - multipart_part **parts = NULL; - int i, count = 0; - - if (msr->mpd == NULL) return 0; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FILE) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, - strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = apr_psprintf(mptmp, "%u", parts[i]->tmp_file_size); - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "FILES_SIZES:%s", - log_escape_nq(mptmp, parts[i]->name)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - } - - return count; -} - -/* FILES_NAMES */ - -static int var_files_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - multipart_part **parts = NULL; - int i, count = 0; - - if (msr->mpd == NULL) return 0; - - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FILE) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = parts[i]->name; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "FILES_NAMES:%s", - log_escape_nq_ex(mptmp, parts[i]->name, rvar->value_len)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* FILES_COMBINED_SIZE */ - -static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - multipart_part **parts = NULL; - msre_var *rvar = NULL; - unsigned int combined_size = 0; - int i; - - if (msr->mpd != NULL) { - parts = (multipart_part **)msr->mpd->parts->elts; - for(i = 0; i < msr->mpd->parts->nelts; i++) { - if (parts[i]->type == MULTIPART_FILE) { - combined_size += parts[i]->tmp_file_size; - } - } - } - - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%u", combined_size); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* MODSEC_BUILD */ - -static int var_modsec_build_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, modsec_build(mptmp)); -} - -/* MULTIPART_FILENAME */ - -static int var_multipart_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->multipart_filename); -} - -/* MULTIPART_NAME */ - -static int var_multipart_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->multipart_name); -} - -/* MULTIPART_BOUNDARY_QUOTED */ - -static int var_multipart_boundary_quoted_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_boundary_quoted != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_BOUNDARY_WHITESPACE */ - -static int var_multipart_boundary_whitespace_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_boundary_whitespace != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_DATA_AFTER */ - -static int var_multipart_data_after_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_data_after != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_DATA_BEFORE */ - -static int var_multipart_data_before_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_data_before != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_HEADER_FOLDING */ - -static int var_multipart_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_header_folding != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_CRLF_LINE */ - -static int var_multipart_crlf_line_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_crlf_line != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_CRLF_LF_LINES */ - -static int var_multipart_crlf_lf_lines_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_lf_line != 0)&&(msr->mpd->flag_crlf_line != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_LF_LINE */ - -static int var_multipart_lf_line_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_lf_line != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_MISSING_SEMICOLON */ - -static int var_multipart_missing_semicolon_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_missing_semicolon != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_INVALID_PART */ - -static int var_multipart_invalid_part_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_part != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* MULTIPART_INVALID_QUOTING */ - -static int var_multipart_invalid_quoting_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_quoting != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* 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, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->mpd != NULL) { - /* Respond positive if at least one of the multipart flags is raised. */ - if ( (msr->mpd->flag_error) - ||(msr->mpd->flag_boundary_quoted != 0) - ||(msr->mpd->flag_boundary_whitespace != 0) - ||(msr->mpd->flag_data_before != 0) - ||(msr->mpd->flag_data_after != 0) - ||(msr->mpd->flag_header_folding != 0) - ||(msr->mpd->flag_lf_line != 0) - ||(msr->mpd->flag_missing_semicolon != 0) - ||(msr->mpd->flag_invalid_quoting != 0) - ||(msr->mpd->flag_invalid_part != 0) - ||(msr->mpd->flag_invalid_header_folding != 0) - ||(msr->mpd->flag_file_limit_exceeded != 0) - ) { - return var_simple_generate(var, vartab, mptmp, "1"); - } - } - - return var_simple_generate(var, vartab, mptmp, "0"); -} - -/* MULTIPART_UNMATCHED_BOUNDARY */ - -static int var_multipart_unmatched_boundary_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_unmatched_boundary != 0)) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* URLENCODED_ERROR */ - -static int var_urlencoded_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->urlencoded_error) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* INBOUND_DATA_ERROR */ - -static int var_inbound_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->inbound_error) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -/* OUTBOUND_DATA_ERROR */ - -static int var_outbound_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->outbound_error) { - return var_simple_generate(var, vartab, mptmp, "1"); - } else { - return var_simple_generate(var, vartab, mptmp, "0"); - } -} - -static apr_time_t calculate_perf_combined(modsec_rec *msr) { - return msr->time_phase1 + msr->time_phase2 + msr->time_phase3 + msr->time_phase4 - + msr->time_phase5 + msr->time_storage_write /* time_storage_read is already - included in phases */ + msr->time_logging + msr->time_gc; -} - -char *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp) { - return apr_psprintf(mp, "combined=%" APR_TIME_T_FMT ", p1=%" APR_TIME_T_FMT - ", p2=%" APR_TIME_T_FMT ", p3=%" APR_TIME_T_FMT ", p4=%" APR_TIME_T_FMT - ", p5=%" APR_TIME_T_FMT ", sr=%" APR_TIME_T_FMT ", sw=%" APR_TIME_T_FMT - ", l=%" APR_TIME_T_FMT ", gc=%" APR_TIME_T_FMT, calculate_perf_combined(msr), - msr->time_phase1, msr->time_phase2, msr->time_phase3, msr->time_phase4, - msr->time_phase5, msr->time_storage_read, msr->time_storage_write, - msr->time_logging, msr->time_gc); -} - -static int generate_performance_variable(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp, apr_time_t value) -{ - msre_var *rvar = NULL; - - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%" APR_TIME_T_FMT, value); - rvar->value_len = strlen(rvar->value); - - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* PERF_ALL */ - -static int var_perf_all_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = format_all_performance_variables(msr, mptmp); - rvar->value_len = strlen(rvar->value); - - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* PERF_COMBINED */ - -static int var_perf_combined_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, calculate_perf_combined(msr)); -} - -/* PERF_GC */ - -static int var_perf_gc_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_gc); -} - -/* PERF_PHASE1 */ - -static int var_perf_phase1_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase1); -} - -/* PERF_PHASE2 */ - -static int var_perf_phase2_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase2); -} - -/* PERF_PHASE3 */ - -static int var_perf_phase3_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase3); -} - -/* PERF_PHASE4 */ - -static int var_perf_phase4_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase4); -} - -/* PERF_PHASE5 */ - -static int var_perf_phase5_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase5); -} - -/* PERF_SREAD */ - -static int var_perf_sread_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_storage_read); -} - -/* PERF_SWRITE */ - -static int var_perf_swrite_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_storage_write); -} - -/* PERF_LOGGING */ - -static int var_perf_logging_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_logging); -} - - -/* PERF_RULES */ - -static int var_perf_rules_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->perf_rules); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].val; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "PERF_RULES:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* DURATION */ - -static int var_duration_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%" APR_TIME_T_FMT, - (apr_time_usec(apr_time_now() - msr->r->request_time))); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME */ - -static int var_time_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d%02d%02d%02d%02d%02d%02d", - (tm->tm_year / 100) + 19, (tm->tm_year % 100), - tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, - tm->tm_sec); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_YEAR */ - -static int var_time_year_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d%02d", - (tm->tm_year / 100) + 19, - tm->tm_year % 100); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_WDAY */ - -static int var_time_wday_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%d", tm->tm_wday); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_SEC */ - -static int var_time_sec_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_sec); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_MIN */ - -static int var_time_min_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_min); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_HOUR */ -static int var_time_hour_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_hour); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_MON */ - -static int var_time_mon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_mon + 1); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_DAY */ - -static int var_time_day_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - struct tm *tm; - time_t tc; - - tc = time(NULL); - tm = localtime(&tc); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_mday); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* TIME_EPOCH */ - -static int var_time_epoch_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - msre_var *rvar = NULL; - time_t tc; - - tc = time(NULL); - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%ld", (long)tc); - rvar->value_len = strlen(rvar->value); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - return 1; -} - -/* QUERY_STRING */ - -static int var_query_string_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->query_string); -} - -/* REQUEST_BASENAME */ - -static int var_request_basename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = file_basename(mptmp, msr->r->parsed_uri.path); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* REQUEST_BODY */ - -static int var_request_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->msc_reqbody_buffer != NULL) { - return var_simple_generate_ex(var, vartab, mptmp, - msr->msc_reqbody_buffer, msr->msc_reqbody_length); - } - return 0; -} - -/* REQUEST_BODY_LENGTH */ - -static int var_request_body_length_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = apr_psprintf(mptmp, "%d", msr->msc_reqbody_length); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* MATCHED_VARS_NAMES */ - -static int var_matched_vars_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->matched_vars); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - msc_string *str = (msc_string *)te[i].val; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - strlen(str->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match && (strncmp(str->name,"MATCHED_VARS:",13) != 0) && (strncmp(str->name,"MATCHED_VARS_NAMES:",19))) { - - msre_var *rvar = apr_palloc(mptmp, sizeof(msre_var)); - - rvar->value = apr_pstrndup(mptmp, str->name, strlen(str->name)); - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "%s", - log_escape_nq(mptmp, str->name)); - - if(var->is_counting == 0) - rvar->is_counting = 0; - else - rvar->is_counting = 1; - - if(var->is_negated == 0) - rvar->is_negated = 0; - else - rvar->is_negated = 1; - - apr_table_addn(vartab, rvar->name, (void *)rvar); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Set variable \"%s\" size %d to collection.", rvar->name,rvar->value_len); - } - - count++; - } - } - - return count; -} - -/* MATCHED_VARS */ - -static int var_matched_vars_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->matched_vars); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - msc_string *str = (msc_string *)te[i].val; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - strlen(str->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(str->name, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match && (strncmp(str->name,"MATCHED_VARS:",13) != 0) && (strncmp(str->name,"MATCHED_VARS_NAMES:",19))) { - - msre_var *rvar = apr_palloc(mptmp, sizeof(msre_var)); - - rvar->value = apr_pstrndup(mptmp, str->value, str->value_len); - rvar->value_len = str->value_len; - rvar->name = apr_psprintf(mptmp, "%s", - log_escape_nq(mptmp, str->name)); - - if(var->is_counting == 0) - rvar->is_counting = 0; - else - rvar->is_counting = 1; - - if(var->is_negated == 0) - rvar->is_negated = 0; - else - rvar->is_negated = 1; - - apr_table_addn(vartab, rvar->name, (void *)rvar); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Set variable \"%s\" value \"%s\" size %d to collection.", rvar->name, rvar->value, rvar->value_len); - } - - count++; - } - } - - return count; -} - -/* REQUEST_COOKIES */ - -static int var_request_cookies_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->request_cookies); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].val; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "REQUEST_COOKIES:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* REQUEST_COOKIES_NAMES */ - -static int var_request_cookies_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->request_cookies); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].key; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "REQUEST_COOKIES_NAMES:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* REQUEST_HEADERS */ - -static int var_request_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->request_headers); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].val; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "REQUEST_HEADERS:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* REQUEST_HEADERS_NAMES */ - -static int var_request_headers_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->request_headers); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].key; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "REQUEST_HEADERS_NAMES:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* REQUEST_FILENAME */ - -static int var_request_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->r->parsed_uri.path); -} - -/* REQUEST_LINE */ - -static int var_request_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->request_line); -} - -/* REQUEST_METHOD */ - -static int var_request_method_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->request_method); -} - -/* REQUEST_PROTOCOL */ - -static int var_request_protocol_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->request_protocol); -} - -/* SERVER_ADDR */ - -static int var_server_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->local_addr); -} - -/* SERVER_NAME */ - -static int var_server_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->hostname); -} - -/* SERVER_PORT */ - -static int var_server_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = apr_psprintf(mptmp, "%u", msr->local_port); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SCRIPT_BASENAME */ - -static int var_script_basename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = file_basename(mptmp, msr->r->filename); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SCRIPT_FILENAME */ - -static int var_script_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = msr->r->filename; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SCRIPT_GID */ - -static int var_script_gid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.group); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SCRIPT_GROUPNAME */ - -static int var_script_groupname_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = NULL; - if (apr_gid_name_get(&value, msr->r->finfo.group, mptmp) == APR_SUCCESS) { - return var_simple_generate(var, vartab, mptmp, value); - } - return 0; -} - -/* SCRIPT_MODE */ - -static int var_script_mode_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = apr_psprintf(mptmp, "%04x", msr->r->finfo.protection); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SCRIPT_UID */ - -static int var_script_uid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.user); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SCRIPT_USERNAME */ - -static int var_script_username_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = NULL; - if (apr_uid_name_get(&value, msr->r->finfo.user, mptmp) == APR_SUCCESS) { - return var_simple_generate(var, vartab, mptmp, value); - } - return 0; -} - -/* AUTH_TYPE */ - -static int var_auth_type_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - char *value = msr->r->ap_auth_type; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* PATH_INFO */ - -static int var_path_info_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = msr->r->path_info; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* STREAM_OUTPUT_BODY */ - -static int var_stream_output_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->stream_output_data != NULL) { - return var_simple_generate_ex(var, vartab, mptmp, - msr->stream_output_data, msr->stream_output_length); - } - - return 0; -} - -/* STREAM_INPUT_BODY */ - -static int var_stream_input_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->stream_input_data != NULL) { - return var_simple_generate_ex(var, vartab, mptmp, - msr->stream_input_data, msr->stream_input_length); - } - - return 0; -} - -/* RESPONSE_BODY */ - -static int var_response_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - if (msr->resbody_data != NULL) { - return var_simple_generate_ex(var, vartab, mptmp, - msr->resbody_data, msr->resbody_length); - } - - return 0; -} - -/* RESPONSE_HEADERS */ - -static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - if (msr->response_headers == NULL) return 0; - - arr = apr_table_elts(msr->response_headers); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].val; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "RESPONSE_HEADERS:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* RESPONSE_HEADERS_NAMES */ - -static int var_response_headers_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const apr_array_header_t *arr = NULL; - const apr_table_entry_t *te = NULL; - int i, count = 0; - - arr = apr_table_elts(msr->response_headers); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - int match = 0; - - /* Figure out if we want to include this variable. */ - if (var->param == NULL) match = 1; - else { - if (var->param_data != NULL) { /* Regex. */ - char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; - } else { /* Simple comparison. */ - if (strcasecmp(te[i].key, var->param) == 0) match = 1; - } - } - - /* If we had a match add this argument to the collection. */ - if (match) { - msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - - rvar->value = te[i].key; - rvar->value_len = strlen(rvar->value); - rvar->name = apr_psprintf(mptmp, "RESPONSE_HEADERS_NAMES:%s", - log_escape_nq(mptmp, te[i].key)); - apr_table_addn(vartab, rvar->name, (void *)rvar); - - count++; - } - } - - return count; -} - -/* STATUS_LINE */ - -static int var_status_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = msr->status_line; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* RESPONSE_PROTOCOL */ - -static int var_response_protocol_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = msr->response_protocol; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* RESPONSE_STATUS */ - -static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = apr_psprintf(mptmp, "%u", msr->response_status); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* RESPONSE_CONTENT_TYPE */ - -static int var_response_content_type(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->r->content_type); -} - -/* RESPONSE_CONTENT_LENGTH */ - -static int var_response_content_length(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = apr_psprintf(mptmp, "%" APR_OFF_T_FMT, msr->r->clength); - return var_simple_generate(var, vartab, mptmp, value); -} - -/* USERID */ - -static int var_userid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = msr->userid; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* SESSIONID */ - -static int var_sessionid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = msr->sessionid; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* WEBAPPID */ - -static int var_webappid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - const char *value = msr->txcfg->webappid; - return var_simple_generate(var, vartab, mptmp, value); -} - -/* ---------------------------------------------- */ - -/** - * - */ -void msre_engine_variable_register(msre_engine *engine, const char *name, - unsigned int type, unsigned int argc_min, unsigned int argc_max, - fn_var_validate_t validate, fn_var_generate_t generate, - unsigned int is_cacheable, unsigned int availability) -{ - msre_var_metadata *metadata = (msre_var_metadata *)apr_pcalloc(engine->mp, - sizeof(msre_var_metadata)); - if (metadata == NULL) return; - - metadata->name = name; - metadata->type = type; - metadata->argc_min = argc_min; - metadata->argc_max = argc_max; - metadata->validate = validate; - metadata->generate = generate; - metadata->is_cacheable = is_cacheable; - metadata->availability = availability; - - apr_table_setn(engine->variables, name, (void *)metadata); -} - -/** - * - */ -void msre_engine_register_default_variables(msre_engine *engine) { - - /* ARGS */ - msre_engine_variable_register(engine, - "ARGS", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_args_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* ARGS_COMBINED_SIZE */ - msre_engine_variable_register(engine, - "ARGS_COMBINED_SIZE", - VAR_LIST, - 0, 0, - NULL, - var_args_combined_size_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* ARGS_GET */ - msre_engine_variable_register(engine, - "ARGS_GET", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_args_get_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* ARGS_GET_NAMES */ - msre_engine_variable_register(engine, - "ARGS_GET_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_args_get_names_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* ARGS_NAMES */ - msre_engine_variable_register(engine, - "ARGS_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_args_names_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* ARGS_POST */ - msre_engine_variable_register(engine, - "ARGS_POST", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_args_post_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* ARGS_POST_NAMES */ - msre_engine_variable_register(engine, - "ARGS_POST_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_args_post_names_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* AUTH_TYPE */ - msre_engine_variable_register(engine, - "AUTH_TYPE", - VAR_SIMPLE, - 0, 0, - NULL, - var_auth_type_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* ENV */ - msre_engine_variable_register(engine, - "ENV", - VAR_LIST, - 0, 1, - var_env_validate, - var_env_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* FILES */ - msre_engine_variable_register(engine, - "FILES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_files_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* FILES_COMBINED_SIZE */ - msre_engine_variable_register(engine, - "FILES_COMBINED_SIZE", - VAR_LIST, - 0, 0, - NULL, - var_files_combined_size_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* FILES_NAMES */ - msre_engine_variable_register(engine, - "FILES_NAMES", - VAR_LIST, - 0, 0, - NULL, - var_files_names_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* FILES_SIZES */ - msre_engine_variable_register(engine, - "FILES_SIZES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_files_sizes_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* FILES_TMPNAMES */ - msre_engine_variable_register(engine, - "FILES_TMPNAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_files_tmpnames_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* GEO */ - msre_engine_variable_register(engine, - "GEO", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_geo_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* GLOBAL */ - msre_engine_variable_register(engine, - "GLOBAL", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_global_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* HIGHEST_SEVERITY */ - msre_engine_variable_register(engine, - "HIGHEST_SEVERITY", - VAR_SIMPLE, - 0, 0, - NULL, - var_highest_severity_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* IP */ - msre_engine_variable_register(engine, - "IP", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_ip_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* MATCHED_VAR */ - msre_engine_variable_register(engine, - "MATCHED_VAR", - VAR_SIMPLE, - 0, 0, - NULL, - var_matched_var_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* MATCHED_VAR_NAME */ - msre_engine_variable_register(engine, - "MATCHED_VAR_NAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_matched_var_name_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* MODSEC_BUILD */ - msre_engine_variable_register(engine, - "MODSEC_BUILD", - VAR_SIMPLE, - 0, 0, - NULL, - var_modsec_build_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* MULTIPART_FILENAME */ - msre_engine_variable_register(engine, - "MULTIPART_FILENAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_filename_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* MULTIPART_NAME */ - msre_engine_variable_register(engine, - "MULTIPART_NAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_name_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* MULTIPART_BOUNDARY_QUOTED */ - msre_engine_variable_register(engine, - "MULTIPART_BOUNDARY_QUOTED", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_boundary_quoted_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_BOUNDARY_WHITESPACE */ - msre_engine_variable_register(engine, - "MULTIPART_BOUNDARY_WHITESPACE", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_boundary_whitespace_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_DATA_AFTER */ - msre_engine_variable_register(engine, - "MULTIPART_DATA_AFTER", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_data_after_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_DATA_BEFORE */ - msre_engine_variable_register(engine, - "MULTIPART_DATA_BEFORE", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_data_before_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_HEADER_FOLDING */ - msre_engine_variable_register(engine, - "MULTIPART_HEADER_FOLDING", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_header_folding_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_CRLF_LINE */ - msre_engine_variable_register(engine, - "MULTIPART_CRLF_LINE", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_crlf_line_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_CRLF_LF_LINES */ - msre_engine_variable_register(engine, - "MULTIPART_CRLF_LF_LINES", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_crlf_lf_lines_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_LF_LINE */ - msre_engine_variable_register(engine, - "MULTIPART_LF_LINE", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_lf_line_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_MISSING_SEMICOLON */ - msre_engine_variable_register(engine, - "MULTIPART_MISSING_SEMICOLON", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_missing_semicolon_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_INVALID_PART */ - msre_engine_variable_register(engine, - "MULTIPART_INVALID_PART", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_invalid_part_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_INVALID_QUOTING */ - msre_engine_variable_register(engine, - "MULTIPART_INVALID_QUOTING", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_invalid_quoting_generate, - VAR_DONT_CACHE, /* flag */ - 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", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_strict_error_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* MULTIPART_UNMATCHED_BOUNDARY */ - msre_engine_variable_register(engine, - "MULTIPART_UNMATCHED_BOUNDARY", - VAR_SIMPLE, - 0, 0, - NULL, - var_multipart_unmatched_boundary_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* PATH_INFO */ - msre_engine_variable_register(engine, - "PATH_INFO", - VAR_SIMPLE, - 0, 0, - NULL, - var_path_info_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* QUERY_STRING */ - msre_engine_variable_register(engine, - "QUERY_STRING", - VAR_SIMPLE, - 0, 0, - NULL, - var_query_string_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - /* USERAGENT_IP */ - msre_engine_variable_register(engine, - "USERAGENT_IP", - VAR_SIMPLE, - 0, 0, - NULL, - var_useragent_ip_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); -#endif - - /* REMOTE_ADDR */ - msre_engine_variable_register(engine, - "REMOTE_ADDR", - VAR_SIMPLE, - 0, 0, - NULL, - var_remote_addr_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REMOTE_HOST */ - msre_engine_variable_register(engine, - "REMOTE_HOST", - VAR_SIMPLE, - 0, 0, - NULL, - var_remote_host_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* REMOTE_PORT */ - msre_engine_variable_register(engine, - "REMOTE_PORT", - VAR_SIMPLE, - 0, 0, - NULL, - var_remote_port_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* REMOTE_USER */ - msre_engine_variable_register(engine, - "REMOTE_USER", - VAR_SIMPLE, - 0, 0, - NULL, - var_remote_user_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* RESOURCE */ - msre_engine_variable_register(engine, - "RESOURCE", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_resource_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* REQBODY_PROCESSOR */ - msre_engine_variable_register(engine, - "REQBODY_PROCESSOR", - VAR_SIMPLE, - 0, 0, - NULL, - var_reqbody_processor_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_HEADERS - ); - - msre_engine_variable_register(engine, - "SDBM_DELETE_ERROR", - VAR_SIMPLE, - 0, 0, - NULL, - var_sdbm_delete_error_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_BODY - ); - - /* REQBODY_PROCESSOR_ERROR - Deprecated */ - msre_engine_variable_register(engine, - "REQBODY_PROCESSOR_ERROR", - VAR_SIMPLE, - 0, 0, - NULL, - var_reqbody_processor_error_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_BODY - ); - - /* REQBODY_PROCESSOR_ERROR_MSG - Deprecated */ - msre_engine_variable_register(engine, - "REQBODY_PROCESSOR_ERROR_MSG", - VAR_SIMPLE, - 0, 0, - NULL, - var_reqbody_processor_error_msg_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_BODY - ); - - /* REQBODY_ERROR */ - msre_engine_variable_register(engine, - "REQBODY_ERROR", - VAR_SIMPLE, - 0, 0, - NULL, - var_reqbody_processor_error_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_BODY - ); - - /* REQBODY_ERROR_MSG */ - msre_engine_variable_register(engine, - "REQBODY_ERROR_MSG", - VAR_SIMPLE, - 0, 0, - NULL, - var_reqbody_processor_error_msg_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_BODY - ); - - /* REQUEST_BASENAME */ - msre_engine_variable_register(engine, - "REQUEST_BASENAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_basename_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_BODY */ - msre_engine_variable_register(engine, - "REQUEST_BODY", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_body_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* REQUEST_BODY_LENGTH */ - msre_engine_variable_register(engine, - "REQUEST_BODY_LENGTH", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_body_length_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* MATCHED_VARS_NAMES */ - msre_engine_variable_register(engine, - "MATCHED_VARS_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_matched_vars_names_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* MATCHED_VARS */ - msre_engine_variable_register(engine, - "MATCHED_VARS", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_matched_vars_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_COOKIES */ - msre_engine_variable_register(engine, - "REQUEST_COOKIES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_request_cookies_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_COOKIES_NAMES */ - msre_engine_variable_register(engine, - "REQUEST_COOKIES_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_request_cookies_names_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_FILENAME */ - msre_engine_variable_register(engine, - "REQUEST_FILENAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_filename_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_HEADERS */ - msre_engine_variable_register(engine, - "REQUEST_HEADERS", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_request_headers_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_HEADERS_NAMES */ - msre_engine_variable_register(engine, - "REQUEST_HEADERS_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_request_headers_names_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_LINE */ - msre_engine_variable_register(engine, - "REQUEST_LINE", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_line_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_METHOD */ - msre_engine_variable_register(engine, - "REQUEST_METHOD", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_method_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_PROTOCOL */ - msre_engine_variable_register(engine, - "REQUEST_PROTOCOL", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_protocol_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_URI */ - msre_engine_variable_register(engine, - "REQUEST_URI", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_uri_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_HEADERS - ); - - /* REQUEST_URI_RAW */ - msre_engine_variable_register(engine, - "REQUEST_URI_RAW", - VAR_SIMPLE, - 0, 0, - NULL, - var_request_uri_raw_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* UNIQUE_ID */ - msre_engine_variable_register(engine, - "UNIQUE_ID", - VAR_SIMPLE, - 0, 0, - NULL, - var_uniqueid_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - /* STREAM_OUTPUT_BODY */ - msre_engine_variable_register(engine, - "STREAM_OUTPUT_BODY", - VAR_SIMPLE, - 0, 0, - NULL, - var_stream_output_generate, - VAR_CACHE, - PHASE_RESPONSE_BODY - ); - - /* STREAM_INPUT_BODY */ - msre_engine_variable_register(engine, - "STREAM_INPUT_BODY", - VAR_SIMPLE, - 0, 0, - NULL, - var_stream_input_generate, - VAR_CACHE, - PHASE_FIRST - ); - - - /* RESPONSE_BODY */ - msre_engine_variable_register(engine, - "RESPONSE_BODY", - VAR_SIMPLE, - 0, 0, - NULL, - var_response_body_generate, - VAR_CACHE, - PHASE_RESPONSE_BODY - ); - - /* RESPONSE_CONTENT_LENGTH */ - msre_engine_variable_register(engine, - "RESPONSE_CONTENT_LENGTH", - VAR_SIMPLE, - 0, 0, - NULL, - var_response_content_length, - VAR_DONT_CACHE, /* temp copy */ - PHASE_RESPONSE_HEADERS - ); - - /* RESPONSE_CONTENT_TYPE */ - msre_engine_variable_register(engine, - "RESPONSE_CONTENT_TYPE", - VAR_SIMPLE, - 0, 0, - NULL, - var_response_content_type, - VAR_CACHE, - PHASE_RESPONSE_HEADERS - ); - - /* RESPONSE_HEADERS */ - msre_engine_variable_register(engine, - "RESPONSE_HEADERS", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_response_headers_generate, - VAR_CACHE, - PHASE_RESPONSE_HEADERS - ); - - /* RESPONSE_HEADERS_NAMES */ - msre_engine_variable_register(engine, - "RESPONSE_HEADERS_NAMES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_response_headers_names_generate, - VAR_CACHE, - PHASE_RESPONSE_HEADERS - ); - - /* RESPONSE_PROTOCOL */ - msre_engine_variable_register(engine, - "RESPONSE_PROTOCOL", - VAR_SIMPLE, - 0, 0, - NULL, - var_response_protocol_generate, - VAR_CACHE, - PHASE_RESPONSE_HEADERS - ); - - /* RESPONSE_STATUS */ - msre_engine_variable_register(engine, - "RESPONSE_STATUS", - VAR_SIMPLE, - 0, 0, - NULL, - var_response_status_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_RESPONSE_HEADERS - ); - - /* RULE */ - msre_engine_variable_register(engine, - "RULE", - VAR_LIST, - 1, 1, - NULL, - var_rule_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_RESPONSE_HEADERS - ); - - /* SCRIPT_GID */ - msre_engine_variable_register(engine, - "SCRIPT_GID", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_gid_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* SCRIPT_BASENAME */ - msre_engine_variable_register(engine, - "SCRIPT_BASENAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_basename_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* SCRIPT_FILENAME */ - msre_engine_variable_register(engine, - "SCRIPT_FILENAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_filename_generate, - VAR_CACHE, - PHASE_REQUEST_BODY - ); - - /* SCRIPT_GROUPNAME */ - msre_engine_variable_register(engine, - "SCRIPT_GROUPNAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_groupname_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* SCRIPT_MODE */ - msre_engine_variable_register(engine, - "SCRIPT_MODE", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_mode_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* SCRIPT_UID */ - msre_engine_variable_register(engine, - "SCRIPT_UID", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_uid_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* SCRIPT_USERNAME */ - msre_engine_variable_register(engine, - "SCRIPT_USERNAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_script_username_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_BODY - ); - - /* SERVER_ADDR */ - msre_engine_variable_register(engine, - "SERVER_ADDR", - VAR_SIMPLE, - 0, 0, - NULL, - var_server_addr_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* SERVER_NAME */ - msre_engine_variable_register(engine, - "SERVER_NAME", - VAR_SIMPLE, - 0, 0, - NULL, - var_server_name_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* SERVER_PORT */ - msre_engine_variable_register(engine, - "SERVER_PORT", - VAR_SIMPLE, - 0, 0, - NULL, - var_server_port_generate, - VAR_DONT_CACHE, /* temp copy */ - PHASE_REQUEST_HEADERS - ); - - /* SESSION */ - msre_engine_variable_register(engine, - "SESSION", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_session_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* SESSIONID */ - msre_engine_variable_register(engine, - "SESSIONID", - VAR_SIMPLE, - 0, 0, - NULL, - var_sessionid_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_RESPONSE_HEADERS - ); - - /* STATUS_LINE */ - msre_engine_variable_register(engine, - "STATUS_LINE", - VAR_SIMPLE, - 0, 0, - NULL, - var_status_line_generate, - VAR_CACHE, - PHASE_RESPONSE_HEADERS - ); - - /* URLENCODED_ERROR */ - msre_engine_variable_register(engine, - "URLENCODED_ERROR", - VAR_SIMPLE, - 0, 0, - NULL, - var_urlencoded_error_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_HEADERS - ); - - /* INBOUND_DATA_ERROR */ - msre_engine_variable_register(engine, - "INBOUND_DATA_ERROR", - VAR_SIMPLE, - 0, 0, - NULL, - var_inbound_error_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_REQUEST_BODY - ); - - /* OUTBOUND_DATA_ERROR */ - msre_engine_variable_register(engine, - "OUTBOUND_DATA_ERROR", - VAR_SIMPLE, - 0, 0, - NULL, - var_outbound_error_generate, - VAR_DONT_CACHE, /* flag */ - PHASE_RESPONSE_BODY - ); - - /* USER */ - msre_engine_variable_register(engine, - "USER", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_user_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* USERID */ - msre_engine_variable_register(engine, - "USERID", - VAR_SIMPLE, - 0, 0, - NULL, - var_userid_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_RESPONSE_HEADERS - ); - - /* PERF_RULES */ - msre_engine_variable_register(engine, - "PERF_RULES", - VAR_LIST, - 0, 1, - var_generic_list_validate, - var_perf_rules_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_ALL */ - msre_engine_variable_register(engine, - "PERF_ALL", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_all_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_COMBINED */ - msre_engine_variable_register(engine, - "PERF_COMBINED", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_combined_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_GC */ - msre_engine_variable_register(engine, - "PERF_GC", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_gc_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_LOGGING */ - msre_engine_variable_register(engine, - "PERF_LOGGING", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_logging_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_PHASE1 */ - msre_engine_variable_register(engine, - "PERF_PHASE1", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_phase1_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_PHASE2 */ - msre_engine_variable_register(engine, - "PERF_PHASE2", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_phase2_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_PHASE3 */ - msre_engine_variable_register(engine, - "PERF_PHASE3", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_phase3_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_PHASE4 */ - msre_engine_variable_register(engine, - "PERF_PHASE4", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_phase4_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_PHASE5 */ - msre_engine_variable_register(engine, - "PERF_PHASE5", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_phase5_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_SREAD */ - msre_engine_variable_register(engine, - "PERF_SREAD", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_sread_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* PERF_SWRITE */ - msre_engine_variable_register(engine, - "PERF_SWRITE", - VAR_SIMPLE, - 0, 0, - NULL, - var_perf_swrite_generate, - VAR_DONT_CACHE, - PHASE_REQUEST_HEADERS - ); - - /* DURATION */ - msre_engine_variable_register(engine, - "DURATION", - VAR_SIMPLE, - 0, 0, - NULL, - var_duration_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME */ - msre_engine_variable_register(engine, - "TIME", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_DAY */ - msre_engine_variable_register(engine, - "TIME_DAY", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_day_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_EPOCH */ - msre_engine_variable_register(engine, - "TIME_EPOCH", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_epoch_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_HOUR */ - msre_engine_variable_register(engine, - "TIME_HOUR", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_hour_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_MIN */ - msre_engine_variable_register(engine, - "TIME_MIN", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_min_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_MON */ - msre_engine_variable_register(engine, - "TIME_MON", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_mon_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_SEC */ - msre_engine_variable_register(engine, - "TIME_SEC", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_sec_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_WDAY */ - msre_engine_variable_register(engine, - "TIME_WDAY", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_wday_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TIME_YEAR */ - msre_engine_variable_register(engine, - "TIME_YEAR", - VAR_SIMPLE, - 0, 0, - NULL, - var_time_year_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* TX */ - msre_engine_variable_register(engine, - "TX", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_tx_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* WEBAPPID */ - msre_engine_variable_register(engine, - "WEBAPPID", - VAR_SIMPLE, - 0, 0, - NULL, - var_webappid_generate, - VAR_DONT_CACHE, - PHASE_RESPONSE_HEADERS - ); - - /* WEBSERVER_ERROR_LOG */ - msre_engine_variable_register(engine, - "WEBSERVER_ERROR_LOG", - VAR_LIST, - 0, 0, - NULL, - var_webserver_error_log_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_HEADERS - ); - - /* XML */ - msre_engine_variable_register(engine, - "XML", - VAR_LIST, - 0, 1, - var_xml_validate, - var_xml_generate, - VAR_DONT_CACHE, /* dynamic */ - PHASE_REQUEST_BODY - ); -} diff --git a/apache2/utf8tables.h b/apache2/utf8tables.h deleted file mode 100644 index 28140ffadb..0000000000 --- a/apache2/utf8tables.h +++ /dev/null @@ -1,815 +0,0 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - - -#ifndef UTF8TABLES_H_ -#define UTF8TABLES_H_ - -/** - * This include file is used by acmp.c only; it's not included anywhere else. - */ - -typedef long acmp_utf8_char_t; - -static const char utf8_seq_lengths[256] = { - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,6,6, -}; - -static const acmp_utf8_char_t utf8_offsets[6] = { - 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL -}; - -/** - * How many element pairs are there in utf8_lcase_map - */ -#define UTF8_LCASEMAP_LEN 759 - -/** - * Table mapping is from PHP's mbstring extension, maps uppercase - */ -static const acmp_utf8_char_t utf8_lcase_map[UTF8_LCASEMAP_LEN * 2] = { - 0x00000061, 0x00000041, - 0x00000062, 0x00000042, - 0x00000063, 0x00000043, - 0x00000064, 0x00000044, - 0x00000065, 0x00000045, - 0x00000066, 0x00000046, - 0x00000067, 0x00000047, - 0x00000068, 0x00000048, - 0x00000069, 0x00000049, - 0x0000006a, 0x0000004a, - 0x0000006b, 0x0000004b, - 0x0000006c, 0x0000004c, - 0x0000006d, 0x0000004d, - 0x0000006e, 0x0000004e, - 0x0000006f, 0x0000004f, - 0x00000070, 0x00000050, - 0x00000071, 0x00000051, - 0x00000072, 0x00000052, - 0x00000073, 0x00000053, - 0x00000074, 0x00000054, - 0x00000075, 0x00000055, - 0x00000076, 0x00000056, - 0x00000077, 0x00000057, - 0x00000078, 0x00000058, - 0x00000079, 0x00000059, - 0x0000007a, 0x0000005a, - 0x000000b5, 0x0000039c, - 0x000000e0, 0x000000c0, - 0x000000e1, 0x000000c1, - 0x000000e2, 0x000000c2, - 0x000000e3, 0x000000c3, - 0x000000e4, 0x000000c4, - 0x000000e5, 0x000000c5, - 0x000000e6, 0x000000c6, - 0x000000e7, 0x000000c7, - 0x000000e8, 0x000000c8, - 0x000000e9, 0x000000c9, - 0x000000ea, 0x000000ca, - 0x000000eb, 0x000000cb, - 0x000000ec, 0x000000cc, - 0x000000ed, 0x000000cd, - 0x000000ee, 0x000000ce, - 0x000000ef, 0x000000cf, - 0x000000f0, 0x000000d0, - 0x000000f1, 0x000000d1, - 0x000000f2, 0x000000d2, - 0x000000f3, 0x000000d3, - 0x000000f4, 0x000000d4, - 0x000000f5, 0x000000d5, - 0x000000f6, 0x000000d6, - 0x000000f8, 0x000000d8, - 0x000000f9, 0x000000d9, - 0x000000fa, 0x000000da, - 0x000000fb, 0x000000db, - 0x000000fc, 0x000000dc, - 0x000000fd, 0x000000dd, - 0x000000fe, 0x000000de, - 0x000000ff, 0x00000178, - 0x00000101, 0x00000100, - 0x00000103, 0x00000102, - 0x00000105, 0x00000104, - 0x00000107, 0x00000106, - 0x00000109, 0x00000108, - 0x0000010b, 0x0000010a, - 0x0000010d, 0x0000010c, - 0x0000010f, 0x0000010e, - 0x00000111, 0x00000110, - 0x00000113, 0x00000112, - 0x00000115, 0x00000114, - 0x00000117, 0x00000116, - 0x00000119, 0x00000118, - 0x0000011b, 0x0000011a, - 0x0000011d, 0x0000011c, - 0x0000011f, 0x0000011e, - 0x00000121, 0x00000120, - 0x00000123, 0x00000122, - 0x00000125, 0x00000124, - 0x00000127, 0x00000126, - 0x00000129, 0x00000128, - 0x0000012b, 0x0000012a, - 0x0000012d, 0x0000012c, - 0x0000012f, 0x0000012e, - 0x00000131, 0x00000049, - 0x00000133, 0x00000132, - 0x00000135, 0x00000134, - 0x00000137, 0x00000136, - 0x0000013a, 0x00000139, - 0x0000013c, 0x0000013b, - 0x0000013e, 0x0000013d, - 0x00000140, 0x0000013f, - 0x00000142, 0x00000141, - 0x00000144, 0x00000143, - 0x00000146, 0x00000145, - 0x00000148, 0x00000147, - 0x0000014b, 0x0000014a, - 0x0000014d, 0x0000014c, - 0x0000014f, 0x0000014e, - 0x00000151, 0x00000150, - 0x00000153, 0x00000152, - 0x00000155, 0x00000154, - 0x00000157, 0x00000156, - 0x00000159, 0x00000158, - 0x0000015b, 0x0000015a, - 0x0000015d, 0x0000015c, - 0x0000015f, 0x0000015e, - 0x00000161, 0x00000160, - 0x00000163, 0x00000162, - 0x00000165, 0x00000164, - 0x00000167, 0x00000166, - 0x00000169, 0x00000168, - 0x0000016b, 0x0000016a, - 0x0000016d, 0x0000016c, - 0x0000016f, 0x0000016e, - 0x00000171, 0x00000170, - 0x00000173, 0x00000172, - 0x00000175, 0x00000174, - 0x00000177, 0x00000176, - 0x0000017a, 0x00000179, - 0x0000017c, 0x0000017b, - 0x0000017e, 0x0000017d, - 0x0000017f, 0x00000053, - 0x00000183, 0x00000182, - 0x00000185, 0x00000184, - 0x00000188, 0x00000187, - 0x0000018c, 0x0000018b, - 0x00000192, 0x00000191, - 0x00000195, 0x000001f6, - 0x00000199, 0x00000198, - 0x0000019e, 0x00000220, - 0x000001a1, 0x000001a0, - 0x000001a3, 0x000001a2, - 0x000001a5, 0x000001a4, - 0x000001a8, 0x000001a7, - 0x000001ad, 0x000001ac, - 0x000001b0, 0x000001af, - 0x000001b4, 0x000001b3, - 0x000001b6, 0x000001b5, - 0x000001b9, 0x000001b8, - 0x000001bd, 0x000001bc, - 0x000001bf, 0x000001f7, - 0x000001c6, 0x000001c4, - 0x000001c9, 0x000001c7, - 0x000001cc, 0x000001ca, - 0x000001ce, 0x000001cd, - 0x000001d0, 0x000001cf, - 0x000001d2, 0x000001d1, - 0x000001d4, 0x000001d3, - 0x000001d6, 0x000001d5, - 0x000001d8, 0x000001d7, - 0x000001da, 0x000001d9, - 0x000001dc, 0x000001db, - 0x000001dd, 0x0000018e, - 0x000001df, 0x000001de, - 0x000001e1, 0x000001e0, - 0x000001e3, 0x000001e2, - 0x000001e5, 0x000001e4, - 0x000001e7, 0x000001e6, - 0x000001e9, 0x000001e8, - 0x000001eb, 0x000001ea, - 0x000001ed, 0x000001ec, - 0x000001ef, 0x000001ee, - 0x000001f3, 0x000001f1, - 0x000001f5, 0x000001f4, - 0x000001f9, 0x000001f8, - 0x000001fb, 0x000001fa, - 0x000001fd, 0x000001fc, - 0x000001ff, 0x000001fe, - 0x00000201, 0x00000200, - 0x00000203, 0x00000202, - 0x00000205, 0x00000204, - 0x00000207, 0x00000206, - 0x00000209, 0x00000208, - 0x0000020b, 0x0000020a, - 0x0000020d, 0x0000020c, - 0x0000020f, 0x0000020e, - 0x00000211, 0x00000210, - 0x00000213, 0x00000212, - 0x00000215, 0x00000214, - 0x00000217, 0x00000216, - 0x00000219, 0x00000218, - 0x0000021b, 0x0000021a, - 0x0000021d, 0x0000021c, - 0x0000021f, 0x0000021e, - 0x00000223, 0x00000222, - 0x00000225, 0x00000224, - 0x00000227, 0x00000226, - 0x00000229, 0x00000228, - 0x0000022b, 0x0000022a, - 0x0000022d, 0x0000022c, - 0x0000022f, 0x0000022e, - 0x00000231, 0x00000230, - 0x00000233, 0x00000232, - 0x00000253, 0x00000181, - 0x00000254, 0x00000186, - 0x00000256, 0x00000189, - 0x00000257, 0x0000018a, - 0x00000259, 0x0000018f, - 0x0000025b, 0x00000190, - 0x00000260, 0x00000193, - 0x00000263, 0x00000194, - 0x00000268, 0x00000197, - 0x00000269, 0x00000196, - 0x0000026f, 0x0000019c, - 0x00000272, 0x0000019d, - 0x00000275, 0x0000019f, - 0x00000280, 0x000001a6, - 0x00000283, 0x000001a9, - 0x00000288, 0x000001ae, - 0x0000028a, 0x000001b1, - 0x0000028b, 0x000001b2, - 0x00000292, 0x000001b7, - 0x00000345, 0x00000399, - 0x000003ac, 0x00000386, - 0x000003ad, 0x00000388, - 0x000003ae, 0x00000389, - 0x000003af, 0x0000038a, - 0x000003b1, 0x00000391, - 0x000003b2, 0x00000392, - 0x000003b3, 0x00000393, - 0x000003b4, 0x00000394, - 0x000003b5, 0x00000395, - 0x000003b6, 0x00000396, - 0x000003b7, 0x00000397, - 0x000003b8, 0x00000398, - 0x000003b9, 0x00000399, - 0x000003ba, 0x0000039a, - 0x000003bb, 0x0000039b, - 0x000003bc, 0x0000039c, - 0x000003bd, 0x0000039d, - 0x000003be, 0x0000039e, - 0x000003bf, 0x0000039f, - 0x000003c0, 0x000003a0, - 0x000003c1, 0x000003a1, - 0x000003c2, 0x000003a3, - 0x000003c3, 0x000003a3, - 0x000003c4, 0x000003a4, - 0x000003c5, 0x000003a5, - 0x000003c6, 0x000003a6, - 0x000003c7, 0x000003a7, - 0x000003c8, 0x000003a8, - 0x000003c9, 0x000003a9, - 0x000003ca, 0x000003aa, - 0x000003cb, 0x000003ab, - 0x000003cc, 0x0000038c, - 0x000003cd, 0x0000038e, - 0x000003ce, 0x0000038f, - 0x000003d0, 0x00000392, - 0x000003d1, 0x00000398, - 0x000003d5, 0x000003a6, - 0x000003d6, 0x000003a0, - 0x000003d9, 0x000003d8, - 0x000003db, 0x000003da, - 0x000003dd, 0x000003dc, - 0x000003df, 0x000003de, - 0x000003e1, 0x000003e0, - 0x000003e3, 0x000003e2, - 0x000003e5, 0x000003e4, - 0x000003e7, 0x000003e6, - 0x000003e9, 0x000003e8, - 0x000003eb, 0x000003ea, - 0x000003ed, 0x000003ec, - 0x000003ef, 0x000003ee, - 0x000003f0, 0x0000039a, - 0x000003f1, 0x000003a1, - 0x000003f2, 0x000003a3, - 0x000003f5, 0x00000395, - 0x00000430, 0x00000410, - 0x00000431, 0x00000411, - 0x00000432, 0x00000412, - 0x00000433, 0x00000413, - 0x00000434, 0x00000414, - 0x00000435, 0x00000415, - 0x00000436, 0x00000416, - 0x00000437, 0x00000417, - 0x00000438, 0x00000418, - 0x00000439, 0x00000419, - 0x0000043a, 0x0000041a, - 0x0000043b, 0x0000041b, - 0x0000043c, 0x0000041c, - 0x0000043d, 0x0000041d, - 0x0000043e, 0x0000041e, - 0x0000043f, 0x0000041f, - 0x00000440, 0x00000420, - 0x00000441, 0x00000421, - 0x00000442, 0x00000422, - 0x00000443, 0x00000423, - 0x00000444, 0x00000424, - 0x00000445, 0x00000425, - 0x00000446, 0x00000426, - 0x00000447, 0x00000427, - 0x00000448, 0x00000428, - 0x00000449, 0x00000429, - 0x0000044a, 0x0000042a, - 0x0000044b, 0x0000042b, - 0x0000044c, 0x0000042c, - 0x0000044d, 0x0000042d, - 0x0000044e, 0x0000042e, - 0x0000044f, 0x0000042f, - 0x00000450, 0x00000400, - 0x00000451, 0x00000401, - 0x00000452, 0x00000402, - 0x00000453, 0x00000403, - 0x00000454, 0x00000404, - 0x00000455, 0x00000405, - 0x00000456, 0x00000406, - 0x00000457, 0x00000407, - 0x00000458, 0x00000408, - 0x00000459, 0x00000409, - 0x0000045a, 0x0000040a, - 0x0000045b, 0x0000040b, - 0x0000045c, 0x0000040c, - 0x0000045d, 0x0000040d, - 0x0000045e, 0x0000040e, - 0x0000045f, 0x0000040f, - 0x00000461, 0x00000460, - 0x00000463, 0x00000462, - 0x00000465, 0x00000464, - 0x00000467, 0x00000466, - 0x00000469, 0x00000468, - 0x0000046b, 0x0000046a, - 0x0000046d, 0x0000046c, - 0x0000046f, 0x0000046e, - 0x00000471, 0x00000470, - 0x00000473, 0x00000472, - 0x00000475, 0x00000474, - 0x00000477, 0x00000476, - 0x00000479, 0x00000478, - 0x0000047b, 0x0000047a, - 0x0000047d, 0x0000047c, - 0x0000047f, 0x0000047e, - 0x00000481, 0x00000480, - 0x0000048b, 0x0000048a, - 0x0000048d, 0x0000048c, - 0x0000048f, 0x0000048e, - 0x00000491, 0x00000490, - 0x00000493, 0x00000492, - 0x00000495, 0x00000494, - 0x00000497, 0x00000496, - 0x00000499, 0x00000498, - 0x0000049b, 0x0000049a, - 0x0000049d, 0x0000049c, - 0x0000049f, 0x0000049e, - 0x000004a1, 0x000004a0, - 0x000004a3, 0x000004a2, - 0x000004a5, 0x000004a4, - 0x000004a7, 0x000004a6, - 0x000004a9, 0x000004a8, - 0x000004ab, 0x000004aa, - 0x000004ad, 0x000004ac, - 0x000004af, 0x000004ae, - 0x000004b1, 0x000004b0, - 0x000004b3, 0x000004b2, - 0x000004b5, 0x000004b4, - 0x000004b7, 0x000004b6, - 0x000004b9, 0x000004b8, - 0x000004bb, 0x000004ba, - 0x000004bd, 0x000004bc, - 0x000004bf, 0x000004be, - 0x000004c2, 0x000004c1, - 0x000004c4, 0x000004c3, - 0x000004c6, 0x000004c5, - 0x000004c8, 0x000004c7, - 0x000004ca, 0x000004c9, - 0x000004cc, 0x000004cb, - 0x000004ce, 0x000004cd, - 0x000004d1, 0x000004d0, - 0x000004d3, 0x000004d2, - 0x000004d5, 0x000004d4, - 0x000004d7, 0x000004d6, - 0x000004d9, 0x000004d8, - 0x000004db, 0x000004da, - 0x000004dd, 0x000004dc, - 0x000004df, 0x000004de, - 0x000004e1, 0x000004e0, - 0x000004e3, 0x000004e2, - 0x000004e5, 0x000004e4, - 0x000004e7, 0x000004e6, - 0x000004e9, 0x000004e8, - 0x000004eb, 0x000004ea, - 0x000004ed, 0x000004ec, - 0x000004ef, 0x000004ee, - 0x000004f1, 0x000004f0, - 0x000004f3, 0x000004f2, - 0x000004f5, 0x000004f4, - 0x000004f9, 0x000004f8, - 0x00000501, 0x00000500, - 0x00000503, 0x00000502, - 0x00000505, 0x00000504, - 0x00000507, 0x00000506, - 0x00000509, 0x00000508, - 0x0000050b, 0x0000050a, - 0x0000050d, 0x0000050c, - 0x0000050f, 0x0000050e, - 0x00000561, 0x00000531, - 0x00000562, 0x00000532, - 0x00000563, 0x00000533, - 0x00000564, 0x00000534, - 0x00000565, 0x00000535, - 0x00000566, 0x00000536, - 0x00000567, 0x00000537, - 0x00000568, 0x00000538, - 0x00000569, 0x00000539, - 0x0000056a, 0x0000053a, - 0x0000056b, 0x0000053b, - 0x0000056c, 0x0000053c, - 0x0000056d, 0x0000053d, - 0x0000056e, 0x0000053e, - 0x0000056f, 0x0000053f, - 0x00000570, 0x00000540, - 0x00000571, 0x00000541, - 0x00000572, 0x00000542, - 0x00000573, 0x00000543, - 0x00000574, 0x00000544, - 0x00000575, 0x00000545, - 0x00000576, 0x00000546, - 0x00000577, 0x00000547, - 0x00000578, 0x00000548, - 0x00000579, 0x00000549, - 0x0000057a, 0x0000054a, - 0x0000057b, 0x0000054b, - 0x0000057c, 0x0000054c, - 0x0000057d, 0x0000054d, - 0x0000057e, 0x0000054e, - 0x0000057f, 0x0000054f, - 0x00000580, 0x00000550, - 0x00000581, 0x00000551, - 0x00000582, 0x00000552, - 0x00000583, 0x00000553, - 0x00000584, 0x00000554, - 0x00000585, 0x00000555, - 0x00000586, 0x00000556, - 0x00001e01, 0x00001e00, - 0x00001e03, 0x00001e02, - 0x00001e05, 0x00001e04, - 0x00001e07, 0x00001e06, - 0x00001e09, 0x00001e08, - 0x00001e0b, 0x00001e0a, - 0x00001e0d, 0x00001e0c, - 0x00001e0f, 0x00001e0e, - 0x00001e11, 0x00001e10, - 0x00001e13, 0x00001e12, - 0x00001e15, 0x00001e14, - 0x00001e17, 0x00001e16, - 0x00001e19, 0x00001e18, - 0x00001e1b, 0x00001e1a, - 0x00001e1d, 0x00001e1c, - 0x00001e1f, 0x00001e1e, - 0x00001e21, 0x00001e20, - 0x00001e23, 0x00001e22, - 0x00001e25, 0x00001e24, - 0x00001e27, 0x00001e26, - 0x00001e29, 0x00001e28, - 0x00001e2b, 0x00001e2a, - 0x00001e2d, 0x00001e2c, - 0x00001e2f, 0x00001e2e, - 0x00001e31, 0x00001e30, - 0x00001e33, 0x00001e32, - 0x00001e35, 0x00001e34, - 0x00001e37, 0x00001e36, - 0x00001e39, 0x00001e38, - 0x00001e3b, 0x00001e3a, - 0x00001e3d, 0x00001e3c, - 0x00001e3f, 0x00001e3e, - 0x00001e41, 0x00001e40, - 0x00001e43, 0x00001e42, - 0x00001e45, 0x00001e44, - 0x00001e47, 0x00001e46, - 0x00001e49, 0x00001e48, - 0x00001e4b, 0x00001e4a, - 0x00001e4d, 0x00001e4c, - 0x00001e4f, 0x00001e4e, - 0x00001e51, 0x00001e50, - 0x00001e53, 0x00001e52, - 0x00001e55, 0x00001e54, - 0x00001e57, 0x00001e56, - 0x00001e59, 0x00001e58, - 0x00001e5b, 0x00001e5a, - 0x00001e5d, 0x00001e5c, - 0x00001e5f, 0x00001e5e, - 0x00001e61, 0x00001e60, - 0x00001e63, 0x00001e62, - 0x00001e65, 0x00001e64, - 0x00001e67, 0x00001e66, - 0x00001e69, 0x00001e68, - 0x00001e6b, 0x00001e6a, - 0x00001e6d, 0x00001e6c, - 0x00001e6f, 0x00001e6e, - 0x00001e71, 0x00001e70, - 0x00001e73, 0x00001e72, - 0x00001e75, 0x00001e74, - 0x00001e77, 0x00001e76, - 0x00001e79, 0x00001e78, - 0x00001e7b, 0x00001e7a, - 0x00001e7d, 0x00001e7c, - 0x00001e7f, 0x00001e7e, - 0x00001e81, 0x00001e80, - 0x00001e83, 0x00001e82, - 0x00001e85, 0x00001e84, - 0x00001e87, 0x00001e86, - 0x00001e89, 0x00001e88, - 0x00001e8b, 0x00001e8a, - 0x00001e8d, 0x00001e8c, - 0x00001e8f, 0x00001e8e, - 0x00001e91, 0x00001e90, - 0x00001e93, 0x00001e92, - 0x00001e95, 0x00001e94, - 0x00001e9b, 0x00001e60, - 0x00001ea1, 0x00001ea0, - 0x00001ea3, 0x00001ea2, - 0x00001ea5, 0x00001ea4, - 0x00001ea7, 0x00001ea6, - 0x00001ea9, 0x00001ea8, - 0x00001eab, 0x00001eaa, - 0x00001ead, 0x00001eac, - 0x00001eaf, 0x00001eae, - 0x00001eb1, 0x00001eb0, - 0x00001eb3, 0x00001eb2, - 0x00001eb5, 0x00001eb4, - 0x00001eb7, 0x00001eb6, - 0x00001eb9, 0x00001eb8, - 0x00001ebb, 0x00001eba, - 0x00001ebd, 0x00001ebc, - 0x00001ebf, 0x00001ebe, - 0x00001ec1, 0x00001ec0, - 0x00001ec3, 0x00001ec2, - 0x00001ec5, 0x00001ec4, - 0x00001ec7, 0x00001ec6, - 0x00001ec9, 0x00001ec8, - 0x00001ecb, 0x00001eca, - 0x00001ecd, 0x00001ecc, - 0x00001ecf, 0x00001ece, - 0x00001ed1, 0x00001ed0, - 0x00001ed3, 0x00001ed2, - 0x00001ed5, 0x00001ed4, - 0x00001ed7, 0x00001ed6, - 0x00001ed9, 0x00001ed8, - 0x00001edb, 0x00001eda, - 0x00001edd, 0x00001edc, - 0x00001edf, 0x00001ede, - 0x00001ee1, 0x00001ee0, - 0x00001ee3, 0x00001ee2, - 0x00001ee5, 0x00001ee4, - 0x00001ee7, 0x00001ee6, - 0x00001ee9, 0x00001ee8, - 0x00001eeb, 0x00001eea, - 0x00001eed, 0x00001eec, - 0x00001eef, 0x00001eee, - 0x00001ef1, 0x00001ef0, - 0x00001ef3, 0x00001ef2, - 0x00001ef5, 0x00001ef4, - 0x00001ef7, 0x00001ef6, - 0x00001ef9, 0x00001ef8, - 0x00001f00, 0x00001f08, - 0x00001f01, 0x00001f09, - 0x00001f02, 0x00001f0a, - 0x00001f03, 0x00001f0b, - 0x00001f04, 0x00001f0c, - 0x00001f05, 0x00001f0d, - 0x00001f06, 0x00001f0e, - 0x00001f07, 0x00001f0f, - 0x00001f10, 0x00001f18, - 0x00001f11, 0x00001f19, - 0x00001f12, 0x00001f1a, - 0x00001f13, 0x00001f1b, - 0x00001f14, 0x00001f1c, - 0x00001f15, 0x00001f1d, - 0x00001f20, 0x00001f28, - 0x00001f21, 0x00001f29, - 0x00001f22, 0x00001f2a, - 0x00001f23, 0x00001f2b, - 0x00001f24, 0x00001f2c, - 0x00001f25, 0x00001f2d, - 0x00001f26, 0x00001f2e, - 0x00001f27, 0x00001f2f, - 0x00001f30, 0x00001f38, - 0x00001f31, 0x00001f39, - 0x00001f32, 0x00001f3a, - 0x00001f33, 0x00001f3b, - 0x00001f34, 0x00001f3c, - 0x00001f35, 0x00001f3d, - 0x00001f36, 0x00001f3e, - 0x00001f37, 0x00001f3f, - 0x00001f40, 0x00001f48, - 0x00001f41, 0x00001f49, - 0x00001f42, 0x00001f4a, - 0x00001f43, 0x00001f4b, - 0x00001f44, 0x00001f4c, - 0x00001f45, 0x00001f4d, - 0x00001f51, 0x00001f59, - 0x00001f53, 0x00001f5b, - 0x00001f55, 0x00001f5d, - 0x00001f57, 0x00001f5f, - 0x00001f60, 0x00001f68, - 0x00001f61, 0x00001f69, - 0x00001f62, 0x00001f6a, - 0x00001f63, 0x00001f6b, - 0x00001f64, 0x00001f6c, - 0x00001f65, 0x00001f6d, - 0x00001f66, 0x00001f6e, - 0x00001f67, 0x00001f6f, - 0x00001f70, 0x00001fba, - 0x00001f71, 0x00001fbb, - 0x00001f72, 0x00001fc8, - 0x00001f73, 0x00001fc9, - 0x00001f74, 0x00001fca, - 0x00001f75, 0x00001fcb, - 0x00001f76, 0x00001fda, - 0x00001f77, 0x00001fdb, - 0x00001f78, 0x00001ff8, - 0x00001f79, 0x00001ff9, - 0x00001f7a, 0x00001fea, - 0x00001f7b, 0x00001feb, - 0x00001f7c, 0x00001ffa, - 0x00001f7d, 0x00001ffb, - 0x00001f80, 0x00001f88, - 0x00001f81, 0x00001f89, - 0x00001f82, 0x00001f8a, - 0x00001f83, 0x00001f8b, - 0x00001f84, 0x00001f8c, - 0x00001f85, 0x00001f8d, - 0x00001f86, 0x00001f8e, - 0x00001f87, 0x00001f8f, - 0x00001f90, 0x00001f98, - 0x00001f91, 0x00001f99, - 0x00001f92, 0x00001f9a, - 0x00001f93, 0x00001f9b, - 0x00001f94, 0x00001f9c, - 0x00001f95, 0x00001f9d, - 0x00001f96, 0x00001f9e, - 0x00001f97, 0x00001f9f, - 0x00001fa0, 0x00001fa8, - 0x00001fa1, 0x00001fa9, - 0x00001fa2, 0x00001faa, - 0x00001fa3, 0x00001fab, - 0x00001fa4, 0x00001fac, - 0x00001fa5, 0x00001fad, - 0x00001fa6, 0x00001fae, - 0x00001fa7, 0x00001faf, - 0x00001fb0, 0x00001fb8, - 0x00001fb1, 0x00001fb9, - 0x00001fb3, 0x00001fbc, - 0x00001fbe, 0x00000399, - 0x00001fc3, 0x00001fcc, - 0x00001fd0, 0x00001fd8, - 0x00001fd1, 0x00001fd9, - 0x00001fe0, 0x00001fe8, - 0x00001fe1, 0x00001fe9, - 0x00001fe5, 0x00001fec, - 0x00001ff3, 0x00001ffc, - 0x00002170, 0x00002160, - 0x00002171, 0x00002161, - 0x00002172, 0x00002162, - 0x00002173, 0x00002163, - 0x00002174, 0x00002164, - 0x00002175, 0x00002165, - 0x00002176, 0x00002166, - 0x00002177, 0x00002167, - 0x00002178, 0x00002168, - 0x00002179, 0x00002169, - 0x0000217a, 0x0000216a, - 0x0000217b, 0x0000216b, - 0x0000217c, 0x0000216c, - 0x0000217d, 0x0000216d, - 0x0000217e, 0x0000216e, - 0x0000217f, 0x0000216f, - 0x000024d0, 0x000024b6, - 0x000024d1, 0x000024b7, - 0x000024d2, 0x000024b8, - 0x000024d3, 0x000024b9, - 0x000024d4, 0x000024ba, - 0x000024d5, 0x000024bb, - 0x000024d6, 0x000024bc, - 0x000024d7, 0x000024bd, - 0x000024d8, 0x000024be, - 0x000024d9, 0x000024bf, - 0x000024da, 0x000024c0, - 0x000024db, 0x000024c1, - 0x000024dc, 0x000024c2, - 0x000024dd, 0x000024c3, - 0x000024de, 0x000024c4, - 0x000024df, 0x000024c5, - 0x000024e0, 0x000024c6, - 0x000024e1, 0x000024c7, - 0x000024e2, 0x000024c8, - 0x000024e3, 0x000024c9, - 0x000024e4, 0x000024ca, - 0x000024e5, 0x000024cb, - 0x000024e6, 0x000024cc, - 0x000024e7, 0x000024cd, - 0x000024e8, 0x000024ce, - 0x000024e9, 0x000024cf, - 0x0000ff41, 0x0000ff21, - 0x0000ff42, 0x0000ff22, - 0x0000ff43, 0x0000ff23, - 0x0000ff44, 0x0000ff24, - 0x0000ff45, 0x0000ff25, - 0x0000ff46, 0x0000ff26, - 0x0000ff47, 0x0000ff27, - 0x0000ff48, 0x0000ff28, - 0x0000ff49, 0x0000ff29, - 0x0000ff4a, 0x0000ff2a, - 0x0000ff4b, 0x0000ff2b, - 0x0000ff4c, 0x0000ff2c, - 0x0000ff4d, 0x0000ff2d, - 0x0000ff4e, 0x0000ff2e, - 0x0000ff4f, 0x0000ff2f, - 0x0000ff50, 0x0000ff30, - 0x0000ff51, 0x0000ff31, - 0x0000ff52, 0x0000ff32, - 0x0000ff53, 0x0000ff33, - 0x0000ff54, 0x0000ff34, - 0x0000ff55, 0x0000ff35, - 0x0000ff56, 0x0000ff36, - 0x0000ff57, 0x0000ff37, - 0x0000ff58, 0x0000ff38, - 0x0000ff59, 0x0000ff39, - 0x0000ff5a, 0x0000ff3a, - 0x00010428, 0x00010400, - 0x00010429, 0x00010401, - 0x0001042a, 0x00010402, - 0x0001042b, 0x00010403, - 0x0001042c, 0x00010404, - 0x0001042d, 0x00010405, - 0x0001042e, 0x00010406, - 0x0001042f, 0x00010407, - 0x00010430, 0x00010408, - 0x00010431, 0x00010409, - 0x00010432, 0x0001040a, - 0x00010433, 0x0001040b, - 0x00010434, 0x0001040c, - 0x00010435, 0x0001040d, - 0x00010436, 0x0001040e, - 0x00010437, 0x0001040f, - 0x00010438, 0x00010410, - 0x00010439, 0x00010411, - 0x0001043a, 0x00010412, - 0x0001043b, 0x00010413, - 0x0001043c, 0x00010414, - 0x0001043d, 0x00010415, - 0x0001043e, 0x00010416, - 0x0001043f, 0x00010417, - 0x00010440, 0x00010418, - 0x00010441, 0x00010419, - 0x00010442, 0x0001041a, - 0x00010443, 0x0001041b, - 0x00010444, 0x0001041c, - 0x00010445, 0x0001041d, - 0x00010446, 0x0001041e, - 0x00010447, 0x0001041f, - 0x00010448, 0x00010420, - 0x00010449, 0x00010421, - 0x0001044a, 0x00010422, - 0x0001044b, 0x00010423, - 0x0001044c, 0x00010424, - 0x0001044d, 0x00010425, -}; - -#endif /*UTF8TABLES_H_*/ diff --git a/authors.txt b/authors.txt deleted file mode 100644 index f7b66fe954..0000000000 --- a/authors.txt +++ /dev/null @@ -1,8 +0,0 @@ -b1v1r = Brian Rectanus -brectanu = Brian Rectanus -brectanus = Brian Rectanus -ivanr = Ivan Ristić -rbarnett = Ryan C. Barnett -[anonymous] = Brian Rectanus -(no author) = Brian Rectanus -brenosilva = Breno Silva diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index e2afe46228..0000000000 --- a/autogen.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -#rm -rf autom4te.cache -#automake --add-missing --copy --foreign -#autoreconf --install -#autoheader - -rm -rf autom4te.cache -rm -f aclocal.m4 -libtoolize --force --copy -autoreconf --install -autoheader -automake --add-missing --foreign --copy --force-missing -autoconf --force -rm -rf autom4te.cache - diff --git a/bindings/python b/bindings/python new file mode 160000 index 0000000000..bc625d5bb0 --- /dev/null +++ b/bindings/python @@ -0,0 +1 @@ +Subproject commit bc625d5bb0bac6a64bcce8dc9902208612399348 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..63f8a60fb3 --- /dev/null +++ b/build.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +rm -rf autom4te.cache +rm -f aclocal.m4 +case `uname` in Darwin*) glibtoolize --force --copy ;; + *) libtoolize --force --copy ;; esac +autoreconf --install +autoheader +automake --add-missing --foreign --copy --force-missing +autoconf --force +rm -rf autom4te.cache + + diff --git a/tests/action/.empty b/build/.empty similarity index 100% rename from tests/action/.empty rename to build/.empty diff --git a/build/apxs-wrapper.in b/build/apxs-wrapper.in deleted file mode 100755 index 7e0327318b..0000000000 --- a/build/apxs-wrapper.in +++ /dev/null @@ -1,15 +0,0 @@ -#!@SHELL@ - -WRAPPED_OPTS="" -for opt in "$@"; do - case "$opt" in - # Fix for -R not working w/apxs - -R*) WRAPPED_OPTS="$WRAPPED_OPTS -Wl,$opt" ;; - # OSF1 compiler option - -pthread) WRAPPED_OPTS="$WRAPPED_OPTS -Wc,$opt" ;; - # Unwrapped - *) WRAPPED_OPTS="$WRAPPED_OPTS $opt" ;; - esac -done - -exec @APXS@ $WRAPPED_OPTS diff --git a/build/ax_cxx_compile_stdcxx_11.m4 b/build/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 0000000000..e76fd6053d --- /dev/null +++ b/build/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,171 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() override {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + auto l = [](){}; + // Prevent Clang error: unused variable 'l' [-Werror,-Wunused-variable] + struct use_l { use_l() { l(); } }; + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this + namespace test_template_alias_sfinae { + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { + func(0); + } + } + + // Check for C++11 attribute support + void noret [[noreturn]] () { throw 0; } +]]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + for switch in -std=c++11 -std=c++0x +std=c++11; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) diff --git a/build/ax_prog_doxygen.m4 b/build/ax_prog_doxygen.m4 new file mode 100644 index 0000000000..fd145991e6 --- /dev/null +++ b/build/ax_prog_doxygen.m4 @@ -0,0 +1,533 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html +# =========================================================================== +# +# SYNOPSIS +# +# DX_INIT_DOXYGEN(PROJECT-NAME, DOXYFILE-PATH, [OUTPUT-DIR]) +# DX_DOXYGEN_FEATURE(ON|OFF) +# DX_DOT_FEATURE(ON|OFF) +# DX_HTML_FEATURE(ON|OFF) +# DX_CHM_FEATURE(ON|OFF) +# DX_CHI_FEATURE(ON|OFF) +# DX_MAN_FEATURE(ON|OFF) +# DX_RTF_FEATURE(ON|OFF) +# DX_XML_FEATURE(ON|OFF) +# DX_PDF_FEATURE(ON|OFF) +# DX_PS_FEATURE(ON|OFF) +# +# DESCRIPTION +# +# The DX_*_FEATURE macros control the default setting for the given +# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for +# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML +# help (for MS users), 'CHI' for generating a seperate .chi file by the +# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate +# output formats. The environment variable DOXYGEN_PAPER_SIZE may be +# specified to override the default 'a4wide' paper size. +# +# By default, HTML, PDF and PS documentation is generated as this seems to +# be the most popular and portable combination. MAN pages created by +# Doxygen are usually problematic, though by picking an appropriate subset +# and doing some massaging they might be better than nothing. CHM and RTF +# are specific for MS (note that you can't generate both HTML and CHM at +# the same time). The XML is rather useless unless you apply specialized +# post-processing to it. +# +# The macros mainly control the default state of the feature. The use can +# override the default by specifying --enable or --disable. The macros +# ensure that contradictory flags are not given (e.g., +# --enable-doxygen-html and --enable-doxygen-chm, +# --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each +# feature will be automatically disabled (with a warning) if the required +# programs are missing. +# +# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN +# with the following parameters: a one-word name for the project for use +# as a filename base etc., an optional configuration file name (the +# default is 'Doxyfile', the same as Doxygen's default), and an optional +# output directory name (the default is 'doxygen-doc'). +# +# Automake Support +# +# The following is a template aminclude.am file for use with Automake. +# Make targets and variables values are controlled by the various +# DX_COND_* conditionals set by autoconf. +# +# The provided targets are: +# +# doxygen-doc: Generate all doxygen documentation. +# +# doxygen-run: Run doxygen, which will generate some of the +# documentation (HTML, CHM, CHI, MAN, RTF, XML) +# but will not do the post processing required +# for the rest of it (PS, PDF, and some MAN). +# +# doxygen-man: Rename some doxygen generated man pages. +# +# doxygen-ps: Generate doxygen PostScript documentation. +# +# doxygen-pdf: Generate doxygen PDF documentation. +# +# Note that by default these are not integrated into the automake targets. +# If doxygen is used to generate man pages, you can achieve this +# integration by setting man3_MANS to the list of man pages generated and +# then adding the dependency: +# +# $(man3_MANS): doxygen-doc +# +# This will cause make to run doxygen and generate all the documentation. +# +# The following variable is intended for use in Makefile.am: +# +# DX_CLEANFILES = everything to clean. +# +# Then add this variable to MOSTLYCLEANFILES. +# +# ----- begin aminclude.am ------------------------------------- +# +# ## --------------------------------- ## +# ## Format-independent Doxygen rules. ## +# ## --------------------------------- ## +# +# if DX_COND_doc +# +# ## ------------------------------- ## +# ## Rules specific for HTML output. ## +# ## ------------------------------- ## +# +# if DX_COND_html +# +# DX_CLEAN_HTML = @DX_DOCDIR@/html +# +# endif DX_COND_html +# +# ## ------------------------------ ## +# ## Rules specific for CHM output. ## +# ## ------------------------------ ## +# +# if DX_COND_chm +# +# DX_CLEAN_CHM = @DX_DOCDIR@/chm +# +# if DX_COND_chi +# +# DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi +# +# endif DX_COND_chi +# +# endif DX_COND_chm +# +# ## ------------------------------ ## +# ## Rules specific for MAN output. ## +# ## ------------------------------ ## +# +# if DX_COND_man +# +# DX_CLEAN_MAN = @DX_DOCDIR@/man +# +# endif DX_COND_man +# +# ## ------------------------------ ## +# ## Rules specific for RTF output. ## +# ## ------------------------------ ## +# +# if DX_COND_rtf +# +# DX_CLEAN_RTF = @DX_DOCDIR@/rtf +# +# endif DX_COND_rtf +# +# ## ------------------------------ ## +# ## Rules specific for XML output. ## +# ## ------------------------------ ## +# +# if DX_COND_xml +# +# DX_CLEAN_XML = @DX_DOCDIR@/xml +# +# endif DX_COND_xml +# +# ## ----------------------------- ## +# ## Rules specific for PS output. ## +# ## ----------------------------- ## +# +# if DX_COND_ps +# +# DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps +# +# DX_PS_GOAL = doxygen-ps +# +# doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps +# +# @DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag +# cd @DX_DOCDIR@/latex; \ +# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ +# $(DX_LATEX) refman.tex; \ +# $(MAKEINDEX_PATH) refman.idx; \ +# $(DX_LATEX) refman.tex; \ +# countdown=5; \ +# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ +# refman.log > /dev/null 2>&1 \ +# && test $$countdown -gt 0; do \ +# $(DX_LATEX) refman.tex; \ +# countdown=`expr $$countdown - 1`; \ +# done; \ +# $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi +# +# endif DX_COND_ps +# +# ## ------------------------------ ## +# ## Rules specific for PDF output. ## +# ## ------------------------------ ## +# +# if DX_COND_pdf +# +# DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf +# +# DX_PDF_GOAL = doxygen-pdf +# +# doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf +# +# @DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag +# cd @DX_DOCDIR@/latex; \ +# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ +# $(DX_PDFLATEX) refman.tex; \ +# $(DX_MAKEINDEX) refman.idx; \ +# $(DX_PDFLATEX) refman.tex; \ +# countdown=5; \ +# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ +# refman.log > /dev/null 2>&1 \ +# && test $$countdown -gt 0; do \ +# $(DX_PDFLATEX) refman.tex; \ +# countdown=`expr $$countdown - 1`; \ +# done; \ +# mv refman.pdf ../@PACKAGE@.pdf +# +# endif DX_COND_pdf +# +# ## ------------------------------------------------- ## +# ## Rules specific for LaTeX (shared for PS and PDF). ## +# ## ------------------------------------------------- ## +# +# if DX_COND_latex +# +# DX_CLEAN_LATEX = @DX_DOCDIR@/latex +# +# endif DX_COND_latex +# +# .PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) +# +# .INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) +# +# doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag +# +# doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) +# +# @DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS) +# rm -rf @DX_DOCDIR@ +# $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) +# echo Timestamp >$@ +# +# DX_CLEANFILES = \ +# @DX_DOCDIR@/@PACKAGE@.tag \ +# -r \ +# $(DX_CLEAN_HTML) \ +# $(DX_CLEAN_CHM) \ +# $(DX_CLEAN_CHI) \ +# $(DX_CLEAN_MAN) \ +# $(DX_CLEAN_RTF) \ +# $(DX_CLEAN_XML) \ +# $(DX_CLEAN_PS) \ +# $(DX_CLEAN_PDF) \ +# $(DX_CLEAN_LATEX) +# +# endif DX_COND_doc +# +# ----- end aminclude.am --------------------------------------- +# +# LICENSE +# +# Copyright (c) 2009 Oren Ben-Kiki +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +## ----------## +## Defaults. ## +## ----------## + +DX_ENV="" +AC_DEFUN([DX_FEATURE_doc], ON) +AC_DEFUN([DX_FEATURE_dot], OFF) +AC_DEFUN([DX_FEATURE_man], OFF) +AC_DEFUN([DX_FEATURE_html], ON) +AC_DEFUN([DX_FEATURE_chm], OFF) +AC_DEFUN([DX_FEATURE_chi], OFF) +AC_DEFUN([DX_FEATURE_rtf], OFF) +AC_DEFUN([DX_FEATURE_xml], OFF) +AC_DEFUN([DX_FEATURE_pdf], ON) +AC_DEFUN([DX_FEATURE_ps], ON) + +## --------------- ## +## Private macros. ## +## --------------- ## + +# DX_ENV_APPEND(VARIABLE, VALUE) +# ------------------------------ +# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. +AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) + +# DX_DIRNAME_EXPR +# --------------- +# Expand into a shell expression prints the directory part of a path. +AC_DEFUN([DX_DIRNAME_EXPR], + [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) + +# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) +# ------------------------------------- +# Expands according to the M4 (static) status of the feature. +AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) + +# DX_REQUIRE_PROG(VARIABLE, PROGRAM) +# ---------------------------------- +# Require the specified program to be found for the DX_CURRENT_FEATURE to work. +AC_DEFUN([DX_REQUIRE_PROG], [ +AC_PATH_TOOL([$1], [$2]) +if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then + AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) + AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) +fi +]) + +# DX_TEST_FEATURE(FEATURE) +# ------------------------ +# Expand to a shell expression testing whether the feature is active. +AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) + +# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) +# ------------------------------------------------- +# Verify that a required features has the right state before trying to turn on +# the DX_CURRENT_FEATURE. +AC_DEFUN([DX_CHECK_DEPEND], [ +test "$DX_FLAG_$1" = "$2" \ +|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, + requires, contradicts) doxygen-DX_CURRENT_FEATURE]) +]) + +# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) +# ---------------------------------------------------------- +# Turn off the DX_CURRENT_FEATURE if the required feature is off. +AC_DEFUN([DX_CLEAR_DEPEND], [ +test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) +]) + +# DX_FEATURE_ARG(FEATURE, DESCRIPTION, +# CHECK_DEPEND, CLEAR_DEPEND, +# REQUIRE, DO-IF-ON, DO-IF-OFF) +# -------------------------------------------- +# Parse the command-line option controlling a feature. CHECK_DEPEND is called +# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), +# otherwise CLEAR_DEPEND is called to turn off the default state if a required +# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional +# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and +# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. +AC_DEFUN([DX_ARG_ABLE], [ + AC_DEFUN([DX_CURRENT_FEATURE], [$1]) + AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) + AC_ARG_ENABLE(doxygen-$1, + [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], + [--enable-doxygen-$1]), + DX_IF_FEATURE([$1], [don't $2], [$2]))], + [ +case "$enableval" in +#( +y|Y|yes|Yes|YES) + AC_SUBST([DX_FLAG_$1], 1) + $3 +;; #( +n|N|no|No|NO) + AC_SUBST([DX_FLAG_$1], 0) +;; #( +*) + AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) +;; +esac +], [ +AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) +$4 +]) +if DX_TEST_FEATURE([$1]); then + $5 + : +fi +AM_CONDITIONAL(DX_COND_$1, DX_TEST_FEATURE([$1])) +if DX_TEST_FEATURE([$1]); then + $6 + : +else + $7 + : +fi +]) + +## -------------- ## +## Public macros. ## +## -------------- ## + +# DX_XXX_FEATURE(DEFAULT_STATE) +# ----------------------------- +AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) +AC_DEFUN([DX_DOT_FEATURE], [AC_DEFUN([DX_FEATURE_dot], [$1])]) +AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) +AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) +AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) +AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) +AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) +AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) + +# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) +# --------------------------------------------------------- +# PROJECT also serves as the base name for the documentation files. +# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". +AC_DEFUN([DX_INIT_DOXYGEN], [ + +# Files: +AC_SUBST([DX_PROJECT], [$1]) +AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) +AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) + +# Environment variables used inside doxygen.cfg: +DX_ENV_APPEND(SRCDIR, $srcdir) +DX_ENV_APPEND(PROJECT, $DX_PROJECT) +DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) +DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) + +# Doxygen itself: +DX_ARG_ABLE(doc, [generate any doxygen documentation], + [], + [], + [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) + DX_REQUIRE_PROG([DX_PERL], perl)], + [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) + +# Dot for graphics: +DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_DOT], dot)], + [DX_ENV_APPEND(HAVE_DOT, YES) + DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], + [DX_ENV_APPEND(HAVE_DOT, NO)]) + +# Man pages generation: +DX_ARG_ABLE(man, [generate doxygen manual pages], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_MAN, YES)], + [DX_ENV_APPEND(GENERATE_MAN, NO)]) + +# RTF file generation: +DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_RTF, YES)], + [DX_ENV_APPEND(GENERATE_RTF, NO)]) + +# XML file generation: +DX_ARG_ABLE(xml, [generate doxygen XML documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_XML, YES)], + [DX_ENV_APPEND(GENERATE_XML, NO)]) + +# (Compressed) HTML help generation: +DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_HHC], hhc)], + [DX_ENV_APPEND(HHC_PATH, $DX_HHC) + DX_ENV_APPEND(GENERATE_HTML, YES) + DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], + [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) + +# Seperate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], + [DX_CHECK_DEPEND(chm, 1)], + [DX_CLEAR_DEPEND(chm, 1)], + [], + [DX_ENV_APPEND(GENERATE_CHI, YES)], + [DX_ENV_APPEND(GENERATE_CHI, NO)]) + +# Plain HTML pages generation: +DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], + [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], + [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], + [], + [DX_ENV_APPEND(GENERATE_HTML, YES)], + [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) + +# PostScript file generation: +DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_LATEX], latex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_DVIPS], dvips) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# PDF file generation: +DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# LaTeX generation for PS and/or PDF: +AM_CONDITIONAL(DX_COND_latex, DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf)) +if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then + DX_ENV_APPEND(GENERATE_LATEX, YES) +else + DX_ENV_APPEND(GENERATE_LATEX, NO) +fi + +# Paper size for PS and/or PDF: +AC_ARG_VAR(DOXYGEN_PAPER_SIZE, + [a4wide (default), a4, letter, legal or executive]) +case "$DOXYGEN_PAPER_SIZE" in +#( +"") + AC_SUBST(DOXYGEN_PAPER_SIZE, "") +;; #( +a4wide|a4|letter|legal|executive) + DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) +;; #( +*) + AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) +;; +esac + +#For debugging: +#echo DX_FLAG_doc=$DX_FLAG_doc +#echo DX_FLAG_dot=$DX_FLAG_dot +#echo DX_FLAG_man=$DX_FLAG_man +#echo DX_FLAG_html=$DX_FLAG_html +#echo DX_FLAG_chm=$DX_FLAG_chm +#echo DX_FLAG_chi=$DX_FLAG_chi +#echo DX_FLAG_rtf=$DX_FLAG_rtf +#echo DX_FLAG_xml=$DX_FLAG_xml +#echo DX_FLAG_pdf=$DX_FLAG_pdf +#echo DX_FLAG_ps=$DX_FLAG_ps +#echo DX_ENV=$DX_ENV +]) diff --git a/build/ax_valgrind_check.m4 b/build/ax_valgrind_check.m4 new file mode 100644 index 0000000000..cb3890ec58 --- /dev/null +++ b/build/ax_valgrind_check.m4 @@ -0,0 +1,236 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) +# AX_VALGRIND_CHECK() +# +# DESCRIPTION +# +# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows +# running `make check` under a variety of Valgrind tools to check for +# memory and threading errors. +# +# Defines VALGRIND_CHECK_RULES which should be substituted in your +# Makefile; and $enable_valgrind which can be used in subsequent configure +# output. VALGRIND_ENABLED is defined and substituted, and corresponds to +# the value of the --enable-valgrind option, which defaults to being +# enabled if Valgrind is installed and disabled otherwise. Individual +# Valgrind tools can be disabled via --disable-valgrind-, the +# default is configurable via the AX_VALGRIND_DFLT command or is to use +# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT +# calls must be made before the call to AX_VALGRIND_CHECK. +# +# If unit tests are written using a shell script and automake's +# LOG_COMPILER system, the $(VALGRIND) variable can be used within the +# shell scripts to enable Valgrind, as described here: +# +# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html +# +# Usage example: +# +# configure.ac: +# +# AX_VALGRIND_DFLT([sgcheck], [off]) +# AX_VALGRIND_CHECK +# +# Makefile.am: +# +# @VALGRIND_CHECK_RULES@ +# VALGRIND_SUPPRESSIONS_FILES = my-project.supp +# EXTRA_DIST = my-project.supp +# +# This results in a "check-valgrind" rule being added to any Makefile.am +# which includes "@VALGRIND_CHECK_RULES@" (assuming the module has been +# configured with --enable-valgrind). Running `make check-valgrind` in +# that directory will run the module's test suite (`make check`) once for +# each of the available Valgrind tools (out of memcheck, helgrind and drd) +# while the sgcheck will be skipped unless enabled again on the +# commandline with --enable-valgrind-sgcheck. The results for each check +# will be output to test-suite-$toolname.log. The target will succeed if +# there are zero errors and fail otherwise. +# +# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in +# memcheck, helgrind, drd and sgcheck. These are useful because often only +# some of those tools can be ran cleanly on a codebase. +# +# The macro supports running with and without libtool. +# +# LICENSE +# +# Copyright (c) 2014, 2015, 2016 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +dnl Configured tools +m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) +m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) +m4_foreach([vgtool], [valgrind_tool_list], + [m4_define([en_dflt_valgrind_]vgtool, [on])]) + +AC_DEFUN([AX_VALGRIND_DFLT],[ + m4_define([en_dflt_valgrind_$1], [$2]) +])dnl + +AC_DEFUN([AX_VALGRIND_CHECK],[ + dnl Check for --enable-valgrind + AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], + [enable_valgrind=$enableval],[enable_valgrind=]) + + AS_IF([test "$enable_valgrind" != "no"],[ + # Check for Valgrind. + AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) + AS_IF([test "$VALGRIND" = ""],[ + AS_IF([test "$enable_valgrind" = "yes"],[ + AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) + ],[ + enable_valgrind=no + ]) + ],[ + enable_valgrind=yes + ]) + ]) + + AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) + + # Check for Valgrind tools we care about. + [valgrind_enabled_tools=] + m4_foreach([vgtool],[valgrind_tool_list],[ + AC_ARG_ENABLE([valgrind-]vgtool, + m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl +[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl +[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), + [enable_valgrind_]vgtool[=$enableval], + [enable_valgrind_]vgtool[=]) + AS_IF([test "$enable_valgrind" = "no"],[ + enable_valgrind_]vgtool[=no], + [test "$enable_valgrind_]vgtool[" ]dnl +m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ + AC_CACHE_CHECK([for Valgrind tool ]vgtool, + [ax_cv_valgrind_tool_]vgtool,[ + ax_cv_valgrind_tool_]vgtool[=no + m4_set_contains([valgrind_exp_tool_set],vgtool, + [m4_define([vgtoolx],[exp-]vgtool)], + [m4_define([vgtoolx],vgtool)]) + AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ + ax_cv_valgrind_tool_]vgtool[=yes + ]) + ]) + AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) + ],[ + enable_valgrind_]vgtool[=no + ]) + ],[ + enable_valgrind_]vgtool[=yes + ]) + ]) + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" + ]) + AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) + ]) + AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) + AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) + +[VALGRIND_CHECK_RULES=' +# Valgrind check +# +# Optional: +# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions +# files to load. (Default: empty) +# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. +# (Default: --num-callers=30) +# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: +# memcheck, helgrind, drd, sgcheck). (Default: various) + +# Optional variables +VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) +VALGRIND_FLAGS ?= --num-callers=30 +VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no +VALGRIND_helgrind_FLAGS ?= --history-level=approx +VALGRIND_drd_FLAGS ?= +VALGRIND_sgcheck_FLAGS ?= + +# Internal use +valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) + +valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) +valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) +valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) +valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) + +valgrind_quiet = $(valgrind_quiet_$(V)) +valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) +valgrind_quiet_0 = --quiet +valgrind_v_use = $(valgrind_v_use_$(V)) +valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) +valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%,%,$''@):; + +# Support running with and without libtool. +ifneq ($(LIBTOOL),) +valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute +else +valgrind_lt = +endif + +# Use recursive makes in order to ignore errors during check +check-valgrind: +ifeq ($(VALGRIND_ENABLED),yes) + -$(A''M_V_at)$(foreach tool,$(valgrind_enabled_tools), \ + $(MAKE) $(AM_MAKEFLAGS) -k check-valgrind-$(tool); \ + ) +else + @echo "Need to reconfigure with --enable-valgrind" +endif + +# Valgrind running +VALGRIND_TESTS_ENVIRONMENT = \ + $(TESTS_ENVIRONMENT) \ + env VALGRIND=$(VALGRIND) \ + G_SLICE=always-malloc,debug-blocks \ + G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly + +VALGRIND_LOG_COMPILER = test/test-suite.sh $(VALGRIND_SUPPRESSIONS) $(VALGRIND_FLAGS) +# $(valgrind_lt) \ +# $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) + +define valgrind_tool_rule = +check-valgrind-$(1): +ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) + $$(valgrind_v_use)$$(MAKE) check-TESTS \ + TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ + LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ + LOG_FLAGS="$$(valgrind_$(1)_flags)" \ + TEST_SUITE_LOG=test-suite-$(1).log +else ifeq ($$(VALGRIND_ENABLED),yes) + @echo "Need to reconfigure with --enable-valgrind-$(1)" +else + @echo "Need to reconfigure with --enable-valgrind" +endif +endef + +$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind + +MOSTLYCLEANFILES ?= +MOSTLYCLEANFILES += $(valgrind_log_files) + +.PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools)) +'] + + AC_SUBST([VALGRIND_CHECK_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) +]) diff --git a/build/compile b/build/compile deleted file mode 100755 index 1b1d232169..0000000000 --- a/build/compile +++ /dev/null @@ -1,142 +0,0 @@ -#! /bin/sh -# Wrapper for compilers which do not understand `-c -o'. - -scriptversion=2005-05-14.22 - -# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. -# Written by Tom Tromey . -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# This file is maintained in Automake, please report -# bugs to or send patches to -# . - -case $1 in - '') - echo "$0: No command. Try \`$0 --help' for more information." 1>&2 - exit 1; - ;; - -h | --h*) - cat <<\EOF -Usage: compile [--help] [--version] PROGRAM [ARGS] - -Wrapper for compilers which do not understand `-c -o'. -Remove `-o dest.o' from ARGS, run PROGRAM with the remaining -arguments, and rename the output as expected. - -If you are trying to build a whole package this is not the -right script to run: please start by reading the file `INSTALL'. - -Report bugs to . -EOF - exit $? - ;; - -v | --v*) - echo "compile $scriptversion" - exit $? - ;; -esac - -ofile= -cfile= -eat= - -for arg -do - if test -n "$eat"; then - eat= - else - case $1 in - -o) - # configure might choose to run compile as `compile cc -o foo foo.c'. - # So we strip `-o arg' only if arg is an object. - eat=1 - case $2 in - *.o | *.obj) - ofile=$2 - ;; - *) - set x "$@" -o "$2" - shift - ;; - esac - ;; - *.c) - cfile=$1 - set x "$@" "$1" - shift - ;; - *) - set x "$@" "$1" - shift - ;; - esac - fi - shift -done - -if test -z "$ofile" || test -z "$cfile"; then - # If no `-o' option was seen then we might have been invoked from a - # pattern rule where we don't need one. That is ok -- this is a - # normal compilation that the losing compiler can handle. If no - # `.c' file was seen then we are probably linking. That is also - # ok. - exec "$@" -fi - -# Name of file we expect compiler to create. -cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` - -# Create the lock directory. -# Note: use `[/.-]' here to ensure that we don't use the same name -# that we are using for the .o file. Also, base the name on the expected -# object file name, since that is what matters with a parallel build. -lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d -while true; do - if mkdir "$lockdir" >/dev/null 2>&1; then - break - fi - sleep 1 -done -# FIXME: race condition here if user kills between mkdir and trap. -trap "rmdir '$lockdir'; exit 1" 1 2 15 - -# Run the compile. -"$@" -ret=$? - -if test -f "$cofile"; then - mv "$cofile" "$ofile" -elif test -f "${cofile}bj"; then - mv "${cofile}bj" "$ofile" -fi - -rmdir "$lockdir" -exit $ret - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" -# End: diff --git a/build/curl.m4 b/build/curl.m4 new file mode 100644 index 0000000000..66126ece78 --- /dev/null +++ b/build/curl.m4 @@ -0,0 +1,131 @@ +dnl Check for CURL Libraries +dnl CHECK_CURL(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) +dnl Sets: +dnl CURL_CFLAGS +dnl CURL_LIBS + +CURL_CONFIG="" +CURL_VERSION="" +CURL_CPPFLAGS="" +CURL_CFLAGS="" +CURL_LDFLAGS="" +CURL_LDADD="" +CURL_MIN_VERSION="7.15.1" + +AC_DEFUN([CHECK_CURL], [ + + +AC_ARG_WITH( + curl, + [AC_HELP_STRING([--with-curl=PATH],[Path to curl prefix or config script])], + [test_paths="${with_curl}"], + [test_paths="/usr/local/libcurl /usr/local/curl /usr/local /opt/libcurl /opt/curl /opt /usr"]) + +AC_MSG_CHECKING([for libcurl config script]) + +for x in ${test_paths}; do + dnl # Determine if the script was specified and use it directly + if test ! -d "$x" -a -e "$x"; then + CURL_CONFIG=$x + curl_path="no" + break + fi + + dnl # Try known config script names/locations + for CURL_CONFIG in curl-config; do + if test -e "${x}/bin/${CURL_CONFIG}"; then + curl_path="${x}/bin" + break + elif test -e "${x}/${CURL_CONFIG}"; then + curl_path="${x}" + break + else + curl_path="" + fi + done + if test -n "$curl_path"; then + break + fi +done + +if test -n "${curl_path}"; then + if test "${curl_path}" != "no"; then + CURL_CONFIG="${curl_path}/${CURL_CONFIG}" + fi + AC_MSG_RESULT([${CURL_CONFIG}]) + CURL_VERSION=`${CURL_CONFIG} --version | sed 's/^[[^0-9]][[^[:space:]]][[^[:space:]]]*[[[:space:]]]*//' | tr '\r\n' ' '` + if test ! -z "${CURL_VERSION}"; then AC_MSG_NOTICE(curl VERSION: $CURL_VERSION); fi + CURL_CFLAGS="`${CURL_CONFIG} --cflags`" + if test ! -z "${CURL_CFLAGS}"; then AC_MSG_NOTICE(curl CFLAGS: $CURL_CFLAGS); fi + CURL_LDADD="`${CURL_CONFIG} --libs`" + if test ! -z "${CURL_CONFIG}"; then AC_MSG_NOTICE(curl LDADD: $CURL_LIBS); fi + + dnl # Check version is ok + AC_MSG_CHECKING([if libcurl is at least v${CURL_MIN_VERSION}]) + curl_min_ver=`echo ${CURL_MIN_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` + curl_ver=`echo ${CURL_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` + if test "$curl_min_ver" -le "$curl_ver"; then + AC_MSG_RESULT([yes, $CURL_VERSION]) + curl_tlsv2_ver=`echo 7.34.0 | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` + if test "$curl_tlsv2_ver" -le "$curl_ver"; then + CURL_CFLAGS="${CURL_CFLAGS} -DWITH_CURL_SSLVERSION_TLSv1_2" + fi + CURL_CFLAGS="${CURL_CFLAGS} -DWITH_CURL" + else + AC_MSG_RESULT([no, $CURL_VERSION]) + AC_MSG_NOTICE([NOTE: curl library may be too old]) + fi + + dnl # Check/warn if GnuTLS is used + AC_MSG_CHECKING([if libcurl is linked with gnutls]) + curl_uses_gnutls=`echo ${CURL_LIBS} | grep gnutls | wc -l` + if test "$curl_uses_gnutls" -ne 0; then + AC_MSG_RESULT([yes]) + AC_MSG_NOTICE([NOTE: curl linked with gnutls may be buggy, openssl recommended]) + CURL_USES_GNUTLS=yes + else + AC_MSG_RESULT([no]) + CURL_USES_GNUTLS=no + fi + +else + AC_MSG_RESULT([no]) +fi + +AC_SUBST(CURL_CONFIG) +AC_SUBST(CURL_VERSION) +AC_SUBST(CURL_CPPFLAGS) +AC_SUBST(CURL_CFLAGS) +AC_SUBST(CURL_LDFLAGS) +AC_SUBST(CURL_LDADD) +AC_SUBST(CURL_USES_GNUTLS) + +if test "x${with_curl}" == "xno"; then + CURL_DISABLED=yes +else + if test "x${with_curl}" != "x"; then + CURL_MANDATORY=yes + fi +fi + +if test -z "${CURL_VERSION}"; then + AC_MSG_NOTICE([*** curl library not found.]) + if test -z "${CURL_MANDATORY}"; then + if test -z "${CURL_DISABLED}"; then + CURL_FOUND=0 + else + CURL_FOUND=2 + fi + else + AC_MSG_ERROR([Curl was explicitly referenced but it was not found]) + CURL_FOUND=-1 + fi +else + CURL_FOUND=1 + AC_MSG_NOTICE([using curl v${CURL_VERSION}]) + CURL_DISPLAY="${CURL_LDADD}, ${CURL_CFLAGS}" +fi + +AC_SUBST(CURL_FOUND) +AC_SUBST(CURL_DISPLAY) +]) diff --git a/build/find_apr.m4 b/build/find_apr.m4 deleted file mode 100644 index 5524b66285..0000000000 --- a/build/find_apr.m4 +++ /dev/null @@ -1,91 +0,0 @@ -dnl Check for APR Libraries -dnl CHECK_APR(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -dnl Sets: -dnl APR_CFLAGS -dnl APR_LDFLAGS -dnl APR_LIBS -dnl APR_LINK_LD - -APR_CONFIG="" -APR_CFLAGS="" -APR_CPPFLAGS="" -APR_LDFLAGS="" -APR_LDADD="" -APR_INCLUDEDIR="" -APR_LINKLD="" -AC_DEFUN([CHECK_APR], -[dnl - -AC_ARG_WITH( - apr, - [AC_HELP_STRING([--with-apr=PATH],[Path to apr prefix or config script])], - [test_paths="${with_apr}"], - [test_paths="/usr/local/libapr /usr/local/apr /usr/local /opt/libapr /opt/apr /opt /usr"]) - -AC_MSG_CHECKING([for libapr config script]) - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - APR_CONFIG=$x - apr_path=no - break - fi - - dnl # Try known config script names/locations - for APR_CONFIG in apr-1-mt-config apr-1-config apr-config-1 apr-mt-config-1 apr-mt-config apr-config; do - if test -e "${x}/bin/${APR_CONFIG}"; then - apr_path="${x}/bin" - break - elif test -e "${x}/${APR_CONFIG}"; then - apr_path="${x}" - break - else - apr_path="" - fi - done - if test -n "$apr_path"; then - break - fi -done - -if test -n "${apr_path}"; then - if test "${apr_path}" != "no"; then - APR_CONFIG="${apr_path}/${APR_CONFIG}" - fi - AC_MSG_RESULT([${APR_CONFIG}]) - APR_VERSION="`${APR_CONFIG} --version`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr VERSION: $APR_VERSION); fi - APR_CFLAGS="`${APR_CONFIG} --includes`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr CFLAGS: $APR_CFLAGS); fi - APR_CPPFLAGS="`${APR_CONFIG} --cppflags`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr CPPFLAGS: $APR_CPPFLAGS); fi - APR_LDFLAGS="`${APR_CONFIG} --libs`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDFLAGS: $APR_LDFLAGS); fi - APR_LDADD="`${APR_CONFIG} --link-libtool`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LDADD: $APR_LDADD); fi - APR_INCLUDEDIR="`${APR_CONFIG} --includedir`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr INCLUDEDIR: $APR_INCLUDEDIR); fi - APR_LINKLD="`${APR_CONFIG} --link-ld`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apr LINKLD: $APR_LINKLD); fi -else - AC_MSG_RESULT([no]) -fi - -AC_SUBST(APR_CONFIG) -AC_SUBST(APR_VERSION) -AC_SUBST(APR_CFLAGS) -AC_SUBST(APR_CPPFLAGS) -AC_SUBST(APR_LDFLAGS) -AC_SUBST(APR_LDADD) -AC_SUBST(APR_INCLUDEDIR) -AC_SUBST(APR_LINKLD) - -if test -z "${APR_VERSION}"; then - AC_MSG_NOTICE([*** apr library not found.]) - ifelse([$2], , AC_MSG_ERROR([apr library is required]), $2) -else - AC_MSG_NOTICE([using apr v${APR_VERSION}]) - ifelse([$1], , , $1) -fi -]) diff --git a/build/find_apu.m4 b/build/find_apu.m4 deleted file mode 100644 index bc82030b7c..0000000000 --- a/build/find_apu.m4 +++ /dev/null @@ -1,88 +0,0 @@ -dnl Check for APU Libraries -dnl CHECK_APU(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -dnl Sets: -dnl APU_CFLAGS -dnl APU_LDFLAGS -dnl APU_LIBS -dnl APU_LINK_LD - -APU_CONFIG="" -APU_CFLAGS="" -APU_LDFLAGS="" -APU_LDADD="" -APU_INCLUDEDIR="" -APU_LINKLD="" - -AC_DEFUN([CHECK_APU], -[dnl - -AC_ARG_WITH( - apu, - [AC_HELP_STRING([--with-apu=PATH],[Path to apu prefix or config script])], - [test_paths="${with_apu}"], - [test_paths="/usr/local/libapr-util /usr/local/apr-util /usr/local/libapu /usr/local/apu /usr/local/apr /usr/local /opt/libapr-util /opt/apr-util /opt/libapu /opt/apu /opt /usr"]) - -AC_MSG_CHECKING([for libapu config script]) - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - APU_CONFIG=$x - apu_path="no" - break - fi - - dnl # Try known config script names/locations - for APU_CONFIG in apu-1-mt-config apu-1-config apu-config-1 apu-mt-config-1 apu-mt-config apu-config; do - if test -e "${x}/bin/${APU_CONFIG}"; then - apu_path="${x}/bin" - break - elif test -e "${x}/${APU_CONFIG}"; then - apu_path="${x}" - break - else - apu_path="" - fi - done - if test -n "$apu_path"; then - break - fi -done - -if test -n "${apu_path}"; then - if test "${apu_path}" != "no"; then - APU_CONFIG="${apu_path}/${APU_CONFIG}" - fi - AC_MSG_RESULT([${APU_CONFIG}]) - APU_VERSION="`${APU_CONFIG} --version`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu VERSION: $APU_VERSION); fi - APU_CFLAGS="`${APU_CONFIG} --includes`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu CFLAGS: $APU_CFLAGS); fi - APU_LDFLAGS="`${APU_CONFIG} --libs`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDFLAGS: $APU_LDFLAGS); fi - APU_LDADD="`${APU_CONFIG} --link-libtool`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDADD: $APU_LDADD); fi - APU_INCLUDEDIR="`${APU_CONFIG} --includedir`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu INCLUDEDIR: $APU_INCLUDEDIR); fi - APU_LINKLD="`${APU_CONFIG} --link-ld`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LINKLD: $APU_LINKLD); fi -else - AC_MSG_RESULT([no]) -fi - -AC_SUBST(APU_CONFIG) -AC_SUBST(APU_VERSION) -AC_SUBST(APU_CFLAGS) -AC_SUBST(APU_LDFLAGS) -AC_SUBST(APU_LDADD) -AC_SUBST(APU_INCLUDEDIR) -AC_SUBST(APU_LINKLD) - -if test -z "${APU_VERSION}"; then - AC_MSG_NOTICE([*** apu library not found.]) - ifelse([$2], , AC_MSG_ERROR([apu library is required]), $2) -else - AC_MSG_NOTICE([using apu v${APU_VERSION}]) - ifelse([$1], , , $1) -fi -]) diff --git a/build/find_curl.m4 b/build/find_curl.m4 deleted file mode 100644 index 7e47694300..0000000000 --- a/build/find_curl.m4 +++ /dev/null @@ -1,105 +0,0 @@ -dnl Check for CURL Libraries -dnl CHECK_CURL(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -dnl Sets: -dnl CURL_CFLAGS -dnl CURL_LIBS - -CURL_CONFIG="" -CURL_VERSION="" -CURL_CPPFLAGS="" -CURL_CFLAGS="" -CURL_LDFLAGS="" -CURL_LDADD="" -CURL_MIN_VERSION="7.15.1" - -AC_DEFUN([CHECK_CURL], -[dnl - -AC_ARG_WITH( - curl, - [AC_HELP_STRING([--with-curl=PATH],[Path to curl prefix or config script])], - [test_paths="${with_curl}"], - [test_paths="/usr/local/libcurl /usr/local/curl /usr/local /opt/libcurl /opt/curl /opt /usr"]) - -AC_MSG_CHECKING([for libcurl config script]) - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - CURL_CONFIG=$x - curl_path="no" - break - fi - - dnl # Try known config script names/locations - for CURL_CONFIG in curl-config; do - if test -e "${x}/bin/${CURL_CONFIG}"; then - curl_path="${x}/bin" - break - elif test -e "${x}/${CURL_CONFIG}"; then - curl_path="${x}" - break - else - curl_path="" - fi - done - if test -n "$curl_path"; then - break - fi -done - -if test -n "${curl_path}"; then - if test "${curl_path}" != "no"; then - CURL_CONFIG="${curl_path}/${CURL_CONFIG}" - fi - AC_MSG_RESULT([${CURL_CONFIG}]) - CURL_VERSION=`${CURL_CONFIG} --version | sed 's/^[[^0-9]][[^[:space:]]][[^[:space:]]]*[[[:space:]]]*//'` - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl VERSION: $CURL_VERSION); fi - CURL_CFLAGS="`${CURL_CONFIG} --cflags`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl CFLAGS: $CURL_CFLAGS); fi - CURL_LDADD="`${CURL_CONFIG} --libs`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl LDADD: $CURL_LIBS); fi - - dnl # Check version is ok - AC_MSG_CHECKING([if libcurl is at least v${CURL_MIN_VERSION}]) - curl_min_ver=`echo ${CURL_MIN_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` - curl_ver=`echo ${CURL_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` - if test "$curl_min_ver" -le "$curl_ver"; then - AC_MSG_RESULT([yes, $CURL_VERSION]) - else - AC_MSG_RESULT([no, $CURL_VERSION]) - AC_MSG_NOTICE([NOTE: curl library may be too old]) - fi - - dnl # Check/warn if GnuTLS is used - AC_MSG_CHECKING([if libcurl is linked with gnutls]) - curl_uses_gnutls=`echo ${CURL_LIBS} | grep gnutls | wc -l` - if test "$curl_uses_gnutls" -ne 0; then - AC_MSG_RESULT([yes]) - AC_MSG_NOTICE([NOTE: curl linked with gnutls may be buggy, openssl recommended]) - CURL_USES_GNUTLS=yes - else - AC_MSG_RESULT([no]) - CURL_USES_GNUTLS=no - fi - -else - AC_MSG_RESULT([no]) -fi - -AC_SUBST(CURL_CONFIG) -AC_SUBST(CURL_VERSION) -AC_SUBST(CURL_CPPFLAGS) -AC_SUBST(CURL_CFLAGS) -AC_SUBST(CURL_LDFLAGS) -AC_SUBST(CURL_LDADD) -AC_SUBST(CURL_USES_GNUTLS) - -if test -z "${CURL_VERSION}"; then - AC_MSG_NOTICE([*** curl library not found.]) - ifelse([$2], , AC_MSG_NOTICE([NOTE: curl library is only required for building mlogc]), $2) -else - AC_MSG_NOTICE([using curl v${CURL_VERSION}]) - ifelse([$1], , , $1) -fi -]) diff --git a/build/find_lua.m4 b/build/find_lua.m4 deleted file mode 100644 index 7998045974..0000000000 --- a/build/find_lua.m4 +++ /dev/null @@ -1,195 +0,0 @@ -dnl Check for LUA Libraries -dnl CHECK_LUA(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -dnl Sets: -dnl LUA_CFLAGS -dnl LUA_LIBS - -AC_DEFUN([CHECK_LUA], -[dnl - -AC_REQUIRE([PKG_PROG_PKG_CONFIG]) - -LUA_CONFIG="" -LUA_VERSION="" -LUA_CFLAGS="" -LUA_CPPFLAGS="" -LUA_LDADD="" -LUA_LDFLAGS="" -LUA_CONFIG=${PKG_CONFIG} -LUA_PKGNAMES="lua5.1 lua-5.1 lua_5.1 lua-51 lua_51 lua51 lua5 lua" -LUA_SONAMES="so la sl dll dylib" - -AC_ARG_WITH( - lua, - [AC_HELP_STRING([--with-lua=PATH],[Path to lua prefix or config script])] - ,, with_lua=yes) - -AS_CASE(["${with_lua}"], - [no], [test_paths=], - [yes], [test_paths="/usr/local/liblua /usr/local/lua /usr/local /opt/liblua /opt/lua /opt /usr"], - [test_paths="${with_lua}"]) - -AS_IF([test "x${test_paths}" != "x"], [ -AC_MSG_CHECKING([for liblua config script]) - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - LUA_CONFIG=$x - break - fi - - dnl # Try known config script names/locations - for y in $LUA_CONFIG; do - if test -e "${x}/bin/${y}"; then - LUA_CONFIG="${x}/bin/${y}" - lua_config="${LUA_CONFIG}" - break - elif test -e "${x}/${y}"; then - LUA_CONFIG="${x}/${y}" - lua_config="${LUA_CONFIG}" - break - fi - done - if test -n "${lua_config}"; then - break - fi -done - -dnl # Try known package names -if test -n "${LUA_CONFIG}"; then - LUA_PKGNAME="" - for x in ${LUA_PKGNAMES}; do - if ${LUA_CONFIG} --exists ${x}; then - LUA_PKGNAME="$x" - break - fi - done -fi - -if test -n "${LUA_PKGNAME}"; then - AC_MSG_RESULT([${LUA_CONFIG}]) - LUA_VERSION="`${LUA_CONFIG} ${LUA_PKGNAME} --modversion`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(lua VERSION: $LUA_VERSION); fi - LUA_CFLAGS="`${LUA_CONFIG} ${LUA_PKGNAME} --cflags`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(lua CFLAGS: $LUA_CFLAGS); fi - LUA_LDADD="`${LUA_CONFIG} ${LUA_PKGNAME} --libs-only-l`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(lua LDADD: $LUA_LDADD); fi - LUA_LDFLAGS="`${LUA_CONFIG} ${LUA_PKGNAME} --libs-only-L --libs-only-other`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(lua LDFLAGS: $LUA_LDFLAGS); fi -else - AC_MSG_RESULT([no]) - - dnl Hack to just try to find the lib and include - AC_MSG_CHECKING([for lua install]) - for x in ${test_paths}; do - for y in ${LUA_SONAMES}; do - if test -e "${x}/liblua5.1.${y}"; then - lua_lib_path="${x}" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib/liblua5.1.${y}"; then - lua_lib_path="${x}/lib" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib64/liblua5.1.${y}"; then - lua_lib_path="${x}/lib64" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib32/liblua5.1.${y}"; then - lua_lib_path="${x}/lib32" - lua_lib_name="lua5.1" - break - elif test -e "${x}/liblua51.${y}"; then - lua_lib_path="${x}" - lua_lib_name="lua51" - break - elif test -e "${x}/lib/liblua51.${y}"; then - lua_lib_path="${x}/lib" - lua_lib_name="lua51" - break - elif test -e "${x}/lib64/liblua51.${y}"; then - lua_lib_path="${x}/lib64" - lua_lib_name="lua51" - break - elif test -e "${x}/lib32/liblua51.${y}"; then - lua_lib_path="${x}/lib32" - lua_lib_name="lua51" - break - elif test -e "${x}/liblua.${y}"; then - lua_lib_path="${x}" - lua_lib_name="lua" - break - elif test -e "${x}/lib/liblua.${y}"; then - lua_lib_path="${x}/lib" - lua_lib_name="lua" - break - elif test -e "${x}/lib64/liblua.${y}"; then - lua_lib_path="${x}/lib64" - lua_lib_name="lua" - break - elif test -e "${x}/lib32/liblua.${y}"; then - lua_lib_path="${x}/lib32" - lua_lib_name="lua" - break - else - lua_lib_path="" - lua_lib_name="" - fi - done - if test -n "$lua_lib_path"; then - break - fi - done - for x in ${test_paths}; do - if test -e "${x}/include/lua.h"; then - lua_inc_path="${x}/include" - break - elif test -e "${x}/lua.h"; then - lua_inc_path="${x}" - break - fi - - dnl # Check some sub-paths as well - for lua_pkg_name in ${lua_lib_name} ${LUA_PKGNAMES}; do - if test -e "${x}/include/${lua_pkg_name}/lua.h"; then - lua_inc_path="${x}/include" - break - elif test -e "${x}/${lua_pkg_name}/lua.h"; then - lua_inc_path="${x}" - break - else - lua_inc_path="" - fi - done - if test -n "$lua_inc_path"; then - break - fi - done - if test -n "${lua_lib_path}" -a -n "${lua_inc_path}"; then - LUA_CONFIG="" - AC_MSG_RESULT([${lua_lib_path} ${lua_inc_path}]) - LUA_VERSION="5.1" - LUA_CFLAGS="-I${lua_inc_path}" - LUA_LDADD="-l${lua_lib_name}" - LUA_LDFLAGS="-L${lua_lib_path}" - else - LUA_VERSION="" - AC_MSG_RESULT([no]) - fi -fi - -]) - -AC_SUBST(LUA_CFLAGS) -AC_SUBST(LUA_LDADD) -AC_SUBST(LUA_LDFLAGS) - - if test -z "${LUA_VERSION}"; then - ifelse([$2], , AC_MSG_NOTICE([optional lua library not found]), $2) - else - AC_MSG_NOTICE([using lua v${LUA_VERSION}]) - LUA_CFLAGS="-DWITH_LUA ${LUA_CFLAGS}" - ifelse([$1], , , $1) - fi -]) diff --git a/build/find_pcre.m4 b/build/find_pcre.m4 deleted file mode 100644 index f5da40a327..0000000000 --- a/build/find_pcre.m4 +++ /dev/null @@ -1,90 +0,0 @@ -dnl Check for PCRE Libraries -dnl CHECK_PCRE(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -dnl Sets: -dnl PCRE_CFLAGS -dnl PCRE_LIBS - -PCRE_CONFIG="" -PCRE_VERSION="" -PCRE_CPPFLAGS="" -PCRE_CFLAGS="" -PCRE_LDFLAGS="" -PCRE_LDADD="" -PCRE_LD_PATH="" - -AC_DEFUN([CHECK_PCRE], -[dnl - -AC_ARG_WITH( - pcre, - [AC_HELP_STRING([--with-pcre=PATH],[Path to pcre prefix or config script])], - [test_paths="${with_pcre}"], - [test_paths="/usr/local/libpcre /usr/local/pcre /usr/local /opt/libpcre /opt/pcre /opt /usr"]) - -AC_MSG_CHECKING([for libpcre config script]) - -dnl # Determine pcre lib directory -if test -z "${with_pcre}"; then - test_paths="/usr/local/pcre /usr/local /usr" -else - test_paths="${with_pcre}" -fi - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - PCRE_CONFIG=$x - pcre_path="no" - break - fi - - dnl # Try known config script names/locations - for PCRE_CONFIG in pcre-config; do - if test -e "${x}/bin/${PCRE_CONFIG}"; then - pcre_path="${x}/bin" - break - elif test -e "${x}/${PCRE_CONFIG}"; then - pcre_path="${x}" - break - else - pcre_path="" - fi - done - if test -n "$pcre_path"; then - break - fi -done - -if test -n "${pcre_path}"; then - if test "${pcre_path}" != "no"; then - PCRE_CONFIG="${pcre_path}/${PCRE_CONFIG}" - fi - AC_MSG_RESULT([${PCRE_CONFIG}]) - PCRE_VERSION="`${PCRE_CONFIG} --version`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre VERSION: $PCRE_VERSION); fi - PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre CFLAGS: $PCRE_CFLAGS); fi - PCRE_LDADD="`${PCRE_CONFIG} --libs`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre LDADD: $PCRE_LDADD); fi - PCRE_LD_PATH="/`${PCRE_CONFIG} --libs | cut -d'/' -f2,3,4,5,6 | cut -d ' ' -f1`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre PCRE_LD_PATH: $PCRE_LD_PATH); fi -else - AC_MSG_RESULT([no]) -fi - -AC_SUBST(PCRE_CONFIG) -AC_SUBST(PCRE_VERSION) -AC_SUBST(PCRE_CPPFLAGS) -AC_SUBST(PCRE_CFLAGS) -AC_SUBST(PCRE_LDFLAGS) -AC_SUBST(PCRE_LDADD) -AC_SUBST(PCRE_LD_PATH) - -if test -z "${PCRE_VERSION}"; then - AC_MSG_NOTICE([*** pcre library not found.]) - ifelse([$2], , AC_MSG_ERROR([pcre library is required]), $2) -else - AC_MSG_NOTICE([using pcre v${PCRE_VERSION}]) - ifelse([$1], , , $1) -fi -]) diff --git a/build/find_xml.m4 b/build/find_xml.m4 deleted file mode 100644 index 691679e467..0000000000 --- a/build/find_xml.m4 +++ /dev/null @@ -1,90 +0,0 @@ -dnl Check for LIBXML2 Libraries -dnl CHECK_LIBXML2(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) -dnl Sets: -dnl LIBXML2_CFLAGS -dnl LIBXML2_LIBS - -LIBXML2_CONFIG="" -LIBXML2_VERSION="" -LIBXML2_CFLAGS="" -LIBXML2_CPPFLAGS="" -LIBXML2_LDADD="" -LIBXML2_LDFLAGS="" - -AC_DEFUN([CHECK_LIBXML2], -[dnl - -AC_ARG_WITH( - libxml, - [AC_HELP_STRING([--with-libxml=PATH],[Path to libxml2 prefix or config script])], - [test_paths="${with_libxml}"], - [test_paths="/usr/local/libxml2 /usr/local/xml2 /usr/local/xml /usr/local /opt/libxml2 /opt/libxml /opt/xml2 /opt/xml /opt /usr"]) - -AC_MSG_CHECKING([for libxml2 config script]) - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - LIBXML2_CONFIG=$x - libxml2_path="no" - break - fi - - dnl # Try known config script names/locations - for LIBXML2_CONFIG in xml2-config xml-2-config xml-config; do - if test -e "${x}/bin/${LIBXML2_CONFIG}"; then - libxml2_path="${x}/bin" - break - elif test -e "${x}/${LIBXML2_CONFIG}"; then - libxml2_path="${x}" - break - else - libxml2_path="" - fi - done - if test -n "$libxml2_path"; then - break - fi -done - -if test -n "${libxml2_path}"; then - if test "${libxml2_path}" != "no"; then - LIBXML2_CONFIG="${libxml2_path}/${LIBXML2_CONFIG}" - fi - AC_MSG_RESULT([${LIBXML2_CONFIG}]) - LIBXML2_VERSION=`${LIBXML2_CONFIG} --version | sed 's/^[[^0-9]][[^[:space:]]][[^[:space:]]]*[[[:space:]]]*//'` - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(xml VERSION: $LIBXML2_VERSION); fi - LIBXML2_CFLAGS="`${LIBXML2_CONFIG} --cflags`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(xml CFLAGS: $LIBXML2_CFLAGS); fi - LIBXML2_LDADD="`${LIBXML2_CONFIG} --libs`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(xml LDADD: $LIBXML2_LDADD); fi - - AC_MSG_CHECKING([if libxml2 is at least v2.6.29]) - libxml2_min_ver=`echo 2.6.29 | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` - libxml2_ver=`echo ${LIBXML2_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` - if test "$libxml2_ver" -ge "$libxml2_min_ver"; then - AC_MSG_RESULT([yes, $LIBXML2_VERSION]) - else - AC_MSG_RESULT([no, $LIBXML2_VERSION]) - AC_MSG_ERROR([NOTE: libxml2 library must be at least 2.6.29]) - fi - -else - AC_MSG_RESULT([no]) -fi - -AC_SUBST(LIBXML2_CONFIG) -AC_SUBST(LIBXML2_VERSION) -AC_SUBST(LIBXML2_CFLAGS) -AC_SUBST(LIBXML2_CPPFLAGS) -AC_SUBST(LIBXML2_LDADD) -AC_SUBST(LIBXML2_LDFLAGS) - -if test -z "${LIBXML2_VERSION}"; then - AC_MSG_NOTICE([*** xml library not found.]) - ifelse([$2], , AC_MSG_ERROR([libxml2 is required]), $2) -else - AC_MSG_NOTICE([using libxml2 v${LIBXML2_VERSION}]) - ifelse([$1], , , $1) -fi -]) diff --git a/build/libgeoip.m4 b/build/libgeoip.m4 new file mode 100644 index 0000000000..74fb076da8 --- /dev/null +++ b/build/libgeoip.m4 @@ -0,0 +1,191 @@ +dnl Check for GEOIP Libraries +dnl CHECK_GEOIP(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) +dnl Sets: +dnl GEOIP_CFLAGS +dnl GEOIP_LDADD +dnl GEOIP_LDFLAGS +dnl GEOIP_LIBS +dnl GEOIP_VERSION + +AC_DEFUN([PROG_GEOIP], [ + + +# Needed if pkg-config will be used. +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + + +# Possible names for the geoip library/package (pkg-config) +GEOIP_POSSIBLE_LIB_NAMES="geoip2 geoip GeoIP" + +# Possible extensions for the library +GEOIP_POSSIBLE_EXTENSIONS="so la sl dll dylib" + +# Possible paths (if pkg-config was not found, proceed with the file lookup) +GEOIP_POSSIBLE_PATHS="/usr/local/libgeoip /usr/local/geoip /usr/local /opt/libgeoip /opt/geoip /opt /usr /opt/local/include /opt/local /usr/lib /usr/local/lib /usr/lib64 /usr" + +# Variables to be set by this very own script. +GEOIP_VERSION="" +GEOIP_CFLAGS="" +GEOIP_CPPFLAGS="" +GEOIP_LDADD="" +GEOIP_LDFLAGS="" + +AC_ARG_WITH( + geoip, + AS_HELP_STRING( + [--with-geoip=PATH], + [Path to GeoIP (including headers). Use 'no' to disable GeoIP support.] + ) +) + +# AS_HELP_STRING( +# [--without-geoip], +# [Complete dsiables GeoIP support] +# ) + + +if test "x${with_geoip}" == "xno"; then + AC_DEFINE(HAVE_GEOIP, 0, [Support for GeoIP was disabled by the utilization of --without-geoip or --with-geoip=no]) + AC_MSG_NOTICE([Support for GeoIP was disabled by the utilization of --without-geoip or --with-geoip=no]) + GEOIP_DISABLED=yes +else + if test "x${with_geoip}" == "xyes"; then + GEOIP_MANDATORY=yes + AC_MSG_NOTICE([GeoIP support was marked as mandatory by the utilization of --with-geoip=yes]) + fi +# for x in ${GEOIP_POSSIBLE_LIB_NAMES}; do +# CHECK_FOR_GEOIP_AT(${x}) +# if test -n "${GEOIP_VERSION}"; then +# break +# fi +# done + +# if test "x${with_geoip}" != "xyes" or test "x${with_geoip}" == "xyes"; then + if test "x${with_geoip}" == "x" || test "x${with_geoip}" == "xyes"; then + # Nothing about GeoIP was informed, using the pkg-config to figure things out. + if test -n "${PKG_CONFIG}"; then + GEOIP_PKG_NAME="" + for x in ${GEOIP_POSSIBLE_LIB_NAMES}; do + if ${PKG_CONFIG} --exists ${x}; then + GEOIP_PKG_NAME="$x" + break + fi + done + fi + AC_MSG_NOTICE([Nothing about GeoIP was informed during the configure phase. Trying to detect it on the platform...]) + if test -n "${GEOIP_PKG_NAME}"; then + # Package was found using the pkg-config scripts + GEOIP_VERSION="`${PKG_CONFIG} ${GEOIP_PKG_NAME} --modversion`" + GEOIP_CFLAGS="`${PKG_CONFIG} ${GEOIP_PKG_NAME} --cflags`" + GEOIP_LDADD="`${PKG_CONFIG} ${GEOIP_PKG_NAME} --libs-only-l`" + GEOIP_LDFLAGS="`${PKG_CONFIG} ${GEOIP_PKG_NAME} --libs-only-L --libs-only-other`" + GEOIP_DISPLAY="${GEOIP_LDADD}, ${GEOIP_CFLAGS}" + else + # If pkg-config did not find anything useful, go over file lookup. + for x in ${GEOIP_POSSIBLE_PATHS}; do + CHECK_FOR_GEOIP_AT(${x}) + if test -n "${GEOIP_VERSION}"; then + break + fi + done + fi + fi + if test "x${with_geoip}" != "x"; then + # An specific path was informed, lets check. + GEOIP_MANDATORY=yes + CHECK_FOR_GEOIP_AT(${with_geoip}) + fi +# fi +fi + +if test -z "${GEOIP_CFLAGS}"; then + if test -z "${GEOIP_MANDATORY}"; then + if test -z "${GEOIP_DISABLED}"; then + AC_MSG_NOTICE([GeoIP library was not found]) + GEOIP_FOUND=0 + else + GEOIP_FOUND=2 + fi + else + AC_MSG_ERROR([GeoIP was explicit requested but it was not found]) + GEOIP_FOUND=-1 + fi +else + GEOIP_FOUND=1 + AC_MSG_NOTICE([using GeoIP v${GEOIP_VERSION}]) + GEOIP_CFLAGS="-DWITH_GEOIP ${GEOIP_CFLAGS}" + AC_SUBST(GEOIP_VERSION) + AC_SUBST(GEOIP_LDADD) + AC_SUBST(GEOIP_LIBS) + AC_SUBST(GEOIP_LDFLAGS) + AC_SUBST(GEOIP_CFLAGS) + AC_SUBST(GEOIP_DISPLAY) +fi + + + +AC_SUBST(GEOIP_FOUND) + +]) # AC_DEFUN [PROG_GEOIP] + + +AC_DEFUN([CHECK_FOR_GEOIP_AT], [ + path=$1 + for y in ${GEOIP_POSSIBLE_EXTENSIONS}; do + for z in ${GEOIP_POSSIBLE_LIB_NAMES}; do + if test -e "${path}/${z}.${y}"; then + geoip_lib_path="${path}/" + geoip_lib_name="${z}" + geoip_lib_file="${geoip_lib_path}/${z}.${y}" + break + fi + if test -e "${path}/lib${z}.${y}"; then + geoip_lib_path="${path}/" + geoip_lib_name="${z}" + geoip_lib_file="${geoip_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/lib${z}.${y}"; then + geoip_lib_path="${path}/lib/" + geoip_lib_name="${z}" + geoip_lib_file="${geoip_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib64/lib${z}.${y}"; then + geoip_lib_path="${path}/lib64/" + geoip_lib_name="${z}" + geoip_lib_file="${geoip_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/x86_64-linux-gnu/lib${z}.${y}"; then + geoip_lib_path="${path}/lib/x86_64-linux-gnu/" + geoip_lib_name="${z}" + geoip_lib_file="${geoip_lib_path}/lib${z}.${y}" + break + fi + done + if test -n "$geoip_lib_path"; then + break + fi + done + if test -e "${path}/include/GeoIPCity.h"; then + geoip_inc_path="${path}/include" + elif test -e "${path}/GeoIPCity.h"; then + geoip_inc_path="${path}" + fi + + + if test -n "${geoip_inc_path}" -a -n "${geoip_lib_path}"; then + + AC_MSG_NOTICE([GeoIP headers found at: ${geoip_inc_path}]) + AC_MSG_NOTICE([GeoIP library found at: ${geoip_lib_file}]) + fi + + if test -n "${geoip_lib_path}" -a -n "${geoip_inc_path}"; then + # TODO: Compile a piece of code to check the version. + GEOIP_CFLAGS="-I${geoip_inc_path}" + GEOIP_LDADD="-l${geoip_lib_name}" + GEOIP_LDFLAGS="-L${geoip_lib_path}" + GEOIP_DISPLAY="${geoip_lib_file}, ${geoip_inc_path}" + fi +]) # AC_DEFUN [CHECK_FOR_GEOIP_AT] diff --git a/build/libmaxmind.m4 b/build/libmaxmind.m4 new file mode 100644 index 0000000000..c674ca57b4 --- /dev/null +++ b/build/libmaxmind.m4 @@ -0,0 +1,194 @@ +dnl Check for MAXMIND Libraries +dnl CHECK_MAXMIND(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) +dnl Sets: +dnl MAXMIND_CFLAGS +dnl MAXMIND_LDADD +dnl MAXMIND_LDFLAGS +dnl MAXMIND_LIBS +dnl MAXMIND_VERSION + +AC_DEFUN([PROG_MAXMIND], [ + + +# Needed if pkg-config will be used. +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + + +# Possible names for the maxmind library/package (pkg-config) +MAXMIND_POSSIBLE_LIB_NAMES="libmaxminddb maxminddb maxmind" + +# Possible extensions for the library +MAXMIND_POSSIBLE_EXTENSIONS="so la sl dll dylib" + +# Possible paths (if pkg-config was not found, proceed with the file lookup) +MAXMIND_POSSIBLE_PATHS="/usr/local/libmaxmind /usr/local/maxmind /usr/local /opt/libmaxmind /opt/maxmind /opt /usr /opt/local/include /opt/local /usr/lib /usr/local/lib /usr/lib64 /usr /usr/include/x86_64-linux-gnu/" + +# Variables to be set by this very own script. +MAXMIND_VERSION="" +MAXMIND_CFLAGS="" +MAXMIND_CPPFLAGS="" +MAXMIND_LDADD="" +MAXMIND_LDFLAGS="" + +AC_ARG_WITH( + maxmind, + AS_HELP_STRING( + [--with-maxmind=PATH], + [Path to MaxMind (including headers). Use 'no' to disable MaxMind support.] + ) +) + +# AS_HELP_STRING( +# [--without-maxmind], +# [Complete dsiables MaxMind support] +# ) + + +if test "x${with_maxmind}" == "xno"; then + AC_DEFINE(HAVE_MAXMIND, 0, [Support for MaxMind was disabled by the utilization of --without-maxmind or --with-maxmind=no]) + AC_MSG_NOTICE([Support for MaxMind was disabled by the utilization of --without-maxmind or --with-maxmind=no]) + MAXMIND_DISABLED=yes +else + if test "x${with_maxmind}" == "xyes"; then + MAXMIND_MANDATORY=yes + AC_MSG_NOTICE([MaxMind support was marked as mandatory by the utilization of --with-maxmind=yes]) + fi +# for x in ${MAXMIND_POSSIBLE_LIB_NAMES}; do +# CHECK_FOR_MAXMIND_AT(${x}) +# if test -n "${MAXMIND_VERSION}"; then +# break +# fi +# done + +# if test "x${with_maxmind}" != "xyes" or test "x${with_maxmind}" == "xyes"; then + if test "x${with_maxmind}" == "x" || test "x${with_maxmind}" == "xyes"; then + # Nothing about MaxMind was informed, using the pkg-config to figure things out. + if test -n "${PKG_CONFIG}"; then + MAXMIND_PKG_NAME="" + for x in ${MAXMIND_POSSIBLE_LIB_NAMES}; do + if ${PKG_CONFIG} --exists ${x}; then + MAXMIND_PKG_NAME="$x" + break + fi + done + fi + AC_MSG_NOTICE([Nothing about MaxMind was informed during the configure phase. Trying to detect it on the platform...]) + if test -n "${MAXMIND_PKG_NAME}"; then + # Package was found using the pkg-config scripts + MAXMIND_VERSION="`${PKG_CONFIG} ${MAXMIND_PKG_NAME} --modversion`" + MAXMIND_CFLAGS="`${PKG_CONFIG} ${MAXMIND_PKG_NAME} --cflags`" + MAXMIND_LDADD="`${PKG_CONFIG} ${MAXMIND_PKG_NAME} --libs-only-l`" + MAXMIND_LDFLAGS="`${PKG_CONFIG} ${MAXMIND_PKG_NAME} --libs-only-L --libs-only-other`" + MAXMIND_DISPLAY="${MAXMIND_LDADD}" + else + # If pkg-config did not find anything useful, go over file lookup. + for x in ${MAXMIND_POSSIBLE_PATHS}; do + CHECK_FOR_MAXMIND_AT(${x}) + if test -n "${MAXMIND_VERSION}"; then + break + fi + done + fi + fi + if test "x${with_maxmind}" != "x"; then + # An specific path was informed, lets check. + MAXMIND_MANDATORY=yes + CHECK_FOR_MAXMIND_AT(${with_maxmind}) + fi +# fi +fi + +if test -z "${MAXMIND_DISPLAY}"; then + if test -z "${MAXMIND_MANDATORY}"; then + if test -z "${MAXMIND_DISABLED}"; then + AC_MSG_NOTICE([MaxMind library was not found]) + MAXMIND_FOUND=0 + else + MAXMIND_FOUND=2 + fi + else + AC_MSG_ERROR([MaxMind was explicit requested but it was not found]) + MAXMIND_FOUND=-1 + fi +else + MAXMIND_FOUND=1 + AC_MSG_NOTICE([using MaxMind v${MAXMIND_VERSION}]) + MAXMIND_CFLAGS="-DWITH_MAXMIND ${MAXMIND_CFLAGS}" + if ! test "x$MAXMIND_CFLAGS" = "x"; then + MAXMIND_DISPLAY="${MAXMIND_DISPLAY}, ${MAXMIND_CFLAGS}" + fi + AC_SUBST(MAXMIND_VERSION) + AC_SUBST(MAXMIND_LDADD) + AC_SUBST(MAXMIND_LIBS) + AC_SUBST(MAXMIND_LDFLAGS) + AC_SUBST(MAXMIND_CFLAGS) + AC_SUBST(MAXMIND_DISPLAY) +fi + + + +AC_SUBST(MAXMIND_FOUND) + +]) # AC_DEFUN [PROG_MAXMIND] + + +AC_DEFUN([CHECK_FOR_MAXMIND_AT], [ + path=$1 + for y in ${MAXMIND_POSSIBLE_EXTENSIONS}; do + for z in ${MAXMIND_POSSIBLE_LIB_NAMES}; do + if test -e "${path}/${z}.${y}"; then + maxmind_lib_path="${path}/" + maxmind_lib_name="${z}" + maxmind_lib_file="${maxmind_lib_path}/${z}.${y}" + break + fi + if test -e "${path}/lib${z}.${y}"; then + maxmind_lib_path="${path}/" + maxmind_lib_name="${z}" + maxmind_lib_file="${maxmind_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/lib${z}.${y}"; then + maxmind_lib_path="${path}/lib/" + maxmind_lib_name="${z}" + maxmind_lib_file="${maxmind_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib64/lib${z}.${y}"; then + maxmind_lib_path="${path}/lib64/" + maxmind_lib_name="${z}" + maxmind_lib_file="${maxmind_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/x86_64-linux-gnu/lib${z}.${y}"; then + maxmind_lib_path="${path}/lib/x86_64-linux-gnu/" + maxmind_lib_name="${z}" + maxmind_lib_file="${maxmind_lib_path}/lib${z}.${y}" + break + fi + done + if test -n "$maxmind_lib_path"; then + break + fi + done + if test -e "${path}/include/maxminddb.h"; then + maxmind_inc_path="${path}/include" + elif test -e "${path}/maxminddb.h"; then + maxmind_inc_path="${path}" + fi + + + if test -n "${maxmind_inc_path}" -a -n "${maxmind_lib_path}"; then + + AC_MSG_NOTICE([MaxMind headers found at: ${maxmind_inc_path}]) + AC_MSG_NOTICE([MaxMind library found at: ${maxmind_lib_file}]) + fi + + if test -n "${maxmind_lib_path}" -a -n "${maxmind_inc_path}"; then + # TODO: Compile a piece of code to check the version. + MAXMIND_CFLAGS="-I${maxmind_inc_path}" + MAXMIND_LDADD="-l${maxmind_lib_name}" + MAXMIND_LDFLAGS="-L${maxmind_lib_path}" + MAXMIND_DISPLAY="${maxmind_lib_file}, ${maxmind_inc_path}" + fi +]) # AC_DEFUN [CHECK_FOR_MAXMIND_AT] diff --git a/build/libxml.m4 b/build/libxml.m4 new file mode 100644 index 0000000000..ba8c50acba --- /dev/null +++ b/build/libxml.m4 @@ -0,0 +1,111 @@ +dnl Check for LIBXML2 Libraries +dnl CHECK_LIBXML2(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) +dnl Sets: +dnl LIBXML2_CFLAGS +dnl LIBXML2_LIBS + +LIBXML2_CONFIG="" +LIBXML2_VERSION="" +LIBXML2_CFLAGS="" +LIBXML2_CPPFLAGS="" +LIBXML2_LDADD="" +LIBXML2_LDFLAGS="" + +AC_DEFUN([CHECK_LIBXML2], [ + +AC_ARG_WITH( + libxml, + [AC_HELP_STRING([--with-libxml=PATH],[Path to libxml2 prefix or config script])], + [test_paths="${with_libxml}"], + [test_paths="/usr/local/libxml2 /usr/local/xml2 /usr/local/xml /usr/local /opt/libxml2 /opt/libxml /opt/xml2 /opt/xml /opt /usr"]) + +AC_MSG_CHECKING([for libxml2 config script]) + +for x in ${test_paths}; do + dnl # Determine if the script was specified and use it directly + if test ! -d "$x" -a -e "$x"; then + LIBXML2_CONFIG=$x + libxml2_path="no" + break + fi + + dnl # Try known config script names/locations + for LIBXML2_CONFIG in xml2-config xml-2-config xml-config; do + if test -e "${x}/bin/${LIBXML2_CONFIG}"; then + libxml2_path="${x}/bin" + break + elif test -e "${x}/${LIBXML2_CONFIG}"; then + libxml2_path="${x}" + break + else + libxml2_path="" + fi + done + if test -n "$libxml2_path"; then + break + fi +done + +if test -n "${libxml2_path}"; then + if test "${libxml2_path}" != "no"; then + LIBXML2_CONFIG="${libxml2_path}/${LIBXML2_CONFIG}" + fi + AC_MSG_RESULT([${LIBXML2_CONFIG}]) + LIBXML2_VERSION=`${LIBXML2_CONFIG} --version | sed 's/^[[^0-9]][[^[:space:]]][[^[:space:]]]*[[[:space:]]]*//'` + if test ! -z "${LIBXML2_VERSION}"; then AC_MSG_NOTICE(xml VERSION: $LIBXML2_VERSION); fi + LIBXML2_CFLAGS="`${LIBXML2_CONFIG} --cflags` -DWITH_LIBXML2" + if test ! -z "${LIBXML2_CFLAGS}"; then AC_MSG_NOTICE(xml CFLAGS: $LIBXML2_CFLAGS); fi + LIBXML2_LDADD="`${LIBXML2_CONFIG} --libs`" + if test ! -z "${LIBXML2_LDADD}"; then AC_MSG_NOTICE(xml LDADD: $LIBXML2_LDADD); fi + + AC_MSG_CHECKING([if libxml2 is at least v2.6.29]) + libxml2_min_ver=`echo 2.6.29 | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` + libxml2_ver=`echo ${LIBXML2_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` + if test "$libxml2_ver" -ge "$libxml2_min_ver"; then + AC_MSG_RESULT([yes, $LIBXML2_VERSION]) + else + AC_MSG_RESULT([no, $LIBXML2_VERSION]) + AC_MSG_ERROR([NOTE: libxml2 library must be at least 2.6.29]) + fi + +else + AC_MSG_RESULT([no]) +fi + +AC_SUBST(LIBXML2_CONFIG) +AC_SUBST(LIBXML2_VERSION) +AC_SUBST(LIBXML2_CFLAGS) +AC_SUBST(LIBXML2_CPPFLAGS) +AC_SUBST(LIBXML2_LDADD) +AC_SUBST(LIBXML2_LDFLAGS) + + +if test "x${with_libxml}" == "xno"; then + LIBXML2_DISABLED=yes +else + if test "x${with_libxml}" != "x"; then + LIBXML2_MANDATORY=yes + fi +fi + +if test -z "${LIBXML2_VERSION}"; then + AC_MSG_NOTICE([*** libxml2 library not found.]) + if test -z "${LIBXML2_MANDATORY}"; then + if test -z "${LIBXML2_DISABLED}"; then + LIBXML2_FOUND=0 + else + LIBXML2_FOUND=2 + fi + else + AC_MSG_ERROR([Libxml2 was explicitly referenced but it was not found]) + LIBXML2_FOUND=-1 + fi +else + LIBXML2_FOUND=1 + AC_MSG_NOTICE([using libxml2 v${LIBXML2_VERSION}]) + LIBXML2_DISPLAY="${LIBXML2_LDADD}, ${LIBXML2_CFLAGS}" +fi + +AC_SUBST(LIBXML2_FOUND) +AC_SUBST(LIBXML2_DISPLAY) +]) diff --git a/build/lmdb.m4 b/build/lmdb.m4 new file mode 100644 index 0000000000..f413226c83 --- /dev/null +++ b/build/lmdb.m4 @@ -0,0 +1,187 @@ +dnl Check for LMDB Libraries +dnl CHECK_LMDB(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) + +AC_DEFUN([PROG_LMDB], [ + +# Needed if pkg-config will be used.LMDB +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + + +# Possible names for the lmdb library/package (pkg-config) +LMDB_POSSIBLE_LIB_NAMES="lmdb" + +# Possible extensions for the library +LMDB_POSSIBLE_EXTENSIONS="so so0 la sl dll dylib so.0.0.0" + +# Possible paths (if pkg-config was not found, proceed with the file lookup) +LMDB_POSSIBLE_PATHS="/usr/lib /usr/local/lib /usr/local/liblmdb /usr/local/lmdb /usr/local /opt/liblmdb /opt/lmdb /opt /usr /usr/lib64 /opt/local" + +# Variables to be set by this very own script. +LMDB_VERSION="" +LMDB_CFLAGS="" +LMDB_CPPFLAGS="" +LMDB_LDADD="" +LMDB_LDFLAGS="" + +AC_ARG_WITH( + lmdb, + AC_HELP_STRING( + [--with-lmdb=PATH], + [Path to lmdb prefix or config script] + ) +) + +if test "x${with_lmdb}" == "xno"; then + AC_DEFINE(HAVE_LMDB, 0, [Support for LMDB was disabled by the utilization of --without-lmdb or --with-lmdb=no]) + AC_MSG_NOTICE([Support for LMDB was disabled by the utilization of --without-lmdb or --with-lmdb=no]) + LMDB_DISABLED=yes +else + if test "x${with_lmdb}" == "xyes"; then + LMDB_MANDATORY=yes + AC_MSG_NOTICE([LMDB support was marked as mandatory by the utilization of --with-lmdb=yes]) + fi +# for x in ${LMDB_POSSIBLE_LIB_NAMES}; do +# CHECK_FOR_LMDB_AT(${x}) +# if test -n "${LMDB_VERSION}"; then +# break +# fi +# done + +# if test "x${with_lmdb}" != "xyes" or test "x${with_lmdb}" == "xyes"; then + if test "x${with_lmdb}" == "x" || test "x${with_lmdb}" == "xyes"; then + # Nothing about LMDB was informed, using the pkg-config to figure things out. + if test -n "${PKG_CONFIG}"; then + LMDB_PKG_NAME="" + for x in ${LMDB_POSSIBLE_LIB_NAMES}; do + if ${PKG_CONFIG} --exists ${x}; then + LMDB_PKG_NAME="$x" + break + fi + done + fi + AC_MSG_NOTICE([Nothing about LMDB was informed during the configure phase. Trying to detect it on the platform...]) + if test -n "${LMDB_PKG_NAME}"; then + # Package was found using the pkg-config scripts + LMDB_VERSION="`${PKG_CONFIG} ${LMDB_PKG_NAME} --modversion`" + LMDB_CFLAGS="`${PKG_CONFIG} ${LMDB_PKG_NAME} --cflags`" + LMDB_LDADD="`${PKG_CONFIG} ${LMDB_PKG_NAME} --libs-only-l`" + LMDB_LDFLAGS="`${PKG_CONFIG} ${LMDB_PKG_NAME} --libs-only-L --libs-only-other`" + LMDB_DISPLAY="${LMDB_LDADD}, ${LMDB_CFLAGS}" + else + # If pkg-config did not find anything useful, go over file lookup. + for x in ${LMDB_POSSIBLE_PATHS}; do + CHECK_FOR_LMDB_AT(${x}) + if test -n "${LMDB_VERSION}"; then + break + fi + done + fi + fi + if test "x${with_lmdb}" != "x"; then + # An specific path was informed, lets check. + LMDB_MANDATORY=yes + CHECK_FOR_LMDB_AT(${with_lmdb}) + fi +# fi +fi + +if test -z "${LMDB_LDADD}"; then + if test -z "${LMDB_MANDATORY}"; then + if test -z "${LMDB_DISABLED}"; then + AC_MSG_NOTICE([LMDB library was not found]) + LMDB_FOUND=0 + else + LMDB_FOUND=2 + fi + else + AC_MSG_ERROR([LMDB was explicitly referenced but it was not found]) + LMDB_FOUND=-1 + fi +else + if test -z "${LMDB_MANDATORY}"; then + LMDB_FOUND=2 + AC_MSG_NOTICE([LMDB is disabled by default.]) + else + LMDB_FOUND=1 + AC_MSG_NOTICE([using LMDB v${LMDB_VERSION}]) + LMDB_CFLAGS="-DWITH_LMDB ${LMDB_CFLAGS}" + LMDB_DISPLAY="${LMDB_LDADD}, ${LMDB_CFLAGS}" + AC_SUBST(LMDB_VERSION) + AC_SUBST(LMDB_LDADD) + AC_SUBST(LMDB_LIBS) + AC_SUBST(LMDB_LDFLAGS) + AC_SUBST(LMDB_CFLAGS) + AC_SUBST(LMDB_DISPLAY) + fi +fi + + +AC_SUBST(LMDB_FOUND) + +]) # AC_DEFUN [PROG_LMDB] + + +AC_DEFUN([CHECK_FOR_LMDB_AT], [ + path=$1 + echo "*** LOOKING AT PATH: " ${path} + for y in ${LMDB_POSSIBLE_EXTENSIONS}; do + for z in ${LMDB_POSSIBLE_LIB_NAMES}; do + if test -e "${path}/${z}.${y}"; then + lmdb_lib_path="${path}/" + lmdb_lib_name="${z}" + lmdb_lib_file="${lmdb_lib_path}/${z}.${y}" + break + fi + if test -e "${path}/lib${z}.${y}"; then + lmdb_lib_path="${path}/" + lmdb_lib_name="${z}" + lmdb_lib_file="${lmdb_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/lib${z}.${y}"; then + lmdb_lib_path="${path}/lib/" + lmdb_lib_name="${z}" + lmdb_lib_file="${lmdb_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/x86_64-linux-gnu/lib${z}.${y}"; then + lmdb_lib_path="${path}/lib/x86_64-linux-gnu/" + lmdb_lib_name="${z}" + lmdb_lib_file="${lmdb_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/i386-linux-gnu/lib${z}.${y}"; then + lmdb_lib_path="${path}/lib/i386-linux-gnu/" + lmdb_lib_name="${z}" + lmdb_lib_file="${lmdb_lib_path}/lib${z}.${y}" + break + fi + done + if test -n "$lmdb_lib_path"; then + break + fi + done + if test -e "${path}/include/lmdb.h"; then + lmdb_inc_path="${path}/include" + elif test -e "${path}/lmdb.h"; then + lmdb_inc_path="${path}" + elif test -e "${path}/include/lmdb/lmdb.h"; then + lmdb_inc_path="${path}/include" + fi + + if test -n "${lmdb_lib_path}"; then + AC_MSG_NOTICE([LMDB library found at: ${lmdb_lib_file}]) + fi + + if test -n "${lmdb_inc_path}"; then + AC_MSG_NOTICE([LMDB headers found at: ${lmdb_inc_path}]) + fi + + if test -n "${lmdb_lib_path}" -a -n "${lmdb_inc_path}"; then + # TODO: Compile a piece of code to check the version. + LMDB_CFLAGS="-I${lmdb_inc_path}" + LMDB_LDADD="-l${lmdb_lib_name}" + LMDB_LDFLAGS="-L${lmdb_lib_path}" + LMDB_DISPLAY="${lmdb_lib_file}, ${lmdb_inc_path}" + fi +]) # AC_DEFUN [CHECK_FOR_LMDB_AT] diff --git a/build/lua.m4 b/build/lua.m4 new file mode 100644 index 0000000000..c93aea739d --- /dev/null +++ b/build/lua.m4 @@ -0,0 +1,251 @@ +dnl Check for LUA Libraries +dnl CHECK_LUA(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) + + +AC_DEFUN([CHECK_LUA], +[dnl + +# Possible names for the lua library/package (pkg-config) +LUA_POSSIBLE_LIB_NAMES="lua54 lua5.4 lua-5.4 lua53 lua5.3 lua-5.3 lua52 lua5.2 lua-5.2 lua51 lua5.1 lua-5.1 lua" + +# Possible extensions for the library +LUA_POSSIBLE_EXTENSIONS="so so0 la sl dll dylib so.0.0.0" + +# Possible paths (if pkg-config was not found, proceed with the file lookup) +LUA_POSSIBLE_PATHS="/usr/lib /usr/local/lib /usr/local/lib64 /usr/local/lua /usr/local/liblua /usr/local /opt /usr /usr/lib64 /opt/local" + +# Variables to be set by this very own script. +LUA_CFLAGS="" +LUA_LDFLAGS="" +LUA_LDADD="" +LUA_DISPLAY="" + +AC_ARG_WITH( + lua, + AC_HELP_STRING( + [--with-lua=PATH], + [Path to lua prefix] + ) +) + + +if test "x${with_lua}" == "xno"; then + AC_DEFINE(HAVE_LUA, 0, [Support for LUA was disabled by the utilization of --without-lua or --with-lua=no]) + AC_MSG_NOTICE([Support for LUA was disabled by the utilization of --without-lua or --with-lua=no]) + LUA_DISABLED=yes +else + if test "x${with_lua}" == "xyes"; then + LUA_MANDATORY=yes + AC_MSG_NOTICE([LUA support was marked as mandatory by the utilization of --with-lua=yes]) + else + LUA_MANDATORY=no + fi + for x in ${LUA_POSSIBLE_PATHS}; do + CHECK_FOR_LUA_AT(${x}) + if test -n "${LUA_CFLAGS}"; then + break + fi + done + if test -z "${LUA_CFLAGS}"; then + #Trying to figure out the version using pkg-config... + if test -n "${PKG_CONFIG}"; then + LUA_PKG_NAME="" + for x in ${LUA_POSSIBLE_LIB_NAMES}; do + if ${PKG_CONFIG} --exists ${x}; then + LUA_PKG_NAME="$x" + LUA_PKG_VERSION="`${PKG_CONFIG} ${LUA_PKG_NAME} --modversion`" + break + fi + done + fi + if test -n "${LUA_PKG_NAME}"; then + # Package was found using the pkg-config scripts + LUA_PKG_VERSION="`${PKG_CONFIG} ${LUA_PKG_NAME} --modversion`" + LUA_CFLAGS="`${PKG_CONFIG} ${LUA_PKG_NAME} --cflags`" + LUA_LDADD="`${PKG_CONFIG} ${LUA_PKG_NAME} --libs-only-l`" + LUA_LDFLAGS="`${PKG_CONFIG} ${LUA_PKG_NAME} --libs-only-L --libs-only-other`" + LUA_DISPLAY="${LUA_LDADD}, ${LUA_CFLAGS}" + case $LUA_PKG_VERSION in + (5.1*) LUA_CFLAGS="-DWITH_LUA_5_1 ${LUA_CFLAGS}" ; lua_5_1=1 ;; + (5.2*) LUA_CFLAGS="-DWITH_LUA_5_2 ${LUA_CFLAGS}" ; lua_5_2=1 ;; + (5.3*) LUA_CFLAGS="-DWITH_LUA_5_3 ${LUA_CFLAGS}" ; lua_5_3=1 ;; + (5.4*) LUA_CFLAGS="-DWITH_LUA_5_4 ${LUA_CFLAGS}" ; lua_5_4=1 ;; + (2.0*) LUA_CFLAGS="-DWITH_LUA_5_1 ${LUA_CFLAGS}" ; lua_5_1=1 ;; + (2.1*) LUA_CFLAGS="-DWITH_LUA_5_1 -DWITH_LUA_JIT_2_1 ${LUA_CFLAGS}" ; lua_5_1=1 ;; + esac + AC_MSG_NOTICE([LUA pkg-config version: ${LUA_PKG_VERSION}]) + fi + fi +fi + + +if test -z "${LUA_CFLAGS}"; then + if test -z "${LUA_MANDATORY}" || test "x${LUA_MANDATORY}" == "xno"; then + if test -z "${LUA_DISABLED}"; then + AC_MSG_NOTICE([LUA library was not found]) + LUA_FOUND=0 + else + LUA_FOUND=2 + fi + else + AC_MSG_ERROR([LUA was explicitly referenced but it was not found]) + LUA_FOUND=-1 + fi +else + if test -z "${LUA_MANDATORY}" || test "x${LUA_MANDATORY}" == "xno"; then + LUA_FOUND=1 + AC_MSG_NOTICE([using LUA ${LUA_LDADD}]) + LUA_CFLAGS="-DWITH_LUA ${LUA_CFLAGS}" + LUA_DISPLAY="${LUA_LDADD} ${LUA_LDFLAGS}, ${LUA_CFLAGS}" + AC_SUBST(LUA_LDFLAGS) + AC_SUBST(LUA_LDADD) + AC_SUBST(LUA_CFLAGS) + AC_SUBST(LUA_DISPLAY) + else + LUA_FOUND=1 + AC_MSG_NOTICE([using LUA ${LUA_LDADD}]) + LUA_CFLAGS="-DWITH_LUA ${LUA_CFLAGS}" + LUA_DISPLAY="${LUA_LDADD} ${LUA_LDFLAGS}, ${LUA_CFLAGS}" + AC_SUBST(LUA_LDFLAGS) + AC_SUBST(LUA_LDADD) + AC_SUBST(LUA_CFLAGS) + AC_SUBST(LUA_DISPLAY) + fi +fi + +AC_SUBST(LUA_FOUND) + +]) # AC_DEFUN [CHECK_LUA] + + +AC_DEFUN([CHECK_FOR_LUA_AT], [ + path=$1 + echo "*** LOOKING AT PATH: " ${path} + for y in ${LUA_POSSIBLE_EXTENSIONS}; do + for z in ${LUA_POSSIBLE_LIB_NAMES}; do + if test -e "${path}/${z}.${y}"; then + lua_lib_path="${path}/" + lua_lib_name="${z}" + lua_lib_file="${lua_lib_path}/${z}.${y}" + break + fi + if test -e "${path}/lib${z}.${y}"; then + lua_lib_path="${path}/" + lua_lib_name="${z}" + lua_lib_file="${lua_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/lib${z}.${y}"; then + lua_lib_path="${path}/lib/" + lua_lib_name="${z}" + lua_lib_file="${lua_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/x86_64-linux-gnu/lib${z}.${y}"; then + lua_lib_path="${path}/lib/x86_64-linux-gnu/" + lua_lib_name="${z}" + lua_lib_file="${lua_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/i386-linux-gnu/lib${z}.${y}"; then + lua_lib_path="${path}/lib/i386-linux-gnu/" + lua_lib_name="${z}" + lua_lib_file="${lua_lib_path}/lib${z}.${y}" + break + fi + done + if test -n "$lua_lib_path"; then + break + fi + done + if test -e "${path}/include/lua.h"; then + lua_inc_path="${path}/include" + elif test -e "${path}/lua.h"; then + lua_inc_path="${path}" + elif test -e "${path}/include/lua/lua.h"; then + lua_inc_path="${path}/include/lua" + elif test -e "${path}/include/lua5.4/lua.h"; then + lua_inc_path="${path}/include/lua5.4" + LUA_VERSION=504 + elif test -e "${path}/include/lua5.3/lua.h"; then + lua_inc_path="${path}/include/lua5.3" + LUA_VERSION=503 + elif test -e "${path}/include/lua5.2/lua.h"; then + lua_inc_path="${path}/include/lua5.2" + LUA_VERSION=502 + elif test -e "${path}/include/lua5.1/lua.h"; then + lua_inc_path="${path}/include/lua5.1" + LUA_VERSION=501 + elif test -e "${path}/include/luajit-2.0/lua.h"; then + lua_inc_path="${path}/include/luajit-2.0" + LUA_VERSION=501 + fi + + if test -n "${lua_lib_path}"; then + AC_MSG_NOTICE([LUA library found at: ${lua_lib_file}]) + fi + + if test -n "${lua_inc_path}"; then + AC_MSG_NOTICE([LUA headers found at: ${lua_inc_path}]) + fi + if test -n "${lua_lib_path}" -a -n "${lua_inc_path}"; then + LUA_CFLAGS="-I${lua_inc_path}" + LUA_LDADD="-l${lua_lib_name}" + LUA_LDFLAGS="-L${lua_lib_path}" + LUA_DISPLAY="${lua_lib_file}, ${lua_inc_path}" + + # Double checking version from lua.h... + AC_TRY_COMPILE([ #include > ], + [ #if (LUA_VERSION_NUM < 502) + return 0; + #else + #error Lua 5.1 not detected + #endif ], + [ LUA_VERSION=501 ], [ lua_5_1=0 ] + ) + + AC_TRY_COMPILE([ #include ], + [ #if (LUA_VERSION_NUM == 502) + return 0; + #else + #error Lua 5.2 not detected + #endif ], + [ LUA_VERSION=502 ], [ lua_5_2=0 ] + ) + AC_TRY_COMPILE([ #include ], + [ #if (LUA_VERSION_NUM == 504) + return 0; + #else + #error Lua 5.4 not detected + #endif ], + [ LUA_VERSION=504 ], [ lua_5_4=0 ] + ) + + if test -z "${LUA_VERSION}" ; then + # As a last resort, try to find LUA version from $lua_inc_path + while read -r line + do + case "$line" in + (\#define\ LUA_VERSION_NUM*501*) LUA_VERSION=501 ;; + (\#define\ LUA_VERSION_NUM*502*) LUA_VERSION=502 ;; + (\#define\ LUA_VERSION_NUM*503*) LUA_VERSION=503 ;; + (\#define\ LUA_VERSION_NUM*504*) LUA_VERSION=504 + esac + done <"${lua_inc_path}/lua.h" + AC_MSG_NOTICE([LUA_VERSION is ${LUA_VERSION} found at: ${lua_inc_path}]) + else + AC_MSG_NOTICE([LUA version from includes: ${LUA_VERSION}]) + fi + + case $LUA_VERSION in + (501) LUA_CFLAGS="-DWITH_LUA_5_1 ${LUA_CFLAGS}" ; lua_5_1=1 ;; + (502) LUA_CFLAGS="-DWITH_LUA_5_2 ${LUA_CFLAGS}" ; lua_5_2=1 ;; + (503) LUA_CFLAGS="-DWITH_LUA_5_3 ${LUA_CFLAGS}" ; lua_5_3=1 ;; + (504) LUA_CFLAGS="-DWITH_LUA_5_4 ${LUA_CFLAGS}" ; lua_5_4=1 ;; + esac + fi + +]) # AC_DEFUN [CHECK_FOR_LUA_AT] + + + diff --git a/build/pcre.m4 b/build/pcre.m4 new file mode 100644 index 0000000000..0de3c9c994 --- /dev/null +++ b/build/pcre.m4 @@ -0,0 +1,109 @@ +dnl Check for PCRE Libraries +dnl CHECK_PCRE(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) +dnl Sets: +dnl PCRE_CFLAGS +dnl PCRE_LIBS + +PCRE_CONFIG="" +PCRE_VERSION="" +PCRE_CPPFLAGS="" +PCRE_CFLAGS="" +PCRE_LDFLAGS="" +PCRE_LDADD="" +PCRE_LD_PATH="" + +AC_DEFUN([CHECK_PCRE], +[dnl + +AC_ARG_WITH( + pcre, + [AC_HELP_STRING([--with-pcre=PATH],[Path to pcre prefix or config script])], + [test_paths="${with_pcre}"], + [test_paths="/usr/local/libpcre /usr/local/pcre /usr/local /opt/libpcre /opt/pcre /opt /usr /opt/local"]) + +AC_MSG_CHECKING([for libpcre config script]) + +for x in ${test_paths}; do + dnl # Determine if the script was specified and use it directly + if test ! -d "$x" -a -e "$x"; then + PCRE_CONFIG=$x + pcre_path="no" + break + fi + + dnl # Try known config script names/locations + for PCRE_CONFIG in pcre-config; do + if test -e "${x}/bin/${PCRE_CONFIG}"; then + pcre_path="${x}/bin" + break + elif test -e "${x}/${PCRE_CONFIG}"; then + pcre_path="${x}" + break + else + pcre_path="" + fi + done + if test -n "$pcre_path"; then + break + fi +done + +if test -n "${pcre_path}"; then + if test "${pcre_path}" != "no"; then + PCRE_CONFIG="${pcre_path}/${PCRE_CONFIG}" + fi + AC_MSG_RESULT([${PCRE_CONFIG}]) + PCRE_VERSION="`${PCRE_CONFIG} --version`" + if test ! -z "${PCRE_VERSION}"; then AC_MSG_NOTICE(pcre VERSION: $PCRE_VERSION); fi + PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" + if test ! -z "${PCRE_CFLAGS}"; then AC_MSG_NOTICE(pcre CFLAGS: $PCRE_CFLAGS); fi + PCRE_LDADD="`${PCRE_CONFIG} --libs`" + if test ! -z "${PCRE_LDADD}"; then AC_MSG_NOTICE(pcre LDADD: $PCRE_LDADD); fi + PCRE_LD_PATH="/`${PCRE_CONFIG} --libs | cut -d'/' -f2,3,4,5,6 | cut -d ' ' -f1`" + if test ! -z "${PCRE_LD_PATH}"; then AC_MSG_NOTICE(pcre PCRE_LD_PATH: $PCRE_LD_PATH); fi +else + AC_MSG_RESULT([no]) +fi + +if test -n "${PCRE_VERSION}"; then + AC_MSG_CHECKING(for PCRE JIT) + save_CFLAGS=$CFLAGS + save_LDFLAGS=$LDFLAGS + CFLAGS="${PCRE_CFLAGS} ${CFLAGS}" + LDFLAGS="${LDFLAGS} ${PCRE_LDADD}" + AC_TRY_COMPILE([ #include + #include ], + [ int jit = 0; + pcre_free_study(NULL); + pcre_config(PCRE_CONFIG_JIT, &jit); + if (jit != 1) return 1; ], + [ pcre_jit_available=yes ], [:] + ) + + if test "x$pcre_jit_available" = "xyes"; then + AC_MSG_RESULT(yes) + PCRE_CFLAGS="${PCRE_CFLAGS} -DPCRE_HAVE_JIT" + else + AC_MSG_RESULT(no) + fi + CFLAGS=$save_CFLAGS + LDFLAGS=$save_$LDFLAGS +fi + +AC_SUBST(PCRE_CONFIG) +AC_SUBST(PCRE_VERSION) +AC_SUBST(PCRE_CPPFLAGS) +AC_SUBST(PCRE_CFLAGS) +AC_SUBST(PCRE_LDFLAGS) +AC_SUBST(PCRE_LDADD) +AC_SUBST(PCRE_LD_PATH) + +if test -z "${PCRE_VERSION}"; then + AC_MSG_NOTICE([*** pcre library not found.]) + ifelse([$2], , AC_MSG_ERROR([pcre library is required]), $2) +else + AC_MSG_NOTICE([using pcre v${PCRE_VERSION}]) + ifelse([$1], , , $1) + PCRE_LDADD="${PCRE_LDADD} -lpcre" +fi +]) diff --git a/build/release.sh b/build/release.sh new file mode 100755 index 0000000000..a182067b3f --- /dev/null +++ b/build/release.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +git clean -xfdi +git submodule foreach --recursive git clean -xfdi + +VERSION=`git describe --tags` +DIR_NAME="modsecurity-$VERSION" +TAR_NAME="modsecurity-$VERSION.tar.gz" + +MY_DIR=${PWD##*/} +./build.sh + +cd .. +tar --transform "s/^$MY_DIR/$DIR_NAME/" -cvzf $TAR_NAME --exclude .git $MY_DIR + +sha256sum $TAR_NAME > $TAR_NAME.sha256 +gpg --detach-sign -a $TAR_NAME + +cd - +echo $TAR_NAME ": done." + diff --git a/build/ssdeep.m4 b/build/ssdeep.m4 new file mode 100644 index 0000000000..ec9747fbca --- /dev/null +++ b/build/ssdeep.m4 @@ -0,0 +1,147 @@ +dnl Check for SSDEEP Libraries +dnl CHECK_SSDEEP(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) + + +AC_DEFUN([CHECK_SSDEEP], +[dnl + +# Possible names for the ssdeep library/package (pkg-config) +SSDEEP_POSSIBLE_LIB_NAMES="fuzzy" + +# Possible extensions for the library +SSDEEP_POSSIBLE_EXTENSIONS="so so0 la sl dll dylib so.0.0.0" + +# Possible paths (if pkg-config was not found, proceed with the file lookup) +SSDEEP_POSSIBLE_PATHS="/usr/lib /usr/local/lib /usr/local/fuzzy /usr/local/libfuzzy /usr/local /opt /usr /usr/lib64 /opt/local" + +# Variables to be set by this very own script. +SSDEEP_CFLAGS="" +SSDEEP_LDFLAGS="" +SSDEEP_LDADD="" +SSDEEP_DISPLAY="" + +AC_ARG_WITH( + ssdeep, + AC_HELP_STRING( + [--with-ssdeep=PATH], + [Path to ssdeep prefix] + ) +) + + +if test "x${with_ssdeep}" == "xno"; then + AC_DEFINE(HAVE_SSDEEP, 0, [Support for SSDEEP was disabled by the utilization of --without-ssdeep or --with-ssdeep=no]) + AC_MSG_NOTICE([Support for SSDEEP was disabled by the utilization of --without-ssdeep or --with-ssdeep=no]) + SSDEEP_DISABLED=yes +else + if test "x${with_ssdeep}" == "xyes"; then + SSDEEP_MANDATORY=yes + AC_MSG_NOTICE([SSDEEP support was marked as mandatory by the utilization of --with-ssdeep=yes]) + else + SSDEEP_MANDATORY=no + fi + for x in ${SSDEEP_POSSIBLE_PATHS}; do + CHECK_FOR_SSDEEP_AT(${x}) + if test -n "${SSDEEP_CFLAGS}"; then + break + fi + done +fi + + +if test -z "${SSDEEP_CFLAGS}"; then + if test -z "${SSDEEP_MANDATORY}" || test "x${SSDEEP_MANDATORY}" == "xno"; then + if test -z "${SSDEEP_DISABLED}"; then + AC_MSG_NOTICE([SSDEEP library was not found]) + SSDEEP_FOUND=0 + else + SSDEEP_FOUND=2 + fi + else + AC_MSG_ERROR([SSDEEP was explicitly referenced but it was not found]) + SSDEEP_FOUND=-1 + fi +else + SSDEEP_FOUND=1 + AC_MSG_NOTICE([using SSDEEP v${SSDEEP_VERSION}]) + SSDEEP_CFLAGS="-DWITH_SSDEEP ${SSDEEP_CFLAGS}" + SSDEEP_DISPLAY="${SSDEEP_LDADD} ${SSDEEP_LDFLAGS}, ${SSDEEP_CFLAGS}" + AC_SUBST(SSDEEP_LDFLAGS) + AC_SUBST(SSDEEP_LDADD) + AC_SUBST(SSDEEP_CFLAGS) + AC_SUBST(SSDEEP_DISPLAY) +fi + + +AC_SUBST(SSDEEP_FOUND) + +]) # AC_DEFUN [CHECK_SSDEEP] + + +AC_DEFUN([CHECK_FOR_SSDEEP_AT], [ + path=$1 + echo "*** LOOKING AT PATH: " ${path} + for y in ${SSDEEP_POSSIBLE_EXTENSIONS}; do + for z in ${SSDEEP_POSSIBLE_LIB_NAMES}; do + if test -e "${path}/${z}.${y}"; then + ssdeep_lib_path="${path}/" + ssdeep_lib_name="${z}" + ssdeep_lib_file="${ssdeep_lib_path}/${z}.${y}" + break + fi + if test -e "${path}/lib${z}.${y}"; then + ssdeep_lib_path="${path}/" + ssdeep_lib_name="${z}" + ssdeep_lib_file="${ssdeep_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/lib${z}.${y}"; then + ssdeep_lib_path="${path}/lib/" + ssdeep_lib_name="${z}" + ssdeep_lib_file="${ssdeep_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/x86_64-linux-gnu/lib${z}.${y}"; then + ssdeep_lib_path="${path}/lib/x86_64-linux-gnu/" + ssdeep_lib_name="${z}" + ssdeep_lib_file="${ssdeep_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/i386-linux-gnu/lib${z}.${y}"; then + ssdeep_lib_path="${path}/lib/i386-linux-gnu/" + ssdeep_lib_name="${z}" + ssdeep_lib_file="${ssdeep_lib_path}/lib${z}.${y}" + break + fi + done + if test -n "$ssdeep_lib_path"; then + break + fi + done + if test -e "${path}/include/fuzzy.h"; then + ssdeep_inc_path="${path}/include" + elif test -e "${path}/fuzzy.h"; then + ssdeep_inc_path="${path}" + elif test -e "${path}/include/fuzzy/fuzzy.h"; then + ssdeep_inc_path="${path}/include" + fi + + if test -n "${ssdeep_lib_path}"; then + AC_MSG_NOTICE([SSDEEP library found at: ${ssdeep_lib_file}]) + fi + + if test -n "${ssdeep_inc_path}"; then + AC_MSG_NOTICE([SSDEEP headers found at: ${ssdeep_inc_path}]) + fi + + if test -n "${ssdeep_lib_path}" -a -n "${ssdeep_inc_path}"; then + # TODO: Compile a piece of code to check the version. + SSDEEP_CFLAGS="-I${ssdeep_inc_path}" + SSDEEP_LDADD="-l${ssdeep_lib_name}" + SSDEEP_LDFLAGS="-L${ssdeep_lib_path}" + SSDEEP_DISPLAY="${ssdeep_lib_file}, ${ssdeep_inc_path}" + fi +]) # AC_DEFUN [CHECK_FOR_SSDEEP_AT] + + + diff --git a/build/yajl.m4 b/build/yajl.m4 new file mode 100644 index 0000000000..54e6e3eb5a --- /dev/null +++ b/build/yajl.m4 @@ -0,0 +1,176 @@ +dnl Check for YAJL Libraries +dnl CHECK_YAJL(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) + +AC_DEFUN([PROG_YAJL], [ + +# Needed if pkg-config will be used. +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + + +# Possible names for the yajl library/package (pkg-config) +YAJL_POSSIBLE_LIB_NAMES="yajl2 yajl" + +# Possible extensions for the library +YAJL_POSSIBLE_EXTENSIONS="so la sl dll dylib" + +# Possible paths (if pkg-config was not found, proceed with the file lookup) +YAJL_POSSIBLE_PATHS="/usr/lib /usr/local/lib /usr/local/libyajl /usr/local/yajl /usr/local /opt/libyajl /opt/yajl /opt /usr /usr/lib64" + +# Variables to be set by this very own script. +YAJL_VERSION="" +YAJL_CFLAGS="" +YAJL_CPPFLAGS="" +YAJL_LDADD="" +YAJL_LDFLAGS="" + +AC_ARG_WITH( + yajl, + AC_HELP_STRING( + [--with-yajl=PATH], + [Path to yajl prefix or config script] + ) +) + +if test "x${with_yajl}" == "xno"; then + AC_DEFINE(HAVE_YAJL, 0, [Support for YAJL was disabled by the utilization of --without-yajl or --with-yajl=no]) + AC_MSG_NOTICE([Support for YAJL was disabled by the utilization of --without-yajl or --with-yajl=no]) + YAJL_DISABLED=yes +else + if test "x${with_yajl}" == "xyes"; then + YAJL_MANDATORY=yes + AC_MSG_NOTICE([YAJL support was marked as mandatory by the utilization of --with-yajl=yes]) + fi +# for x in ${YAJL_POSSIBLE_LIB_NAMES}; do +# CHECK_FOR_YAJL_AT(${x}) +# if test -n "${YAJL_VERSION}"; then +# break +# fi +# done + +# if test "x${with_yajl}" != "xyes" or test "x${with_yajl}" == "xyes"; then + if test "x${with_yajl}" == "x" || test "x${with_yajl}" == "xyes"; then + # Nothing about YAJL was informed, using the pkg-config to figure things out. + if test -n "${PKG_CONFIG}"; then + YAJL_PKG_NAME="" + for x in ${YAJL_POSSIBLE_LIB_NAMES}; do + if ${PKG_CONFIG} --exists ${x}; then + YAJL_PKG_NAME="$x" + break + fi + done + fi + AC_MSG_NOTICE([Nothing about YAJL was informed during the configure phase. Trying to detect it on the platform...]) + if test -n "${YAJL_PKG_NAME}"; then + # Package was found using the pkg-config scripts + YAJL_VERSION="`${PKG_CONFIG} ${YAJL_PKG_NAME} --modversion`" + YAJL_CFLAGS="`${PKG_CONFIG} ${YAJL_PKG_NAME} --cflags`" + YAJL_LDADD="`${PKG_CONFIG} ${YAJL_PKG_NAME} --libs-only-l`" + YAJL_LDFLAGS="`${PKG_CONFIG} ${YAJL_PKG_NAME} --libs-only-L --libs-only-other`" + YAJL_DISPLAY="${YAJL_LDADD}, ${YAJL_CFLAGS}" + else + # If pkg-config did not find anything useful, go over file lookup. + for x in ${YAJL_POSSIBLE_LIB_NAMES}; do + CHECK_FOR_YAJL_AT(${x}) + if test -n "${YAJL_VERSION}"; then + break + fi + done + fi + fi + if test "x${with_yajl}" != "x"; then + # An specific path was informed, lets check. + YAJL_MANDATORY=yes + CHECK_FOR_YAJL_AT(${with_yajl}) + fi +# fi +fi + +if test -z "${YAJL_LDADD}"; then + if test -z "${YAJL_MANDATORY}"; then + if test -z "${YAJL_DISABLED}"; then + AC_MSG_NOTICE([YAJL library was not found]) + YAJL_FOUND=0 + else + YAJL_FOUND=2 + fi + else + AC_MSG_ERROR([YAJL was explicitly referenced but it was not found]) + YAJL_FOUND=-1 + fi +else + YAJL_FOUND=1 + AC_MSG_NOTICE([using YAJL v${YAJL_VERSION}]) + YAJL_CFLAGS="-DWITH_YAJL ${YAJL_CFLAGS}" + YAJL_DISPLAY="${YAJL_LDADD}, ${YAJL_CFLAGS}" + AC_SUBST(YAJL_VERSION) + AC_SUBST(YAJL_LDADD) + AC_SUBST(YAJL_LIBS) + AC_SUBST(YAJL_LDFLAGS) + AC_SUBST(YAJL_CFLAGS) + AC_SUBST(YAJL_DISPLAY) +fi + + + +AC_SUBST(YAJL_FOUND) + +]) # AC_DEFUN [PROG_YAJL] + + +AC_DEFUN([CHECK_FOR_YAJL_AT], [ + path=$1 + for y in ${YAJL_POSSIBLE_EXTENSIONS}; do + for z in ${YAJL_POSSIBLE_LIB_NAMES}; do + if test -e "${path}/${z}.${y}"; then + yajl_lib_path="${path}/" + yajl_lib_name="${z}" + yajl_lib_file="${yajl_lib_path}/${z}.${y}" + break + fi + if test -e "${path}/lib${z}.${y}"; then + yajl_lib_path="${path}/" + yajl_lib_name="${z}" + yajl_lib_file="${yajl_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/lib${z}.${y}"; then + yajl_lib_path="${path}/lib/" + yajl_lib_name="${z}" + yajl_lib_file="${yajl_lib_path}/lib${z}.${y}" + break + fi + if test -e "${path}/lib/x86_64-linux-gnu/lib${z}.${y}"; then + yajl_lib_path="${path}/lib/x86_64-linux-gnu/" + yajl_lib_name="${z}" + yajl_lib_file="${yajl_lib_path}/lib${z}.${y}" + break + fi + done + if test -n "$yajl_lib_path"; then + break + fi + done + if test -e "${path}/include/yajl_parse.h"; then + yajl_inc_path="${path}/include" + elif test -e "${path}/yajl_parse.h"; then + yajl_inc_path="${path}" + elif test -e "${path}/include/yajl/yajl_parse.h"; then + yajl_inc_path="${path}/include" + fi + + if test -n "${yajl_lib_path}"; then + AC_MSG_NOTICE([YAJL library found at: ${yajl_lib_file}]) + fi + + if test -n "${yajl_inc_path}"; then + AC_MSG_NOTICE([YAJL headers found at: ${yajl_inc_path}]) + fi + + if test -n "${yajl_lib_path}" -a -n "${yajl_inc_path}"; then + # TODO: Compile a piece of code to check the version. + YAJL_CFLAGS="-I${yajl_inc_path}" + YAJL_LDADD="-l${yajl_lib_name}" + YAJL_LDFLAGS="-L${yajl_lib_path}" + YAJL_DISPLAY="${yajl_lib_file}, ${yajl_inc_path}" + fi +]) # AC_DEFUN [CHECK_FOR_YAJL_AT] diff --git a/configure.ac b/configure.ac index 59b40cebe0..33afa4c610 100644 --- a/configure.ac +++ b/configure.ac @@ -1,733 +1,609 @@ -dnl -dnl Autoconf configuration for ModSecurity -dnl -dnl Use ./autogen.sh to produce a configure script -dnl +# ModSecurity configure.ac -AC_PREREQ(2.63) -AC_INIT([modsecurity], [2.7], [support@modsecurity.org]) +# Get the hash of the last commit, to be used if it is not an +# official release. +AC_DEFUN([MSC_GIT_HASH], m4_esyscmd_s(git log -1 --format="%h" --abbrev-commit)) +AC_DEFUN([MSC_MAJOR], m4_esyscmd_s(cat headers/modsecurity/modsecurity.h | grep "define MODSECURITY_MAJOR " | awk {'print $3'} | sed 's/\"//g')) +AC_DEFUN([MSC_MINOR], m4_esyscmd_s(cat headers/modsecurity/modsecurity.h | grep "define MODSECURITY_MINOR " | awk {'print $3'} | sed 's/\"//g')) +AC_DEFUN([MSC_PATCHLEVEL], m4_esyscmd_s(cat headers/modsecurity/modsecurity.h | grep "define MODSECURITY_PATCHLEVEL " | awk {'print $3'} | sed 's/\"//g')) +AC_DEFUN([MSC_TAG], m4_esyscmd_s(cat headers/modsecurity/modsecurity.h | grep "define MODSECURITY_FTAG " | awk {'print $3'} | sed 's/\"//g')) + +# Version definition to be further used by AC_INIT and +# .so file naming. +m4_define([msc_version_major], [MSC_MAJOR]) +m4_define([msc_version_minor], [MSC_MINOR]) +m4_define([msc_version_patchlevel], [MSC_PATCHLEVEL]) + +m4_define([msc_version_c_plus_a], [m4_eval(msc_version_major + msc_version_minor)]) + + +m4_define([msc_version], + [msc_version_major.msc_version_minor]) + +m4_define([msc_version_with_patchlevel], + [msc_version_major.msc_version_minor.msc_version_patchlevel]) + +m4_define([msc_version_git], + [m4_esyscmd_s(git describe)]) + +m4_define([msc_version_info], + [msc_version_c_plus_a:msc_version_patchlevel:msc_version_minor]) + + +# Project Information +AC_INIT([modsecurity], [3.0], [security@modsecurity.org]) + + +# General definitions AC_CONFIG_MACRO_DIR([build]) -AC_CONFIG_SRCDIR([LICENSE]) -AC_CONFIG_HEADERS([apache2/modsecurity_config_auto.h]) -AC_CONFIG_AUX_DIR([build]) AC_PREFIX_DEFAULT([/usr/local/modsecurity]) -AM_INIT_AUTOMAKE([-Wall foreign]) -m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) -LT_PREREQ([2.2]) -LT_INIT([dlopen]) +# General automake options. +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) + -# Checks for programs. -AC_PROG_AWK -AC_PROG_CC -AC_PROG_CPP -AC_PROG_INSTALL -AC_PROG_LN_S +# Check for dependencies (C++, AR, Lex, Yacc and Make) +AC_PROG_CXX +AM_PROG_AR AC_PROG_MAKE_SET -AC_PROG_GREP -AC_PATH_PROGS(PERL, [perl perl5], ) -AC_PATH_PROGS(ENV_CMD, [env printenv], ) + + +# Check if the compiler is c++11 compatible. +# AX_CXX_COMPILE_STDCXX_11(,mandatory) + +# Check for libinjection +if ! test -f "others/libinjection/src/libinjection_html5.c"; then +AC_MSG_ERROR([\ + + + libInjection was not found within ModSecurity source directory. + + libInjection code is available as part of ModSecurity source code in a format + of a git-submodule. git-submodule allow us to specify the correct version of + libInjection and still uses the libInjection repository to download it. + + You can download libInjection using git: + + $ git submodule init + $ git submodule update + + ]) +fi +# Libinjection version +AC_DEFUN([LIBINJECTION_VERSION], m4_esyscmd_s(cd "others/libinjection" && git describe && cd ../..)) + +# SecLang test version +AC_DEFUN([SECLANG_TEST_VERSION], m4_esyscmd_s(cd "test/test-cases/secrules-language-tests" && git log -1 --format="%h" --abbrev-commit && cd ../../..)) + + +# Check for yajl +PROG_YAJL + +AM_CONDITIONAL([YAJL_VERSION], [test "$YAJL_VERSION" != ""]) + +# Check for LibGeoIP +PROG_GEOIP +AM_CONDITIONAL([GEOIP_CFLAGS], [test "GEOIP_CFLAGS" != ""]) + +# Check for MaxMind +PROG_MAXMIND +AM_CONDITIONAL([MAXMIND_CFLAGS], [test "MAXMIND_CFLAGS" != ""]) + + +# Check for LMDB +PROG_LMDB +AM_CONDITIONAL([LMDB_CFLAGS], [test "LMDB_CFLAGS" != ""]) + +# Check for SSDEEP +CHECK_SSDEEP +AM_CONDITIONAL([SSDEEP_CFLAGS], [test "SSDEEP_CFLAGS" != ""]) + +# Check for LUA +CHECK_LUA +AM_CONDITIONAL([LUA_CFLAGS], [test "LUA_CFLAGS" != ""]) + + +# +# Check for curl +# +CHECK_CURL + +if ! test -z "${CURL_VERSION}"; then + AC_DEFINE([MSC_WITH_CURL], [1], [Define if libcurl is available]) +fi + + +# +# Check for LibXML +# +CHECK_LIBXML2 + + +# +# Check for libpcre +# +CHECK_PCRE + # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h limits.h stdlib.h string.h unistd.h sys/types.h sys/stat.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_C_INLINE -AC_C_RESTRICT -AC_TYPE_PID_T -AC_TYPE_SIZE_T -AC_STRUCT_TM -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 fchmod strcasestr]) - -# Some directories -MSC_BASE_DIR=`pwd` -MSC_PKGBASE_DIR="$MSC_BASE_DIR/.." -MSC_TEST_DIR="$MSC_BASE_DIR/tests" -MSC_REGRESSION_DIR="$MSC_TEST_DIR/regression" -MSC_REGRESSION_SERVERROOT_DIR="$MSC_REGRESSION_DIR/server_root" -MSC_REGRESSION_CONF_DIR="$MSC_REGRESSION_SERVERROOT_DIR/conf" -MSC_REGRESSION_LOGS_DIR="$MSC_REGRESSION_SERVERROOT_DIR/logs" -MSC_REGRESSION_DOCROOT_DIR="$MSC_REGRESSION_SERVERROOT_DIR/htdocs" - -AC_SUBST(MSC_BASE_DIR) -AC_SUBST(MSC_PKGBASE_DIR) -AC_SUBST(MSC_TEST_DIR) -AC_SUBST(MSC_REGRESSION_DIR) -AC_SUBST(MSC_REGRESSION_SERVERROOT_DIR) -AC_SUBST(MSC_REGRESSION_CONF_DIR) -AC_SUBST(MSC_REGRESSION_LOGS_DIR) -AC_SUBST(MSC_REGRESSION_DOCROOT_DIR) - -### Configure Options - -#OS type +AC_CHECK_HEADERS([string]) +AC_CHECK_HEADERS([iostream]) +AC_CHECK_HEADERS([sys/utsname.h]) -AC_CANONICAL_HOST -CANONICAL_HOST=$host -AH_TEMPLATE([AIX], [Define if the operating system is AIX]) -AH_TEMPLATE([LINUX], [Define if the operating system is LINUX]) -AH_TEMPLATE([OPENBSD], [Define if the operating system is OpenBSD]) -AH_TEMPLATE([SOLARIS], [Define if the operating system is SOLARIS]) -AH_TEMPLATE([HPUX], [Define if the operating system is HPUX]) -AH_TEMPLATE([MACOSX], [Define if the operating system is Macintosh OSX]) -AH_TEMPLATE([FREEBSD], [Define if the operating system is FREEBSD]) -AH_TEMPLATE([NETBSD], [Define if the operating system is NetBSD]) +# ?? +LT_INIT([dlopen]) +# Identify platform +AC_CANONICAL_HOST case $host in *-*-aix*) - echo "Checking plataform... Identified as AIX" - aixos=true + echo "Checking platform... Identified as AIX" + AC_DEFINE([AIX], [1], [Define if the operating system is AIX]) + PLATFORM="AIX" ;; *-*-hpux*) - echo "Checking plataform... Identified as HPUX" - hpuxos=true + echo "Checking platform... Identified as HPUX" + AC_DEFINE([HPUX], [1], [Define if the operating system is HPUX]) + PLATFORM="HPUX" ;; *-*-darwin*) - echo "Checking plataform... Identified as Macintosh OS X" - macos=true + echo "Checking platform... Identified as Macintosh OS X" + AC_DEFINE([MACOSX], [1], [Define if the operating system is Macintosh OSX]) + PLATFORM="MacOSX" ;; - *-*-linux*) - echo "Checking plataform... Identified as Linux" - linuxos=true - case "${host_cpu}" in - s390x) - cpu_type="-DLINUX_S390" - ;; - esac + *-*-linux* | *-*uclinux*) + echo "Checking platform... Identified as Linux" + AC_DEFINE([LINUX], [1], [Define if the operating system is LINUX]) + PLATFORM="Linux" ;; *-*-solaris*) - echo "Checking plataform... Identified as Solaris" - solarisos=true + echo "Checking platform... Identified as Solaris" + AC_DEFINE([SOLARIS], [1], [Define if the operating system is SOLARIS]) + PLATFORM="Solaris" ;; *-*-freebsd*) - echo "Checking plataform... Identified as FreeBSD" - freebsdos=true + echo "Checking platform... Identified as FreeBSD" + AC_DEFINE([FREEBSD], [1], [Define if the operating system is FREEBSD]) + PLATFORM="FreeBSD" ;; *-*-netbsd*) - echo "Checking plataform... Identified as NetBSD" - netbsdos=true + echo "Checking platform... Identified as NetBSD" + AC_DEFINE([NETBSD], [1], [Define if the operating system is NETBSD]) + PLATFORM="NetBSD" ;; *-*-openbsd*) - echo "Checking plataform... Identified as OpenBSD" - openbsdos=true + echo "Checking platform... Identified as OpenBSD" + AC_DEFINE([OPENBSD], [1], [Define if the operating system is OPENBSD]) + PLATFORM="OpenBSD" ;; *-*-kfreebsd*) - echo "Checking plataform... Identified as kFreeBSD, treating as linux" - linuxos=true + echo "Checking platform... Identified as kFreeBSD, treating as linux" + AC_DEFINE([FREEBSD], [1], [Define if the operating system is FREEBSD]) + PLATFORM="kFreeBSD" ;; *-*-gnu*.*) - echo "Checking plataform... Identified as HURD, treating as linux" - linuxos=true + echo "Checking platform... Identified as HURD, treating as linux" + AC_DEFINE([LINUX], [1], [Define if the operating system is LINUX]) + PLATFORM="HURD" ;; *) echo "Unknown CANONICAL_HOST $host" - exit + exit 1 ;; esac -AM_CONDITIONAL([AIX], [test x$aixos = xtrue]) -AM_CONDITIONAL([HPUX], [test x$hpuxos = xtrue]) -AM_CONDITIONAL([MACOSX], [test x$macos = xtrue]) -AM_CONDITIONAL([LINUX], [test x$linuxos = xtrue]) -AM_CONDITIONAL([LINUX390], [test x$linuxos390 = xtrue]) -AM_CONDITIONAL([SOLARIS], [test x$solarisos = xtrue]) -AM_CONDITIONAL([FREEBSD], [test x$freebsdos = xtrue]) -AM_CONDITIONAL([OPENBSD], [test x$openbsdos = xtrue]) -AM_CONDITIONAL([NETBSD], [test x$netbsdos = xtrue]) - -#Subdirs -TOPLEVEL_SUBDIRS="tools" - -# Apache2 Module -AC_ARG_ENABLE(apache2-module, - AS_HELP_STRING([--disable-apache2-module], - [Disable building Apache2 module.]), -[ - if test "$enableval" != "no"; then - build_apache2_module=1 - else - build_apache2_module=0 - fi -], -[ - build_apache2_module=1 -]) -AM_CONDITIONAL([BUILD_APACHE2_MODULE], [test "$build_apache2_module" -eq 1]) -if test "$build_apache2_module" -eq 1; then - TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS apache2" -fi - - -# Standalone Module -AC_ARG_ENABLE(standalone-module, - AS_HELP_STRING([--enable-standalone-module], - [Enable building standalone module.]), -[ - if test "$enableval" != "no"; then - build_standalone_module=1 - else - build_standalone_module=0 - fi -], -[ - build_standalone_module=0 -]) -AM_CONDITIONAL([BUILD_STANDALONE_MODULE], [test "$build_standalone_module" -eq 1]) -if test "$build_standalone_module" -eq 1; then - TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS standalone" -fi - - -# Extensions -AC_ARG_ENABLE(extentions, - AS_HELP_STRING([--enable-extentions], - [Enable building extension.]), -[ - if test "$enableval" != "no"; then - build_extentions=1 - else - build_extentions=0 - fi -], -[ - build_extentions=0 -]) -AM_CONDITIONAL([BUILD_extentions], [test "$build_extentions" -eq 1]) -if test "$build_extentions" -eq 1; then - TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS ext" -fi - - -# Mlogc -AC_ARG_ENABLE(mlogc, - AS_HELP_STRING([--disable-mlogc], - [Disable building mlogc.]), -[ - if test "$enableval" != "no"; then - build_mlogc=1 - else - build_mlogc=0 - fi -], -[ - build_mlogc=1 -]) -AM_CONDITIONAL([BUILD_MLOGC], [test "$build_mlogc" -eq 1]) -if test "$build_mlogc" -eq 1; then - TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS mlogc" -fi - -# Audit Log Parser v2 (ALP2) -AC_ARG_ENABLE(alp2, - AS_HELP_STRING([--enable-alp2], - [Enable building audit log parser lib.]), -[ - if test "$enableval" != "no"; then - build_alp2=1 - else - build_alp2=0 - fi -], -[ - build_alp2=0 -]) -AM_CONDITIONAL([BUILD_ALP2], [test "$build_alp2" -eq 1]) -if test "$build_alp2" -eq 1; then - TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS alp2" -fi - -# Documentation -AC_ARG_ENABLE(docs, - AS_HELP_STRING([--enable-docs], - [Enable building documentation.]), -[ - if test "$enableval" != "no"; then - build_docs=1 - else - build_docs=0 - fi -], -[ - build_docs=0 -]) -AM_CONDITIONAL([BUILD_DOCS], [test "$build_docs" -eq 1]) -if test "$build_docs" -eq 1; then - TOPLEVEL_SUBDIRS="$TOPLEVEL_SUBDIRS docs" -fi - - -# 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' - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $pcre_study" - else - pcre_study='' - fi -], -[ - pcre_study='-DWITH_PCRE_STUDY' -]) - -# Add PCRE JIT - -AC_ARG_ENABLE(pcre-jit, - AS_HELP_STRING([--enable-pcre-jit], - [Enable PCRE regex jit support during configure.]), -[ - if test "$enableval" != "no"; then - pcre_jit='-DWITH_PCRE_JIT' - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $pcre_jit" - else - pcre_jit='' - fi -], -[ - pcre_jit='' -]) - - -# 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" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $pcre_match_limit" - 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" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $pcre_match_limit_recursion" - fi -], -[ - pcre_match_limit_recursion='-DMODSEC_PCRE_MATCH_LIMIT_RECURSION=1500' -]) - -# Enable Lua per transaction cache -AC_ARG_ENABLE(lua-cache, - AS_HELP_STRING([--enable-lua-cache], - [Enable Lua per transaction cache.]), -[ - if test "$enableval" != "no"; then - lua_cache="-DCACHE_LUA" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $lua_cache" - else - lua_cache= - fi -], -[ - lua_cache= -]) - -# Enable phase-1 in post_read_request -AC_ARG_ENABLE(htaccess-config, - AS_HELP_STRING([--enable-htaccess-config], - [Enable some mod_security directives into htaccess files.]), -[ - if test "$enableval" != "no"; then - htaccess_config="-DHTACCESS_CONFIG" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $htaccess_config" - else - htaccess_config= - fi -], -[ - htaccess_config= -]) - -# Enable phase-1 in post_read_request -AC_ARG_ENABLE(request-early, - AS_HELP_STRING([--enable-request-early], - [Place phase1 into post_read_request hook. default is hook_request_early]), -[ - if test "$enableval" != "no"; then - request_early="-DREQUEST_EARLY" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $request_early" - else - request_early= - fi -], -[ - request_early='-DREQUEST_EARLY' -]) - -# Ignore configure errors -AC_ARG_ENABLE(errors, - AS_HELP_STRING([--disable-errors], - [Disable errors during configure.]), -[ - if test "$enableval" != "no"; then - report_errors=1 - else - report_errors=0 - fi -], -[ - report_errors=1 -]) - -# Verbose output -AC_ARG_ENABLE(verbose-output, - AS_HELP_STRING([--enable-verbose-output], - [Enable more verbose configure output.]), -[ - if test "$enableval" != "no"; then - verbose_output=1 - else - verbose_output=0 - fi -], -[ - verbose_output=0 -]) - -# Strict Compile -AC_ARG_ENABLE(strict-compile, - AS_HELP_STRING([--enable-strict-compile], - [Enable strict compilation (warnings are errors).]), -[ - if test "$enableval" != "no"; then - strict_compile="-std=c99 -Wstrict-overflow=1 -Wextra -Wno-missing-field-initializers -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wno-unused-parameter -Wformat -Wformat-security -Werror -fstack-protector -D_FORTIFY_SOURCE=2" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $strict_compile" - else - strict_compile= - fi -], -[ - strict_compile= -]) - -# DEBUG_CONF -AC_ARG_ENABLE(debug-conf, - AS_HELP_STRING([--enable-debug-conf], - [Enable debug during configuration.]), -[ - if test "$enableval" != "no"; then - debug_conf="-DDEBUG_CONF" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $debug_conf" - else - debug_conf= - fi -], -[ - debug_conf= -]) - -# CACHE_DEBUG -AC_ARG_ENABLE(debug-cache, - AS_HELP_STRING([--enable-debug-cache], - [Enable debug for transformation caching.]), -[ - if test "$enableval" != "no"; then - debug_cache="-DCACHE_DEBUG" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $debug_cache" - else - debug_cache= - fi -], -[ - debug_cache= -]) - -# DEBUG_ACMP -AC_ARG_ENABLE(debug-acmp, - AS_HELP_STRING([--enable-debug-acmp], - [Enable debugging acmp code.]), -[ - if test "$enableval" != "no"; then - debug_acmp="-DDEBUG_ACMP" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $debug_acmp" - else - debug_acmp= - fi -], -[ - debug_acmp= -]) - -# DEBUG_MEM -AC_ARG_ENABLE(debug-mem, - AS_HELP_STRING([--enable-debug-mem], - [Enable debug during configuration.]), -[ - if test "$enableval" != "no"; then - debug_mem="-DDEBUG_MEM" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $debug_mem" - else - debug_mem= - fi -], -[ - debug_mem= -]) - -# PERFORMANCE_MEASUREMENT -AC_ARG_ENABLE(performance-measurement, - AS_HELP_STRING([--enable-performance-measurement], - [Enable performance-measurement stats.]), -[ - if test "$enableval" != "no"; then - perf_meas="-DPERFORMANCE_MEASUREMENT" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $perf_meas" - else - perf_meas= - fi -], -[ - perf_meas= -]) - -# NO_MODSEC_API -AC_ARG_ENABLE(modsec-api, - AS_HELP_STRING([--disable-modsec-api], - [Disable the API; compiling against some older Apache versions require this.]), -[ - if test "$enableval" != "yes"; then - modsec_api="-DNO_MODSEC_API" - MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $modsec_api" - else - modsec_api= - fi -], -[ - modsec_api= -]) - -# Find apxs -AC_MSG_NOTICE(looking for Apache module support via DSO through APXS) -AC_ARG_WITH(apxs, - [AS_HELP_STRING([[--with-apxs=FILE]], - [FILE is the path to apxs; defaults to "apxs".])], -[ - if test "$withval" = "yes"; then - APXS=apxs - else - APXS="$withval" - fi -]) - -if test -z "$APXS"; then - for i in /usr/local/apache22/bin \ - /usr/local/apache2/bin \ - /usr/local/apache/bin \ - /usr/local/sbin \ - /usr/local/bin \ - /usr/sbin \ - /usr/bin; - do - if test -f "$i/apxs2"; then - APXS="$i/apxs2" - break - elif test -f "$i/apxs"; then - APXS="$i/apxs" - break +# Variables to be used inside the Makefile.am files. +MSC_BASE_DIR=`pwd` +AC_SUBST([MSC_BASE_DIR]) + +MSC_VERSION_INFO=msc_version_info +AC_SUBST([MSC_VERSION_INFO]) + +MSC_VERSION_WITH_PATCHLEVEL=msc_version_with_patchlevel +AC_SUBST([MSC_VERSION_WITH_PATCHLEVEL]) + +MSC_VERSION=msc_version +AC_SUBST([MSC_VERSION]) + +MSC_GIT_VERSION=msc_version_git +AC_SUBST([MSC_GIT_VERSION]) + + +AC_ARG_ENABLE(debug-logs, + [AC_HELP_STRING([--disable-debug-logs],[Turn off the SecDebugLog feature])], + + [case "${enableval}" in + yes) debugLogs=true ;; + no) debugLogs=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug-logs) ;; + esac], + + [debugLogs=true] + ) +if test "$debugLogs" != "true"; then + MODSEC_NO_LOGS="-DNO_LOGS=1" + AC_SUBST(MODSEC_NO_LOGS) +fi + + +# Fuzzer +AC_ARG_ENABLE(afl-fuzz, + [AC_HELP_STRING([--enable-afl-fuzz],[Turn on the afl fuzzer compilation utilities])], + + [case "${enableval}" in + yes) aflFuzzer=true ;; + no) aflFuzzer=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-afl-fuzz) ;; + esac], + + [aflFuzzer=false] + ) + +# Examples +AC_ARG_ENABLE(examples, + [AC_HELP_STRING([--enable-examples],[Turn on the examples compilation (default option)])], + + [case "${enableval}" in + yes) buildExamples=true ;; + no) buildExamples=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-examples) ;; + esac], + + [buildExamples=true] + ) + +# Parser +AC_ARG_ENABLE(parser-generation, + [AC_HELP_STRING([--enable-parser-generation],[Enables parser generation during the build])], + + [case "${enableval}" in + yes) buildParser=true ;; + no) buildParser=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-parser-generation) ;; + esac], + + [buildParser=false] + ) + +# Mutex +AC_ARG_ENABLE(mutex-on-pm, + [AC_HELP_STRING([--enable-mutex-on-pm],[Treats pm operations as a critical section])], + + [case "${enableval}" in + yes) mutexPm=true ;; + no) mutexPm=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-mutex-on-pm) ;; + esac], + + [mutexPm=false] + ) +if test "$mutexPm" == "true"; then + MODSEC_MUTEX_ON_PM="-DMUTEX_ON_PM=1" + AC_SUBST(MODSEC_MUTEX_ON_PM) +fi + + +if test $buildParser = true; then + AC_PROG_YACC + AC_PROG_LEX + AC_PATH_PROG([FLEX], [flex]) + test "x$FLEX" = "x" && AC_MSG_ERROR([flex is needed to build ModSecurity]) + + AC_PATH_PROG([BISON], [bison]) + test "x$BISON" = "x" && AC_MSG_ERROR([bison is needed to build ModSecurity]) + + AC_PATH_PROG([YACC_INST], $YACC) + if test ! -f "$srcdir/gram.c"; then + if test -z "$YACC_INST"; then + AC_MSG_ERROR([yacc not found - unable to compile ModSecurity]) + fi fi - done -fi - -# arbitrarily picking the same version subversion looks for, don't know how -# accurate this really is, but at least it'll force us to have apache2... -HTTPD_WANTED_MMN=20020903 - -if test -n "$APXS" -a "$APXS" != "no" -a -x "$APXS" ; then - APXS_INCLUDE="`$APXS -q INCLUDEDIR`" - if test -r $APXS_INCLUDE/httpd.h; then - AC_MSG_NOTICE(found apxs at $APXS) - AC_MSG_NOTICE(checking httpd version) - AC_EGREP_CPP(VERSION_OK, - [ -#include "$APXS_INCLUDE/ap_mmn.h" -#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) -VERSION_OK -#endif], - [AC_MSG_NOTICE(httpd is recent enough)], - [ - if test "$report_errors" -eq 1; then - AC_MSG_ERROR(apache is too old, mmn must be at least $HTTPD_WANTED_MMN) - else - AC_MSG_NOTICE(apache is too old, mmn must be at least $HTTPD_WANTED_MMN) - fi +fi + + +# Decide if we want to build the tests or not. +buildTestUtilities=false +if test "x$YAJL_FOUND" = "x1"; then + # Regression tests will not be able to run without the logging support. + # But we still have the unit tests. + # if test "$debugLogs" = "true"; then + buildTestUtilities=true + # fi +fi + + +AM_CONDITIONAL([TEST_UTILITIES], [test $buildTestUtilities = true]) +if test $buildTestUtilities = true; then + if test $debugLogs = true; then + if test -f ./test/test-list.sh; then + TEST_CASES=`./test/test-list.sh` + fi + fi +fi + +AM_CONDITIONAL([EXAMPLES], [test $buildExamples = true]) +AM_CONDITIONAL([BUILD_PARSER], [test $buildParser = true]) +AM_CONDITIONAL([USE_MUTEX_ON_PM], [test $mutexPm = true]) + + +# General link options +if test "$PLATFORM" != "MacOSX" -a "$PLATFORM" != "OpenBSD"; then + GLOBAL_LDADD="-lrt " +fi + +if test "$aflFuzzer" == "true"; then + FUZZ_CPPCFLAGS="-fsanitize=address -fsanitize-coverage=4 " + GLOBAL_LDADD="$GLOBAL_LDADD -fsanitize=address " + GLOBAL_CPPFLAGS="$GLOBAL_CPPFLAGS $FUZZ_CPPCFLAGS" + $buildExamples = false +fi +AC_SUBST(GLOBAL_LDADD) +AC_SUBST(GLOBAL_CPPFLAGS) + +AM_CONDITIONAL([AFL_FUZZER], [test $aflFuzzer = true]) + +GLOBAL_CFLAGS="" +AC_SUBST(GLOBAL_CFLAGS) + +# Files to be generated via autotools. +AC_CONFIG_FILES([\ + modsecurity.pc \ + Makefile \ + doc/Makefile \ + src/Makefile \ + others/Makefile \ + tools/Makefile \ + tools/rules-check/Makefile ]) - fi - APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs INCLUDEDIR: $APXS_INCLUDEDIR); fi - # Make sure the include dir is used - if test -n "$APXS_INCLUDEDIR"; then - APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" - else - APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" - fi - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs INCLUDES: $APXS_INCLUDES); fi - APXS_CFLAGS=-I`$APXS -q INCLUDEDIR` - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs CFLAGS: $APXS_CFLAGS); fi - APXS_LDFLAGS= - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LDFLAGS: $APXS_LDFLAGS); fi - APXS_LIBDIR="`$APXS -q LIBDIR`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBDIR: $APXS_LIBDIR); fi - # Make sure the lib dir is used - if test -n "$APXS_LIBDIR"; then - APXS_LIBS="-L${APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" - else - APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" - fi - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBS: $APXS_LIBS); fi - APXS_LIBTOOL="`$APXS -q LIBTOOL`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBTOOL: $APXS_LIBTOOL); fi - APXS_CC="`$APXS -q CC`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs CC: $APXS_CC); fi - APXS_BINDIR="`$APXS -q BINDIR`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs BINDIR: $APXS_BINDIR); fi - APXS_SBINDIR="`$APXS -q SBINDIR`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs SBINDIR: $APXS_SBINDIR); fi - APXS_PROGNAME="`$APXS -q PROGNAME`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs PROGNAME: $APXS_PROGNAME); fi - APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBEXECDIR: $APXS_LIBEXECDIR); fi - APXS_MODULES=$APXS_LIBEXECDIR - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs MODULES: $APXS_MODULES); fi - if test "$APXS_SBINDIR" = "/"; then - APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" - else - APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" - fi - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs HTTPD: $APXS_HTTPD); fi + +AM_COND_IF([TEST_UTILITIES], + [AC_CONFIG_FILES([test/Makefile test/benchmark/Makefile])]) + +AM_COND_IF([EXAMPLES], + [AC_CONFIG_FILES([ \ + examples/Makefile \ + examples/simple_example_using_c/Makefile \ + examples/multiprocess_c/Makefile \ + examples/reading_logs_with_offset/Makefile \ + examples/reading_logs_via_rule_message/Makefile \ + examples/using_bodies_in_chunks/Makefile \ + ])]) + +AM_COND_IF([AFL_FUZZER], + [AC_CONFIG_FILES([test/fuzzer/Makefile])]) + +AM_COND_IF([BUILD_PARSER], + [AC_CONFIG_FILES([src/parser/Makefile])]) + + +AC_CONFIG_HEADERS([src/config.h]) + + +# Doxygen support +DX_HTML_FEATURE(ON) +DX_CHM_FEATURE(OFF) +DX_CHI_FEATURE(OFF) +DX_MAN_FEATURE(OFF) +DX_RTF_FEATURE(OFF) +DX_XML_FEATURE(OFF) +DX_PDF_FEATURE(OFF) +DX_PS_FEATURE(OFF) + +DX_INIT_DOXYGEN([ModSecurity],[doc/doxygen.cfg]) + +# make check-valgrind +AX_VALGRIND_DFLT([sgcheck], [off]) +AX_VALGRIND_CHECK + +# Generate the files. +AC_OUTPUT + + +# Print a fancy summary +echo " " +echo " " +echo "ModSecurity - ${MSC_GIT_VERSION} for $PLATFORM" +echo " " +echo " Mandatory dependencies" +echo -n " + libInjection ...." +echo LIBINJECTION_VERSION +echo -n " + SecLang tests ...." +echo SECLANG_TEST_VERSION + +echo " " +echo " Optional dependencies" + + + +## GeoIP - MaxMind +if test "x$GEOIP_FOUND" = "x0" && test "x$MAXMIND_FOUND" = "x0"; then + echo " + GeoIP/MaxMind ....not found" +fi +if test "x$GEOIP_FOUND" = "x1" || test "x$MAXMIND_FOUND" = "x1"; then + echo -n " + GeoIP/MaxMind ....found " + echo "" + if test "x$MAXMIND_FOUND" = "x1"; then + echo " * (MaxMind) v${MAXMIND_VERSION}" + echo " ${MAXMIND_DISPLAY}" + fi + if test "x$GEOIP_FOUND" = "x1"; then + echo " * (GeoIP) v${GEOIP_VERSION}" + echo " ${GEOIP_DISPLAY}" + fi +fi +if test "x$GEOIP_FOUND" = "x2" && test "x$MAXMIND_FOUND" = "x2"; then + echo " + GeoIP/MaxMind ....disabled" +fi + + +## LibCurl +if test "x$CURL_FOUND" = "x0"; then + echo " + LibCURL ....not found" +fi +if test "x$CURL_FOUND" = "x1"; then + echo -n " + LibCURL ....found " + if ! test "x$CURL_VERSION" = "x"; then + echo "v${CURL_VERSION}" + else + echo "" + fi + echo " ${CURL_DISPLAY}" +fi +if test "x$CURL_FOUND" = "x2"; then + echo " + LibCURL ....disabled" +fi + + +## YAJL +if test "x$YAJL_FOUND" = "x0"; then + echo " + YAJL ....not found" +fi +if test "x$YAJL_FOUND" = "x1"; then + echo -n " + YAJL ....found " + if ! test "x$YAJL_VERSION" = "x"; then + echo "v${YAJL_VERSION}" + else + echo "" + fi + echo " ${YAJL_DISPLAY}" +fi +if test "x$YAJL_FOUND" = "x2"; then + echo " + YAJL ....disabled" +fi + + +## LMDB +if test "x$LMDB_FOUND" = "x0"; then + echo " + LMDB ....not found" +fi +if test "x$LMDB_FOUND" = "x1"; then + echo -n " + LMDB ....found " + if ! test "x$LMDB_VERSION" = "x"; then + echo "v${LMDB_VERSION}" + else + echo "" + fi + echo " ${LMDB_DISPLAY}" +fi +if test "x$LMDB_FOUND" = "x2"; then + echo " + LMDB ....disabled" +fi + + +## libxml2 +if test "x$LIBXML2_FOUND" = "x0"; then + echo " + LibXML2 ....not found" +fi +if test "x$LIBXML2_FOUND" = "x1"; then + echo -n " + LibXML2 ....found " + if ! test "x$LIBXML2_VERSION" = "x"; then + echo "v${LIBXML2_VERSION}" + else + echo "" + fi + echo " ${LIBXML2_DISPLAY}" +fi +if test "x$LIBXML2_FOUND" = "x2"; then + echo " + LibXML2 ....disabled" +fi + + +## SSDEEP +if test "x$SSDEEP_FOUND" = "x0"; then + echo " + SSDEEP ....not found" +fi +if test "x$SSDEEP_FOUND" = "x1"; then + echo -n " + SSDEEP ....found " + if ! test "x$SSDEEP_VERSION" = "x"; then + echo "v${SSDEEP_VERSION}" + else + echo "" + fi + echo " ${SSDEEP_DISPLAY}" +fi +if test "x$SSDEEP_FOUND" = "x2"; then + echo " + SSDEEP ....disabled" +fi + +## LUA +if test "x$LUA_FOUND" = "x0"; then + echo " + LUA ....not found" +fi +if test "x$LUA_FOUND" = "x1"; then + echo -n " + LUA ....found " + if ! test "x$LUA_VERSION" = "x"; then + echo "v${LUA_VERSION}" + else + echo "" + fi + echo " ${LUA_DISPLAY}" +fi +if test "x$LUA_FOUND" = "x2"; then + echo " + LUA ....disabled" +fi + + +echo " " +echo " Other Options" +if test $buildTestUtilities = true; then + if test $debugLogs = true; then + echo " + Test Utilities ....enabled" + else + echo " + Test Utilities ....partially" + fi +else + echo " + Test Utilities ....disabled" +fi +if test $debugLogs = true; then + echo " + SecDebugLog ....enabled" else - if test "$report_errors" -eq 1; then - AC_MSG_ERROR(couldn't find APXS) - else - AC_MSG_NOTICE(couldn't find APXS) - fi + echo " + SecDebugLog ....disabled" fi -### Build *EXTRA_CFLAGS vars +if test "$aflFuzzer" = "true"; then + echo " + afl fuzzer ....enabled" + echo " ($FUZZ_CPPCFLAGS)" +else + echo " + afl fuzzer ....disabled" +fi -# Allow overriding EXTRA_CFLAGS -if $ENV_CMD | $GREP "^EXTRA_CFLAGS" > /dev/null 2>&1; then - if test -z "$debug_mem"; then - EXTRA_CFLAGS="$EXTRA_CFLAGS $strict_compile" - fi +if test "$buildExamples" = "true"; then + echo " + library examples ....enabled" else - if test -n "$debug_mem"; then - EXTRA_CFLAGS="-O0 -g -Wall" - else - EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" - fi -fi - -MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $pcre_jit $request_early $htaccess_config $lua_cache $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api $cpu_type" - -APXS_WRAPPER=build/apxs-wrapper -APXS_EXTRA_CFLAGS="" -for f in $EXTRA_CFLAGS; do - APXS_EXTRA_CFLAGS="$APXS_EXTRA_CFLAGS -Wc,$f" -done; -MODSEC_APXS_EXTRA_CFLAGS="" -for f in $MODSEC_EXTRA_CFLAGS; do - MODSEC_APXS_EXTRA_CFLAGS="$MODSEC_APXS_EXTRA_CFLAGS -Wc,$f" -done; - -### Substitute the vars - -AC_SUBST(TOPLEVEL_SUBDIRS) -AC_SUBST(EXTRA_CFLAGS) -AC_SUBST(MODSEC_EXTRA_CFLAGS) -AC_SUBST(APXS) -AC_SUBST(APXS_WRAPPER) -AC_SUBST(APXS_INCLUDEDIR) -AC_SUBST(APXS_INCLUDES) -AC_SUBST(APXS_EXTRA_CFLAGS) -AC_SUBST(MODSEC_APXS_EXTRA_CFLAGS) -AC_SUBST(APXS_LDFLAGS) -AC_SUBST(APXS_LIBS) -AC_SUBST(APXS_CFLAGS) -AC_SUBST(APXS_LIBTOOL) -AC_SUBST(APXS_CC) -AC_SUBST(APXS_LIBDIR) -AC_SUBST(APXS_BINDIR) -AC_SUBST(APXS_SBINDIR) -AC_SUBST(APXS_PROGNAME) -AC_SUBST(APXS_LIBEXECDIR) -AC_SUBST(APXS_MODULES) -AC_SUBST(APXS_HTTPD) - -CHECK_PCRE() -if test "$build_apache2_module" -ne 0 -o "$build_mlogc" -ne 0; then -CHECK_APR() -CHECK_APU() -fi -CHECK_LIBXML2() -CHECK_LUA() -if test "$build_mlogc" -ne 0; then -CHECK_CURL() -fi - -AC_CONFIG_FILES([Makefile]) -AC_CONFIG_FILES([tools/Makefile]) -if test "$build_alp2" -ne 0; then -AC_CONFIG_FILES([alp2/Makefile]) -fi -if test "$build_apache2_module" -ne 0; then -AC_CONFIG_FILES([apache2/Makefile]) -fi -if test "$build_standalone_module" -ne 0; then -AC_CONFIG_FILES([standalone/Makefile]) -fi -if test "$build_extentions" -ne 0; then -AC_CONFIG_FILES([ext/Makefile]) -fi -AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper]) -if test -e "$PERL"; then - if test "$build_mlogc" -ne 0; then - AC_CONFIG_FILES([mlogc/mlogc-batch-load.pl], [chmod +x mlogc/mlogc-batch-load.pl]) - fi - AC_CONFIG_FILES([tests/run-unit-tests.pl], [chmod +x tests/run-unit-tests.pl]) - AC_CONFIG_FILES([tests/run-regression-tests.pl], [chmod +x tests/run-regression-tests.pl]) - AC_CONFIG_FILES([tests/gen_rx-pm.pl], [chmod +x tests/gen_rx-pm.pl]) - AC_CONFIG_FILES([tests/csv_rx-pm.pl], [chmod +x tests/csv_rx-pm.pl]) - AC_CONFIG_FILES([tests/regression/server_root/conf/httpd.conf]) + echo " + library examples ....disabled" +fi - # Perl based tools - AC_CONFIG_FILES([tools/rules-updater.pl], [chmod +x tools/rules-updater.pl]) +if test "$buildParser" = "true"; then + echo " + Building parser ....enabled" +else + echo " + Building parser ....disabled" fi -if test "$build_mlogc" -ne 0; then - AC_CONFIG_FILES([mlogc/Makefile]) + +if test "$mutexPm" = "true"; then + echo " + Treating pm operations as critical section ....enabled" +else + echo " + Treating pm operations as critical section ....disabled" +fi + + +echo " " + + +if test "$aflFuzzer" = "true"; then + echo "WARNING: afl fuzzer was enabled. Make sure you are using the" + echo " 'afl-clang-fast' as the compiler, otherwise the compilation" + echo " will fail." + echo " " + echo " You can set the compiler using:" + echo " " + echo " $ export CXX=afl-clang-fast++ " + echo " $ export CC=afl-clang-fast " + echo " " fi -AC_CONFIG_FILES([tests/Makefile]) -AC_OUTPUT diff --git a/tests/regression/server_root/data/.empty b/doc/.empty similarity index 100% rename from tests/regression/server_root/data/.empty rename to doc/.empty diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000000..dfada090ff --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,17 @@ + +ACLOCAL_AMFLAGS = -I build + +# Doxygen support +# include $(top_srcdir)/build/ax_prog_doxygen.m4 + +# distribution of the Doxygen configuration file +EXTRA_DIST = \ + doxygen.cfg + + +MAINTAINERCLEANFILES = \ + Makefile.in \ + doxygen_sqlite3.db \ + html \ + latex + diff --git a/doc/README.txt b/doc/README.txt deleted file mode 100644 index ec0ed3572d..0000000000 --- a/doc/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -Please access the ModSecurity Github space to access the below documentation. - - * ModSecurity 2 Data Formats - * ModSecurity Frequently Asked Questions (FAQ) - * ModSecurity Migration Matrix - * ModSecurity Rules Language Porting Specification - * ModSecurity Wiki - * Reference Manual - * RoadMap - -https://github.com/SpiderLabs/ModSecurity/wiki/ diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg new file mode 100644 index 0000000000..44326e01da --- /dev/null +++ b/doc/doxygen.cfg @@ -0,0 +1,2403 @@ +# Doxyfile 1.8.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = ModSecurty + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 3.0.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs. It has a robust event-based programming language which provides protection from a range of attacks against web applications and allows for HTTP traffic monitoring, logging and real-time analys…" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = doc/ms-doxygen-logo.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ../headers/modsecurity/modsecurity.h ../src ../headers + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.as \ + *.js + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /